summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImage
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage')
-rw-r--r--plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp1210
-rw-r--r--plugins/AdvaImg/src/FreeImage/CacheFile.cpp271
-rw-r--r--plugins/AdvaImg/src/FreeImage/ColorLookup.cpp782
-rw-r--r--plugins/AdvaImg/src/FreeImage/Conversion.cpp513
-rw-r--r--plugins/AdvaImg/src/FreeImage/Conversion16_555.cpp209
-rw-r--r--plugins/AdvaImg/src/FreeImage/Conversion16_565.cpp204
-rw-r--r--plugins/AdvaImg/src/FreeImage/Conversion24.cpp214
-rw-r--r--plugins/AdvaImg/src/FreeImage/Conversion32.cpp345
-rw-r--r--plugins/AdvaImg/src/FreeImage/Conversion4.cpp246
-rw-r--r--plugins/AdvaImg/src/FreeImage/Conversion8.cpp327
-rw-r--r--plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp192
-rw-r--r--plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp144
-rw-r--r--plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp241
-rw-r--r--plugins/AdvaImg/src/FreeImage/ConversionType.cpp689
-rw-r--r--plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp134
-rw-r--r--plugins/AdvaImg/src/FreeImage/FreeImage.cpp226
-rw-r--r--plugins/AdvaImg/src/FreeImage/FreeImageC.c22
-rw-r--r--plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp168
-rw-r--r--plugins/AdvaImg/src/FreeImage/GetType.cpp92
-rw-r--r--plugins/AdvaImg/src/FreeImage/Halftoning.cpp474
-rw-r--r--plugins/AdvaImg/src/FreeImage/J2KHelper.cpp500
-rw-r--r--plugins/AdvaImg/src/FreeImage/MemoryIO.cpp237
-rw-r--r--plugins/AdvaImg/src/FreeImage/MultiPage.cpp990
-rw-r--r--plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp507
-rw-r--r--plugins/AdvaImg/src/FreeImage/PSDParser.cpp1053
-rw-r--r--plugins/AdvaImg/src/FreeImage/PSDParser.h271
-rw-r--r--plugins/AdvaImg/src/FreeImage/PixelAccess.cpp209
-rw-r--r--plugins/AdvaImg/src/FreeImage/Plugin.cpp747
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginBMP.cpp1480
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginCUT.cpp240
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginDDS.cpp651
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginEXR.cpp764
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginG3.cpp433
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginGIF.cpp1398
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginHDR.cpp719
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginICO.cpp805
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginIFF.cpp459
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp339
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginJP2.cpp339
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp1801
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginKOALA.cpp243
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginMNG.cpp153
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginPCD.cpp251
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginPCX.cpp659
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginPFM.cpp402
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginPICT.cpp1342
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginPNG.cpp953
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginPNM.cpp831
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginPSD.cpp131
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginRAS.cpp512
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginRAW.cpp542
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginSGI.cpp425
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp1565
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp2629
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginWBMP.cpp372
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginXBM.cpp399
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginXPM.cpp487
-rw-r--r--plugins/AdvaImg/src/FreeImage/TIFFLogLuv.cpp65
-rw-r--r--plugins/AdvaImg/src/FreeImage/ToneMapping.cpp75
-rw-r--r--plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp538
-rw-r--r--plugins/AdvaImg/src/FreeImage/ZLibInterface.cpp223
-rw-r--r--plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp479
-rw-r--r--plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp295
-rw-r--r--plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp689
-rw-r--r--plugins/AdvaImg/src/FreeImage/tmoReinhard05.cpp260
65 files changed, 35165 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp b/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp
new file mode 100644
index 0000000000..584870f359
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp
@@ -0,0 +1,1210 @@
+// ==========================================================
+// FreeImage implementation
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Petr Supina (psup@centrum.cz)
+// - Carsten Klein (c.klein@datagis.com)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include <stdlib.h>
+#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__)
+#include <malloc.h>
+#endif // _WIN32 || _WIN64 || __MINGW32__
+
+#include "FreeImage.h"
+#include "FreeImageIO.h"
+#include "Utilities.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+// ----------------------------------------------------------
+// Metadata definitions
+// ----------------------------------------------------------
+
+// helper for map<key, value> where value is a pointer to a FreeImage tag
+typedef std::map<std::string, FITAG*> TAGMAP;
+
+// helper for map<FREE_IMAGE_MDMODEL, TAGMAP*>
+typedef std::map<int, TAGMAP*> METADATAMAP;
+
+// helper for metadata iterator
+FI_STRUCT (METADATAHEADER) {
+ long pos; // current position when iterating the map
+ TAGMAP *tagmap; // pointer to the tag map
+};
+
+// ----------------------------------------------------------
+// FIBITMAP definition
+// ----------------------------------------------------------
+
+FI_STRUCT (FREEIMAGEHEADER) {
+ FREE_IMAGE_TYPE type; // data type - bitmap, array of long, double, complex, etc
+
+ unsigned red_mask; // bit layout of the red components
+ unsigned green_mask; // bit layout of the green components
+ unsigned blue_mask; // bit layout of the blue components
+
+ RGBQUAD bkgnd_color; // background color used for RGB transparency
+
+ BOOL transparent; // why another table? for easy transparency table retrieval!
+ int transparency_count; // transparency could be stored in the palette, which is better
+ BYTE transparent_table[256];// overall, but it requires quite some changes and it will render
+ // FreeImage_GetTransparencyTable obsolete in its current form;
+ FIICCPROFILE iccProfile; // space to hold ICC profile
+
+ METADATAMAP *metadata; // contains a list of metadata models attached to the bitmap
+
+ BOOL has_pixels; // FALSE if the FIBITMAP only contains the header and no pixel data
+
+ FIBITMAP *thumbnail; // optionally contains a thumbnail attached to the bitmap
+
+ //BYTE filler[1]; // fill to 32-bit alignment
+};
+
+// ----------------------------------------------------------
+// Memory allocation on a specified alignment boundary
+// ----------------------------------------------------------
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER >= 1300)
+
+void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
+ assert(alignment == FIBITMAP_ALIGNMENT);
+ return _aligned_malloc(amount, alignment);
+}
+
+void FreeImage_Aligned_Free(void* mem) {
+ _aligned_free(mem);
+}
+
+#elif defined (__MINGW32__)
+
+void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
+ assert(alignment == FIBITMAP_ALIGNMENT);
+ return __mingw_aligned_malloc (amount, alignment);
+}
+
+void FreeImage_Aligned_Free(void* mem) {
+ __mingw_aligned_free (mem);
+}
+
+#else
+
+void* FreeImage_Aligned_Malloc(size_t amount, size_t alignment) {
+ assert(alignment == FIBITMAP_ALIGNMENT);
+ /*
+ In some rare situations, the malloc routines can return misaligned memory.
+ The routine FreeImage_Aligned_Malloc allocates a bit more memory to do
+ aligned writes. Normally, it *should* allocate "alignment" extra memory and then writes
+ one dword back the true pointer. But if the memory manager returns a
+ misaligned block that is less than a dword from the next alignment,
+ then the writing back one dword will corrupt memory.
+
+ For example, suppose that alignment is 16 and malloc returns the address 0xFFFF.
+
+ 16 - 0xFFFF % 16 + 0xFFFF = 16 - 15 + 0xFFFF = 0x10000.
+
+ Now, you subtract one dword from that and write and that will corrupt memory.
+
+ That's why the code below allocates *two* alignments instead of one.
+ */
+ void* mem_real = malloc(amount + 2 * alignment);
+ if (!mem_real) return NULL;
+ char* mem_align = (char*)((unsigned long)(2 * alignment - (unsigned long)mem_real % (unsigned long)alignment) + (unsigned long)mem_real);
+ *((long*)mem_align - 1) = (long)mem_real;
+ return mem_align;
+}
+
+void FreeImage_Aligned_Free(void* mem) {
+ free((void*)*((long*)mem - 1));
+}
+
+#endif // _WIN32 || _WIN64
+
+// ----------------------------------------------------------
+// DIB information functions
+// ----------------------------------------------------------
+
+/**
+Calculate the size of a FreeImage image.
+Align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary.
+
+@param header_only If TRUE, calculate a 'header only' FIBITMAP size, otherwise calculate a full FIBITMAP size
+@param width
+@param height
+@param bpp
+@see FreeImage_AllocateHeaderT
+*/
+static size_t
+FreeImage_GetImageSizeHeader(BOOL header_only, unsigned width, unsigned height, unsigned bpp) {
+ size_t dib_size = sizeof(FREEIMAGEHEADER);
+ dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0);
+ dib_size += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT;
+ dib_size += sizeof(BITMAPINFOHEADER);
+ // palette is aligned on a 16 bytes boundary
+ dib_size += sizeof(RGBQUAD) * CalculateUsedPaletteEntries(bpp);
+ dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0);
+ if (!header_only) {
+ const size_t header_size = dib_size;
+
+ // pixels are aligned on a 16 bytes boundary
+ dib_size += (size_t)CalculatePitch(CalculateLine(width, bpp)) * (size_t)height;
+
+ // check for possible malloc overflow using a KISS integer overflow detection mechanism
+ {
+ /*
+ The following constant take into account the additionnal memory used by
+ aligned malloc functions as well as debug malloc functions.
+ It is supposed here that using a (8 * FIBITMAP_ALIGNMENT) risk margin will be enough
+ for the target compiler.
+ */
+ const double FIBITMAP_MAX_MEMORY = (double)((size_t)-1) - 8 * FIBITMAP_ALIGNMENT;
+ const double dPitch = floor( ((double)bpp * width + 31.0) / 32.0 ) * 4.0;
+ const double dImageSize = (double)header_size + dPitch * height;
+ if(dImageSize != (double)dib_size) {
+ // here, we are sure to encounter a malloc overflow: try to avoid it ...
+ return 0;
+ }
+ if(dImageSize > FIBITMAP_MAX_MEMORY) {
+ // avoid possible overflow inside C allocation functions
+ return 0;
+ }
+ }
+ }
+
+ return dib_size;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+ FIBITMAP *bitmap = (FIBITMAP *)malloc(sizeof(FIBITMAP));
+
+ if (bitmap != NULL) {
+ width = abs(width);
+ height = abs(height);
+
+ // check pixel bit depth
+ switch(type) {
+ case FIT_BITMAP:
+ switch(bpp) {
+ case 1:
+ case 4:
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ bpp = 8;
+ break;
+ }
+ break;
+ case FIT_UINT16:
+ bpp = 8 * sizeof(unsigned short);
+ break;
+ case FIT_INT16:
+ bpp = 8 * sizeof(short);
+ break;
+ case FIT_UINT32:
+ bpp = 8 * sizeof(DWORD);
+ break;
+ case FIT_INT32:
+ bpp = 8 * sizeof(LONG);
+ break;
+ case FIT_FLOAT:
+ bpp = 8 * sizeof(float);
+ break;
+ case FIT_DOUBLE:
+ bpp = 8 * sizeof(double);
+ break;
+ case FIT_COMPLEX:
+ bpp = 8 * sizeof(FICOMPLEX);
+ break;
+ case FIT_RGB16:
+ bpp = 8 * sizeof(FIRGB16);
+ break;
+ case FIT_RGBA16:
+ bpp = 8 * sizeof(FIRGBA16);
+ break;
+ case FIT_RGBF:
+ bpp = 8 * sizeof(FIRGBF);
+ break;
+ case FIT_RGBAF:
+ bpp = 8 * sizeof(FIRGBAF);
+ break;
+ default:
+ free(bitmap);
+ return NULL;
+ }
+
+ // calculate the size of a FreeImage image
+ // align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary
+ // palette is aligned on a 16 bytes boundary
+ // pixels are aligned on a 16 bytes boundary
+
+ size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp);
+
+ if(dib_size == 0) {
+ // memory allocation will fail (probably a malloc overflow)
+ free(bitmap);
+ return NULL;
+ }
+
+ bitmap->data = (BYTE *)FreeImage_Aligned_Malloc(dib_size * sizeof(BYTE), FIBITMAP_ALIGNMENT);
+
+ if (bitmap->data != NULL) {
+ memset(bitmap->data, 0, dib_size);
+
+ // write out the FREEIMAGEHEADER
+
+ FREEIMAGEHEADER *fih = (FREEIMAGEHEADER *)bitmap->data;
+ fih->type = type;
+
+ fih->red_mask = red_mask;
+ fih->green_mask = green_mask;
+ fih->blue_mask = blue_mask;
+
+ memset(&fih->bkgnd_color, 0, sizeof(RGBQUAD));
+
+ fih->transparent = FALSE;
+ fih->transparency_count = 0;
+ memset(fih->transparent_table, 0xff, 256);
+
+ fih->has_pixels = header_only ? FALSE : TRUE;
+
+ // initialize FIICCPROFILE link
+
+ FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(bitmap);
+ iccProfile->size = 0;
+ iccProfile->data = 0;
+ iccProfile->flags = 0;
+
+ // initialize metadata models list
+
+ fih->metadata = new(std::nothrow) METADATAMAP;
+
+ // initialize attached thumbnail
+
+ fih->thumbnail = NULL;
+
+ // write out the BITMAPINFOHEADER
+
+ BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(bitmap);
+ bih->biSize = sizeof(BITMAPINFOHEADER);
+ bih->biWidth = width;
+ bih->biHeight = height;
+ bih->biPlanes = 1;
+ bih->biCompression = 0;
+ bih->biBitCount = (WORD)bpp;
+ bih->biClrUsed = CalculateUsedPaletteEntries(bpp);
+ bih->biClrImportant = bih->biClrUsed;
+ bih->biXPelsPerMeter = 2835; // 72 dpi
+ bih->biYPelsPerMeter = 2835; // 72 dpi
+
+ if(bpp == 8) {
+ // build a default greyscale palette (very useful for image processing)
+ RGBQUAD *pal = FreeImage_GetPalette(bitmap);
+ for(int i = 0; i < 256; i++) {
+ pal[i].rgbRed = (BYTE)i;
+ pal[i].rgbGreen = (BYTE)i;
+ pal[i].rgbBlue = (BYTE)i;
+ }
+ }
+
+ return bitmap;
+ }
+
+ free(bitmap);
+ }
+
+ return NULL;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+ return FreeImage_AllocateHeaderT(header_only, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+ return FreeImage_AllocateHeaderT(FALSE, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+ return FreeImage_AllocateHeaderT(FALSE, type, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+void DLL_CALLCONV
+FreeImage_Unload(FIBITMAP *dib) {
+ if (NULL != dib) {
+ if (NULL != dib->data) {
+ // delete possible icc profile ...
+ if (FreeImage_GetICCProfile(dib)->data)
+ free(FreeImage_GetICCProfile(dib)->data);
+
+ // delete metadata models
+ METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+
+ for(METADATAMAP::iterator i = (*metadata).begin(); i != (*metadata).end(); i++) {
+ TAGMAP *tagmap = (*i).second;
+
+ if(tagmap) {
+ for(TAGMAP::iterator j = tagmap->begin(); j != tagmap->end(); j++) {
+ FITAG *tag = (*j).second;
+ FreeImage_DeleteTag(tag);
+ }
+
+ delete tagmap;
+ }
+ }
+
+ delete metadata;
+
+ // delete embedded thumbnail
+ FreeImage_Unload(FreeImage_GetThumbnail(dib));
+
+ // delete bitmap ...
+ FreeImage_Aligned_Free(dib->data);
+ }
+ free(dib); // ... and the wrapper
+ }
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Clone(FIBITMAP *dib) {
+ if (!dib) return NULL;
+
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned bpp = FreeImage_GetBPP(dib);
+
+ // check for pixel availability ...
+ BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE;
+
+ // allocate a new dib
+ FIBITMAP *new_dib = FreeImage_AllocateHeaderT(header_only, FreeImage_GetImageType(dib), width, height, bpp,
+ FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib));
+
+ if (new_dib) {
+ // save ICC profile links
+ FIICCPROFILE *src_iccProfile = FreeImage_GetICCProfile(dib);
+ FIICCPROFILE *dst_iccProfile = FreeImage_GetICCProfile(new_dib);
+
+ // save metadata links
+ METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+ METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)new_dib->data)->metadata;
+
+ // calculate the size of a FreeImage image
+ // align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary
+ // palette is aligned on a 16 bytes boundary
+ // pixels are aligned on a 16 bytes boundary
+
+ size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp);
+
+ // copy the bitmap + internal pointers (remember to restore new_dib internal pointers later)
+ memcpy(new_dib->data, dib->data, dib_size);
+
+ // reset ICC profile link for new_dib
+ memset(dst_iccProfile, 0, sizeof(FIICCPROFILE));
+
+ // restore metadata link for new_dib
+ ((FREEIMAGEHEADER *)new_dib->data)->metadata = dst_metadata;
+
+ // reset thumbnail link for new_dib
+ ((FREEIMAGEHEADER *)new_dib->data)->thumbnail = NULL;
+
+ // copy possible ICC profile
+ FreeImage_CreateICCProfile(new_dib, src_iccProfile->data, src_iccProfile->size);
+ dst_iccProfile->flags = src_iccProfile->flags;
+
+ // copy metadata models
+ for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) {
+ int model = (*i).first;
+ TAGMAP *src_tagmap = (*i).second;
+
+ if(src_tagmap) {
+ // create a metadata model
+ TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP();
+
+ if(dst_tagmap) {
+ // fill the model
+ for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) {
+ std::string dst_key = (*j).first;
+ FITAG *dst_tag = FreeImage_CloneTag( (*j).second );
+
+ // assign key and tag value
+ (*dst_tagmap)[dst_key] = dst_tag;
+ }
+
+ // assign model and tagmap
+ (*dst_metadata)[model] = dst_tagmap;
+ }
+ }
+ }
+
+ // copy the thumbnail
+ FreeImage_SetThumbnail(new_dib, FreeImage_GetThumbnail(dib));
+
+ return new_dib;
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP* DLL_CALLCONV
+FreeImage_GetThumbnail(FIBITMAP *dib) {
+ return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->thumbnail : NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SetThumbnail(FIBITMAP *dib, FIBITMAP *thumbnail) {
+ if(dib == NULL) {
+ return FALSE;
+ }
+ FIBITMAP *currentThumbnail = ((FREEIMAGEHEADER *)dib->data)->thumbnail;
+ if(currentThumbnail == thumbnail) {
+ return TRUE;
+ }
+ FreeImage_Unload(currentThumbnail);
+
+ ((FREEIMAGEHEADER *)dib->data)->thumbnail = FreeImage_HasPixels(thumbnail) ? FreeImage_Clone(thumbnail) : NULL;
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+FREE_IMAGE_COLOR_TYPE DLL_CALLCONV
+FreeImage_GetColorType(FIBITMAP *dib) {
+ RGBQUAD *rgb;
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ // special bitmap type
+ if(image_type != FIT_BITMAP) {
+ switch(image_type) {
+ case FIT_RGB16:
+ case FIT_RGBF:
+ return FIC_RGB;
+ case FIT_RGBA16:
+ case FIT_RGBAF:
+ return FIC_RGBALPHA;
+ }
+
+ return FIC_MINISBLACK;
+ }
+
+ // standard image type
+ switch (FreeImage_GetBPP(dib)) {
+ case 1:
+ {
+ rgb = FreeImage_GetPalette(dib);
+
+ if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) {
+ rgb++;
+
+ if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255))
+ return FIC_MINISBLACK;
+ }
+
+ if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) {
+ rgb++;
+
+ if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0))
+ return FIC_MINISWHITE;
+ }
+
+ return FIC_PALETTE;
+ }
+
+ case 4:
+ case 8: // Check if the DIB has a color or a greyscale palette
+ {
+ int ncolors = FreeImage_GetColorsUsed(dib);
+ int minisblack = 1;
+ rgb = FreeImage_GetPalette(dib);
+
+ for (int i = 0; i < ncolors; i++) {
+ if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue))
+ return FIC_PALETTE;
+
+ // The DIB has a color palette if the greyscale isn't a linear ramp
+ // Take care of reversed grey images
+ if (rgb->rgbRed != i) {
+ if ((ncolors-i-1) != rgb->rgbRed)
+ return FIC_PALETTE;
+ else
+ minisblack = 0;
+ }
+
+ rgb++;
+ }
+
+ return minisblack ? FIC_MINISBLACK : FIC_MINISWHITE;
+ }
+
+ case 16:
+ case 24:
+ return FIC_RGB;
+
+ case 32:
+ {
+ if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK)
+ return FIC_CMYK;
+
+ for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ rgb = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
+
+ for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++)
+ if (rgb[x].rgbReserved != 0xFF)
+ return FIC_RGBALPHA;
+ }
+
+ return FIC_RGB;
+ }
+
+ default :
+ return FIC_MINISBLACK;
+ }
+}
+
+// ----------------------------------------------------------
+
+FREE_IMAGE_TYPE DLL_CALLCONV
+FreeImage_GetImageType(FIBITMAP *dib) {
+ return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->type : FIT_UNKNOWN;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_HasPixels(FIBITMAP *dib) {
+ return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->has_pixels : FALSE;
+}
+
+// ----------------------------------------------------------
+
+unsigned DLL_CALLCONV
+FreeImage_GetRedMask(FIBITMAP *dib) {
+ return dib ? ((FREEIMAGEHEADER *)dib->data)->red_mask : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetGreenMask(FIBITMAP *dib) {
+ return dib ? ((FREEIMAGEHEADER *)dib->data)->green_mask : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetBlueMask(FIBITMAP *dib) {
+ return dib ? ((FREEIMAGEHEADER *)dib->data)->blue_mask : 0;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_HasBackgroundColor(FIBITMAP *dib) {
+ if(dib) {
+ RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
+ return (bkgnd_color->rgbReserved != 0) ? TRUE : FALSE;
+ }
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) {
+ if(dib && bkcolor) {
+ if(FreeImage_HasBackgroundColor(dib)) {
+ // get the background color
+ RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
+ memcpy(bkcolor, bkgnd_color, sizeof(RGBQUAD));
+ // get the background index
+ if(FreeImage_GetBPP(dib) == 8) {
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
+ if(bkgnd_color->rgbRed == pal[i].rgbRed) {
+ if(bkgnd_color->rgbGreen == pal[i].rgbGreen) {
+ if(bkgnd_color->rgbBlue == pal[i].rgbBlue) {
+ bkcolor->rgbReserved = (BYTE)i;
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ bkcolor->rgbReserved = 0;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) {
+ if(dib) {
+ RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color;
+ if(bkcolor) {
+ // set the background color
+ memcpy(bkgnd_color, bkcolor, sizeof(RGBQUAD));
+ // enable the file background color
+ bkgnd_color->rgbReserved = 1;
+ } else {
+ // clear and disable the file background color
+ memset(bkgnd_color, 0, sizeof(RGBQUAD));
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_IsTransparent(FIBITMAP *dib) {
+ if(dib) {
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ switch(image_type) {
+ case FIT_BITMAP:
+ if(FreeImage_GetBPP(dib) == 32) {
+ if(FreeImage_GetColorType(dib) == FIC_RGBALPHA) {
+ return TRUE;
+ }
+ } else {
+ return ((FREEIMAGEHEADER *)dib->data)->transparent ? TRUE : FALSE;
+ }
+ break;
+ case FIT_RGBA16:
+ case FIT_RGBAF:
+ return TRUE;
+ default:
+ break;
+ }
+ }
+ return FALSE;
+}
+
+BYTE * DLL_CALLCONV
+FreeImage_GetTransparencyTable(FIBITMAP *dib) {
+ return dib ? ((FREEIMAGEHEADER *)dib->data)->transparent_table : NULL;
+}
+
+void DLL_CALLCONV
+FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled) {
+ if (dib) {
+ if ((FreeImage_GetBPP(dib) <= 8) || (FreeImage_GetBPP(dib) == 32)) {
+ ((FREEIMAGEHEADER *)dib->data)->transparent = enabled;
+ } else {
+ ((FREEIMAGEHEADER *)dib->data)->transparent = FALSE;
+ }
+ }
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetTransparencyCount(FIBITMAP *dib) {
+ return dib ? ((FREEIMAGEHEADER *)dib->data)->transparency_count : 0;
+}
+
+void DLL_CALLCONV
+FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table, int count) {
+ if (dib) {
+ count = MIN(count, 256);
+ if (FreeImage_GetBPP(dib) <= 8) {
+ ((FREEIMAGEHEADER *)dib->data)->transparent = TRUE;
+ ((FREEIMAGEHEADER *)dib->data)->transparency_count = count;
+
+ if (table) {
+ memcpy(((FREEIMAGEHEADER *)dib->data)->transparent_table, table, count);
+ } else {
+ memset(((FREEIMAGEHEADER *)dib->data)->transparent_table, 0xff, count);
+ }
+ }
+ }
+}
+
+/** @brief Sets the index of the palette entry to be used as transparent color
+ for the image specified. Does nothing on high color images.
+
+ This method sets the index of the palette entry to be used as single transparent
+ color for the image specified. This works on palletised images only and does
+ nothing for high color images.
+
+ Although it is possible for palletised images to have more than one transparent
+ color, this method sets the palette entry specified as the single transparent
+ color for the image. All other colors will be set to be non-transparent by this
+ method.
+
+ As with FreeImage_SetTransparencyTable(), this method also sets the image's
+ transparency property to TRUE (as it is set and obtained by
+ FreeImage_SetTransparent() and FreeImage_IsTransparent() respectively) for
+ palletised images.
+
+ @param dib Input image, whose transparent color is to be set.
+ @param index The index of the palette entry to be set as transparent color.
+ */
+void DLL_CALLCONV
+FreeImage_SetTransparentIndex(FIBITMAP *dib, int index) {
+ if (dib) {
+ int count = FreeImage_GetColorsUsed(dib);
+ if (count) {
+ BYTE *new_tt = (BYTE *)malloc(count * sizeof(BYTE));
+ memset(new_tt, 0xFF, count);
+ if ((index >= 0) && (index < count)) {
+ new_tt[index] = 0x00;
+ }
+ FreeImage_SetTransparencyTable(dib, new_tt, count);
+ free(new_tt);
+ }
+ }
+}
+
+/** @brief Returns the palette entry used as transparent color for the image
+ specified. Works for palletised images only and returns -1 for high color
+ images or if the image has no color set to be transparent.
+
+ Although it is possible for palletised images to have more than one transparent
+ color, this function always returns the index of the first palette entry, set
+ to be transparent.
+
+ @param dib Input image, whose transparent color is to be returned.
+ @return Returns the index of the palette entry used as transparent color for
+ the image specified or -1 if there is no transparent color found (e.g. the image
+ is a high color image).
+ */
+int DLL_CALLCONV
+FreeImage_GetTransparentIndex(FIBITMAP *dib) {
+ int count = FreeImage_GetTransparencyCount(dib);
+ BYTE *tt = FreeImage_GetTransparencyTable(dib);
+ for (int i = 0; i < count; i++) {
+ if (tt[i] == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+// ----------------------------------------------------------
+
+FIICCPROFILE * DLL_CALLCONV
+FreeImage_GetICCProfile(FIBITMAP *dib) {
+ FIICCPROFILE *profile = (dib) ? (FIICCPROFILE *)&((FREEIMAGEHEADER *)dib->data)->iccProfile : NULL;
+ return profile;
+}
+
+FIICCPROFILE * DLL_CALLCONV
+FreeImage_CreateICCProfile(FIBITMAP *dib, void *data, long size) {
+ // clear the profile but preserve profile->flags
+ FreeImage_DestroyICCProfile(dib);
+ // create the new profile
+ FIICCPROFILE *profile = FreeImage_GetICCProfile(dib);
+ if(size && profile) {
+ profile->data = malloc(size);
+ if(profile->data) {
+ memcpy(profile->data, data, profile->size = size);
+ }
+ }
+ return profile;
+}
+
+void DLL_CALLCONV
+FreeImage_DestroyICCProfile(FIBITMAP *dib) {
+ FIICCPROFILE *profile = FreeImage_GetICCProfile(dib);
+ if(profile) {
+ if (profile->data) {
+ free (profile->data);
+ }
+ // clear the profile but preserve profile->flags
+ profile->data = NULL;
+ profile->size = 0;
+ }
+}
+
+// ----------------------------------------------------------
+
+unsigned DLL_CALLCONV
+FreeImage_GetWidth(FIBITMAP *dib) {
+ return dib ? FreeImage_GetInfoHeader(dib)->biWidth : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetHeight(FIBITMAP *dib) {
+ return (dib) ? FreeImage_GetInfoHeader(dib)->biHeight : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetBPP(FIBITMAP *dib) {
+ return dib ? FreeImage_GetInfoHeader(dib)->biBitCount : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetLine(FIBITMAP *dib) {
+ return dib ? ((FreeImage_GetWidth(dib) * FreeImage_GetBPP(dib)) + 7) / 8 : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetPitch(FIBITMAP *dib) {
+ return dib ? FreeImage_GetLine(dib) + 3 & ~3 : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetColorsUsed(FIBITMAP *dib) {
+ return dib ? FreeImage_GetInfoHeader(dib)->biClrUsed : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetDIBSize(FIBITMAP *dib) {
+ return (dib) ? sizeof(BITMAPINFOHEADER) + (FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD)) + (FreeImage_GetPitch(dib) * FreeImage_GetHeight(dib)) : 0;
+}
+
+RGBQUAD * DLL_CALLCONV
+FreeImage_GetPalette(FIBITMAP *dib) {
+ return (dib && FreeImage_GetBPP(dib) < 16) ? (RGBQUAD *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetDotsPerMeterX(FIBITMAP *dib) {
+ return (dib) ? FreeImage_GetInfoHeader(dib)->biXPelsPerMeter : 0;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetDotsPerMeterY(FIBITMAP *dib) {
+ return (dib) ? FreeImage_GetInfoHeader(dib)->biYPelsPerMeter : 0;
+}
+
+void DLL_CALLCONV
+FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res) {
+ if(dib) {
+ FreeImage_GetInfoHeader(dib)->biXPelsPerMeter = res;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res) {
+ if(dib) {
+ FreeImage_GetInfoHeader(dib)->biYPelsPerMeter = res;
+ }
+}
+
+BITMAPINFOHEADER * DLL_CALLCONV
+FreeImage_GetInfoHeader(FIBITMAP *dib) {
+ if (!dib) return NULL;
+ size_t lp = (size_t)dib->data + sizeof(FREEIMAGEHEADER);
+ lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0);
+ lp += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT;
+ return (BITMAPINFOHEADER *)lp;
+}
+
+BITMAPINFO * DLL_CALLCONV
+FreeImage_GetInfo(FIBITMAP *dib) {
+ return (BITMAPINFO *)FreeImage_GetInfoHeader(dib);
+}
+
+// ----------------------------------------------------------
+// Metadata routines
+// ----------------------------------------------------------
+
+FIMETADATA * DLL_CALLCONV
+FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag) {
+ if (!dib)
+ return NULL;
+
+ // get the metadata model
+ METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+ TAGMAP *tagmap = NULL;
+ if ( (*metadata).find(model) != (*metadata).end()) {
+ tagmap = (*metadata)[model];
+ }
+ if(tagmap) {
+ // allocate a handle
+ FIMETADATA *handle = (FIMETADATA *)malloc(sizeof(FIMETADATA));
+ if(handle) {
+ // calculate the size of a METADATAHEADER
+ int header_size = sizeof(METADATAHEADER);
+
+ handle->data = (BYTE *)malloc(header_size * sizeof(BYTE));
+
+ if(handle->data) {
+ memset(handle->data, 0, header_size * sizeof(BYTE));
+
+ // write out the METADATAHEADER
+ METADATAHEADER *mdh = (METADATAHEADER *)handle->data;
+
+ mdh->pos = 1;
+ mdh->tagmap = tagmap;
+
+ // get the first element
+ TAGMAP::iterator i = tagmap->begin();
+ *tag = (*i).second;
+
+ return handle;
+ }
+
+ free(handle);
+ }
+ }
+
+ return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag) {
+ if (!mdhandle)
+ return FALSE;
+
+ METADATAHEADER *mdh = (METADATAHEADER *)mdhandle->data;
+ TAGMAP *tagmap = mdh->tagmap;
+
+ int current_pos = mdh->pos;
+ int mapsize = (int)tagmap->size();
+
+ if(current_pos < mapsize) {
+ // get the tag element at position pos
+ int count = 0;
+
+ for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
+ if(count == current_pos) {
+ *tag = (*i).second;
+ mdh->pos++;
+ break;
+ }
+ count++;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void DLL_CALLCONV
+FreeImage_FindCloseMetadata(FIMETADATA *mdhandle) {
+ if (NULL != mdhandle) { // delete the handle
+ if (NULL != mdhandle->data) {
+ free(mdhandle->data);
+ }
+ free(mdhandle); // ... and the wrapper
+ }
+}
+
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) {
+ if (!src || !dst) return FALSE;
+
+ // get metadata links
+ METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)src->data)->metadata;
+ METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)dst->data)->metadata;
+
+ // copy metadata models, *except* the FIMD_ANIMATION model
+ for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) {
+ int model = (*i).first;
+ if(model == (int)FIMD_ANIMATION) {
+ continue;
+ }
+ TAGMAP *src_tagmap = (*i).second;
+
+ if(src_tagmap) {
+ if ( dst_metadata->find(model) != dst_metadata->end()) {
+ // destroy dst model
+ FreeImage_SetMetadata((FREE_IMAGE_MDMODEL)model, dst, NULL, NULL);
+ }
+
+ // create a metadata model
+ TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP();
+
+ if(dst_tagmap) {
+ // fill the model
+ for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) {
+ std::string dst_key = (*j).first;
+ FITAG *dst_tag = FreeImage_CloneTag( (*j).second );
+
+ // assign key and tag value
+ (*dst_tagmap)[dst_key] = dst_tag;
+ }
+
+ // assign model and tagmap
+ (*dst_metadata)[model] = dst_tagmap;
+ }
+ }
+ }
+
+ // clone resolution
+ FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src));
+ FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src));
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) {
+ if (!dib)
+ return FALSE;
+
+ TAGMAP *tagmap = NULL;
+
+ // get the metadata model
+ METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+ METADATAMAP::iterator model_iterator = metadata->find(model);
+ if (model_iterator != metadata->end()) {
+ tagmap = model_iterator->second;
+ }
+
+ if(key != NULL) {
+
+ if (!tagmap) {
+ // this model, doesn't exist: create it
+ tagmap = new(std::nothrow) TAGMAP();
+ (*metadata)[model] = tagmap;
+ }
+
+ if(tag) {
+ // first check the tag
+ if(FreeImage_GetTagKey(tag) == NULL) {
+ FreeImage_SetTagKey(tag, key);
+ } else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) {
+ // set the tag key
+ FreeImage_SetTagKey(tag, key);
+ }
+ if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) {
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key);
+ return FALSE;
+ }
+
+ // fill the tag ID if possible and if it's needed
+ TagLib& tag_lib = TagLib::instance();
+ switch(model) {
+ case FIMD_IPTC:
+ {
+ int id = tag_lib.getTagID(TagLib::IPTC, key);
+ /*
+ if(id == -1) {
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key);
+ }
+ */
+ FreeImage_SetTagID(tag, (WORD)id);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // delete existing tag
+ FITAG *old_tag = (*tagmap)[key];
+ if(old_tag) {
+ FreeImage_DeleteTag(old_tag);
+ }
+
+ // create a new tag
+ (*tagmap)[key] = FreeImage_CloneTag(tag);
+ }
+ else {
+ // delete existing tag
+ TAGMAP::iterator i = tagmap->find(key);
+ if(i != tagmap->end()) {
+ FITAG *old_tag = (*i).second;
+ FreeImage_DeleteTag(old_tag);
+ tagmap->erase(key);
+ }
+ }
+ }
+ else {
+ // destroy the metadata model
+ if(tagmap) {
+ for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) {
+ FITAG *tag = (*i).second;
+ FreeImage_DeleteTag(tag);
+ }
+
+ delete tagmap;
+ metadata->erase(model_iterator);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag) {
+ if (!dib || !key || !tag)
+ return FALSE;
+
+ TAGMAP *tagmap = NULL;
+ *tag = NULL;
+
+ // get the metadata model
+ METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+ if (!(*metadata).empty()) {
+ METADATAMAP::iterator model_iterator = metadata->find(model);
+ if (model_iterator != metadata->end()) {
+ // this model exists : try to get the requested tag
+ tagmap = model_iterator->second;
+ TAGMAP::iterator tag_iterator = tagmap->find(key);
+ if (tag_iterator != tagmap->end()) {
+ // get the requested tag
+ *tag = tag_iterator->second;
+ }
+ }
+ }
+
+ return (*tag != NULL) ? TRUE : FALSE;
+}
+
+// ----------------------------------------------------------
+
+unsigned DLL_CALLCONV
+FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib) {
+ if (!dib)
+ return FALSE;
+
+ TAGMAP *tagmap = NULL;
+
+ // get the metadata model
+ METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata;
+ if ( (*metadata).find(model) != (*metadata).end()) {
+ tagmap = (*metadata)[model];
+ }
+ if (!tagmap) {
+ // this model, doesn't exist: return
+ return 0;
+ }
+
+ // get the tag count
+ return (unsigned)tagmap->size();
+}
+
+// ----------------------------------------------------------
+
+
diff --git a/plugins/AdvaImg/src/FreeImage/CacheFile.cpp b/plugins/AdvaImg/src/FreeImage/CacheFile.cpp
new file mode 100644
index 0000000000..309faf8faf
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/CacheFile.cpp
@@ -0,0 +1,271 @@
+// ==========================================================
+// Multi-Page functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - checkered (checkered@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "CacheFile.h"
+
+// ----------------------------------------------------------
+
+CacheFile::CacheFile(const std::string filename, BOOL keep_in_memory) :
+m_file(NULL),
+m_filename(filename),
+m_free_pages(),
+m_page_cache_mem(),
+m_page_cache_disk(),
+m_page_map(),
+m_page_count(0),
+m_current_block(NULL),
+m_keep_in_memory(keep_in_memory) {
+}
+
+CacheFile::~CacheFile() {
+}
+
+BOOL
+CacheFile::open() {
+ if ((!m_filename.empty()) && (!m_keep_in_memory)) {
+ m_file = fopen(m_filename.c_str(), "w+b");
+ return (m_file != NULL);
+ }
+
+ return (m_keep_in_memory == TRUE);
+}
+
+void
+CacheFile::close() {
+ // dispose the cache entries
+
+ while (!m_page_cache_disk.empty()) {
+ Block *block = *m_page_cache_disk.begin();
+ m_page_cache_disk.pop_front();
+ delete [] block->data;
+ delete block;
+ }
+ while (!m_page_cache_mem.empty()) {
+ Block *block = *m_page_cache_mem.begin();
+ m_page_cache_mem.pop_front();
+ delete [] block->data;
+ delete block;
+ }
+
+ if (m_file) {
+ // close the file
+
+ fclose(m_file);
+
+ // delete the file
+
+ remove(m_filename.c_str());
+ }
+}
+
+void
+CacheFile::cleanupMemCache() {
+ if (!m_keep_in_memory) {
+ if (m_page_cache_mem.size() > CACHE_SIZE) {
+ // flush the least used block to file
+
+ Block *old_block = m_page_cache_mem.back();
+ fseek(m_file, old_block->nr * BLOCK_SIZE, SEEK_SET);
+ fwrite(old_block->data, BLOCK_SIZE, 1, m_file);
+
+ // remove the data
+
+ delete [] old_block->data;
+ old_block->data = NULL;
+
+ // move the block to another list
+
+ m_page_cache_disk.splice(m_page_cache_disk.begin(), m_page_cache_mem, --m_page_cache_mem.end());
+ m_page_map[old_block->nr] = m_page_cache_disk.begin();
+ }
+ }
+}
+
+int
+CacheFile::allocateBlock() {
+ Block *block = new Block;
+ block->data = new BYTE[BLOCK_SIZE];
+ block->next = 0;
+
+ if (!m_free_pages.empty()) {
+ block->nr = *m_free_pages.begin();
+ m_free_pages.pop_front();
+ } else {
+ block->nr = m_page_count++;
+ }
+
+ m_page_cache_mem.push_front(block);
+ m_page_map[block->nr] = m_page_cache_mem.begin();
+
+ cleanupMemCache();
+
+ return block->nr;
+}
+
+Block *
+CacheFile::lockBlock(int nr) {
+ if (m_current_block == NULL) {
+ PageMapIt it = m_page_map.find(nr);
+
+ if (it != m_page_map.end()) {
+ m_current_block = *(it->second);
+
+ // the block is swapped out to disc. load it back
+ // and remove the block from the cache. it might get cached
+ // again as soon as the memory buffer fills up
+
+ if (m_current_block->data == NULL) {
+ m_current_block->data = new BYTE[BLOCK_SIZE];
+
+ fseek(m_file, m_current_block->nr * BLOCK_SIZE, SEEK_SET);
+ fread(m_current_block->data, BLOCK_SIZE, 1, m_file);
+
+ m_page_cache_mem.splice(m_page_cache_mem.begin(), m_page_cache_disk, it->second);
+ m_page_map[nr] = m_page_cache_mem.begin();
+ }
+
+ // if the memory cache size is too large, swap an item to disc
+
+ cleanupMemCache();
+
+ // return the current block
+
+ return m_current_block;
+ }
+ }
+
+ return NULL;
+}
+
+BOOL
+CacheFile::unlockBlock(int nr) {
+ if (m_current_block) {
+ m_current_block = NULL;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL
+CacheFile::deleteBlock(int nr) {
+ if (!m_current_block) {
+ PageMapIt it = m_page_map.find(nr);
+
+ // remove block from cache
+
+ if (it != m_page_map.end())
+ m_page_map.erase(nr);
+
+ // add block to free page list
+
+ m_free_pages.push_back(nr);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL
+CacheFile::readFile(BYTE *data, int nr, int size) {
+ if ((data) && (size > 0)) {
+ int s = 0;
+ int block_nr = nr;
+
+ do {
+ int copy_nr = block_nr;
+
+ Block *block = lockBlock(copy_nr);
+
+ block_nr = block->next;
+
+ memcpy(data + s, block->data, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
+
+ unlockBlock(copy_nr);
+
+ s += BLOCK_SIZE;
+ } while (block_nr != 0);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int
+CacheFile::writeFile(BYTE *data, int size) {
+ if ((data) && (size > 0)) {
+ int nr_blocks_required = 1 + (size / BLOCK_SIZE);
+ int count = 0;
+ int s = 0;
+ int stored_alloc;
+ int alloc;
+
+ stored_alloc = alloc = allocateBlock();
+
+ do {
+ int copy_alloc = alloc;
+
+ Block *block = lockBlock(copy_alloc);
+
+ block->next = 0;
+
+ memcpy(block->data, data + s, (s + BLOCK_SIZE > size) ? size - s : BLOCK_SIZE);
+
+ if (count + 1 < nr_blocks_required)
+ alloc = block->next = allocateBlock();
+
+ unlockBlock(copy_alloc);
+
+ s += BLOCK_SIZE;
+ } while (++count < nr_blocks_required);
+
+ return stored_alloc;
+ }
+
+ return 0;
+}
+
+void
+CacheFile::deleteFile(int nr) {
+ do {
+ Block *block = lockBlock(nr);
+
+ if (block == NULL)
+ break;
+
+ int next = block->next;
+
+ unlockBlock(nr);
+
+ deleteBlock(nr);
+
+ nr = next;
+ } while (nr != 0);
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/ColorLookup.cpp b/plugins/AdvaImg/src/FreeImage/ColorLookup.cpp
new file mode 100644
index 0000000000..3e81abdb95
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ColorLookup.cpp
@@ -0,0 +1,782 @@
+// ==========================================================
+// X11 and SVG Color name lookup
+//
+// Design and implementation by
+// - Karl-Heinz Bussian (khbussian@moss.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+//
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// RGB color names ---------------------------------------------------------
+
+typedef struct tagNamedColor {
+ const char *name; // color name
+ BYTE r; // red value
+ BYTE g; // green value
+ BYTE b; // blue value
+} NamedColor;
+
+// --------------------------------------------------------------------------
+
+/**
+Helper function : perform a binary search on a color array
+@param name Color name
+@param color_array Color array
+@param n Length of the color array
+@return Returns the color index in the array if successful, returns -1 otherwise
+*/
+static int
+binsearch(const char *name, const NamedColor *color_array, int n) {
+ int cond, low, mid, high;
+
+ low = 0;
+ high = n - 1;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if ((cond = strcmp(name, color_array[mid].name)) < 0)
+ high = mid - 1;
+ else if (cond > 0)
+ low = mid + 1;
+ else
+ return mid;
+ }
+ return -1;
+}
+
+/**
+Perform a binary search on a color array
+@param szColor Color name
+@param color_array Color array
+@param ncolors Length of the color array
+@return Returns the color index in the array if successful, returns -1 otherwise
+*/
+static int
+FreeImage_LookupNamedColor(const char *szColor, const NamedColor *color_array, int ncolors) {
+ int i;
+ char color[64];
+
+ // make lower case name, squezze white space
+
+ for (i = 0; szColor[i] && i < sizeof(color) - 1; i++) {
+ if (isspace(szColor[i]))
+ continue;
+ if (isupper(szColor[i]))
+ color[i] = (char)tolower(szColor[i]);
+ else
+ color[i] = szColor[i];
+ }
+ color[i] = 0;
+
+ return (binsearch(color, color_array, ncolors));
+}
+
+// ==========================================================
+// X11 Color name lookup
+
+/**
+ This big list of color names was formed from the file: /usr/X11R6/lib/X11/rgb.txt
+ found on a standard Linux installation.
+*/
+
+static NamedColor X11ColorMap[] = {
+ { "aliceblue", 240, 248, 255 },
+ { "antiquewhite", 250, 235, 215 },
+ { "antiquewhite1", 255, 239, 219 },
+ { "antiquewhite2", 238, 223, 204 },
+ { "antiquewhite3", 205, 192, 176 },
+ { "antiquewhite4", 139, 131, 120 },
+ { "aquamarine", 127, 255, 212 },
+ { "aquamarine1", 127, 255, 212 },
+ { "aquamarine2", 118, 238, 198 },
+ { "aquamarine3", 102, 205, 170 },
+ { "aquamarine4", 69, 139, 116 },
+ { "azure", 240, 255, 255 },
+ { "azure1", 240, 255, 255 },
+ { "azure2", 224, 238, 238 },
+ { "azure3", 193, 205, 205 },
+ { "azure4", 131, 139, 139 },
+ { "beige", 245, 245, 220 },
+ { "bisque", 255, 228, 196 },
+ { "bisque1", 255, 228, 196 },
+ { "bisque2", 238, 213, 183 },
+ { "bisque3", 205, 183, 158 },
+ { "bisque4", 139, 125, 107 },
+ { "black", 0, 0, 0 },
+ { "blanchedalmond", 255, 235, 205 },
+ { "blue", 0, 0, 255 },
+ { "blue1", 0, 0, 255 },
+ { "blue2", 0, 0, 238 },
+ { "blue3", 0, 0, 205 },
+ { "blue4", 0, 0, 139 },
+ { "blueviolet", 138, 43, 226 },
+ { "brown", 165, 42, 42 },
+ { "brown1", 255, 64, 64 },
+ { "brown2", 238, 59, 59 },
+ { "brown3", 205, 51, 51 },
+ { "brown4", 139, 35, 35 },
+ { "burlywood", 222, 184, 135 },
+ { "burlywood1", 255, 211, 155 },
+ { "burlywood2", 238, 197, 145 },
+ { "burlywood3", 205, 170, 125 },
+ { "burlywood4", 139, 115, 85 },
+ { "cadetblue", 95, 158, 160 },
+ { "cadetblue1", 152, 245, 255 },
+ { "cadetblue2", 142, 229, 238 },
+ { "cadetblue3", 122, 197, 205 },
+ { "cadetblue4", 83, 134, 139 },
+ { "chartreuse", 127, 255, 0 },
+ { "chartreuse1", 127, 255, 0 },
+ { "chartreuse2", 118, 238, 0 },
+ { "chartreuse3", 102, 205, 0 },
+ { "chartreuse4", 69, 139, 0 },
+ { "chocolate", 210, 105, 30 },
+ { "chocolate1", 255, 127, 36 },
+ { "chocolate2", 238, 118, 33 },
+ { "chocolate3", 205, 102, 29 },
+ { "chocolate4", 139, 69, 19 },
+ { "coral", 255, 127, 80 },
+ { "coral1", 255, 114, 86 },
+ { "coral2", 238, 106, 80 },
+ { "coral3", 205, 91, 69 },
+ { "coral4", 139, 62, 47 },
+ { "cornflowerblue", 100, 149, 237 },
+ { "cornsilk", 255, 248, 220 },
+ { "cornsilk1", 255, 248, 220 },
+ { "cornsilk2", 238, 232, 205 },
+ { "cornsilk3", 205, 200, 177 },
+ { "cornsilk4", 139, 136, 120 },
+ { "cyan", 0, 255, 255 },
+ { "cyan1", 0, 255, 255 },
+ { "cyan2", 0, 238, 238 },
+ { "cyan3", 0, 205, 205 },
+ { "cyan4", 0, 139, 139 },
+ { "darkblue", 0, 0, 139 },
+ { "darkcyan", 0, 139, 139 },
+ { "darkgoldenrod", 184, 134, 11 },
+ { "darkgoldenrod1", 255, 185, 15 },
+ { "darkgoldenrod2", 238, 173, 14 },
+ { "darkgoldenrod3", 205, 149, 12 },
+ { "darkgoldenrod4", 139, 101, 8 },
+ { "darkgreen", 0, 100, 0 },
+ { "darkkhaki", 189, 183, 107 },
+ { "darkmagenta", 139, 0, 139 },
+ { "darkolivegreen", 85, 107, 47 },
+ { "darkolivegreen1", 202, 255, 112 },
+ { "darkolivegreen2", 188, 238, 104 },
+ { "darkolivegreen3", 162, 205, 90 },
+ { "darkolivegreen4", 110, 139, 61 },
+ { "darkorange", 255, 140, 0 },
+ { "darkorange1", 255, 127, 0 },
+ { "darkorange2", 238, 118, 0 },
+ { "darkorange3", 205, 102, 0 },
+ { "darkorange4", 139, 69, 0 },
+ { "darkorchid", 153, 50, 204 },
+ { "darkorchid1", 191, 62, 255 },
+ { "darkorchid2", 178, 58, 238 },
+ { "darkorchid3", 154, 50, 205 },
+ { "darkorchid4", 104, 34, 139 },
+ { "darkred", 139, 0, 0 },
+ { "darksalmon", 233, 150, 122 },
+ { "darkseagreen", 143, 188, 143 },
+ { "darkseagreen1", 193, 255, 193 },
+ { "darkseagreen2", 180, 238, 180 },
+ { "darkseagreen3", 155, 205, 155 },
+ { "darkseagreen4", 105, 139, 105 },
+ { "darkslateblue", 72, 61, 139 },
+ { "darkslategray", 47, 79, 79 },
+ { "darkslategray1", 151, 255, 255 },
+ { "darkslategray2", 141, 238, 238 },
+ { "darkslategray3", 121, 205, 205 },
+ { "darkslategray4", 82, 139, 139 },
+ { "darkslategrey", 47, 79, 79 },
+ { "darkturquoise", 0, 206, 209 },
+ { "darkviolet", 148, 0, 211 },
+ { "deeppink", 255, 20, 147 },
+ { "deeppink1", 255, 20, 147 },
+ { "deeppink2", 238, 18, 137 },
+ { "deeppink3", 205, 16, 118 },
+ { "deeppink4", 139, 10, 80 },
+ { "deepskyblue", 0, 191, 255 },
+ { "deepskyblue1", 0, 191, 255 },
+ { "deepskyblue2", 0, 178, 238 },
+ { "deepskyblue3", 0, 154, 205 },
+ { "deepskyblue4", 0, 104, 139 },
+ { "dimgray", 105, 105, 105 },
+ { "dimgrey", 105, 105, 105 },
+ { "dodgerblue", 30, 144, 255 },
+ { "dodgerblue1", 30, 144, 255 },
+ { "dodgerblue2", 28, 134, 238 },
+ { "dodgerblue3", 24, 116, 205 },
+ { "dodgerblue4", 16, 78, 139 },
+ { "firebrick", 178, 34, 34 },
+ { "firebrick1", 255, 48, 48 },
+ { "firebrick2", 238, 44, 44 },
+ { "firebrick3", 205, 38, 38 },
+ { "firebrick4", 139, 26, 26 },
+ { "floralwhite", 255, 250, 240 },
+ { "forestgreen", 176, 48, 96 },
+ { "gainsboro", 220, 220, 220 },
+ { "ghostwhite", 248, 248, 255 },
+ { "gold", 255, 215, 0 },
+ { "gold1", 255, 215, 0 },
+ { "gold2", 238, 201, 0 },
+ { "gold3", 205, 173, 0 },
+ { "gold4", 139, 117, 0 },
+ { "goldenrod", 218, 165, 32 },
+ { "goldenrod1", 255, 193, 37 },
+ { "goldenrod2", 238, 180, 34 },
+ { "goldenrod3", 205, 155, 29 },
+ { "goldenrod4", 139, 105, 20 },
+ { "gray", 190, 190, 190 },
+ { "green", 0, 255, 0 },
+ { "green1", 0, 255, 0 },
+ { "green2", 0, 238, 0 },
+ { "green3", 0, 205, 0 },
+ { "green4", 0, 139, 0 },
+ { "greenyellow", 173, 255, 47 },
+ { "grey", 190, 190, 190 },
+ { "honeydew", 240, 255, 240 },
+ { "honeydew1", 240, 255, 240 },
+ { "honeydew2", 224, 238, 224 },
+ { "honeydew3", 193, 205, 193 },
+ { "honeydew4", 131, 139, 131 },
+ { "hotpink", 255, 105, 180 },
+ { "hotpink1", 255, 110, 180 },
+ { "hotpink2", 238, 106, 167 },
+ { "hotpink3", 205, 96, 144 },
+ { "hotpink4", 139, 58, 98 },
+ { "indianred", 205, 92, 92 },
+ { "indianred1", 255, 106, 106 },
+ { "indianred2", 238, 99, 99 },
+ { "indianred3", 205, 85, 85 },
+ { "indianred4", 139, 58, 58 },
+ { "ivory", 255, 255, 240 },
+ { "ivory1", 255, 255, 240 },
+ { "ivory2", 238, 238, 224 },
+ { "ivory3", 205, 205, 193 },
+ { "ivory4", 139, 139, 131 },
+ { "khaki", 240, 230, 140 },
+ { "khaki1", 255, 246, 143 },
+ { "khaki2", 238, 230, 133 },
+ { "khaki3", 205, 198, 115 },
+ { "khaki4", 139, 134, 78 },
+ { "lavender", 230, 230, 250 },
+ { "lavenderblush", 255, 240, 245 },
+ { "lavenderblush1", 255, 240, 245 },
+ { "lavenderblush2", 238, 224, 229 },
+ { "lavenderblush3", 205, 193, 197 },
+ { "lavenderblush4", 139, 131, 134 },
+ { "lawngreen", 124, 252, 0 },
+ { "lemonchiffon", 255, 250, 205 },
+ { "lemonchiffon1", 255, 250, 205 },
+ { "lemonchiffon2", 238, 233, 191 },
+ { "lemonchiffon3", 205, 201, 165 },
+ { "lemonchiffon4", 139, 137, 112 },
+ { "lightblue", 173, 216, 230 },
+ { "lightblue1", 191, 239, 255 },
+ { "lightblue2", 178, 223, 238 },
+ { "lightblue3", 154, 192, 205 },
+ { "lightblue4", 104, 131, 139 },
+ { "lightcoral", 240, 128, 128 },
+ { "lightcyan", 224, 255, 255 },
+ { "lightcyan1", 224, 255, 255 },
+ { "lightcyan2", 209, 238, 238 },
+ { "lightcyan3", 180, 205, 205 },
+ { "lightcyan4", 122, 139, 139 },
+ { "lightgoldenrod", 238, 221, 130 },
+ { "lightgoldenrod1", 255, 236, 139 },
+ { "lightgoldenrod2", 238, 220, 130 },
+ { "lightgoldenrod3", 205, 190, 112 },
+ { "lightgoldenrod4", 139, 129, 76 },
+ { "lightgoldenrodyellow", 250, 250, 210 },
+ { "lightgray", 211, 211, 211 },
+ { "lightgreen", 144, 238, 144 },
+ { "lightgrey", 211, 211, 211 },
+ { "lightpink", 255, 182, 193 },
+ { "lightpink1", 255, 174, 185 },
+ { "lightpink2", 238, 162, 173 },
+ { "lightpink3", 205, 140, 149 },
+ { "lightpink4", 139, 95, 101 },
+ { "lightsalmon", 255, 160, 122 },
+ { "lightsalmon1", 255, 160, 122 },
+ { "lightsalmon2", 238, 149, 114 },
+ { "lightsalmon3", 205, 129, 98 },
+ { "lightsalmon4", 139, 87, 66 },
+ { "lightseagreen", 32, 178, 170 },
+ { "lightskyblue", 135, 206, 250 },
+ { "lightskyblue1", 176, 226, 255 },
+ { "lightskyblue2", 164, 211, 238 },
+ { "lightskyblue3", 141, 182, 205 },
+ { "lightskyblue4", 96, 123, 139 },
+ { "lightslateblue", 132, 112, 255 },
+ { "lightslategray", 119, 136, 153 },
+ { "lightslategrey", 119, 136, 153 },
+ { "lightsteelblue", 176, 196, 222 },
+ { "lightsteelblue1", 202, 225, 255 },
+ { "lightsteelblue2", 188, 210, 238 },
+ { "lightsteelblue3", 162, 181, 205 },
+ { "lightsteelblue4", 110, 123, 139 },
+ { "lightyellow", 255, 255, 224 },
+ { "lightyellow1", 255, 255, 224 },
+ { "lightyellow2", 238, 238, 209 },
+ { "lightyellow3", 205, 205, 180 },
+ { "lightyellow4", 139, 139, 122 },
+ { "limegreen", 50, 205, 50 },
+ { "linen", 250, 240, 230 },
+ { "magenta", 255, 0, 255 },
+ { "magenta1", 255, 0, 255 },
+ { "magenta2", 238, 0, 238 },
+ { "magenta3", 205, 0, 205 },
+ { "magenta4", 139, 0, 139 },
+ { "maroon", 0, 255, 255 },
+ { "maroon1", 255, 52, 179 },
+ { "maroon2", 238, 48, 167 },
+ { "maroon3", 205, 41, 144 },
+ { "maroon4", 139, 28, 98 },
+ { "mediumaquamarine", 102, 205, 170 },
+ { "mediumblue", 0, 0, 205 },
+ { "mediumorchid", 186, 85, 211 },
+ { "mediumorchid1", 224, 102, 255 },
+ { "mediumorchid2", 209, 95, 238 },
+ { "mediumorchid3", 180, 82, 205 },
+ { "mediumorchid4", 122, 55, 139 },
+ { "mediumpurple", 147, 112, 219 },
+ { "mediumpurple1", 171, 130, 255 },
+ { "mediumpurple2", 159, 121, 238 },
+ { "mediumpurple3", 137, 104, 205 },
+ { "mediumpurple4", 93, 71, 139 },
+ { "mediumseagreen", 60, 179, 113 },
+ { "mediumslateblue", 123, 104, 238 },
+ { "mediumspringgreen", 0, 250, 154 },
+ { "mediumturquoise", 72, 209, 204 },
+ { "mediumvioletred", 199, 21, 133 },
+ { "midnightblue", 25, 25, 112 },
+ { "mintcream", 245, 255, 250 },
+ { "mistyrose", 255, 228, 225 },
+ { "mistyrose1", 255, 228, 225 },
+ { "mistyrose2", 238, 213, 210 },
+ { "mistyrose3", 205, 183, 181 },
+ { "mistyrose4", 139, 125, 123 },
+ { "moccasin", 255, 228, 181 },
+ { "navajowhite", 255, 222, 173 },
+ { "navajowhite1", 255, 222, 173 },
+ { "navajowhite2", 238, 207, 161 },
+ { "navajowhite3", 205, 179, 139 },
+ { "navajowhite4", 139, 121, 94 },
+ { "navy", 0, 0, 128 },
+ { "navyblue", 0, 0, 128 },
+ { "oldlace", 253, 245, 230 },
+ { "olivedrab", 107, 142, 35 },
+ { "olivedrab1", 192, 255, 62 },
+ { "olivedrab2", 179, 238, 58 },
+ { "olivedrab3", 154, 205, 50 },
+ { "olivedrab4", 105, 139, 34 },
+ { "orange", 255, 165, 0 },
+ { "orange1", 255, 165, 0 },
+ { "orange2", 238, 154, 0 },
+ { "orange3", 205, 133, 0 },
+ { "orange4", 139, 90, 0 },
+ { "orangered", 255, 69, 0 },
+ { "orangered1", 255, 69, 0 },
+ { "orangered2", 238, 64, 0 },
+ { "orangered3", 205, 55, 0 },
+ { "orangered4", 139, 37, 0 },
+ { "orchid", 218, 112, 214 },
+ { "orchid1", 255, 131, 250 },
+ { "orchid2", 238, 122, 233 },
+ { "orchid3", 205, 105, 201 },
+ { "orchid4", 139, 71, 137 },
+ { "palegoldenrod", 238, 232, 170 },
+ { "palegreen", 152, 251, 152 },
+ { "palegreen1", 154, 255, 154 },
+ { "palegreen2", 144, 238, 144 },
+ { "palegreen3", 124, 205, 124 },
+ { "palegreen4", 84, 139, 84 },
+ { "paleturquoise", 175, 238, 238 },
+ { "paleturquoise1", 187, 255, 255 },
+ { "paleturquoise2", 174, 238, 238 },
+ { "paleturquoise3", 150, 205, 205 },
+ { "paleturquoise4", 102, 139, 139 },
+ { "palevioletred", 219, 112, 147 },
+ { "palevioletred1", 255, 130, 171 },
+ { "palevioletred2", 238, 121, 159 },
+ { "palevioletred3", 205, 104, 137 },
+ { "palevioletred4", 139, 71, 93 },
+ { "papayawhip", 255, 239, 213 },
+ { "peachpuff", 255, 218, 185 },
+ { "peachpuff1", 255, 218, 185 },
+ { "peachpuff2", 238, 203, 173 },
+ { "peachpuff3", 205, 175, 149 },
+ { "peachpuff4", 139, 119, 101 },
+ { "peru", 205, 133, 63 },
+ { "pink", 255, 192, 203 },
+ { "pink1", 255, 181, 197 },
+ { "pink2", 238, 169, 184 },
+ { "pink3", 205, 145, 158 },
+ { "pink4", 139, 99, 108 },
+ { "plum", 221, 160, 221 },
+ { "plum1", 255, 187, 255 },
+ { "plum2", 238, 174, 238 },
+ { "plum3", 205, 150, 205 },
+ { "plum4", 139, 102, 139 },
+ { "powderblue", 176, 224, 230 },
+ { "purple", 160, 32, 240 },
+ { "purple1", 155, 48, 255 },
+ { "purple2", 145, 44, 238 },
+ { "purple3", 125, 38, 205 },
+ { "purple4", 85, 26, 139 },
+ { "red", 255, 0, 0 },
+ { "red1", 255, 0, 0 },
+ { "red2", 238, 0, 0 },
+ { "red3", 205, 0, 0 },
+ { "red4", 139, 0, 0 },
+ { "rosybrown", 188, 143, 143 },
+ { "rosybrown1", 255, 193, 193 },
+ { "rosybrown2", 238, 180, 180 },
+ { "rosybrown3", 205, 155, 155 },
+ { "rosybrown4", 139, 105, 105 },
+ { "royalblue", 65, 105, 225 },
+ { "royalblue1", 72, 118, 255 },
+ { "royalblue2", 67, 110, 238 },
+ { "royalblue3", 58, 95, 205 },
+ { "royalblue4", 39, 64, 139 },
+ { "saddlebrown", 139, 69, 19 },
+ { "salmon", 250, 128, 114 },
+ { "salmon1", 255, 140, 105 },
+ { "salmon2", 238, 130, 98 },
+ { "salmon3", 205, 112, 84 },
+ { "salmon4", 139, 76, 57 },
+ { "sandybrown", 244, 164, 96 },
+ { "seagreen", 46, 139, 87 },
+ { "seagreen1", 84, 255, 159 },
+ { "seagreen2", 78, 238, 148 },
+ { "seagreen3", 67, 205, 128 },
+ { "seagreen4", 46, 139, 87 },
+ { "seashell", 255, 245, 238 },
+ { "seashell1", 255, 245, 238 },
+ { "seashell2", 238, 229, 222 },
+ { "seashell3", 205, 197, 191 },
+ { "seashell4", 139, 134, 130 },
+ { "sienna", 160, 82, 45 },
+ { "sienna1", 255, 130, 71 },
+ { "sienna2", 238, 121, 66 },
+ { "sienna3", 205, 104, 57 },
+ { "sienna4", 139, 71, 38 },
+ { "skyblue", 135, 206, 235 },
+ { "skyblue1", 135, 206, 255 },
+ { "skyblue2", 126, 192, 238 },
+ { "skyblue3", 108, 166, 205 },
+ { "skyblue4", 74, 112, 139 },
+ { "slateblue", 106, 90, 205 },
+ { "slateblue1", 131, 111, 255 },
+ { "slateblue2", 122, 103, 238 },
+ { "slateblue3", 105, 89, 205 },
+ { "slateblue4", 71, 60, 139 },
+ { "slategray", 112, 128, 144 },
+ { "slategray1", 198, 226, 255 },
+ { "slategray2", 185, 211, 238 },
+ { "slategray3", 159, 182, 205 },
+ { "slategray4", 108, 123, 139 },
+ { "slategrey", 112, 128, 144 },
+ { "snow", 255, 250, 250 },
+ { "snow1", 255, 250, 250 },
+ { "snow2", 238, 233, 233 },
+ { "snow3", 205, 201, 201 },
+ { "snow4", 139, 137, 137 },
+ { "springgreen", 0, 255, 127 },
+ { "springgreen1", 0, 255, 127 },
+ { "springgreen2", 0, 238, 118 },
+ { "springgreen3", 0, 205, 102 },
+ { "springgreen4", 0, 139, 69 },
+ { "steelblue", 70, 130, 180 },
+ { "steelblue1", 99, 184, 255 },
+ { "steelblue2", 92, 172, 238 },
+ { "steelblue3", 79, 148, 205 },
+ { "steelblue4", 54, 100, 139 },
+ { "tan", 210, 180, 140 },
+ { "tan1", 255, 165, 79 },
+ { "tan2", 238, 154, 73 },
+ { "tan3", 205, 133, 63 },
+ { "tan4", 139, 90, 43 },
+ { "thistle", 216, 191, 216 },
+ { "thistle1", 255, 225, 255 },
+ { "thistle2", 238, 210, 238 },
+ { "thistle3", 205, 181, 205 },
+ { "thistle4", 139, 123, 139 },
+ { "tomato", 255, 99, 71 },
+ { "tomato1", 255, 99, 71 },
+ { "tomato2", 238, 92, 66 },
+ { "tomato3", 205, 79, 57 },
+ { "tomato4", 139, 54, 38 },
+ { "turquoise", 64, 224, 208 },
+ { "turquoise1", 0, 245, 255 },
+ { "turquoise2", 0, 229, 238 },
+ { "turquoise3", 0, 197, 205 },
+ { "turquoise4", 0, 134, 139 },
+ { "violet", 238, 130, 238 },
+ { "violetred", 208, 32, 144 },
+ { "violetred1", 255, 62, 150 },
+ { "violetred2", 238, 58, 140 },
+ { "violetred3", 205, 50, 120 },
+ { "violetred4", 139, 34, 82 },
+ { "wheat", 245, 222, 179 },
+ { "wheat1", 255, 231, 186 },
+ { "wheat2", 238, 216, 174 },
+ { "wheat3", 205, 186, 150 },
+ { "wheat4", 139, 126, 102 },
+ { "white", 255, 255, 255 },
+ { "whitesmoke", 245, 245, 245 },
+ { "yellow", 255, 255, 0 },
+ { "yellow1", 255, 255, 0 },
+ { "yellow2", 238, 238, 0 },
+ { "yellow3", 205, 205, 0 },
+ { "yellow4", 139, 139, 0 },
+ { "yellowgreen", 154, 205, 50 }
+};
+
+
+BOOL DLL_CALLCONV
+FreeImage_LookupX11Color(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue) {
+ int i;
+
+ // lookup color
+ i = FreeImage_LookupNamedColor(szColor, X11ColorMap, sizeof(X11ColorMap)/sizeof(X11ColorMap[0]));
+ if (i >= 0) {
+ *nRed = X11ColorMap[i].r;
+ *nGreen = X11ColorMap[i].g;
+ *nBlue = X11ColorMap[i].b;
+ return TRUE;
+ }
+
+ // not found, try for grey color with attached percent value
+ if ( (szColor[0] == 'g' || szColor[0] == 'G') &&
+ (szColor[1] == 'r' || szColor[1] == 'R') &&
+ (szColor[2] == 'e' || szColor[2] == 'E' || szColor[2] == 'a' || szColor[2] == 'A' ) &&
+ (szColor[3] == 'y' || szColor[3] == 'Y' )) {
+
+ // grey<num>, or gray<num>, num 1...100
+ i = strtol(szColor+4, NULL, 10);
+ *nRed = (BYTE)(255.0/100.0 * i);
+ *nGreen = *nRed;
+ *nBlue = *nRed;
+
+ return TRUE;
+ }
+
+ // not found at all
+ *nRed = 0;
+ *nGreen = 0;
+ *nBlue = 0;
+
+ return FALSE;
+}
+
+// ==========================================================
+// SVG Color name lookup
+
+/**
+ These are the colors defined in the SVG standard (I haven't checked
+ the final recommendation for changes)
+*/
+static NamedColor SVGColorMap[] = {
+ { "aliceblue", 240, 248, 255 },
+ { "antiquewhite", 250, 235, 215 },
+ { "aqua", 0, 255, 255 },
+ { "aquamarine", 127, 255, 212 },
+ { "azure", 240, 255, 255 },
+ { "beige", 245, 245, 220 },
+ { "bisque", 255, 228, 196 },
+ { "black", 0, 0, 0 },
+ { "blanchedalmond", 255, 235, 205 },
+ { "blue", 0, 0, 255 },
+ { "blueviolet", 138, 43, 226 },
+ { "brown", 165, 42, 42 },
+ { "burlywood", 222, 184, 135 },
+ { "cadetblue", 95, 158, 160 },
+ { "chartreuse", 127, 255, 0 },
+ { "chocolate", 210, 105, 30 },
+ { "coral", 255, 127, 80 },
+ { "cornflowerblue", 100, 149, 237 },
+ { "cornsilk", 255, 248, 220 },
+ { "crimson", 220, 20, 60 },
+ { "cyan", 0, 255, 255 },
+ { "darkblue", 0, 0, 139 },
+ { "darkcyan", 0, 139, 139 },
+ { "darkgoldenrod", 184, 134, 11 },
+ { "darkgray", 169, 169, 169 },
+ { "darkgreen", 0, 100, 0 },
+ { "darkgrey", 169, 169, 169 },
+ { "darkkhaki", 189, 183, 107 },
+ { "darkmagenta", 139, 0, 139 },
+ { "darkolivegreen", 85, 107, 47 },
+ { "darkorange", 255, 140, 0 },
+ { "darkorchid", 153, 50, 204 },
+ { "darkred", 139, 0, 0 },
+ { "darksalmon", 233, 150, 122 },
+ { "darkseagreen", 143, 188, 143 },
+ { "darkslateblue", 72, 61, 139 },
+ { "darkslategray", 47, 79, 79 },
+ { "darkslategrey", 47, 79, 79 },
+ { "darkturquoise", 0, 206, 209 },
+ { "darkviolet", 148, 0, 211 },
+ { "deeppink", 255, 20, 147 },
+ { "deepskyblue", 0, 191, 255 },
+ { "dimgray", 105, 105, 105 },
+ { "dimgrey", 105, 105, 105 },
+ { "dodgerblue", 30, 144, 255 },
+ { "firebrick", 178, 34, 34 },
+ { "floralwhite", 255, 250, 240 },
+ { "forestgreen", 34, 139, 34 },
+ { "fuchsia", 255, 0, 255 },
+ { "gainsboro", 220, 220, 220 },
+ { "ghostwhite", 248, 248, 255 },
+ { "gold", 255, 215, 0 },
+ { "goldenrod", 218, 165, 32 },
+ { "gray", 128, 128, 128 },
+ { "grey", 128, 128, 128 },
+ { "green", 0, 128, 0 },
+ { "greenyellow", 173, 255, 47 },
+ { "honeydew", 240, 255, 240 },
+ { "hotpink", 255, 105, 180 },
+ { "indianred", 205, 92, 92 },
+ { "indigo", 75, 0, 130 },
+ { "ivory", 255, 255, 240 },
+ { "khaki", 240, 230, 140 },
+ { "lavender", 230, 230, 250 },
+ { "lavenderblush", 255, 240, 245 },
+ { "lawngreen", 124, 252, 0 },
+ { "lemonchiffon", 255, 250, 205 },
+ { "lightblue", 173, 216, 230 },
+ { "lightcoral", 240, 128, 128 },
+ { "lightcyan", 224, 255, 255 },
+ { "lightgoldenrodyellow", 250, 250, 210 },
+ { "lightgray", 211, 211, 211 },
+ { "lightgreen", 144, 238, 144 },
+ { "lightgrey", 211, 211, 211 },
+ { "lightpink", 255, 182, 193 },
+ { "lightsalmon", 255, 160, 122 },
+ { "lightseagreen", 32, 178, 170 },
+ { "lightskyblue", 135, 206, 250 },
+ { "lightslategray", 119, 136, 153 },
+ { "lightslategrey", 119, 136, 153 },
+ { "lightsteelblue", 176, 196, 222 },
+ { "lightyellow", 255, 255, 224 },
+ { "lime", 0, 255, 0 },
+ { "limegreen", 50, 205, 50 },
+ { "linen", 250, 240, 230 },
+ { "magenta", 255, 0, 255 },
+ { "maroon", 128, 0, 0 },
+ { "mediumaquamarine", 102, 205, 170 },
+ { "mediumblue", 0, 0, 205 },
+ { "mediumorchid", 186, 85, 211 },
+ { "mediumpurple", 147, 112, 219 },
+ { "mediumseagreen", 60, 179, 113 },
+ { "mediumslateblue", 123, 104, 238 },
+ { "mediumspringgreen", 0, 250, 154 },
+ { "mediumturquoise", 72, 209, 204 },
+ { "mediumvioletred", 199, 21, 133 },
+ { "midnightblue", 25, 25, 112 },
+ { "mintcream", 245, 255, 250 },
+ { "mistyrose", 255, 228, 225 },
+ { "moccasin", 255, 228, 181 },
+ { "navajowhite", 255, 222, 173 },
+ { "navy", 0, 0, 128 },
+ { "oldlace", 253, 245, 230 },
+ { "olive", 128, 128, 0 },
+ { "olivedrab", 107, 142, 35 },
+ { "orange", 255, 165, 0 },
+ { "orangered", 255, 69, 0 },
+ { "orchid", 218, 112, 214 },
+ { "palegoldenrod", 238, 232, 170 },
+ { "palegreen", 152, 251, 152 },
+ { "paleturquoise", 175, 238, 238 },
+ { "palevioletred", 219, 112, 147 },
+ { "papayawhip", 255, 239, 213 },
+ { "peachpuff", 255, 218, 185 },
+ { "peru", 205, 133, 63 },
+ { "pink", 255, 192, 203 },
+ { "plum", 221, 160, 221 },
+ { "powderblue", 176, 224, 230 },
+ { "purple", 128, 0, 128 },
+ { "red", 255, 0, 0 },
+ { "rosybrown", 188, 143, 143 },
+ { "royalblue", 65, 105, 225 },
+ { "saddlebrown", 139, 69, 19 },
+ { "salmon", 250, 128, 114 },
+ { "sandybrown", 244, 164, 96 },
+ { "seagreen", 46, 139, 87 },
+ { "seashell", 255, 245, 238 },
+ { "sienna", 160, 82, 45 },
+ { "silver", 192, 192, 192 },
+ { "skyblue", 135, 206, 235 },
+ { "slateblue", 106, 90, 205 },
+ { "slategray", 112, 128, 144 },
+ { "slategrey", 112, 128, 144 },
+ { "snow", 255, 250, 250 },
+ { "springgreen", 0, 255, 127 },
+ { "steelblue", 70, 130, 180 },
+ { "tan", 210, 180, 140 },
+ { "teal", 0, 128, 128 },
+ { "thistle", 216, 191, 216 },
+ { "tomato", 255, 99, 71 },
+ { "turquoise", 64, 224, 208 },
+ { "violet", 238, 130, 238 },
+ { "wheat", 245, 222, 179 },
+ { "white", 255, 255, 255 },
+ { "whitesmoke", 245, 245, 245 },
+ { "yellow", 255, 255, 0 },
+ { "yellowgreen", 154, 205, 50 }
+};
+
+
+BOOL DLL_CALLCONV
+FreeImage_LookupSVGColor(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nBlue) {
+ int i;
+
+ // lookup color
+ i = FreeImage_LookupNamedColor(szColor, SVGColorMap, sizeof(SVGColorMap)/sizeof(SVGColorMap[0]));
+ if (i >= 0) {
+ *nRed = SVGColorMap[i].r;
+ *nGreen = SVGColorMap[i].g;
+ *nBlue = SVGColorMap[i].b;
+ return TRUE;
+ }
+
+ // not found, try for grey color with attached percent value
+ if ( (szColor[0] == 'g' || szColor[0] == 'G') &&
+ (szColor[1] == 'r' || szColor[1] == 'R') &&
+ (szColor[2] == 'e' || szColor[2] == 'E' || szColor[2] == 'a' || szColor[2] == 'A' ) &&
+ (szColor[3] == 'y' || szColor[3] == 'Y' )) {
+
+ // grey<num>, or gray<num>, num 1...100
+ i = strtol(szColor+4, NULL, 10);
+ *nRed = (BYTE)(255.0/100.0 * i);
+ *nGreen = *nRed;
+ *nBlue = *nRed;
+ return TRUE;
+ }
+
+ // not found at all
+ *nRed = 0;
+ *nGreen = 0;
+ *nBlue = 0;
+
+ return FALSE;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/Conversion.cpp b/plugins/AdvaImg/src/FreeImage/Conversion.cpp
new file mode 100644
index 0000000000..3c14bab9dd
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Conversion.cpp
@@ -0,0 +1,513 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "Quantizers.h"
+
+// ----------------------------------------------------------
+
+#define CONVERT(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib)); break;
+#define CONVERTWITHPALETTE(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); break;
+
+#define CONVERTTO16(from) \
+ case 16 : \
+ if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \
+ FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib)); \
+ } else { \
+ FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib)); \
+ } \
+ break;
+
+#define CONVERTTO16WITHPALETTE(from) \
+ case 16 : \
+ if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \
+ FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \
+ } else { \
+ FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \
+ } \
+ break;
+
+// ==========================================================
+// Utility functions declared in Utilities.h
+
+BOOL SwapRedBlue32(FIBITMAP* dib) {
+ if(FreeImage_GetImageType(dib) != FIT_BITMAP) {
+ return FALSE;
+ }
+
+ const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8;
+ if(bytesperpixel > 4 || bytesperpixel < 3) {
+ return FALSE;
+ }
+
+ const unsigned height = FreeImage_GetHeight(dib);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+ const unsigned lineSize = FreeImage_GetLine(dib);
+
+ BYTE* line = FreeImage_GetBits(dib);
+ for(unsigned y = 0; y < height; ++y, line += pitch) {
+ for(BYTE* pixel = line; pixel < line + lineSize ; pixel += bytesperpixel) {
+ INPLACESWAP(pixel[0], pixel[2]);
+ }
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static inline void
+assignRGB(WORD r, WORD g, WORD b, WORD* out) {
+ out[0] = r;
+ out[1] = g;
+ out[2] = b;
+}
+
+static inline void
+assignRGB(BYTE r, BYTE g, BYTE b, BYTE* out) {
+ out[FI_RGBA_RED] = r;
+ out[FI_RGBA_GREEN] = g;
+ out[FI_RGBA_BLUE] = b;
+}
+
+/**
+CMYK -> CMY -> RGB conversion from http://www.easyrgb.com/
+
+CMYK to CMY [0-1]: C,M,Y * (1 - K) + K
+CMY to RGB [0-1]: (1 - C,M,Y)
+
+=> R,G,B = (1 - C,M,Y) * (1 - K)
+mapped to [0-MAX_VAL]:
+(MAX_VAL - C,M,Y) * (MAX_VAL - K) / MAX_VAL
+*/
+template <class T>
+static inline void
+CMYKToRGB(T C, T M, T Y, T K, T* out) {
+ unsigned max_val = std::numeric_limits<T>::max();
+
+ unsigned r = (max_val - C) * (max_val - K) / max_val;
+ unsigned g = (max_val - M) * (max_val - K) / max_val;
+ unsigned b = (max_val - Y) * (max_val - K) / max_val;
+
+ // clamp values to [0..max_val]
+ T red = (T)CLAMP(r, (unsigned)0, max_val);
+ T green = (T)CLAMP(g, (unsigned)0, max_val);
+ T blue = (T)CLAMP(b, (unsigned)0, max_val);
+
+ assignRGB(red, green, blue, out);
+}
+
+template <class T>
+static void
+_convertCMYKtoRGBA(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) {
+ const BOOL hasBlack = (samplesperpixel > 3) ? TRUE : FALSE;
+ const T MAX_VAL = std::numeric_limits<T>::max();
+
+ T K = 0;
+ for(unsigned y = 0; y < height; y++) {
+ T *line = (T*)line_start;
+
+ for(unsigned x = 0; x < width; x++) {
+ if(hasBlack) {
+ K = line[FI_RGBA_ALPHA];
+ line[FI_RGBA_ALPHA] = MAX_VAL; // TODO write the first extra channel as alpha!
+ }
+
+ CMYKToRGB<T>(line[0], line[1], line[2], K, line);
+
+ line += samplesperpixel;
+ }
+ line_start += pitch;
+ }
+}
+
+BOOL
+ConvertCMYKtoRGBA(FIBITMAP* dib) {
+ if (!FreeImage_HasPixels(dib)) {
+ return FALSE;
+ }
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ const unsigned bytesperpixel = FreeImage_GetBPP(dib)/8;
+
+ unsigned channelSize = 1;
+ if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) {
+ channelSize = sizeof(WORD);
+ } else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) {
+ return FALSE;
+ }
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ BYTE *line_start = FreeImage_GetScanLine(dib, 0);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+
+ unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize;
+
+ if(channelSize == sizeof(WORD)) {
+ _convertCMYKtoRGBA<WORD>(width, height, line_start, pitch, samplesperpixel);
+ } else {
+ _convertCMYKtoRGBA<BYTE>(width, height, line_start, pitch, samplesperpixel);
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+/**
+CIELab -> XYZ conversion from http://www.easyrgb.com/
+*/
+static void
+CIELabToXYZ(float L, float a, float b, float *X, float *Y, float *Z) {
+ float pow_3;
+
+ // CIELab -> XYZ conversion
+ // ------------------------
+ float var_Y = (L + 16.F ) / 116.F;
+ float var_X = a / 500.F + var_Y;
+ float var_Z = var_Y - b / 200.F;
+
+ pow_3 = powf(var_Y, 3);
+ if(pow_3 > 0.008856F) {
+ var_Y = pow_3;
+ } else {
+ var_Y = ( var_Y - 16.F / 116.F ) / 7.787F;
+ }
+ pow_3 = powf(var_X, 3);
+ if(pow_3 > 0.008856F) {
+ var_X = pow_3;
+ } else {
+ var_X = ( var_X - 16.F / 116.F ) / 7.787F;
+ }
+ pow_3 = powf(var_Z, 3);
+ if(pow_3 > 0.008856F) {
+ var_Z = pow_3;
+ } else {
+ var_Z = ( var_Z - 16.F / 116.F ) / 7.787F;
+ }
+
+ static const float ref_X = 95.047F;
+ static const float ref_Y = 100.000F;
+ static const float ref_Z = 108.883F;
+
+ *X = ref_X * var_X; // ref_X = 95.047 (Observer= 2°, Illuminant= D65)
+ *Y = ref_Y * var_Y; // ref_Y = 100.000
+ *Z = ref_Z * var_Z; // ref_Z = 108.883
+}
+
+/**
+XYZ -> RGB conversion from http://www.easyrgb.com/
+*/
+static void
+XYZToRGB(float X, float Y, float Z, float *R, float *G, float *B) {
+ float var_X = X / 100; // X from 0 to 95.047 (Observer = 2°, Illuminant = D65)
+ float var_Y = Y / 100; // Y from 0 to 100.000
+ float var_Z = Z / 100; // Z from 0 to 108.883
+
+ float var_R = var_X * 3.2406F + var_Y * -1.5372F + var_Z * -0.4986F;
+ float var_G = var_X * -0.9689F + var_Y * 1.8758F + var_Z * 0.0415F;
+ float var_B = var_X * 0.0557F + var_Y * -0.2040F + var_Z * 1.0570F;
+
+ float exponent = 1.F / 2.4F;
+
+ if(var_R > 0.0031308F) {
+ var_R = 1.055F * powf(var_R, exponent) - 0.055F;
+ } else {
+ var_R = 12.92F * var_R;
+ }
+ if(var_G > 0.0031308F) {
+ var_G = 1.055F * powf(var_G, exponent) - 0.055F;
+ } else {
+ var_G = 12.92F * var_G;
+ }
+ if(var_B > 0.0031308F) {
+ var_B = 1.055F * powf(var_B, exponent) - 0.055F;
+ } else {
+ var_B = 12.92F * var_B;
+ }
+
+ *R = var_R;
+ *G = var_G;
+ *B = var_B;
+}
+
+template<class T>
+static void
+CIELabToRGB(float L, float a, float b, T *rgb) {
+ float X, Y, Z;
+ float R, G, B;
+ const float max_val = std::numeric_limits<T>::max();
+
+ CIELabToXYZ(L, a, b, &X, &Y, &Z);
+ XYZToRGB(X, Y, Z, &R, &G, &B);
+
+ // clamp values to [0..max_val]
+ T red = (T)CLAMP(R * max_val, 0.0F, max_val);
+ T green = (T)CLAMP(G * max_val, 0.0F, max_val);
+ T blue = (T)CLAMP(B * max_val, 0.0F, max_val);
+
+ assignRGB(red, green, blue, rgb);
+}
+
+template<class T>
+static void
+_convertLABtoRGB(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) {
+ const unsigned max_val = std::numeric_limits<T>::max();
+ const float sL = 100.F / max_val;
+ const float sa = 256.F / max_val;
+ const float sb = 256.F / max_val;
+
+ for(unsigned y = 0; y < height; y++) {
+ T *line = (T*)line_start;
+
+ for(unsigned x = 0; x < width; x++) {
+ CIELabToRGB(line[0]* sL, line[1]* sa - 128.F, line[2]* sb - 128.F, line);
+
+ line += samplesperpixel;
+ }
+ line_start += pitch;
+ }
+}
+
+BOOL
+ConvertLABtoRGB(FIBITMAP* dib) {
+ if (!FreeImage_HasPixels(dib)) {
+ return FALSE;
+ }
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8;
+
+ unsigned channelSize = 1;
+ if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) {
+ channelSize = sizeof(WORD);
+ } else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) {
+ return FALSE;
+ }
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ BYTE *line_start = FreeImage_GetScanLine(dib, 0);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+
+ unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize;
+
+ if(channelSize == 1) {
+ _convertLABtoRGB<BYTE>(width, height, line_start, pitch, samplesperpixel);
+ }
+ else {
+ _convertLABtoRGB<WORD>(width, height, line_start, pitch, samplesperpixel);
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP*
+RemoveAlphaChannel(FIBITMAP* src) {
+
+ if (!FreeImage_HasPixels(src)) {
+ return NULL;
+ }
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ if(FreeImage_GetBPP(src) == 32) {
+ // convert to 24-bit
+ return FreeImage_ConvertTo24Bits(src);
+ }
+ break;
+ case FIT_RGBA16:
+ // convert to RGB16
+ return FreeImage_ConvertToRGB16(src);
+ case FIT_RGBAF:
+ // convert to RGBF
+ return FreeImage_ConvertToRGBF(src);
+ default:
+ // unsupported image type
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+// ==========================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ColorQuantize(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize) {
+ return FreeImage_ColorQuantizeEx(dib, quantize);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) {
+ if ( PaletteSize < 2 ) PaletteSize = 2;
+ if ( PaletteSize > 256 ) PaletteSize = 256;
+ if ( ReserveSize < 0 ) ReserveSize = 0;
+ if ( ReserveSize > PaletteSize ) ReserveSize = PaletteSize;
+ if (FreeImage_HasPixels(dib)) {
+ if (FreeImage_GetBPP(dib) == 24) {
+ switch(quantize) {
+ case FIQ_WUQUANT :
+ {
+ try {
+ WuQuantizer Q (dib);
+ FIBITMAP *dst = Q.Quantize(PaletteSize, ReserveSize, ReservePalette);
+ if(dst) {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+ }
+ return dst;
+ } catch (const char *) {
+ return NULL;
+ }
+ }
+ case FIQ_NNQUANT :
+ {
+ // sampling factor in range 1..30.
+ // 1 => slower (but better), 30 => faster. Default value is 1
+ const int sampling = 1;
+
+ NNQuantizer Q(PaletteSize);
+ FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette, sampling);
+ if(dst) {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+ }
+ return dst;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
+ FIBITMAP *dib = FreeImage_Allocate(width, height, bpp, red_mask, green_mask, blue_mask);
+
+ if (dib != NULL) {
+ if (topdown) {
+ for (int i = height - 1; i >= 0; --i) {
+ memcpy(FreeImage_GetScanLine(dib, i), bits, FreeImage_GetLine(dib));
+ bits += pitch;
+ }
+ } else {
+ for (int i = 0; i < height; ++i) {
+ memcpy(FreeImage_GetScanLine(dib, i), bits, FreeImage_GetLine(dib));
+ bits += pitch;
+ }
+ }
+ }
+
+ return dib;
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
+ if (FreeImage_HasPixels(dib) && (bits != NULL)) {
+ for (unsigned i = 0; i < FreeImage_GetHeight(dib); ++i) {
+ BYTE *scanline = FreeImage_GetScanLine(dib, topdown ? (FreeImage_GetHeight(dib) - i - 1) : i);
+
+ if ((bpp == 16) && (FreeImage_GetBPP(dib) == 16)) {
+ // convert 555 to 565 or vice versa
+
+ if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) {
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ FreeImage_ConvertLine16_565_To16_555(bits, scanline, FreeImage_GetWidth(dib));
+ } else {
+ memcpy(bits, scanline, FreeImage_GetLine(dib));
+ }
+ } else {
+ if ((FreeImage_GetRedMask(dib) == FI16_555_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_555_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_555_BLUE_MASK)) {
+ FreeImage_ConvertLine16_555_To16_565(bits, scanline, FreeImage_GetWidth(dib));
+ } else {
+ memcpy(bits, scanline, FreeImage_GetLine(dib));
+ }
+ }
+ } else if (FreeImage_GetBPP(dib) != bpp) {
+ switch(FreeImage_GetBPP(dib)) {
+ case 1 :
+ switch(bpp) {
+ CONVERT(1, 8)
+ CONVERTTO16WITHPALETTE(1)
+ CONVERTWITHPALETTE(1, 24)
+ CONVERTWITHPALETTE(1, 32)
+ }
+
+ break;
+
+ case 4 :
+ switch(bpp) {
+ CONVERT(4, 8)
+ CONVERTTO16WITHPALETTE(4)
+ CONVERTWITHPALETTE(4, 24)
+ CONVERTWITHPALETTE(4, 32)
+ }
+
+ break;
+
+ case 8 :
+ switch(bpp) {
+ CONVERTTO16WITHPALETTE(8)
+ CONVERTWITHPALETTE(8, 24)
+ CONVERTWITHPALETTE(8, 32)
+ }
+
+ break;
+
+ case 24 :
+ switch(bpp) {
+ CONVERT(24, 8)
+ CONVERTTO16(24)
+ CONVERT(24, 32)
+ }
+
+ break;
+
+ case 32 :
+ switch(bpp) {
+ CONVERT(32, 8)
+ CONVERTTO16(32)
+ CONVERT(32, 24)
+ }
+
+ break;
+ }
+ } else {
+ memcpy(bits, scanline, FreeImage_GetLine(dib));
+ }
+
+ bits += pitch;
+ }
+ }
+}
diff --git a/plugins/AdvaImg/src/FreeImage/Conversion16_555.cpp b/plugins/AdvaImg/src/FreeImage/Conversion16_555.cpp
new file mode 100644
index 0000000000..c0c4785704
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Conversion16_555.cpp
@@ -0,0 +1,209 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+
+#define RGB555(b, g, r) ((((b) >> 3) << FI16_555_BLUE_SHIFT) | (((g) >> 3) << FI16_555_GREEN_SHIFT) | (((r) >> 3) << FI16_555_RED_SHIFT))
+
+// ----------------------------------------------------------
+// internal conversions X to 16 bits (555)
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ int index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+ new_bits[cols] = RGB555(palette[index].rgbBlue, palette[index].rgbGreen, palette[index].rgbRed);
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ WORD *new_bits = (WORD *)target;
+ BOOL lonibble = FALSE;
+ int x = 0;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ RGBQUAD *grab_palette;
+
+ if (lonibble) {
+ grab_palette = palette + LOWNIBBLE(source[x++]);
+ } else {
+ grab_palette = palette + (HINIBBLE(source[x]) >> 4);
+ }
+
+ new_bits[cols] = RGB555(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+
+ lonibble = !lonibble;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To16_555(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ RGBQUAD *grab_palette = palette + source[cols];
+
+ new_bits[cols] = RGB555(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16_565_To16_555(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *src_bits = (WORD *)source;
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ new_bits[cols] = RGB555((((src_bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F,
+ (((src_bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+ (((src_bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To16_555(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ new_bits[cols] = RGB555(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+ source += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To16_555(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ new_bits[cols] = RGB555(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+ source += 4;
+ }
+}
+
+// ----------------------------------------------------------
+// smart convert X to 16 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo16Bits555(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) return NULL;
+
+ const int width = FreeImage_GetWidth(dib);
+ const int height = FreeImage_GetHeight(dib);
+ const int bpp = FreeImage_GetBPP(dib);
+
+ if(bpp == 16) {
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ // RGB 565
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+ if(new_dib == NULL) {
+ return NULL;
+ }
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine16_565_To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ return new_dib;
+ } else {
+ // RGB 555
+ return FreeImage_Clone(dib);
+ }
+ }
+ else {
+ // other bpp cases => convert to RGB 555
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ switch (bpp) {
+ case 1 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+
+ return new_dib;
+ }
+
+ case 4 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine4To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+
+ return new_dib;
+ }
+
+ case 8 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine8To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+
+ return new_dib;
+ }
+
+ case 24 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine24To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+
+ return new_dib;
+ }
+
+ case 32 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine32To16_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+
+ return new_dib;
+ }
+
+ default :
+ // unreachable code ...
+ FreeImage_Unload(new_dib);
+ break;
+
+ }
+ }
+
+ return NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/Conversion16_565.cpp b/plugins/AdvaImg/src/FreeImage/Conversion16_565.cpp
new file mode 100644
index 0000000000..28e8255b4e
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Conversion16_565.cpp
@@ -0,0 +1,204 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// internal conversions X to 16 bits (565)
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ int index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+ new_bits[cols] = RGB565(palette[index].rgbBlue, palette[index].rgbGreen, palette[index].rgbRed);
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ WORD *new_bits = (WORD *)target;
+ BOOL lonibble = FALSE;
+ int x = 0;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ RGBQUAD *grab_palette;
+
+ if (lonibble) {
+ grab_palette = palette + LOWNIBBLE(source[x++]);
+ } else {
+ grab_palette = palette + (HINIBBLE(source[x]) >> 4);
+ }
+
+ new_bits[cols] = RGB565(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+
+ lonibble = !lonibble;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To16_565(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ RGBQUAD *grab_palette = palette + source[cols];
+
+ new_bits[cols] = RGB565(grab_palette->rgbBlue, grab_palette->rgbGreen, grab_palette->rgbRed);
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16_555_To16_565(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *src_bits = (WORD *)source;
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ new_bits[cols] = RGB565((((src_bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F,
+ (((src_bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+ (((src_bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To16_565(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ new_bits[cols] = RGB565(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+ source += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To16_565(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *new_bits = (WORD *)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ new_bits[cols] = RGB565(source[FI_RGBA_BLUE], source[FI_RGBA_GREEN], source[FI_RGBA_RED]);
+
+ source += 4;
+ }
+}
+
+// ----------------------------------------------------------
+// smart convert X to 16 bits (565)
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo16Bits565(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) return NULL;
+
+ const int width = FreeImage_GetWidth(dib);
+ const int height = FreeImage_GetHeight(dib);
+ const int bpp = FreeImage_GetBPP(dib);
+
+ if(bpp == 16) {
+ if ((FreeImage_GetRedMask(dib) == FI16_555_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_555_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_555_BLUE_MASK)) {
+ // RGB 555
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK);
+ if(new_dib == NULL) {
+ return NULL;
+ }
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine16_555_To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ return new_dib;
+ } else {
+ // RGB 565
+ return FreeImage_Clone(dib);
+ }
+ }
+ else {
+ // other bpp cases => convert to RGB 565
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 16, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK);
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ switch (bpp) {
+ case 1 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+
+ return new_dib;
+ }
+
+ case 4 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine4To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+
+ return new_dib;
+ }
+
+ case 8 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine8To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+
+ return new_dib;
+ }
+
+ case 24 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine24To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+
+ return new_dib;
+ }
+
+ case 32 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine32To16_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+
+ return new_dib;
+ }
+
+ default :
+ // unreachable code ...
+ FreeImage_Unload(new_dib);
+ break;
+ }
+ }
+
+ return NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/Conversion24.cpp b/plugins/AdvaImg/src/FreeImage/Conversion24.cpp
new file mode 100644
index 0000000000..f04b946ef0
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Conversion24.cpp
@@ -0,0 +1,214 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Dale Larson (dlarson@norsesoft.com)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// internal conversions X to 24 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ BYTE index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+ target[FI_RGBA_BLUE] = palette[index].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[index].rgbGreen;
+ target[FI_RGBA_RED] = palette[index].rgbRed;
+
+ target += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ BOOL low_nibble = FALSE;
+ int x = 0;
+
+ for (int cols = 0; cols < width_in_pixels; ++cols ) {
+ if (low_nibble) {
+ target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen;
+ target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed;
+
+ x++;
+ } else {
+ target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen;
+ target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed;
+ }
+
+ low_nibble = !low_nibble;
+
+ target += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen;
+ target[FI_RGBA_RED] = palette[source[cols]].rgbRed;
+
+ target += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To24_555(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+
+ target += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To24_565(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F);
+ target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+
+ target += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To24(BYTE *target, BYTE *source, int width_in_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE];
+ target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN];
+ target[FI_RGBA_RED] = source[FI_RGBA_RED];
+
+ target += 3;
+ source += 4;
+ }
+}
+
+// ----------------------------------------------------------
+// smart convert X to 24 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo24Bits(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const unsigned bpp = FreeImage_GetBPP(dib);
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ if ((image_type != FIT_BITMAP) && (image_type != FIT_RGB16)) {
+ return NULL;
+ }
+
+ if (bpp != 24) {
+ const int width = FreeImage_GetWidth(dib);
+ const int height = FreeImage_GetHeight(dib);
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ switch(bpp) {
+ case 1 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+ return new_dib;
+ }
+
+ case 4 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine4To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+ return new_dib;
+ }
+
+ case 8 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine8To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+ return new_dib;
+ }
+
+ case 16 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ FreeImage_ConvertLine16To24_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ } else {
+ // includes case where all the masks are 0
+ FreeImage_ConvertLine16To24_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ }
+ return new_dib;
+ }
+
+ case 32 :
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine32To24(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+
+ case 48:
+ {
+ const unsigned src_pitch = FreeImage_GetPitch(dib);
+ const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+ const BYTE *src_bits = FreeImage_GetBits(dib);
+ BYTE *dst_bits = FreeImage_GetBits(new_dib);
+ for (int rows = 0; rows < height; rows++) {
+ const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
+ RGBTRIPLE *dst_pixel = (RGBTRIPLE*)dst_bits;
+ for(int cols = 0; cols < width; cols++) {
+ dst_pixel[cols].rgbtRed = (BYTE)(src_pixel[cols].red >> 8);
+ dst_pixel[cols].rgbtGreen = (BYTE)(src_pixel[cols].green >> 8);
+ dst_pixel[cols].rgbtBlue = (BYTE)(src_pixel[cols].blue >> 8);
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ return new_dib;
+ }
+ }
+ }
+
+ return FreeImage_Clone(dib);
+}
diff --git a/plugins/AdvaImg/src/FreeImage/Conversion32.cpp b/plugins/AdvaImg/src/FreeImage/Conversion32.cpp
new file mode 100644
index 0000000000..fad74671cd
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Conversion32.cpp
@@ -0,0 +1,345 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// internal conversions X to 32 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+ target[FI_RGBA_BLUE] = palette[index].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[index].rgbGreen;
+ target[FI_RGBA_RED] = palette[index].rgbRed;
+ target[FI_RGBA_ALPHA] = 0xFF;
+ target += 4;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ BOOL low_nibble = FALSE;
+ int x = 0;
+
+ for (int cols = 0 ; cols < width_in_pixels ; ++cols) {
+ if (low_nibble) {
+ target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen;
+ target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed;
+
+ x++;
+ } else {
+ target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen;
+ target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed;
+ }
+
+ low_nibble = !low_nibble;
+
+ target[FI_RGBA_ALPHA] = 0xFF;
+ target += 4;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen;
+ target[FI_RGBA_RED] = palette[source[cols]].rgbRed;
+ target[FI_RGBA_ALPHA] = 0xFF;
+ target += 4;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_ALPHA] = 0xFF;
+ target += 4;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F);
+ target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+ target[FI_RGBA_ALPHA] = 0xFF;
+ target += 4;
+ }
+}
+/*
+void DLL_CALLCONV
+FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ *(DWORD *)target = (*(DWORD *) source & FI_RGBA_RGB_MASK) | FI_RGBA_ALPHA_MASK;
+ target += 4;
+ source += 3;
+ }
+}
+*/
+/**
+This unoptimized version of the conversion function avoid an undetermined bug with VC++ SP6.
+The bug occurs in release mode only, when the image height is equal to 537
+(try e.g. a size of 432x537 to reproduce the bug with the optimized function).
+*/
+void DLL_CALLCONV
+FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_RED] = source[FI_RGBA_RED];
+ target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN];
+ target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE];
+ target[FI_RGBA_ALPHA] = 0xFF;
+ target += 4;
+ source += 3;
+ }
+}
+
+// ----------------------------------------------------------
+
+inline void
+FreeImage_ConvertLine1To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
+
+ target[FI_RGBA_BLUE] = palette[index].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[index].rgbGreen;
+ target[FI_RGBA_RED] = palette[index].rgbRed;
+ target[FI_RGBA_ALPHA] = (index < transparent_pixels) ? table[index] : 255;
+ target += 4;
+ }
+}
+
+inline void
+FreeImage_ConvertLine4To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
+ BOOL low_nibble = FALSE;
+ int x = 0;
+
+ for (int cols = 0 ; cols < width_in_pixels ; ++cols) {
+ if (low_nibble) {
+ target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen;
+ target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed;
+ target[FI_RGBA_ALPHA] = (LOWNIBBLE(source[x]) < transparent_pixels) ? table[LOWNIBBLE(source[x])] : 255;
+
+ x++;
+ } else {
+ target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen;
+ target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed;
+ target[FI_RGBA_ALPHA] = (HINIBBLE(source[x] >> 4) < transparent_pixels) ? table[HINIBBLE(source[x]) >> 4] : 255;
+ }
+
+ low_nibble = !low_nibble;
+
+ target += 4;
+ }
+}
+
+inline void
+FreeImage_ConvertLine8To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue;
+ target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen;
+ target[FI_RGBA_RED] = palette[source[cols]].rgbRed;
+ target[FI_RGBA_ALPHA] = (source[cols] < transparent_pixels) ? table[source[cols]] : 255;
+ target += 4;
+ }
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo32Bits(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const int bpp = FreeImage_GetBPP(dib);
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ if ((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (image_type != FIT_RGBA16)) {
+ return NULL;
+ }
+
+ const int width = FreeImage_GetWidth(dib);
+ const int height = FreeImage_GetHeight(dib);
+
+ if(image_type == FIT_BITMAP) {
+
+ if(bpp == 32) {
+ return FreeImage_Clone(dib);
+ }
+
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ BOOL bIsTransparent = FreeImage_IsTransparent(dib);
+
+ switch(bpp) {
+ case 1:
+ {
+ if(bIsTransparent) {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+ }
+ } else {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+ }
+
+ return new_dib;
+ }
+
+ case 4:
+ {
+ if(bIsTransparent) {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine4To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+ }
+ } else {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine4To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+ }
+
+ return new_dib;
+ }
+
+ case 8:
+ {
+ if(bIsTransparent) {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine8To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+ }
+ } else {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine8To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+ }
+
+ return new_dib;
+ }
+
+ case 16:
+ {
+ for (int rows = 0; rows < height; rows++) {
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ FreeImage_ConvertLine16To32_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ } else {
+ // includes case where all the masks are 0
+ FreeImage_ConvertLine16To32_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ }
+
+ return new_dib;
+ }
+
+ case 24:
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine24To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+
+ return new_dib;
+ }
+ }
+
+ } else if(image_type == FIT_RGB16) {
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ const unsigned src_pitch = FreeImage_GetPitch(dib);
+ const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+ const BYTE *src_bits = FreeImage_GetBits(dib);
+ BYTE *dst_bits = FreeImage_GetBits(new_dib);
+ for (int rows = 0; rows < height; rows++) {
+ const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
+ RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits;
+ for(int cols = 0; cols < width; cols++) {
+ dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8);
+ dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8);
+ dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8);
+ dst_pixel[cols].rgbReserved = (BYTE)0xFF;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+
+ return new_dib;
+
+ } else if(image_type == FIT_RGBA16) {
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ const unsigned src_pitch = FreeImage_GetPitch(dib);
+ const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+ const BYTE *src_bits = FreeImage_GetBits(dib);
+ BYTE *dst_bits = FreeImage_GetBits(new_dib);
+ for (int rows = 0; rows < height; rows++) {
+ const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits;
+ RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits;
+ for(int cols = 0; cols < width; cols++) {
+ dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8);
+ dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8);
+ dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8);
+ dst_pixel[cols].rgbReserved = (BYTE)(src_pixel[cols].alpha >> 8);
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+
+ return new_dib;
+ }
+
+ return NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/Conversion4.cpp b/plugins/AdvaImg/src/FreeImage/Conversion4.cpp
new file mode 100644
index 0000000000..7c09ac824a
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Conversion4.cpp
@@ -0,0 +1,246 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Riley McNiff (rmcniff@marexgroup.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// internal conversions X to 4 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To4(BYTE *target, BYTE *source, int width_in_pixels) {
+ BOOL hinibble = TRUE;
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ if (hinibble == TRUE){
+ target[cols >> 1] = ((source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 15 : 0) << 4;
+ }
+ else {
+ target[cols >> 1] |= ((source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 15 : 0);
+ }
+
+ hinibble = !hinibble;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine8To4(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
+ BOOL hinibble = TRUE;
+ BYTE index;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ index = GREY(palette[source[cols]].rgbRed, palette[source[cols]].rgbGreen, palette[source[cols]].rgbBlue);
+ if (hinibble) {
+ target[cols >> 1] = (index & 0xF0);
+ } else {
+ target[cols >> 1] |= (index >> 4);
+ }
+
+ hinibble = !hinibble;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To4_555(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+ BOOL hinibble = TRUE;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ if (hinibble) {
+ target[cols >> 1] = GREY((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F)
+ & 0xF0;
+ } else {
+ target[cols >> 1] |= GREY((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F)
+ >> 4;
+ }
+
+ hinibble = !hinibble;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To4_565(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+ BOOL hinibble = TRUE;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ if (hinibble) {
+ target[cols >> 1] = GREY((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+ (((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F)
+ & 0xF0;
+ } else {
+ target[cols >> 1] |= GREY((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+ (((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F)
+ >> 4;
+ }
+
+ hinibble = !hinibble;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To4(BYTE *target, BYTE *source, int width_in_pixels) {
+ BOOL hinibble = TRUE;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ if (hinibble) {
+ target[cols >> 1] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) & 0xF0;
+ } else {
+ target[cols >> 1] |= GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) >> 4;
+ }
+
+ source += 3;
+ hinibble = !hinibble;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To4(BYTE *target, BYTE *source, int width_in_pixels) {
+ BOOL hinibble = TRUE;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ if (hinibble) {
+ target[cols >> 1] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) & 0xF0;
+ } else {
+ target[cols >> 1] |= GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]) >> 4;
+ }
+
+ source += 4;
+ hinibble = !hinibble;
+ }
+}
+
+// ----------------------------------------------------------
+// smart convert X to 4 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo4Bits(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const int bpp = FreeImage_GetBPP(dib);
+
+ if(bpp != 4) {
+ const int width = FreeImage_GetWidth(dib);
+ const int height = FreeImage_GetHeight(dib);
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 4);
+
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ // Build a greyscale palette (*always* needed for image processing)
+
+ RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+ for(int i = 0; i < 16; i++) {
+ new_pal[i].rgbRed = (BYTE)((i << 4) + i);
+ new_pal[i].rgbGreen = (BYTE)((i << 4) + i);
+ new_pal[i].rgbBlue = (BYTE)((i << 4) + i);
+ }
+
+ switch(bpp) {
+ case 1:
+ {
+ if(FreeImage_GetColorType(dib) == FIC_PALETTE) {
+
+ // Copy the palette
+
+ RGBQUAD *old_pal = FreeImage_GetPalette(dib);
+ memcpy(&new_pal[0], &old_pal[0], sizeof(RGBQUAD));
+ memcpy(&new_pal[15], &old_pal[1], sizeof(RGBQUAD));
+
+ }
+ else if(FreeImage_GetColorType(dib) == FIC_MINISWHITE) {
+
+ // Reverse the grayscale palette
+
+ for(int i = 0; i < 16; i++) {
+ new_pal[i].rgbRed = new_pal[i].rgbGreen = new_pal[i].rgbBlue = (BYTE)(255 - ((i << 4) + i));
+ }
+ }
+
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+
+ case 8 :
+ {
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine8To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ }
+ return new_dib;
+ }
+
+ case 16 :
+ {
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ FreeImage_ConvertLine16To4_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ } else {
+ FreeImage_ConvertLine16To4_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ }
+
+ return new_dib;
+ }
+
+ case 24 :
+ {
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine24To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+
+ case 32 :
+ {
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine32To4(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+ }
+ }
+
+ return FreeImage_Clone(dib);
+}
diff --git a/plugins/AdvaImg/src/FreeImage/Conversion8.cpp b/plugins/AdvaImg/src/FreeImage/Conversion8.cpp
new file mode 100644
index 0000000000..867cf756ed
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Conversion8.cpp
@@ -0,0 +1,327 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jani Kajala (janik@remedy.fi)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// internal conversions X to 8 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++)
+ target[cols] = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 255 : 0;
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine4To8(BYTE *target, BYTE *source, int width_in_pixels) {
+ int count_new = 0;
+ int count_org = 0;
+ BOOL hinibble = TRUE;
+
+ while (count_new < width_in_pixels) {
+ if (hinibble) {
+ target[count_new] = (source[count_org] & 0xF0) >> 4;
+ } else {
+ target[count_new] = (source[count_org] & 0x0F);
+
+ count_org++;
+ }
+
+ hinibble = !hinibble;
+
+ count_new++;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To8_555(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[cols] = GREY((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine16To8_565(BYTE *target, BYTE *source, int width_in_pixels) {
+ WORD *bits = (WORD *)source;
+
+ for (int cols = 0; cols < width_in_pixels; cols++)
+ target[cols] = GREY((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F,
+ (((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F,
+ (((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine24To8(BYTE *target, BYTE *source, int width_in_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[cols] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]);
+
+ source += 3;
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_ConvertLine32To8(BYTE *target, BYTE *source, int width_in_pixels) {
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ target[cols] = GREY(source[FI_RGBA_RED], source[FI_RGBA_GREEN], source[FI_RGBA_BLUE]);
+
+ source += 4;
+ }
+}
+
+// ----------------------------------------------------------
+// smart convert X to 8 bits
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertTo8Bits(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const int bpp = FreeImage_GetBPP(dib);
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ if ((image_type != FIT_BITMAP) && (image_type != FIT_UINT16)) {
+ return NULL;
+ }
+
+ if(bpp != 8) {
+ const int width = FreeImage_GetWidth(dib);
+ const int height = FreeImage_GetHeight(dib);
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8);
+
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ // Build a greyscale palette (*always* needed for image processing)
+
+ RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+ for(int i = 0; i < 256; i++) {
+ new_pal[i].rgbRed = (BYTE)i;
+ new_pal[i].rgbGreen = (BYTE)i;
+ new_pal[i].rgbBlue = (BYTE)i;
+ }
+
+ if(image_type == FIT_BITMAP) {
+
+ switch(bpp) {
+ case 1:
+ {
+ if(FreeImage_GetColorType(dib) == FIC_PALETTE) {
+
+ // Copy the palette
+
+ RGBQUAD *old_pal = FreeImage_GetPalette(dib);
+ memcpy(&new_pal[0], &old_pal[0], sizeof(RGBQUAD));
+ memcpy(&new_pal[255], &old_pal[1], sizeof(RGBQUAD));
+
+ }
+ else if(FreeImage_GetColorType(dib) == FIC_MINISWHITE) {
+
+ // Reverse the grayscale palette
+
+ for(int i = 0; i < 256; i++) {
+ new_pal[i].rgbRed = new_pal[i].rgbGreen = new_pal[i].rgbBlue = (BYTE)(255 - i);
+ }
+ }
+
+
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+
+ case 4 :
+ {
+ if(FreeImage_GetColorType(dib) == FIC_PALETTE) {
+
+ // Copy the palette
+
+ RGBQUAD *old_pal = FreeImage_GetPalette(dib);
+
+ for (int i = 0; i < 16; i++) {
+ new_pal[i].rgbRed = old_pal[i].rgbRed;
+ new_pal[i].rgbGreen = old_pal[i].rgbGreen;
+ new_pal[i].rgbBlue = old_pal[i].rgbBlue;
+ }
+ }
+
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine4To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+
+ case 16 :
+ {
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ FreeImage_ConvertLine16To8_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ } else {
+ FreeImage_ConvertLine16To8_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ }
+
+ return new_dib;
+ }
+
+ case 24 :
+ {
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine24To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+
+ case 32 :
+ {
+ // Expand and copy the bitmap data
+
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine32To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+ }
+
+ } else if(image_type == FIT_UINT16) {
+
+ const unsigned src_pitch = FreeImage_GetPitch(dib);
+ const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+ const BYTE *src_bits = FreeImage_GetBits(dib);
+ BYTE *dst_bits = FreeImage_GetBits(new_dib);
+ for (int rows = 0; rows < height; rows++) {
+ const WORD *src_pixel = (WORD*)src_bits;
+ BYTE *dst_pixel = (BYTE*)dst_bits;
+ for(int cols = 0; cols < width; cols++) {
+ dst_pixel[cols] = (BYTE)(src_pixel[cols] >> 8);
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+
+ return new_dib;
+
+ }
+
+ } // bpp != 8
+
+ return FreeImage_Clone(dib);
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToGreyscale(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+ const int bpp = FreeImage_GetBPP(dib);
+
+ if ((color_type == FIC_PALETTE) || (color_type == FIC_MINISWHITE)) {
+ const int width = FreeImage_GetWidth(dib);
+ const int height = FreeImage_GetHeight(dib);
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8);
+
+ if(new_dib == NULL) {
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ // Build a greyscale palette
+
+ RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+ for(int i = 0; i < 256; i++) {
+ new_pal[i].rgbRed = (BYTE)i;
+ new_pal[i].rgbGreen = (BYTE)i;
+ new_pal[i].rgbBlue = (BYTE)i;
+ }
+
+ // allocate a 24-bit buffer
+
+ BYTE *buffer = (BYTE*)malloc( CalculatePitch(CalculateLine(width, 24)) * sizeof(BYTE));
+ if(NULL == buffer) {
+ FreeImage_Unload(new_dib);
+ return NULL;
+ }
+
+ // Convert the palette to 24-bit, then to 8-bit
+
+ switch(bpp) {
+ case 1:
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To24(buffer, FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ FreeImage_ConvertLine24To8(FreeImage_GetScanLine(new_dib, rows), buffer, width);
+ }
+ }
+ break;
+
+ case 4:
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine4To24(buffer, FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ FreeImage_ConvertLine24To8(FreeImage_GetScanLine(new_dib, rows), buffer, width);
+ }
+ }
+ break;
+
+ case 8:
+ {
+ for (int rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine8To24(buffer, FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
+ FreeImage_ConvertLine24To8(FreeImage_GetScanLine(new_dib, rows), buffer, width);
+ }
+ }
+ break;
+
+ }
+ free(buffer);
+
+ return new_dib;
+
+ }
+
+ // Convert the bitmap to 8-bit greyscale
+ return FreeImage_ConvertTo8Bits(dib);
+}
diff --git a/plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp b/plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp
new file mode 100644
index 0000000000..181ff396e1
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp
@@ -0,0 +1,192 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// smart convert X to Float
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToFloat(FIBITMAP *dib) {
+ FIBITMAP *src = NULL;
+ FIBITMAP *dst = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+ // check for allowed conversions
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ // allow conversion from 8-bit
+ if ((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) {
+ src = dib;
+ } else {
+ src = FreeImage_ConvertToGreyscale(dib);
+ if (!src) return NULL;
+ }
+ break;
+ }
+ case FIT_UINT16:
+ case FIT_RGB16:
+ case FIT_RGBA16:
+ case FIT_RGBF:
+ case FIT_RGBAF:
+ src = dib;
+ break;
+ case FIT_FLOAT:
+ // float type : clone the src
+ return FreeImage_Clone(dib);
+ default:
+ return NULL;
+ }
+
+ // allocate dst image
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+
+ dst = FreeImage_AllocateT(FIT_FLOAT, width, height);
+ if (!dst) {
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ // convert from src type to float
+
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+ const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const BYTE *src_pixel = (BYTE*)src_bits;
+ float *dst_pixel = (float*)dst_bits;
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ dst_pixel[x] = (float)(src_pixel[x]) / 255;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const WORD *src_pixel = (WORD*)src_bits;
+ float *dst_pixel = (float*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ dst_pixel[x] = (float)(src_pixel[x]) / 65535;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
+ float *dst_pixel = (float*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits;
+ float *dst_pixel = (float*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGBF:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBF *src_pixel = (FIRGBF*)src_bits;
+ float *dst_pixel = (float*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert (assume pixel values are in the range [0..1])
+ dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGBAF:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBAF *src_pixel = (FIRGBAF*)src_bits;
+ float *dst_pixel = (float*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert (assume pixel values are in the range [0..1])
+ dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp b/plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp
new file mode 100644
index 0000000000..6b9d17fbed
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp
@@ -0,0 +1,144 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// smart convert X to RGB16
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGB16(FIBITMAP *dib) {
+ FIBITMAP *src = NULL;
+ FIBITMAP *dst = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+ // check for allowed conversions
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ // convert to 24-bit if needed
+ if ((FreeImage_GetBPP(dib) == 24) || (FreeImage_GetBPP(dib) == 32)) {
+ src = dib;
+ } else {
+ src = FreeImage_ConvertTo24Bits(dib);
+ if (!src) return NULL;
+ }
+ break;
+ }
+ case FIT_UINT16:
+ // allow conversion from unsigned 16-bit
+ src = dib;
+ break;
+ case FIT_RGB16:
+ // RGB16 type : clone the src
+ return FreeImage_Clone(dib);
+ break;
+ case FIT_RGBA16:
+ // allow conversion from 64-bit RGBA (ignore the alpha channel)
+ src = dib;
+ break;
+ default:
+ return NULL;
+ }
+
+ // allocate dst image
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+
+ dst = FreeImage_AllocateT(FIT_RGB16, width, height);
+ if (!dst) {
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ // convert from src type to RGB16
+
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit)
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ for(unsigned y = 0; y < height; y++) {
+ const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y);
+ FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ dst_bits[x].red = src_bits[FI_RGBA_RED] << 8;
+ dst_bits[x].green = src_bits[FI_RGBA_GREEN] << 8;
+ dst_bits[x].blue = src_bits[FI_RGBA_BLUE] << 8;
+ src_bits += bytespp;
+ }
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
+ FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ // convert by copying greyscale channel to each R, G, B channels
+ dst_bits[x].red = src_bits[x];
+ dst_bits[x].green = src_bits[x];
+ dst_bits[x].blue = src_bits[x];
+ }
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y);
+ FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ // convert and skip alpha channel
+ dst_bits[x].red = src_bits[x].red;
+ dst_bits[x].green = src_bits[x].green;
+ dst_bits[x].blue = src_bits[x].blue;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp b/plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp
new file mode 100644
index 0000000000..6b8739532f
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp
@@ -0,0 +1,241 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// smart convert X to RGBF
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGBF(FIBITMAP *dib) {
+ FIBITMAP *src = NULL;
+ FIBITMAP *dst = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+ // check for allowed conversions
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ // allow conversion from 24- and 32-bit
+ const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+ if ((color_type != FIC_RGB) && (color_type != FIC_RGBALPHA)) {
+ src = FreeImage_ConvertTo24Bits(dib);
+ if (!src) return NULL;
+ } else {
+ src = dib;
+ }
+ break;
+ }
+ case FIT_UINT16:
+ // allow conversion from 16-bit
+ src = dib;
+ break;
+ case FIT_RGB16:
+ // allow conversion from 48-bit RGB
+ src = dib;
+ break;
+ case FIT_RGBA16:
+ // allow conversion from 64-bit RGBA (ignore the alpha channel)
+ src = dib;
+ break;
+ case FIT_FLOAT:
+ // allow conversion from 32-bit float
+ src = dib;
+ break;
+ case FIT_RGBAF:
+ // allow conversion from 128-bit RGBAF
+ src = dib;
+ break;
+ case FIT_RGBF:
+ // RGBF type : clone the src
+ return FreeImage_Clone(dib);
+ break;
+ default:
+ return NULL;
+ }
+
+ // allocate dst image
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+
+ dst = FreeImage_AllocateT(FIT_RGBF, width, height);
+ if (!dst) {
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ // convert from src type to RGBF
+
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ // calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+ const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src);
+
+ const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const BYTE *src_pixel = (BYTE*)src_bits;
+ FIRGBF *dst_pixel = (FIRGBF*)dst_bits;
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ dst_pixel->red = (float)(src_pixel[FI_RGBA_RED]) / 255.0F;
+ dst_pixel->green = (float)(src_pixel[FI_RGBA_GREEN]) / 255.0F;
+ dst_pixel->blue = (float)(src_pixel[FI_RGBA_BLUE]) / 255.0F;
+
+ src_pixel += bytespp;
+ dst_pixel ++;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const WORD *src_pixel = (WORD*)src_bits;
+ FIRGBF *dst_pixel = (FIRGBF*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ const float dst_value = (float)src_pixel[x] / 65535.0F;
+ dst_pixel[x].red = dst_value;
+ dst_pixel[x].green = dst_value;
+ dst_pixel[x].blue = dst_value;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGB16 *src_pixel = (FIRGB16*) src_bits;
+ FIRGBF *dst_pixel = (FIRGBF*) dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F;
+ dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F;
+ dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBA16 *src_pixel = (FIRGBA16*) src_bits;
+ FIRGBF *dst_pixel = (FIRGBF*) dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert and scale to the range [0..1]
+ dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F;
+ dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F;
+ dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_FLOAT:
+ {
+ const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const float *src_pixel = (float*) src_bits;
+ FIRGBF *dst_pixel = (FIRGBF*) dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert by copying greyscale channel to each R, G, B channels
+ dst_pixel[x].red = src_pixel[x];
+ dst_pixel[x].green = src_pixel[x];
+ dst_pixel[x].blue = src_pixel[x];
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGBAF:
+ {
+ const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBAF *src_pixel = (FIRGBAF*) src_bits;
+ FIRGBF *dst_pixel = (FIRGBF*) dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert and skip alpha channel
+ dst_pixel[x].red = src_pixel[x].red;
+ dst_pixel[x].green = src_pixel[x].green;
+ dst_pixel[x].blue = src_pixel[x].blue;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/ConversionType.cpp b/plugins/AdvaImg/src/FreeImage/ConversionType.cpp
new file mode 100644
index 0000000000..23528b60fe
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ConversionType.cpp
@@ -0,0 +1,689 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+
+/** Convert a greyscale image of type Tsrc to type Tdst.
+ Conversion is done using standard C language casting convention.
+*/
+template<class Tdst, class Tsrc>
+class CONVERT_TYPE
+{
+public:
+ FIBITMAP* convert(FIBITMAP *src, FREE_IMAGE_TYPE dst_type);
+};
+
+template<class Tdst, class Tsrc> FIBITMAP*
+CONVERT_TYPE<Tdst, Tsrc>::convert(FIBITMAP *src, FREE_IMAGE_TYPE dst_type) {
+
+ FIBITMAP *dst = NULL;
+
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+ unsigned bpp = FreeImage_GetBPP(src);
+
+ // allocate dst image
+
+ dst = FreeImage_AllocateT(dst_type, width, height, bpp,
+ FreeImage_GetRedMask(src), FreeImage_GetGreenMask(src), FreeImage_GetBlueMask(src));
+ if (!dst) return NULL;
+
+ // convert from src_type to dst_type
+
+ for(unsigned y = 0; y < height; y++) {
+ const Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+ Tdst *dst_bits = reinterpret_cast<Tdst*>(FreeImage_GetScanLine(dst, y));
+
+ for(unsigned x = 0; x < width; x++) {
+ *dst_bits++ = static_cast<Tdst>(*src_bits++);
+ }
+ }
+
+ return dst;
+}
+
+
+/** Convert a greyscale image of type Tsrc to a 8-bit grayscale dib.
+ Conversion is done using either a linear scaling from [min, max] to [0, 255]
+ or a rounding from src_pixel to (BYTE) MIN(255, MAX(0, q)) where int q = int(src_pixel + 0.5);
+*/
+template<class Tsrc>
+class CONVERT_TO_BYTE
+{
+public:
+ FIBITMAP* convert(FIBITMAP *src, BOOL scale_linear);
+};
+
+template<class Tsrc> FIBITMAP*
+CONVERT_TO_BYTE<Tsrc>::convert(FIBITMAP *src, BOOL scale_linear) {
+ FIBITMAP *dst = NULL;
+ unsigned x, y;
+
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+
+ // allocate a 8-bit dib
+
+ dst = FreeImage_AllocateT(FIT_BITMAP, width, height, 8, 0, 0, 0);
+ if (!dst) return NULL;
+
+ // build a greyscale palette
+ RGBQUAD *pal = FreeImage_GetPalette(dst);
+ for(int i = 0; i < 256; i++) {
+ pal[i].rgbRed = (BYTE)i;
+ pal[i].rgbGreen = (BYTE)i;
+ pal[i].rgbBlue = (BYTE)i;
+ }
+
+ // convert the src image to dst
+ // (FIBITMAP are stored upside down)
+ if(scale_linear) {
+ Tsrc max, min;
+ double scale;
+
+ // find the min and max value of the image
+ Tsrc l_min, l_max;
+ min = 255, max = 0;
+ for(y = 0; y < height; y++) {
+ Tsrc *bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+ MAXMIN(bits, width, l_max, l_min);
+ if(l_max > max) max = l_max;
+ if(l_min < min) min = l_min;
+ }
+ if(max == min) {
+ max = 255; min = 0;
+ }
+
+ // compute the scaling factor
+ scale = 255 / (double)(max - min);
+
+ // scale to 8-bit
+ for(y = 0; y < height; y++) {
+ Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < width; x++) {
+ dst_bits[x] = (BYTE)( scale * (src_bits[x] - min) + 0.5);
+ }
+ }
+ } else {
+ for(y = 0; y < height; y++) {
+ Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+ BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
+ for(x = 0; x < width; x++) {
+ // rounding
+ int q = int(src_bits[x] + 0.5);
+ dst_bits[x] = (BYTE) MIN(255, MAX(0, q));
+ }
+ }
+ }
+
+ return dst;
+}
+
+/** Convert a greyscale image of type Tsrc to a FICOMPLEX dib.
+*/
+template<class Tsrc>
+class CONVERT_TO_COMPLEX
+{
+public:
+ FIBITMAP* convert(FIBITMAP *src);
+};
+
+template<class Tsrc> FIBITMAP*
+CONVERT_TO_COMPLEX<Tsrc>::convert(FIBITMAP *src) {
+ FIBITMAP *dst = NULL;
+
+ unsigned width = FreeImage_GetWidth(src);
+ unsigned height = FreeImage_GetHeight(src);
+
+ // allocate dst image
+
+ dst = FreeImage_AllocateT(FIT_COMPLEX, width, height);
+ if (!dst) return NULL;
+
+ // convert from src_type to FIT_COMPLEX
+
+ for(unsigned y = 0; y < height; y++) {
+ const Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y));
+ FICOMPLEX *dst_bits = (FICOMPLEX *)FreeImage_GetScanLine(dst, y);
+
+ for(unsigned x = 0; x < width; x++) {
+ dst_bits[x].r = (double)src_bits[x];
+ dst_bits[x].i = 0;
+ }
+ }
+
+ return dst;
+}
+
+// ----------------------------------------------------------
+
+// Convert from type BYTE to type X
+CONVERT_TYPE<unsigned short, BYTE> convertByteToUShort;
+CONVERT_TYPE<short, BYTE> convertByteToShort;
+CONVERT_TYPE<DWORD, BYTE> convertByteToULong;
+CONVERT_TYPE<LONG, BYTE> convertByteToLong;
+CONVERT_TYPE<float, BYTE> convertByteToFloat;
+CONVERT_TYPE<double, BYTE> convertByteToDouble;
+
+// Convert from type X to type BYTE
+CONVERT_TO_BYTE<unsigned short> convertUShortToByte;
+CONVERT_TO_BYTE<short> convertShortToByte;
+CONVERT_TO_BYTE<DWORD> convertULongToByte;
+CONVERT_TO_BYTE<LONG> convertLongToByte;
+CONVERT_TO_BYTE<float> convertFloatToByte;
+CONVERT_TO_BYTE<double> convertDoubleToByte;
+
+// Convert from type X to type float
+CONVERT_TYPE<float, unsigned short> convertUShortToFloat;
+CONVERT_TYPE<float, short> convertShortToFloat;
+CONVERT_TYPE<float, DWORD> convertULongToFloat;
+CONVERT_TYPE<float, LONG> convertLongToFloat;
+
+// Convert from type X to type double
+CONVERT_TYPE<double, unsigned short> convertUShortToDouble;
+CONVERT_TYPE<double, short> convertShortToDouble;
+CONVERT_TYPE<double, DWORD> convertULongToDouble;
+CONVERT_TYPE<double, LONG> convertLongToDouble;
+CONVERT_TYPE<double, float> convertFloatToDouble;
+
+// Convert from type X to type FICOMPLEX
+CONVERT_TO_COMPLEX<BYTE> convertByteToComplex;
+CONVERT_TO_COMPLEX<unsigned short> convertUShortToComplex;
+CONVERT_TO_COMPLEX<short> convertShortToComplex;
+CONVERT_TO_COMPLEX<DWORD> convertULongToComplex;
+CONVERT_TO_COMPLEX<LONG> convertLongToComplex;
+CONVERT_TO_COMPLEX<float> convertFloatToComplex;
+CONVERT_TO_COMPLEX<double> convertDoubleToComplex;
+
+// ----------------------------------------------------------
+
+// ----------------------------------------------------------
+// smart convert X to standard FIBITMAP
+// ----------------------------------------------------------
+
+/** Convert image of any type to a standard 8-bit greyscale image.
+For standard images, a clone of the input image is returned.
+When the scale_linear parameter is TRUE, conversion is done by scaling linearly
+each pixel to an integer value between [0..255]. When it is FALSE, conversion is done
+by rounding each float pixel to an integer between [0..255].
+For complex images, the magnitude is extracted as a double image, then converted according to the scale parameter.
+@param image Image to convert
+@param scale_linear Linear scaling / rounding switch
+*/
+FIBITMAP* DLL_CALLCONV
+FreeImage_ConvertToStandardType(FIBITMAP *src, BOOL scale_linear) {
+ FIBITMAP *dst = NULL;
+
+ if (!src) return NULL;
+
+ // convert from src_type to FIT_BITMAP
+
+ const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
+
+ switch(src_type) {
+ case FIT_BITMAP: // standard image: 1-, 4-, 8-, 16-, 24-, 32-bit
+ dst = FreeImage_Clone(src);
+ break;
+ case FIT_UINT16: // array of unsigned short: unsigned 16-bit
+ dst = convertUShortToByte.convert(src, scale_linear);
+ break;
+ case FIT_INT16: // array of short: signed 16-bit
+ dst = convertShortToByte.convert(src, scale_linear);
+ break;
+ case FIT_UINT32: // array of unsigned long: unsigned 32-bit
+ dst = convertULongToByte.convert(src, scale_linear);
+ break;
+ case FIT_INT32: // array of long: signed 32-bit
+ dst = convertLongToByte.convert(src, scale_linear);
+ break;
+ case FIT_FLOAT: // array of float: 32-bit
+ dst = convertFloatToByte.convert(src, scale_linear);
+ break;
+ case FIT_DOUBLE: // array of double: 64-bit
+ dst = convertDoubleToByte.convert(src, scale_linear);
+ break;
+ case FIT_COMPLEX: // array of FICOMPLEX: 2 x 64-bit
+ {
+ // Convert to type FIT_DOUBLE
+ FIBITMAP *dib_double = FreeImage_GetComplexChannel(src, FICC_MAG);
+ if(dib_double) {
+ // Convert to a standard bitmap (linear scaling)
+ dst = convertDoubleToByte.convert(dib_double, scale_linear);
+ // Free image of type FIT_DOUBLE
+ FreeImage_Unload(dib_double);
+ }
+ }
+ break;
+ case FIT_RGB16: // 48-bit RGB image: 3 x 16-bit
+ break;
+ case FIT_RGBA16: // 64-bit RGBA image: 4 x 16-bit
+ break;
+ case FIT_RGBF: // 96-bit RGB float image: 3 x 32-bit IEEE floating point
+ break;
+ case FIT_RGBAF: // 128-bit RGBA float image: 4 x 32-bit IEEE floating point
+ break;
+ }
+
+ if(NULL == dst) {
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "FREE_IMAGE_TYPE: Unable to convert from type %d to type %d.\n No such conversion exists.", src_type, FIT_BITMAP);
+ } else {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+ }
+
+ return dst;
+}
+
+
+
+// ----------------------------------------------------------
+// smart convert X to Y
+// ----------------------------------------------------------
+
+FIBITMAP* DLL_CALLCONV
+FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear) {
+ FIBITMAP *dst = NULL;
+
+ if (!FreeImage_HasPixels(src)) return NULL;
+
+ // convert from src_type to dst_type
+
+ const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(src);
+
+ if(src_type == dst_type) {
+ return FreeImage_Clone(src);
+ }
+
+ const unsigned src_bpp = FreeImage_GetBPP(src);
+
+ switch(src_type) {
+ case FIT_BITMAP:
+ switch(dst_type) {
+ case FIT_UINT16:
+ dst = FreeImage_ConvertToUINT16(src);
+ break;
+ case FIT_INT16:
+ dst = (src_bpp == 8) ? convertByteToShort.convert(src, dst_type) : NULL;
+ break;
+ case FIT_UINT32:
+ dst = (src_bpp == 8) ? convertByteToULong.convert(src, dst_type) : NULL;
+ break;
+ case FIT_INT32:
+ dst = (src_bpp == 8) ? convertByteToLong.convert(src, dst_type) : NULL;
+ break;
+ case FIT_FLOAT:
+ dst = FreeImage_ConvertToFloat(src);
+ break;
+ case FIT_DOUBLE:
+ dst = (src_bpp == 8) ? convertByteToDouble.convert(src, dst_type) : NULL;
+ break;
+ case FIT_COMPLEX:
+ dst = (src_bpp == 8) ? convertByteToComplex.convert(src) : NULL;
+ break;
+ case FIT_RGB16:
+ dst = FreeImage_ConvertToRGB16(src);
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_UINT16:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertToStandardType(src, scale_linear);
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ dst = FreeImage_ConvertToFloat(src);
+ break;
+ case FIT_DOUBLE:
+ dst = convertUShortToDouble.convert(src, dst_type);
+ break;
+ case FIT_COMPLEX:
+ dst = convertUShortToComplex.convert(src);
+ break;
+ case FIT_RGB16:
+ dst = FreeImage_ConvertToRGB16(src);
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_INT16:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertToStandardType(src, scale_linear);
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ dst = convertShortToFloat.convert(src, dst_type);
+ break;
+ case FIT_DOUBLE:
+ dst = convertShortToDouble.convert(src, dst_type);
+ break;
+ case FIT_COMPLEX:
+ dst = convertShortToComplex.convert(src);
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_UINT32:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertToStandardType(src, scale_linear);
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ dst = convertULongToFloat.convert(src, dst_type);
+ break;
+ case FIT_DOUBLE:
+ dst = convertULongToDouble.convert(src, dst_type);
+ break;
+ case FIT_COMPLEX:
+ dst = convertULongToComplex.convert(src);
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_INT32:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertToStandardType(src, scale_linear);
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_FLOAT:
+ dst = convertLongToFloat.convert(src, dst_type);
+ break;
+ case FIT_DOUBLE:
+ dst = convertLongToDouble.convert(src, dst_type);
+ break;
+ case FIT_COMPLEX:
+ dst = convertLongToComplex.convert(src);
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_FLOAT:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertToStandardType(src, scale_linear);
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_DOUBLE:
+ dst = convertFloatToDouble.convert(src, dst_type);
+ break;
+ case FIT_COMPLEX:
+ dst = convertFloatToComplex.convert(src);
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_DOUBLE:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertToStandardType(src, scale_linear);
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ break;
+ case FIT_COMPLEX:
+ dst = convertDoubleToComplex.convert(src);
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_COMPLEX:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ break;
+ case FIT_DOUBLE:
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_RGB16:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertTo24Bits(src);
+ break;
+ case FIT_UINT16:
+ dst = FreeImage_ConvertToUINT16(src);
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ dst = FreeImage_ConvertToFloat(src);
+ break;
+ case FIT_DOUBLE:
+ break;
+ case FIT_COMPLEX:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_RGBA16:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ dst = FreeImage_ConvertTo32Bits(src);
+ break;
+ case FIT_UINT16:
+ dst = FreeImage_ConvertToUINT16(src);
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ dst = FreeImage_ConvertToFloat(src);
+ break;
+ case FIT_DOUBLE:
+ break;
+ case FIT_COMPLEX:
+ break;
+ case FIT_RGB16:
+ dst = FreeImage_ConvertToRGB16(src);
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_RGBF:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ dst = FreeImage_ConvertToFloat(src);
+ break;
+ case FIT_DOUBLE:
+ break;
+ case FIT_COMPLEX:
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBAF:
+ break;
+ }
+ break;
+ case FIT_RGBAF:
+ switch(dst_type) {
+ case FIT_BITMAP:
+ break;
+ case FIT_UINT16:
+ break;
+ case FIT_INT16:
+ break;
+ case FIT_UINT32:
+ break;
+ case FIT_INT32:
+ break;
+ case FIT_FLOAT:
+ dst = FreeImage_ConvertToFloat(src);
+ break;
+ case FIT_DOUBLE:
+ break;
+ case FIT_COMPLEX:
+ break;
+ case FIT_RGB16:
+ break;
+ case FIT_RGBA16:
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ }
+ break;
+ }
+
+ if(NULL == dst) {
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "FREE_IMAGE_TYPE: Unable to convert from type %d to type %d.\n No such conversion exists.", src_type, dst_type);
+ } else {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+ }
+
+ return dst;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp b/plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp
new file mode 100644
index 0000000000..3744cfe3b4
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp
@@ -0,0 +1,134 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// smart convert X to UINT16
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToUINT16(FIBITMAP *dib) {
+ FIBITMAP *src = NULL;
+ FIBITMAP *dst = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib);
+
+ // check for allowed conversions
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ // convert to greyscale if needed
+ if ((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) {
+ src = dib;
+ } else {
+ src = FreeImage_ConvertToGreyscale(dib);
+ if (!src) return NULL;
+ }
+ break;
+ }
+ case FIT_UINT16:
+ // UINT16 type : clone the src
+ return FreeImage_Clone(dib);
+ break;
+ case FIT_RGB16:
+ // allow conversion from 48-bit RGB
+ src = dib;
+ break;
+ case FIT_RGBA16:
+ // allow conversion from 64-bit RGBA (ignore the alpha channel)
+ src = dib;
+ break;
+ default:
+ return NULL;
+ }
+
+ // allocate dst image
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+
+ dst = FreeImage_AllocateT(FIT_UINT16, width, height);
+ if (!dst) {
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+ return NULL;
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ // convert from src type to UINT16
+
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ dst_bits[x] = src_bits[x] << 8;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGB16 *src_bits = (FIRGB16*)FreeImage_GetScanLine(src, y);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ // convert to grey
+ dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue);
+ }
+ }
+ }
+ break;
+
+ case FIT_RGBA16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y);
+ WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ // convert to grey
+ dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/FreeImage.cpp b/plugins/AdvaImg/src/FreeImage/FreeImage.cpp
new file mode 100644
index 0000000000..b36f10aca1
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/FreeImage.cpp
@@ -0,0 +1,226 @@
+// ==========================================================
+// FreeImage implementation
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+//----------------------------------------------------------------------
+
+static const char *s_copyright = "This program uses FreeImage, a free, open source image library supporting all common bitmap formats. See http://freeimage.sourceforge.net for details";
+
+//----------------------------------------------------------------------
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+#ifndef FREEIMAGE_LIB
+
+BOOL APIENTRY
+DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
+ switch (ul_reason_for_call) {
+ case DLL_PROCESS_ATTACH :
+ FreeImage_Initialise(FALSE);
+ break;
+
+ case DLL_PROCESS_DETACH :
+ FreeImage_DeInitialise();
+ break;
+
+ case DLL_THREAD_ATTACH :
+ case DLL_THREAD_DETACH :
+ break;
+ }
+
+ return TRUE;
+}
+
+#endif // FREEIMAGE_LIB
+
+#else // !_WIN32
+#ifndef FREEIMAGE_LIB
+
+void FreeImage_SO_Initialise() __attribute__((constructor));
+void FreeImage_SO_DeInitialise() __attribute__((destructor));
+
+void FreeImage_SO_Initialise() {
+ FreeImage_Initialise(FALSE);
+}
+
+void FreeImage_SO_DeInitialise() {
+ FreeImage_DeInitialise();
+}
+#endif // FREEIMAGE_LIB
+
+#endif // _WIN32
+
+//----------------------------------------------------------------------
+
+const char * DLL_CALLCONV
+FreeImage_GetVersion() {
+ static char s_version[16];
+ sprintf(s_version, "%d.%d.%d", FREEIMAGE_MAJOR_VERSION, FREEIMAGE_MINOR_VERSION, FREEIMAGE_RELEASE_SERIAL);
+ return s_version;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetCopyrightMessage() {
+ return s_copyright;
+}
+
+//----------------------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_IsLittleEndian() {
+ union {
+ DWORD i;
+ BYTE c[4];
+ } u;
+ u.i = 1;
+ return (u.c[0] != 0);
+}
+
+//----------------------------------------------------------------------
+
+static FreeImage_OutputMessageFunction freeimage_outputmessage_proc = NULL;
+static FreeImage_OutputMessageFunctionStdCall freeimage_outputmessagestdcall_proc = NULL;
+
+void DLL_CALLCONV
+FreeImage_SetOutputMessage(FreeImage_OutputMessageFunction omf) {
+ freeimage_outputmessage_proc = omf;
+}
+
+void DLL_CALLCONV
+FreeImage_SetOutputMessageStdCall(FreeImage_OutputMessageFunctionStdCall omf) {
+ freeimage_outputmessagestdcall_proc = omf;
+}
+
+void DLL_CALLCONV
+FreeImage_OutputMessageProc(int fif, const char *fmt, ...) {
+ const int MSG_SIZE = 512; // 512 bytes should be more than enough for a short message
+
+ if ((fmt != NULL) && ((freeimage_outputmessage_proc != NULL) || (freeimage_outputmessagestdcall_proc != NULL))) {
+ char message[MSG_SIZE];
+ memset(message, 0, MSG_SIZE);
+
+ // initialize the optional parameter list
+
+ va_list arg;
+ va_start(arg, fmt);
+
+ // check the length of the format string
+
+ int str_length = (int)( (strlen(fmt) > MSG_SIZE) ? MSG_SIZE : strlen(fmt));
+
+ // parse the format string and put the result in 'message'
+
+ for (int i = 0, j = 0; i < str_length; ++i) {
+ if (fmt[i] == '%') {
+ if (i + 1 < str_length) {
+ switch(tolower(fmt[i + 1])) {
+ case '%' :
+ message[j++] = '%';
+ break;
+
+ case 'o' : // octal numbers
+ {
+ char tmp[16];
+
+ _itoa(va_arg(arg, int), tmp, 8);
+
+ strcat(message, tmp);
+
+ j += (int)strlen(tmp);
+
+ ++i;
+
+ break;
+ }
+
+ case 'i' : // decimal numbers
+ case 'd' :
+ {
+ char tmp[16];
+
+ _itoa(va_arg(arg, int), tmp, 10);
+
+ strcat(message, tmp);
+
+ j += (int)strlen(tmp);
+
+ ++i;
+
+ break;
+ }
+
+ case 'x' : // hexadecimal numbers
+ {
+ char tmp[16];
+
+ _itoa(va_arg(arg, int), tmp, 16);
+
+ strcat(message, tmp);
+
+ j += (int)strlen(tmp);
+
+ ++i;
+
+ break;
+ }
+
+ case 's' : // strings
+ {
+ char *tmp = va_arg(arg, char*);
+
+ strcat(message, tmp);
+
+ j += (int)strlen(tmp);
+
+ ++i;
+
+ break;
+ }
+ };
+ } else {
+ message[j++] = fmt[i];
+ }
+ } else {
+ message[j++] = fmt[i];
+ };
+ }
+
+ // deinitialize the optional parameter list
+
+ va_end(arg);
+
+ // output the message to the user program
+
+ if (freeimage_outputmessage_proc != NULL)
+ freeimage_outputmessage_proc((FREE_IMAGE_FORMAT)fif, message);
+
+ if (freeimage_outputmessagestdcall_proc != NULL)
+ freeimage_outputmessagestdcall_proc((FREE_IMAGE_FORMAT)fif, message);
+ }
+}
diff --git a/plugins/AdvaImg/src/FreeImage/FreeImageC.c b/plugins/AdvaImg/src/FreeImage/FreeImageC.c
new file mode 100644
index 0000000000..9e8125955f
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/FreeImageC.c
@@ -0,0 +1,22 @@
+// ==========================================================
+// Use from c compiler test file
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
diff --git a/plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp b/plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp
new file mode 100644
index 0000000000..b289530be7
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp
@@ -0,0 +1,168 @@
+// ==========================================================
+// Input/Output functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+
+// =====================================================================
+// File IO functions
+// =====================================================================
+
+unsigned DLL_CALLCONV
+_ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+ return (unsigned)fread(buffer, size, count, (FILE *)handle);
+}
+
+unsigned DLL_CALLCONV
+_WriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+ return (unsigned)fwrite(buffer, size, count, (FILE *)handle);
+}
+
+int DLL_CALLCONV
+_SeekProc(fi_handle handle, long offset, int origin) {
+ return fseek((FILE *)handle, offset, origin);
+}
+
+long DLL_CALLCONV
+_TellProc(fi_handle handle) {
+ return ftell((FILE *)handle);
+}
+
+// ----------------------------------------------------------
+
+void
+SetDefaultIO(FreeImageIO *io) {
+ io->read_proc = _ReadProc;
+ io->seek_proc = _SeekProc;
+ io->tell_proc = _TellProc;
+ io->write_proc = _WriteProc;
+}
+
+// =====================================================================
+// Memory IO functions
+// =====================================================================
+
+unsigned DLL_CALLCONV
+_MemoryReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+ unsigned x;
+
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+ for(x = 0; x < count; x++) {
+ //if there isnt size bytes left to read, set pos to eof and return a short count
+ if ( (mem_header->filelen - mem_header->curpos) < (long)size ) {
+ mem_header->curpos = mem_header->filelen;
+ break;
+ }
+ //copy size bytes count times
+ memcpy( buffer, (char *)mem_header->data + mem_header->curpos, size );
+ mem_header->curpos += size;
+ buffer = (char *)buffer + size;
+ }
+ return x;
+}
+
+unsigned DLL_CALLCONV
+_MemoryWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) {
+ void *newdata;
+ long newdatalen;
+
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+ //double the data block size if we need to
+ while( (mem_header->curpos + (long)(size*count)) >= mem_header->datalen ) {
+ //if we are at or above 1G, we cant double without going negative
+ if ( mem_header->datalen & 0x40000000 ) {
+ //max 2G
+ if ( mem_header->datalen == 0x7FFFFFFF ) {
+ return 0;
+ }
+ newdatalen = 0x7FFFFFFF;
+ } else if ( mem_header->datalen == 0 ) {
+ //default to 4K if nothing yet
+ newdatalen = 4096;
+ } else {
+ //double size
+ newdatalen = mem_header->datalen << 1;
+ }
+ newdata = realloc( mem_header->data, newdatalen );
+ if ( !newdata ) {
+ return 0;
+ }
+ mem_header->data = newdata;
+ mem_header->datalen = newdatalen;
+ }
+ memcpy( (char *)mem_header->data + mem_header->curpos, buffer, size*count );
+ mem_header->curpos += size*count;
+ if ( mem_header->curpos > mem_header->filelen ) {
+ mem_header->filelen = mem_header->curpos;
+ }
+ return count;
+}
+
+int DLL_CALLCONV
+_MemorySeekProc(fi_handle handle, long offset, int origin) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+ switch(origin) { //0 to filelen-1 are 'inside' the file
+ default:
+ case SEEK_SET: //can fseek() to 0-7FFFFFFF always
+ if ( offset >= 0 ) {
+ mem_header->curpos = offset;
+ return 0;
+ }
+ break;
+
+ case SEEK_CUR:
+ if ( mem_header->curpos + offset >= 0 ) {
+ mem_header->curpos += offset;
+ return 0;
+ }
+ break;
+
+ case SEEK_END:
+ if ( mem_header->filelen + offset >= 0 ) {
+ mem_header->curpos = mem_header->filelen + offset;
+ return 0;
+ }
+ break;
+ }
+
+ return -1;
+}
+
+long DLL_CALLCONV
+_MemoryTellProc(fi_handle handle) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+ return mem_header->curpos;
+}
+
+// ----------------------------------------------------------
+
+void
+SetMemoryIO(FreeImageIO *io) {
+ io->read_proc = _MemoryReadProc;
+ io->seek_proc = _MemorySeekProc;
+ io->tell_proc = _MemoryTellProc;
+ io->write_proc = _MemoryWriteProc;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/GetType.cpp b/plugins/AdvaImg/src/FreeImage/GetType.cpp
new file mode 100644
index 0000000000..76edc7f3f9
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/GetType.cpp
@@ -0,0 +1,92 @@
+// ==========================================================
+// GetType
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+#include "Plugin.h"
+#include "../DeprecationManager/DeprecationMgr.h"
+
+// ----------------------------------------------------------
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFileTypeFromHandle(FreeImageIO *io, fi_handle handle, int size) {
+ if (handle != NULL) {
+ int fif_count = FreeImage_GetFIFCount();
+
+ for (int i = 0; i < fif_count; ++i) {
+ FREE_IMAGE_FORMAT fif = (FREE_IMAGE_FORMAT)i;
+ if (FreeImage_Validate(fif, io, handle)) {
+ if(fif == FIF_TIFF) {
+ // many camera raw files use a TIFF signature ...
+ // ... try to revalidate against FIF_RAW (even if it breaks the code genericity)
+ if (FreeImage_Validate(FIF_RAW, io, handle)) {
+ return FIF_RAW;
+ }
+ }
+ return fif;
+ }
+ }
+ }
+
+ return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFileType(const char *filename, int size) {
+ FreeImageIO io;
+ SetDefaultIO(&io);
+
+ FILE *handle = fopen(filename, "rb");
+
+ if (handle != NULL) {
+ FREE_IMAGE_FORMAT format = FreeImage_GetFileTypeFromHandle(&io, (fi_handle)handle, size);
+
+ fclose(handle);
+
+ return format;
+ }
+
+ return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFileTypeU(const wchar_t *filename, int size) {
+#ifdef _WIN32
+ FreeImageIO io;
+ SetDefaultIO(&io);
+ FILE *handle = _wfopen(filename, L"rb");
+
+ if (handle != NULL) {
+ FREE_IMAGE_FORMAT format = FreeImage_GetFileTypeFromHandle(&io, (fi_handle)handle, size);
+
+ fclose(handle);
+
+ return format;
+ }
+#endif
+ return FIF_UNKNOWN;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/Halftoning.cpp b/plugins/AdvaImg/src/FreeImage/Halftoning.cpp
new file mode 100644
index 0000000000..1542fc9268
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Halftoning.cpp
@@ -0,0 +1,474 @@
+// ==========================================================
+// Bitmap conversion routines
+// Thresholding and halftoning functions
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Dennis Lim (dlkj@users.sourceforge.net)
+// - Thomas Chmielewski (Chmielewski.Thomas@oce.de)
+//
+// Main reference : Ulichney, R., Digital Halftoning, The MIT Press, Cambridge, MA, 1987
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+static const int WHITE = 255;
+static const int BLACK = 0;
+
+// Floyd & Steinberg error diffusion dithering
+// This algorithm use the following filter
+// * 7
+// 3 5 1 (1/16)
+static FIBITMAP* FloydSteinberg(FIBITMAP *dib) {
+
+#define RAND(RN) (((seed = 1103515245 * seed + 12345) >> 12) % (RN))
+#define INITERR(X, Y) (((int) X) - (((int) Y) ? WHITE : BLACK) + ((WHITE/2)-((int)X)) / 2)
+
+ int seed = 0;
+ int x, y, p, pixel, threshold, error;
+ int width, height, pitch;
+ BYTE *bits, *new_bits;
+ FIBITMAP *new_dib = NULL;
+
+ // allocate a 8-bit DIB
+ width = FreeImage_GetWidth(dib);
+ height = FreeImage_GetHeight(dib);
+ pitch = FreeImage_GetPitch(dib);
+ new_dib = FreeImage_Allocate(width, height, 8);
+ if(NULL == new_dib) return NULL;
+
+ // allocate space for error arrays
+ int *lerr = (int*)malloc (width * sizeof(int));
+ int *cerr = (int*)malloc (width * sizeof(int));
+ memset(lerr, 0, width * sizeof(int));
+ memset(cerr, 0, width * sizeof(int));
+
+ // left border
+ error = 0;
+ for(y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(dib, y);
+ new_bits = FreeImage_GetScanLine(new_dib, y);
+
+ threshold = (WHITE / 2 + RAND(129) - 64);
+ pixel = bits[0] + error;
+ p = (pixel > threshold) ? WHITE : BLACK;
+ error = pixel - p;
+ new_bits[0] = (BYTE)p;
+ }
+ // right border
+ error = 0;
+ for(y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(dib, y);
+ new_bits = FreeImage_GetScanLine(new_dib, y);
+
+ threshold = (WHITE / 2 + RAND(129) - 64);
+ pixel = bits[width-1] + error;
+ p = (pixel > threshold) ? WHITE : BLACK;
+ error = pixel - p;
+ new_bits[width-1] = (BYTE)p;
+ }
+ // top border
+ bits = FreeImage_GetBits(dib);
+ new_bits = FreeImage_GetBits(new_dib);
+ error = 0;
+ for(x = 0; x < width; x++) {
+ threshold = (WHITE / 2 + RAND(129) - 64);
+ pixel = bits[x] + error;
+ p = (pixel > threshold) ? WHITE : BLACK;
+ error = pixel - p;
+ new_bits[x] = (BYTE)p;
+ lerr[x] = INITERR(bits[x], p);
+ }
+
+ // interior bits
+ for(y = 1; y < height; y++) {
+ // scan left to right
+ bits = FreeImage_GetScanLine(dib, y);
+ new_bits = FreeImage_GetScanLine(new_dib, y);
+
+ cerr[0] = INITERR(bits[0], new_bits[0]);
+ for(x = 1; x < width - 1; x++) {
+ error = (lerr[x-1] + 5 * lerr[x] + 3 * lerr[x+1] + 7 * cerr[x-1]) / 16;
+ pixel = bits[x] + error;
+ if(pixel > (WHITE / 2)) {
+ new_bits[x] = WHITE;
+ cerr[x] = pixel - WHITE;
+ } else {
+ new_bits[x] = BLACK;
+ cerr[x] = pixel - BLACK;
+ }
+ }
+ // set errors for ends of the row
+ cerr[0] = INITERR (bits[0], new_bits[0]);
+ cerr[width - 1] = INITERR (bits[width - 1], new_bits[width - 1]);
+
+ // swap error buffers
+ int *terr = lerr; lerr = cerr; cerr = terr;
+ }
+
+ free(lerr);
+ free(cerr);
+
+ return new_dib;
+}
+
+// ==========================================================
+// Bayer ordered dispersed dot dithering
+//
+
+// Function taken from "Ordered Dithering, Stephen Hawley, Graphics Gems, Academic Press, 1990"
+// This function is used to generate a Bayer dithering matrice whose dimension are 2^size by 2^size
+//
+static int dithervalue(int x, int y, int size) {
+ int d = 0;
+ /*
+ * calculate the dither value at a particular
+ * (x, y) over the size of the matrix.
+ */
+ while (size-->0) {
+ /* Think of d as the density. At every iteration,
+ * d is shifted left one and a new bit is put in the
+ * low bit based on x and y. If x is odd and y is even,
+ * or x is even and y is odd, a bit is put in. This
+ * generates the checkerboard seen in dithering.
+ * This quantity is shifted left again and the low bit of
+ * y is added in.
+ * This whole thing interleaves a checkerboard bit pattern
+ * and y's bits, which is the value you want.
+ */
+ d = (d <<1 | (x&1 ^ y&1))<<1 | y&1;
+ x >>= 1;
+ y >>= 1;
+ }
+ return d;
+}
+
+// Ordered dithering with a Bayer matrix of size 2^order by 2^order
+//
+static FIBITMAP* OrderedDispersedDot(FIBITMAP *dib, int order) {
+ int x, y;
+ int width, height;
+ BYTE *bits, *new_bits;
+ FIBITMAP *new_dib = NULL;
+
+ // allocate a 8-bit DIB
+ width = FreeImage_GetWidth(dib);
+ height = FreeImage_GetHeight(dib);
+ new_dib = FreeImage_Allocate(width, height, 8);
+ if(NULL == new_dib) return NULL;
+
+ // build the dithering matrix
+ int l = (1 << order); // square of dither matrix order; the dimensions of the matrix
+ BYTE *matrix = (BYTE*)malloc(l*l * sizeof(BYTE));
+ for(int i = 0; i < l*l; i++) {
+ // according to "Purdue University: Digital Image Processing Laboratory: Image Halftoning, April 30th, 2006
+ matrix[i] = (BYTE)( 255 * (((double)dithervalue(i / l, i % l, order) + 0.5) / (l*l)));
+ }
+
+ // perform the dithering
+ for(y = 0; y < height; y++) {
+ // scan left to right
+ bits = FreeImage_GetScanLine(dib, y);
+ new_bits = FreeImage_GetScanLine(new_dib, y);
+ for(x = 0; x < width; x++) {
+ if(bits[x] > matrix[(x % l) + l * (y % l)]) {
+ new_bits[x] = WHITE;
+ } else {
+ new_bits[x] = BLACK;
+ }
+ }
+ }
+
+ free(matrix);
+
+ return new_dib;
+}
+
+// ==========================================================
+// Ordered clustered dot dithering
+//
+
+// NB : The predefined dither matrices are the same as matrices used in
+// the Netpbm package (http://netpbm.sourceforge.net) and are defined in Ulichney's book.
+// See also : The newsprint web site at http://www.cl.cam.ac.uk/~and1000/newsprint/
+// for more technical info on this dithering technique
+//
+static FIBITMAP* OrderedClusteredDot(FIBITMAP *dib, int order) {
+ // Order-3 clustered dithering matrix.
+ int cluster3[] = {
+ 9,11,10, 8, 6, 7,
+ 12,17,16, 5, 0, 1,
+ 13,14,15, 4, 3, 2,
+ 8, 6, 7, 9,11,10,
+ 5, 0, 1,12,17,16,
+ 4, 3, 2,13,14,15
+ };
+
+ // Order-4 clustered dithering matrix.
+ int cluster4[] = {
+ 18,20,19,16,13,11,12,15,
+ 27,28,29,22, 4, 3, 2, 9,
+ 26,31,30,21, 5, 0, 1,10,
+ 23,25,24,17, 8, 6, 7,14,
+ 13,11,12,15,18,20,19,16,
+ 4, 3, 2, 9,27,28,29,22,
+ 5, 0, 1,10,26,31,30,21,
+ 8, 6, 7,14,23,25,24,17
+ };
+
+ // Order-8 clustered dithering matrix.
+ int cluster8[] = {
+ 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60,
+ 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52,
+ 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44,
+ 88,110,123,124,125,118,107, 85, 39, 17, 4, 3, 2, 9, 20, 42,
+ 89,111,122,127,126,117,106, 84, 38, 16, 5, 0, 1, 10, 21, 43,
+ 79,102,119,121,120,113, 97, 82, 48, 25, 8, 6, 7, 14, 30, 45,
+ 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53,
+ 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61,
+ 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67,
+ 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75,
+ 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83,
+ 39, 17, 4, 3, 2, 9, 20, 42, 88,110,123,124,125,118,107, 85,
+ 38, 16, 5, 0, 1, 10, 21, 43, 89,111,122,127,126,117,106, 84,
+ 48, 25, 8, 6, 7, 14, 30, 45, 79,102,119,121,120,113, 97, 82,
+ 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74,
+ 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66
+ };
+
+ int x, y, pixel;
+ int width, height;
+ BYTE *bits, *new_bits;
+ FIBITMAP *new_dib = NULL;
+
+ // allocate a 8-bit DIB
+ width = FreeImage_GetWidth(dib);
+ height = FreeImage_GetHeight(dib);
+ new_dib = FreeImage_Allocate(width, height, 8);
+ if(NULL == new_dib) return NULL;
+
+ // select the dithering matrix
+ int *matrix = NULL;
+ switch(order) {
+ case 3:
+ matrix = &cluster3[0];
+ break;
+ case 4:
+ matrix = &cluster4[0];
+ break;
+ case 8:
+ matrix = &cluster8[0];
+ break;
+ default:
+ return NULL;
+ }
+
+ // scale the dithering matrix
+ int l = 2 * order;
+ int scale = 256 / (l * order);
+ for(y = 0; y < l; y++) {
+ for(x = 0; x < l; x++) {
+ matrix[y*l + x] *= scale;
+ }
+ }
+
+ // perform the dithering
+ for(y = 0; y < height; y++) {
+ // scan left to right
+ bits = FreeImage_GetScanLine(dib, y);
+ new_bits = FreeImage_GetScanLine(new_dib, y);
+ for(x = 0; x < width; x++) {
+ pixel = bits[x];
+ if(pixel >= matrix[(y % l) + l * (x % l)]) {
+ new_bits[x] = WHITE;
+ } else {
+ new_bits[x] = BLACK;
+ }
+ }
+ }
+
+ return new_dib;
+}
+
+
+// ==========================================================
+// Halftoning function
+//
+FIBITMAP * DLL_CALLCONV
+FreeImage_Dither(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm) {
+ FIBITMAP *input = NULL, *dib8 = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const unsigned bpp = FreeImage_GetBPP(dib);
+
+ if(bpp == 1) {
+ // Just clone the dib and adjust the palette if needed
+ FIBITMAP *new_dib = FreeImage_Clone(dib);
+ if(NULL == new_dib) return NULL;
+ if(FreeImage_GetColorType(new_dib) == FIC_PALETTE) {
+ // Build a monochrome palette
+ RGBQUAD *pal = FreeImage_GetPalette(new_dib);
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+ }
+ return new_dib;
+ }
+
+ // Convert the input dib to a 8-bit greyscale dib
+ //
+ switch(bpp) {
+ case 8:
+ if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+ input = dib;
+ } else {
+ input = FreeImage_ConvertToGreyscale(dib);
+ }
+ break;
+ case 4:
+ case 16:
+ case 24:
+ case 32:
+ input = FreeImage_ConvertToGreyscale(dib);
+ break;
+ }
+ if(NULL == input) return NULL;
+
+ // Apply the dithering algorithm
+ switch(algorithm) {
+ case FID_FS:
+ dib8 = FloydSteinberg(input);
+ break;
+ case FID_BAYER4x4:
+ dib8 = OrderedDispersedDot(input, 2);
+ break;
+ case FID_BAYER8x8:
+ dib8 = OrderedDispersedDot(input, 3);
+ break;
+ case FID_BAYER16x16:
+ dib8 = OrderedDispersedDot(input, 4);
+ break;
+ case FID_CLUSTER6x6:
+ dib8 = OrderedClusteredDot(input, 3);
+ break;
+ case FID_CLUSTER8x8:
+ dib8 = OrderedClusteredDot(input, 4);
+ break;
+ case FID_CLUSTER16x16:
+ dib8 = OrderedClusteredDot(input, 8);
+ break;
+ }
+ if(input != dib) {
+ FreeImage_Unload(input);
+ }
+
+ // Build a greyscale palette (needed by threshold)
+ RGBQUAD *grey_pal = FreeImage_GetPalette(dib8);
+ for(int i = 0; i < 256; i++) {
+ grey_pal[i].rgbRed = (BYTE)i;
+ grey_pal[i].rgbGreen = (BYTE)i;
+ grey_pal[i].rgbBlue = (BYTE)i;
+ }
+
+ // Convert to 1-bit
+ FIBITMAP *new_dib = FreeImage_Threshold(dib8, 128);
+ FreeImage_Unload(dib8);
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ return new_dib;
+}
+
+// ==========================================================
+// Thresholding function
+//
+FIBITMAP * DLL_CALLCONV
+FreeImage_Threshold(FIBITMAP *dib, BYTE T) {
+ FIBITMAP *dib8 = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ const unsigned bpp = FreeImage_GetBPP(dib);
+
+ if(bpp == 1) {
+ // Just clone the dib and adjust the palette if needed
+ FIBITMAP *new_dib = FreeImage_Clone(dib);
+ if(NULL == new_dib) return NULL;
+ if(FreeImage_GetColorType(new_dib) == FIC_PALETTE) {
+ // Build a monochrome palette
+ RGBQUAD *pal = FreeImage_GetPalette(new_dib);
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+ }
+ return new_dib;
+ }
+
+ // Convert the input dib to a 8-bit greyscale dib
+ //
+ switch(bpp) {
+ case 8:
+ if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+ dib8 = dib;
+ } else {
+ dib8 = FreeImage_ConvertToGreyscale(dib);
+ }
+ break;
+ case 4:
+ case 16:
+ case 24:
+ case 32:
+ dib8 = FreeImage_ConvertToGreyscale(dib);
+ break;
+ }
+ if(NULL == dib8) return NULL;
+
+ // Allocate a new 1-bit DIB
+ int width = FreeImage_GetWidth(dib);
+ int height = FreeImage_GetHeight(dib);
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 1);
+ if(NULL == new_dib) return NULL;
+ // Build a monochrome palette
+ RGBQUAD *pal = FreeImage_GetPalette(new_dib);
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+
+ // Perform the thresholding
+ //
+ for(int y = 0; y < height; y++) {
+ BYTE *bits8 = FreeImage_GetScanLine(dib8, y);
+ BYTE *bits1 = FreeImage_GetScanLine(new_dib, y);
+ for(int x = 0; x < width; x++) {
+ if(bits8[x] < T) {
+ // Set bit(x, y) to 0
+ bits1[x >> 3] &= (0xFF7F >> (x & 0x7));
+ } else {
+ // Set bit(x, y) to 1
+ bits1[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+ }
+ if(dib8 != dib) {
+ FreeImage_Unload(dib8);
+ }
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(new_dib, dib);
+
+ return new_dib;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/J2KHelper.cpp b/plugins/AdvaImg/src/FreeImage/J2KHelper.cpp
new file mode 100644
index 0000000000..44b210aca0
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/J2KHelper.cpp
@@ -0,0 +1,500 @@
+// ==========================================================
+// JPEG2000 helpers
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../LibOpenJPEG/openjpeg.h"
+
+/**
+Divide an integer by a power of 2 and round upwards
+@return Returns a divided by 2^b
+*/
+static int int_ceildivpow2(int a, int b) {
+ return (a + (1 << b) - 1) >> b;
+}
+
+/**
+Convert a OpenJPEG image to a FIBITMAP
+@param format_id Plugin ID
+@param image OpenJPEG image
+@return Returns the converted image if successful, returns NULL otherwise
+*/
+FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image) {
+ FIBITMAP *dib = NULL;
+
+ try {
+ // compute image width and height
+
+ //int w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx);
+ int wr = image->comps[0].w;
+ int wrr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor);
+
+ //int h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy);
+ //int hr = image->comps[0].h;
+ int hrr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor);
+
+ // check the number of components
+
+ int numcomps = image->numcomps;
+
+ BOOL bIsValid = TRUE;
+ for(int c = 0; c < numcomps - 1; c++) {
+ if ( (image->comps[c].dx == image->comps[c+1].dx) &&
+ (image->comps[c].dy == image->comps[c+1].dy) &&
+ (image->comps[c].prec == image->comps[c+1].prec) ) {
+ continue;
+ } else {
+ bIsValid = FALSE;
+ break;
+ }
+ }
+ bIsValid &= ((numcomps == 1) || (numcomps == 3) || (numcomps == 4));
+ if (!bIsValid) {
+ if(numcomps) {
+ FreeImage_OutputMessageProc(format_id, "Warning: image contains %d greyscale components. Only the first will be loaded.\n", numcomps);
+ numcomps = 1;
+ } else {
+ // unknown type
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+ }
+
+ // create a new DIB
+
+ if(image->comps[0].prec <= 8) {
+ switch(numcomps) {
+ case 1:
+ dib = FreeImage_Allocate(wrr, hrr, 8);
+ break;
+ case 3:
+ dib = FreeImage_Allocate(wrr, hrr, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ break;
+ case 4:
+ dib = FreeImage_Allocate(wrr, hrr, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ break;
+ }
+ } else if(image->comps[0].prec <= 16) {
+ switch(numcomps) {
+ case 1:
+ dib = FreeImage_AllocateT(FIT_UINT16, wrr, hrr);
+ break;
+ case 3:
+ dib = FreeImage_AllocateT(FIT_RGB16, wrr, hrr);
+ break;
+ case 4:
+ dib = FreeImage_AllocateT(FIT_RGBA16, wrr, hrr);
+ break;
+ }
+ } else {
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+ if (!dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ if(image->comps[0].prec <= 8) {
+ if(numcomps == 1) {
+ // 8-bit greyscale
+ // ----------------------------------------------------------
+
+ // build a greyscale palette
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ for (int i = 0; i < 256; i++) {
+ pal[i].rgbRed = (BYTE)i;
+ pal[i].rgbGreen = (BYTE)i;
+ pal[i].rgbBlue = (BYTE)i;
+ }
+
+ // load pixel data
+
+ unsigned pixel_count = 0;
+
+ for(int y = 0; y < hrr; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+ for(int x = 0; x < wrr; x++) {
+ const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+ int index = image->comps[0].data[pixel_pos];
+ index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+ bits[x] = (BYTE)index;
+
+ pixel_count++;
+ }
+ }
+ }
+ else if(numcomps == 3) {
+
+ // 24-bit RGB
+ // ----------------------------------------------------------
+
+ // load pixel data
+
+ unsigned pixel_count = 0;
+
+ for(int y = 0; y < hrr; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+ for(int x = 0; x < wrr; x++) {
+ const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+ int r = image->comps[0].data[pixel_pos];
+ r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+ int g = image->comps[1].data[pixel_pos];
+ g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+
+ int b = image->comps[2].data[pixel_pos];
+ b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+ bits[FI_RGBA_RED] = (BYTE)r;
+ bits[FI_RGBA_GREEN] = (BYTE)g;
+ bits[FI_RGBA_BLUE] = (BYTE)b;
+ bits += 3;
+
+ pixel_count++;
+ }
+ }
+ }
+ else if(numcomps == 4) {
+
+ // 32-bit RGBA
+ // ----------------------------------------------------------
+
+ // load pixel data
+
+ unsigned pixel_count = 0;
+
+ for(int y = 0; y < hrr; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+ for(int x = 0; x < wrr; x++) {
+ const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+ int r = image->comps[0].data[pixel_pos];
+ r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+ int g = image->comps[1].data[pixel_pos];
+ g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+
+ int b = image->comps[2].data[pixel_pos];
+ b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+ int a = image->comps[3].data[pixel_pos];
+ a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0);
+
+ bits[FI_RGBA_RED] = (BYTE)r;
+ bits[FI_RGBA_GREEN] = (BYTE)g;
+ bits[FI_RGBA_BLUE] = (BYTE)b;
+ bits[FI_RGBA_ALPHA] = (BYTE)a;
+ bits += 4;
+
+ pixel_count++;
+ }
+ }
+ }
+ }
+ else if(image->comps[0].prec <= 16) {
+ if(numcomps == 1) {
+ // 16-bit greyscale
+ // ----------------------------------------------------------
+
+ // load pixel data
+
+ unsigned pixel_count = 0;
+
+ for(int y = 0; y < hrr; y++) {
+ unsigned short *bits = (unsigned short*)FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+ for(int x = 0; x < wrr; x++) {
+ const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+ int index = image->comps[0].data[pixel_pos];
+ index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+ bits[x] = (unsigned short)index;
+
+ pixel_count++;
+ }
+ }
+ }
+ else if(numcomps == 3) {
+
+ // 48-bit RGB
+ // ----------------------------------------------------------
+
+ // load pixel data
+
+ unsigned pixel_count = 0;
+
+ for(int y = 0; y < hrr; y++) {
+ FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+ for(int x = 0; x < wrr; x++) {
+ const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+ int r = image->comps[0].data[pixel_pos];
+ r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+ int g = image->comps[1].data[pixel_pos];
+ g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+
+ int b = image->comps[2].data[pixel_pos];
+ b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+ bits[x].red = (WORD)r;
+ bits[x].green = (WORD)g;
+ bits[x].blue = (WORD)b;
+
+ pixel_count++;
+ }
+ }
+ }
+ else if(numcomps == 4) {
+
+ // 64-bit RGBA
+ // ----------------------------------------------------------
+
+ // load pixel data
+
+ unsigned pixel_count = 0;
+
+ for(int y = 0; y < hrr; y++) {
+ FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, hrr - 1 - y);
+
+ for(int x = 0; x < wrr; x++) {
+ const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr;
+
+ int r = image->comps[0].data[pixel_pos];
+ r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0);
+
+ int g = image->comps[1].data[pixel_pos];
+ g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0);
+
+ int b = image->comps[2].data[pixel_pos];
+ b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0);
+
+ int a = image->comps[3].data[pixel_pos];
+ a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0);
+
+ bits[x].red = (WORD)r;
+ bits[x].green = (WORD)g;
+ bits[x].blue = (WORD)b;
+ bits[x].alpha = (WORD)a;
+
+ pixel_count++;
+ }
+ }
+ }
+ }
+
+ return dib;
+
+ } catch(const char *text) {
+ if(dib) FreeImage_Unload(dib);
+ FreeImage_OutputMessageProc(format_id, text);
+ return NULL;
+ }
+
+}
+
+/**
+Convert a FIBITMAP to a OpenJPEG image
+@param format_id Plugin ID
+@param dib FreeImage image
+@param parameters Compression parameters
+@return Returns the converted image if successful, returns NULL otherwise
+*/
+opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters) {
+ int prec, numcomps, x, y, index;
+ OPJ_COLOR_SPACE color_space;
+ opj_image_cmptparm_t cmptparm[4]; // maximum of 4 components
+ opj_image_t *image = NULL; // image to encode
+
+ try {
+ int w = FreeImage_GetWidth(dib);
+ int h = FreeImage_GetHeight(dib);
+
+ // get image characteristics
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ if(image_type == FIT_BITMAP) {
+ // standard image ...
+ prec = 8;
+ switch(FreeImage_GetColorType(dib)) {
+ case FIC_MINISBLACK:
+ numcomps = 1;
+ color_space = CLRSPC_GRAY;
+ break;
+ case FIC_RGB:
+ if(FreeImage_GetBPP(dib) == 32) {
+ // 32-bit image with a fully opaque layer
+ numcomps = 4;
+ color_space = CLRSPC_SRGB;
+ } else {
+ // 24-bit image
+ numcomps = 3;
+ color_space = CLRSPC_SRGB;
+ }
+ break;
+ case FIC_RGBALPHA:
+ numcomps = 4;
+ color_space = CLRSPC_SRGB;
+ break;
+ default:
+ return NULL;
+ }
+ } else {
+ // HDR image ...
+ prec = 16;
+ switch(image_type) {
+ case FIT_UINT16:
+ numcomps = 1;
+ color_space = CLRSPC_GRAY;
+ break;
+ case FIT_RGB16:
+ numcomps = 3;
+ color_space = CLRSPC_SRGB;
+ break;
+ case FIT_RGBA16:
+ numcomps = 4;
+ color_space = CLRSPC_SRGB;
+ break;
+ default:
+ return NULL;
+ }
+ }
+
+ // initialize image components
+ memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t));
+ for(int i = 0; i < numcomps; i++) {
+ cmptparm[i].dx = parameters->subsampling_dx;
+ cmptparm[i].dy = parameters->subsampling_dy;
+ cmptparm[i].w = w;
+ cmptparm[i].h = h;
+ cmptparm[i].prec = prec;
+ cmptparm[i].bpp = prec;
+ cmptparm[i].sgnd = 0;
+ }
+ // create the image
+ image = opj_image_create(numcomps, &cmptparm[0], color_space);
+ if (!image) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set image offset and reference grid
+ image->x0 = parameters->image_offset_x0;
+ image->y0 = parameters->image_offset_y0;
+ image->x1 = parameters->image_offset_x0 + (w - 1) * parameters->subsampling_dx + 1;
+ image->y1 = parameters->image_offset_y0 + (h - 1) * parameters->subsampling_dy + 1;
+
+ // set image data
+ if(prec == 8) {
+ switch(numcomps) {
+ case 1:
+ index = 0;
+ for(y = 0; y < h; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y);
+ for(x = 0; x < w; x++) {
+ image->comps[0].data[index] = bits[x];
+ index++;
+ }
+ }
+ break;
+ case 3:
+ index = 0;
+ for(y = 0; y < h; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y);
+ for(x = 0; x < w; x++) {
+ image->comps[0].data[index] = bits[FI_RGBA_RED];
+ image->comps[1].data[index] = bits[FI_RGBA_GREEN];
+ image->comps[2].data[index] = bits[FI_RGBA_BLUE];
+ bits += 3;
+ index++;
+ }
+ }
+ break;
+ case 4:
+ index = 0;
+ for(y = 0; y < h; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y);
+ for(x = 0; x < w; x++) {
+ image->comps[0].data[index] = bits[FI_RGBA_RED];
+ image->comps[1].data[index] = bits[FI_RGBA_GREEN];
+ image->comps[2].data[index] = bits[FI_RGBA_BLUE];
+ image->comps[3].data[index] = bits[FI_RGBA_ALPHA];
+ bits += 4;
+ index++;
+ }
+ }
+ break;
+ }
+ }
+ else if(prec == 16) {
+ switch(numcomps) {
+ case 1:
+ index = 0;
+ for(y = 0; y < h; y++) {
+ WORD *bits = (WORD*)FreeImage_GetScanLine(dib, h - 1 - y);
+ for(x = 0; x < w; x++) {
+ image->comps[0].data[index] = bits[x];
+ index++;
+ }
+ }
+ break;
+ case 3:
+ index = 0;
+ for(y = 0; y < h; y++) {
+ FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, h - 1 - y);
+ for(x = 0; x < w; x++) {
+ image->comps[0].data[index] = bits[x].red;
+ image->comps[1].data[index] = bits[x].green;
+ image->comps[2].data[index] = bits[x].blue;
+ index++;
+ }
+ }
+ break;
+ case 4:
+ index = 0;
+ for(y = 0; y < h; y++) {
+ FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, h - 1 - y);
+ for(x = 0; x < w; x++) {
+ image->comps[0].data[index] = bits[x].red;
+ image->comps[1].data[index] = bits[x].green;
+ image->comps[2].data[index] = bits[x].blue;
+ image->comps[3].data[index] = bits[x].alpha;
+ index++;
+ }
+ }
+ break;
+ }
+ }
+
+ return image;
+
+ } catch (const char *text) {
+ if(image) opj_image_destroy(image);
+ FreeImage_OutputMessageProc(format_id, text);
+ return NULL;
+ }
+}
diff --git a/plugins/AdvaImg/src/FreeImage/MemoryIO.cpp b/plugins/AdvaImg/src/FreeImage/MemoryIO.cpp
new file mode 100644
index 0000000000..6ae3fb2e11
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/MemoryIO.cpp
@@ -0,0 +1,237 @@
+// ==========================================================
+// Memory Input/Output functions
+//
+// Design and implementation by
+// - Ryan Rubley <ryan@lostreality.org>
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+
+// =====================================================================
+
+
+// =====================================================================
+// Open and close a memory handle
+// =====================================================================
+
+FIMEMORY * DLL_CALLCONV
+FreeImage_OpenMemory(BYTE *data, DWORD size_in_bytes) {
+ // allocate a memory handle
+ FIMEMORY *stream = (FIMEMORY*)malloc(sizeof(FIMEMORY));
+ if(stream) {
+ stream->data = (BYTE*)malloc(sizeof(FIMEMORYHEADER));
+
+ if(stream->data) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+
+ // initialize the memory header
+ memset(mem_header, 0, sizeof(FIMEMORYHEADER));
+
+ if(data && size_in_bytes) {
+ // wrap a user buffer
+ mem_header->delete_me = FALSE;
+ mem_header->data = (BYTE*)data;
+ mem_header->datalen = mem_header->filelen = size_in_bytes;
+ } else {
+ mem_header->delete_me = TRUE;
+ }
+
+ return stream;
+ }
+ free(stream);
+ }
+
+ return NULL;
+}
+
+
+void DLL_CALLCONV
+FreeImage_CloseMemory(FIMEMORY *stream) {
+ if(stream && stream->data) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+ if(mem_header->delete_me) {
+ free(mem_header->data);
+ }
+ free(mem_header);
+ free(stream);
+ }
+}
+
+// =====================================================================
+// Memory stream load/save functions
+// =====================================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LoadFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
+ if (stream && stream->data) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ return FreeImage_LoadFromHandle(fif, &io, (fi_handle)stream, flags);
+ }
+
+ return NULL;
+}
+
+
+BOOL DLL_CALLCONV
+FreeImage_SaveToMemory(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FIMEMORY *stream, int flags) {
+ if (stream) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+
+ if(mem_header->delete_me == TRUE) {
+ return FreeImage_SaveToHandle(fif, dib, &io, (fi_handle)stream, flags);
+ } else {
+ // do not save in a user buffer
+ FreeImage_OutputMessageProc(fif, "Memory buffer is read only");
+ }
+ }
+
+ return FALSE;
+}
+
+// =====================================================================
+// Memory stream buffer access
+// =====================================================================
+
+BOOL DLL_CALLCONV
+FreeImage_AcquireMemory(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes) {
+ if (stream) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data);
+
+ *data = (BYTE*)mem_header->data;
+ *size_in_bytes = mem_header->filelen;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// =====================================================================
+// Memory stream file type access
+// =====================================================================
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFileTypeFromMemory(FIMEMORY *stream, int size) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ if (stream != NULL) {
+ return FreeImage_GetFileTypeFromHandle(&io, (fi_handle)stream, size);
+ }
+
+ return FIF_UNKNOWN;
+}
+
+// =====================================================================
+// Seeking in Memory stream
+// =====================================================================
+
+/**
+Moves the memory pointer to a specified location
+@param stream Pointer to FIMEMORY structure
+@param offset Number of bytes from origin
+@param origin Initial position
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL DLL_CALLCONV
+FreeImage_SeekMemory(FIMEMORY *stream, long offset, int origin) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ if (stream != NULL) {
+ int success = io.seek_proc((fi_handle)stream, offset, origin);
+ return (success == 0) ? TRUE : FALSE;
+ }
+
+ return FALSE;
+}
+
+/**
+Gets the current position of a memory pointer
+@param stream Target FIMEMORY structure
+@return Returns the current file position if successful, -1 otherwise
+*/
+long DLL_CALLCONV
+FreeImage_TellMemory(FIMEMORY *stream) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ if (stream != NULL) {
+ return io.tell_proc((fi_handle)stream);
+ }
+
+ return -1L;
+}
+
+// =====================================================================
+// Reading or Writing in Memory stream
+// =====================================================================
+
+/**
+Reads data from a memory stream
+@param buffer Storage location for data
+@param size Item size in bytes
+@param count Maximum number of items to be read
+@param stream Pointer to FIMEMORY structure
+@return Returns the number of full items actually read, which may be less than count if an error occurs
+*/
+unsigned DLL_CALLCONV
+FreeImage_ReadMemory(void *buffer, unsigned size, unsigned count, FIMEMORY *stream) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ if (stream != NULL) {
+ return io.read_proc(buffer, size, count, stream);
+ }
+
+ return 0;
+}
+
+/**
+Writes data to a memory stream.
+@param buffer Pointer to data to be written
+@param size Item size in bytes
+@param count Maximum number of items to be written
+@param stream Pointer to FIMEMORY structure
+@return Returns the number of full items actually written, which may be less than count if an error occurs
+*/
+unsigned DLL_CALLCONV
+FreeImage_WriteMemory(const void *buffer, unsigned size, unsigned count, FIMEMORY *stream) {
+ if (stream != NULL) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)stream)->data);
+
+ if(mem_header->delete_me == TRUE) {
+ return io.write_proc((void *)buffer, size, count, stream);
+ } else {
+ // do not write in a user buffer
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Memory buffer is read only");
+ }
+ }
+
+ return 0;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/MultiPage.cpp b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp
new file mode 100644
index 0000000000..e5bcee3ded
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp
@@ -0,0 +1,990 @@
+// ==========================================================
+// Multi-Page functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Laurent Rocher (rocherl@club-internet.fr)
+// - Steve Johnson (steve@parisgroup.net)
+// - Petr Pytelka (pyta@lightcomp.com)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net
+// - Martin Dyring-Andersen (mda@spamfighter.com)
+// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "CacheFile.h"
+#include "FreeImageIO.h"
+#include "Plugin.h"
+#include "Utilities.h"
+#include "FreeImage.h"
+
+// ----------------------------------------------------------
+
+enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE };
+
+// ----------------------------------------------------------
+
+struct BlockTypeS {
+ BlockType m_type;
+
+ BlockTypeS(BlockType type) : m_type(type) {
+ }
+ virtual ~BlockTypeS() {}
+};
+
+struct BlockContinueus : public BlockTypeS {
+ int m_start;
+ int m_end;
+
+ BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS),
+ m_start(s),
+ m_end(e) {
+ }
+};
+
+struct BlockReference : public BlockTypeS {
+ int m_reference;
+ int m_size;
+
+ BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE),
+ m_reference(r),
+ m_size(size) {
+ }
+};
+
+// ----------------------------------------------------------
+
+typedef std::list<BlockTypeS *> BlockList;
+typedef std::list<BlockTypeS *>::iterator BlockListIterator;
+
+// ----------------------------------------------------------
+
+FI_STRUCT (MULTIBITMAPHEADER) {
+ PluginNode *node;
+ FREE_IMAGE_FORMAT fif;
+ FreeImageIO *io;
+ fi_handle handle;
+ CacheFile *m_cachefile;
+ std::map<FIBITMAP *, int> locked_pages;
+ BOOL changed;
+ int page_count;
+ BlockList m_blocks;
+ char *m_filename;
+ BOOL read_only;
+ FREE_IMAGE_FORMAT cache_fif;
+ int load_flags;
+};
+
+// =====================================================================
+// Helper functions
+// =====================================================================
+
+inline void
+ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) {
+ size_t lastDot = src_filename.find_last_of('.');
+ if (lastDot == std::string::npos) {
+ dst_filename = src_filename;
+ dst_filename += ".";
+ dst_filename += dst_extension;
+ }
+ else {
+ dst_filename = src_filename.substr(0, lastDot + 1);
+ dst_filename += dst_extension;
+ }
+}
+
+// =====================================================================
+// Internal Multipage functions
+// =====================================================================
+
+inline MULTIBITMAPHEADER *
+FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) {
+ return (MULTIBITMAPHEADER *)bitmap->data;
+}
+
+static BlockListIterator DLL_CALLCONV
+FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) {
+ assert(NULL != bitmap);
+
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ // step 1: find the block that matches the given position
+
+ int prev_count = 0;
+ int count = 0;
+ BlockListIterator i;
+ BlockTypeS *current_block = NULL;
+
+ for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
+ prev_count = count;
+
+ switch((*i)->m_type) {
+ case BLOCK_CONTINUEUS :
+ count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
+ break;
+
+ case BLOCK_REFERENCE :
+ count++;
+ break;
+ }
+
+ current_block = *i;
+
+ if (count > position)
+ break;
+ }
+
+ // step 2: make sure we found the node. from here it gets a little complicated:
+ // * if the block is there, just return it
+ // * if the block is a series of blocks, split it in max 3 new blocks
+ // and return the splitted block
+
+ if ((current_block) && (count > position)) {
+ switch(current_block->m_type) {
+ case BLOCK_REFERENCE :
+ return i;
+
+ case BLOCK_CONTINUEUS :
+ {
+ BlockContinueus *block = (BlockContinueus *)current_block;
+
+ if (block->m_start != block->m_end) {
+ int item = block->m_start + (position - prev_count);
+
+ // left part
+
+ if (item != block->m_start) {
+ BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1);
+ header->m_blocks.insert(i, (BlockTypeS *)block_a);
+ }
+
+ // middle part
+
+ BlockContinueus *block_b = new BlockContinueus(item, item);
+ BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b);
+
+ // right part
+
+ if (item != block->m_end) {
+ BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end);
+ header->m_blocks.insert(i, (BlockTypeS *)block_c);
+ }
+
+ // remove the old block that was just splitted
+
+ header->m_blocks.remove((BlockTypeS *)block);
+ delete block;
+
+ // return the splitted block
+
+ return block_target;
+ }
+
+ return i;
+ }
+ }
+ }
+ // we should never go here ...
+ assert(false);
+ return header->m_blocks.end();
+}
+
+int DLL_CALLCONV
+FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) {
+ if (bitmap) {
+ if (((MULTIBITMAPHEADER *)bitmap->data)->handle) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ header->io->seek_proc(header->handle, 0, SEEK_SET);
+
+ void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
+
+ int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1;
+
+ FreeImage_Close(header->node, header->io, header->handle, data);
+
+ return page_count;
+ }
+ }
+
+ return 0;
+}
+
+// =====================================================================
+// Multipage functions
+// =====================================================================
+
+FIMULTIBITMAP * DLL_CALLCONV
+FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
+
+ FILE *handle = NULL;
+ try {
+ // sanity check on the parameters
+
+ if (create_new) {
+ read_only = FALSE;
+ }
+
+ // retrieve the plugin list to find the node belonging to this plugin
+
+ PluginList *list = FreeImage_GetPluginList();
+
+ if (list) {
+ PluginNode *node = list->FindNodeFromFIF(fif);
+
+ if (node) {
+ std::auto_ptr<FreeImageIO> io (new FreeImageIO);
+
+ SetDefaultIO(io.get());
+
+ if (!create_new) {
+ handle = fopen(filename, "rb");
+ if (handle == NULL) {
+ return NULL;
+ }
+ }
+
+ std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
+ std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
+ header->m_filename = new char[strlen(filename) + 1];
+ strcpy(header->m_filename, filename);
+ header->node = node;
+ header->fif = fif;
+ header->io = io.get ();
+ header->handle = handle;
+ header->changed = FALSE;
+ header->read_only = read_only;
+ header->m_cachefile = NULL;
+ header->cache_fif = fif;
+ header->load_flags = flags;
+
+ // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
+
+ bitmap->data = header.get();
+
+ // cache the page count
+
+ header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
+
+ // allocate a continueus block to describe the bitmap
+
+ if (!create_new) {
+ header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
+ }
+
+ // set up the cache
+
+ if (!read_only) {
+ std::string cache_name;
+ ReplaceExtension(cache_name, filename, "ficache");
+
+ std::auto_ptr<CacheFile> cache_file (new CacheFile(cache_name, keep_cache_in_memory));
+
+ if (cache_file->open()) {
+ // we can use release() as std::bad_alloc won't be thrown from here on
+ header->m_cachefile = cache_file.release();
+ } else {
+ // an error occured ...
+ fclose(handle);
+ return NULL;
+ }
+ }
+ // return the multibitmap
+ // std::bad_alloc won't be thrown from here on
+ header.release(); // now owned by bitmap
+ io.release(); // now owned by bitmap
+ return bitmap.release(); // now owned by caller
+ }
+ }
+ } catch (std::bad_alloc &) {
+ /** @todo report error */
+ }
+ if (handle)
+ fclose(handle);
+ return NULL;
+}
+
+FIMULTIBITMAP * DLL_CALLCONV
+FreeImage_OpenMultiBitmapU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
+
+ // convert to single character - no national chars in extensions
+ char *extension = (char *)malloc(wcslen(filename)+1);
+ unsigned int i=0;
+ for (; i < wcslen(filename); i++) // convert 16-bit to 8-bit
+ extension[i] = (char)(filename[i] & 0x00FF);
+ // set terminating 0
+ extension[i]=0;
+ FIMULTIBITMAP *fRet = FreeImage_OpenMultiBitmap(fif, extension, create_new, read_only, keep_cache_in_memory, flags);
+ free(extension);
+
+ return fRet;
+}
+
+FIMULTIBITMAP * DLL_CALLCONV
+FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
+ try {
+ BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
+
+ if (io && handle) {
+
+ // retrieve the plugin list to find the node belonging to this plugin
+ PluginList *list = FreeImage_GetPluginList();
+
+ if (list) {
+ PluginNode *node = list->FindNodeFromFIF(fif);
+
+ if (node) {
+ std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
+ std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
+ std::auto_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io));
+ header->io = tmp_io.get();
+ header->m_filename = NULL;
+ header->node = node;
+ header->fif = fif;
+ header->handle = handle;
+ header->changed = FALSE;
+ header->read_only = read_only;
+ header->m_cachefile = NULL;
+ header->cache_fif = fif;
+ header->load_flags = flags;
+
+ // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
+
+ bitmap->data = header.get();
+
+ // cache the page count
+
+ header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
+
+ // allocate a continueus block to describe the bitmap
+
+ header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
+
+ if (!read_only) {
+ // set up the cache
+ std::auto_ptr<CacheFile> cache_file (new CacheFile("", TRUE));
+
+ if (cache_file->open()) {
+ header->m_cachefile = cache_file.release();
+ }
+ }
+ tmp_io.release();
+ header.release();
+ return bitmap.release();
+ }
+ }
+ }
+ } catch (std::bad_alloc &) {
+ /** @todo report error */
+ }
+ return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) {
+ if (!bitmap || !bitmap->data || !io || !handle) {
+ return FALSE;
+ }
+
+ BOOL success = TRUE;
+
+ // retrieve the plugin list to find the node belonging to this plugin
+ PluginList *list = FreeImage_GetPluginList();
+
+ if (list) {
+ PluginNode *node = list->FindNodeFromFIF(fif);
+
+ if(node) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ // dst data
+ void *data = FreeImage_Open(node, io, handle, FALSE);
+ // src data
+ void *data_read = NULL;
+
+ if(header->handle) {
+ // open src
+ header->io->seek_proc(header->handle, 0, SEEK_SET);
+ data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE);
+ }
+
+ // write all the pages to the file using handle and io
+
+ int count = 0;
+
+ for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) {
+ if (success) {
+ switch((*i)->m_type) {
+ case BLOCK_CONTINUEUS:
+ {
+ BlockContinueus *block = (BlockContinueus *)(*i);
+
+ for (int j = block->m_start; j <= block->m_end; j++) {
+
+ // load the original source data
+ FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read);
+
+ // save the data
+ success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
+ count++;
+
+ FreeImage_Unload(dib);
+ }
+
+ break;
+ }
+
+ case BLOCK_REFERENCE:
+ {
+ BlockReference *ref = (BlockReference *)(*i);
+
+ // read the compressed data
+
+ BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE));
+
+ header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size);
+
+ // uncompress the data
+
+ FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size);
+ FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0);
+ FreeImage_CloseMemory(hmem);
+
+ // get rid of the buffer
+ free(compressed_data);
+
+ // save the data
+
+ success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
+ count++;
+
+ // unload the dib
+
+ FreeImage_Unload(dib);
+
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ // close the files
+
+ FreeImage_Close(header->node, header->io, header->handle, data_read);
+
+ FreeImage_Close(node, io, handle, data);
+
+ return success;
+ }
+ }
+
+ return FALSE;
+}
+
+
+BOOL DLL_CALLCONV
+FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) {
+ if (bitmap) {
+ BOOL success = TRUE;
+
+ if (bitmap->data) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ // saves changes only of images loaded directly from a file
+ if (header->changed && header->m_filename) {
+ try {
+ // open a temp file
+
+ std::string spool_name;
+
+ ReplaceExtension(spool_name, header->m_filename, "fispool");
+
+ // open the spool file and the source file
+
+ FILE *f = fopen(spool_name.c_str(), "w+b");
+
+ // saves changes
+ if (f == NULL) {
+ FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno));
+ success = FALSE;
+ } else {
+ success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags);
+
+ // close the files
+
+ if (fclose(f) != 0) {
+ success = FALSE;
+ FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno));
+ }
+ }
+ if (header->handle) {
+ fclose((FILE *)header->handle);
+ }
+
+ // applies changes to the destination file
+
+ if (success) {
+ remove(header->m_filename);
+ success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE;
+ if (!success) {
+ FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename);
+ }
+ } else {
+ remove(spool_name.c_str());
+ }
+ } catch (std::bad_alloc &) {
+ success = FALSE;
+ }
+
+ } else {
+ if (header->handle && header->m_filename) {
+ fclose((FILE *)header->handle);
+ }
+ }
+
+ // clear the blocks list
+
+ for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
+ delete *i;
+ }
+
+ // flush and dispose the cache
+
+ if (header->m_cachefile) {
+ header->m_cachefile->close();
+ delete header->m_cachefile;
+ }
+
+ // delete the last open bitmaps
+
+ while (!header->locked_pages.empty()) {
+ FreeImage_Unload(header->locked_pages.begin()->first);
+
+ header->locked_pages.erase(header->locked_pages.begin()->first);
+ }
+
+ // get rid of the IO structure
+
+ delete header->io;
+
+ // delete the filename
+
+ if(header->m_filename) {
+ delete[] header->m_filename;
+ }
+
+ // delete the FIMULTIBITMAPHEADER
+
+ delete header;
+ }
+
+ delete bitmap;
+
+ return success;
+ }
+
+ return FALSE;
+}
+
+int DLL_CALLCONV
+FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) {
+ if (bitmap) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ if (header->page_count == -1) {
+ header->page_count = 0;
+
+ for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
+ switch((*i)->m_type) {
+ case BLOCK_CONTINUEUS :
+ header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
+ break;
+
+ case BLOCK_REFERENCE :
+ header->page_count++;
+ break;
+ }
+ }
+ }
+
+ return header->page_count;
+ }
+
+ return 0;
+}
+
+static BlockReference*
+FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) {
+ if (header->read_only || !header->locked_pages.empty())
+ return NULL;
+
+ DWORD compressed_size = 0;
+ BYTE *compressed_data = NULL;
+
+ // compress the bitmap data
+
+ // open a memory handle
+ FIMEMORY *hmem = FreeImage_OpenMemory();
+ if(hmem==NULL) return NULL;
+ // save the file to memory
+ if (!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) {
+ FreeImage_CloseMemory(hmem);
+ return NULL;
+ }
+ // get the buffer from the memory stream
+ if (!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) {
+ FreeImage_CloseMemory(hmem);
+ return NULL;
+ }
+
+ // write the compressed data to the cache
+ int ref = header->m_cachefile->writeFile(compressed_data, compressed_size);
+ // get rid of the compressed data
+ FreeImage_CloseMemory(hmem);
+
+ return new(std::nothrow) BlockReference(ref, compressed_size);
+}
+
+void DLL_CALLCONV
+FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) {
+ if (!bitmap || !data)
+ return;
+
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ BlockReference *block = FreeImage_SavePageToBlock(header, data);
+ if(block==NULL) return;
+
+ // add the block
+ header->m_blocks.push_back((BlockTypeS *)block);
+ header->changed = TRUE;
+ header->page_count = -1;
+}
+
+void DLL_CALLCONV
+FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) {
+ if (!bitmap || !data)
+ return;
+
+ if (page >= FreeImage_GetPageCount(bitmap))
+ return;
+
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ BlockReference *block = FreeImage_SavePageToBlock(header, data);
+ if(block==NULL) return;
+
+ // add a block
+ if (page > 0) {
+ BlockListIterator block_source = FreeImage_FindBlock(bitmap, page);
+
+ header->m_blocks.insert(block_source, (BlockTypeS *)block);
+ } else {
+ header->m_blocks.push_front((BlockTypeS *)block);
+ }
+
+ header->changed = TRUE;
+ header->page_count = -1;
+}
+
+void DLL_CALLCONV
+FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) {
+ if (bitmap) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ if ((!header->read_only) && (header->locked_pages.empty())) {
+ if (FreeImage_GetPageCount(bitmap) > 1) {
+ BlockListIterator i = FreeImage_FindBlock(bitmap, page);
+
+ if (i != header->m_blocks.end()) {
+ switch((*i)->m_type) {
+ case BLOCK_CONTINUEUS :
+ delete *i;
+ header->m_blocks.erase(i);
+ break;
+
+ case BLOCK_REFERENCE :
+ header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference);
+ delete *i;
+ header->m_blocks.erase(i);
+ break;
+ }
+
+ header->changed = TRUE;
+ header->page_count = -1;
+ }
+ }
+ }
+ }
+}
+
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) {
+ if (bitmap) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ // only lock if the page wasn't locked before...
+
+ for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
+ if (i->second == page) {
+ return NULL;
+ }
+ }
+
+ // open the bitmap
+
+ header->io->seek_proc(header->handle, 0, SEEK_SET);
+
+ void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
+
+ // load the bitmap data
+
+ if (data != NULL) {
+ FIBITMAP *dib = (header->node->m_plugin->load_proc != NULL) ? header->node->m_plugin->load_proc(header->io, header->handle, page, header->load_flags, data) : NULL;
+
+ // close the file
+
+ FreeImage_Close(header->node, header->io, header->handle, data);
+
+ // if there was still another bitmap open, get rid of it
+
+ if (dib) {
+ header->locked_pages[dib] = page;
+
+ return dib;
+ }
+
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+void DLL_CALLCONV
+FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) {
+ if ((bitmap) && (page)) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ // find out if the page we try to unlock is actually locked...
+
+ if (header->locked_pages.find(page) != header->locked_pages.end()) {
+ // store the bitmap compressed in the cache for later writing
+
+ if (changed && !header->read_only) {
+ header->changed = TRUE;
+
+ // cut loose the block from the rest
+
+ BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]);
+
+ // compress the data
+
+ DWORD compressed_size = 0;
+ BYTE *compressed_data = NULL;
+
+ // open a memory handle
+ FIMEMORY *hmem = FreeImage_OpenMemory();
+ // save the page to memory
+ FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0);
+ // get the buffer from the memory stream
+ FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size);
+
+ // write the data to the cache
+
+ switch ((*i)->m_type) {
+ case BLOCK_CONTINUEUS :
+ {
+ int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
+
+ delete (*i);
+
+ *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
+
+ break;
+ }
+
+ case BLOCK_REFERENCE :
+ {
+ BlockReference *reference = (BlockReference *)(*i);
+
+ header->m_cachefile->deleteFile(reference->m_reference);
+
+ delete (*i);
+
+ int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
+
+ *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
+
+ break;
+ }
+ }
+
+ // get rid of the compressed data
+
+ FreeImage_CloseMemory(hmem);
+ }
+
+ // reset the locked page so that another page can be locked
+
+ FreeImage_Unload(page);
+
+ header->locked_pages.erase(page);
+ }
+ }
+}
+
+BOOL DLL_CALLCONV
+FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) {
+ if (bitmap) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ if ((!header->read_only) && (header->locked_pages.empty())) {
+ if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) {
+ BlockListIterator block_source = FreeImage_FindBlock(bitmap, target);
+ BlockListIterator block_target = FreeImage_FindBlock(bitmap, source);
+
+ header->m_blocks.insert(block_target, *block_source);
+ header->m_blocks.erase(block_source);
+
+ header->changed = TRUE;
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) {
+ if ((bitmap) && (count)) {
+ MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
+
+ if ((pages == NULL) || (*count == 0)) {
+ *count = (int)header->locked_pages.size();
+ } else {
+ int c = 0;
+
+ for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
+ pages[c] = i->second;
+
+ c++;
+
+ if (c == *count)
+ break;
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// =====================================================================
+// Memory IO Multipage functions
+// =====================================================================
+
+FIMULTIBITMAP * DLL_CALLCONV
+FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
+ BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
+
+ // retrieve the plugin list to find the node belonging to this plugin
+
+ PluginList *list = FreeImage_GetPluginList();
+
+ if (list) {
+ PluginNode *node = list->FindNodeFromFIF(fif);
+
+ if (node) {
+ FreeImageIO *io = new(std::nothrow) FreeImageIO;
+
+ if (io) {
+ SetMemoryIO(io);
+
+ FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP;
+
+ if (bitmap) {
+ MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER;
+
+ if (header) {
+ header->m_filename = NULL;
+ header->node = node;
+ header->fif = fif;
+ header->io = io;
+ header->handle = (fi_handle)stream;
+ header->changed = FALSE;
+ header->read_only = read_only;
+ header->m_cachefile = NULL;
+ header->cache_fif = fif;
+ header->load_flags = flags;
+
+ // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
+
+ bitmap->data = header;
+
+ // cache the page count
+
+ header->page_count = FreeImage_InternalGetPageCount(bitmap);
+
+ // allocate a continueus block to describe the bitmap
+
+ header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
+
+ if (!read_only) {
+ // set up the cache
+ CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE);
+
+ if (cache_file && cache_file->open()) {
+ header->m_cachefile = cache_file;
+ }
+ }
+
+ return bitmap;
+ }
+
+ delete bitmap;
+ }
+
+ delete io;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) {
+ if (stream && stream->data) {
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags);
+ }
+
+ return FALSE;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp b/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp
new file mode 100644
index 0000000000..6eb9aeaf48
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/NNQuantizer.cpp
@@ -0,0 +1,507 @@
+// NeuQuant Neural-Net Quantization Algorithm
+// ------------------------------------------
+//
+// Copyright (c) 1994 Anthony Dekker
+//
+// NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994.
+// See "Kohonen neural networks for optimal colour quantization"
+// in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367.
+// for a discussion of the algorithm.
+//
+// Any party obtaining a copy of these files from the author, directly or
+// indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+// world-wide, paid up, royalty-free, nonexclusive right and license to deal
+// in this software and documentation files (the "Software"), including without
+// limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons who receive
+// copies from any such party to do so, with the only requirement being
+// that this copyright notice remain intact.
+
+///////////////////////////////////////////////////////////////////////
+// History
+// -------
+// January 2001: Adaptation of the Neural-Net Quantization Algorithm
+// for the FreeImage 2 library
+// Author: Hervé Drolon (drolon@infonie.fr)
+// March 2004: Adaptation for the FreeImage 3 library (port to big endian processors)
+// Author: Hervé Drolon (drolon@infonie.fr)
+// April 2004: Algorithm rewritten as a C++ class.
+// Fixed a bug in the algorithm with handling of 4-byte boundary alignment.
+// Author: Hervé Drolon (drolon@infonie.fr)
+///////////////////////////////////////////////////////////////////////
+
+#include "Quantizers.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+
+
+// Four primes near 500 - assume no image has a length so large
+// that it is divisible by all four primes
+// ==========================================================
+
+#define prime1 499
+#define prime2 491
+#define prime3 487
+#define prime4 503
+
+// ----------------------------------------------------------------
+
+NNQuantizer::NNQuantizer(int PaletteSize)
+{
+ netsize = PaletteSize;
+ maxnetpos = netsize - 1;
+ initrad = netsize < 8 ? 1 : (netsize >> 3);
+ initradius = (initrad * radiusbias);
+
+ network = NULL;
+
+ network = (pixel *)malloc(netsize * sizeof(pixel));
+ bias = (int *)malloc(netsize * sizeof(int));
+ freq = (int *)malloc(netsize * sizeof(int));
+ radpower = (int *)malloc(initrad * sizeof(int));
+
+ if ( !network || !bias || !freq || !radpower ) {
+ if(network) free(network);
+ if(bias) free(bias);
+ if(freq) free(freq);
+ if(radpower) free(radpower);
+ throw FI_MSG_ERROR_MEMORY;
+ }
+}
+
+NNQuantizer::~NNQuantizer()
+{
+ if(network) free(network);
+ if(bias) free(bias);
+ if(freq) free(freq);
+ if(radpower) free(radpower);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Initialise network in range (0,0,0) to (255,255,255) and set parameters
+// -----------------------------------------------------------------------
+
+void NNQuantizer::initnet() {
+ int i, *p;
+
+ for (i = 0; i < netsize; i++) {
+ p = network[i];
+ p[FI_RGBA_BLUE] = p[FI_RGBA_GREEN] = p[FI_RGBA_RED] = (i << (netbiasshift+8))/netsize;
+ freq[i] = intbias/netsize; /* 1/netsize */
+ bias[i] = 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Unbias network to give byte values 0..255 and record position i to prepare for sort
+// ------------------------------------------------------------------------------------
+
+void NNQuantizer::unbiasnet() {
+ int i, j, temp;
+
+ for (i = 0; i < netsize; i++) {
+ for (j = 0; j < 3; j++) {
+ // OLD CODE: network[i][j] >>= netbiasshift;
+ // Fix based on bug report by Juergen Weigert jw@suse.de
+ temp = (network[i][j] + (1 << (netbiasshift - 1))) >> netbiasshift;
+ if (temp > 255) temp = 255;
+ network[i][j] = temp;
+ }
+ network[i][3] = i; // record colour no
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// Insertion sort of network and building of netindex[0..255] (to do after unbias)
+// -------------------------------------------------------------------------------
+
+void NNQuantizer::inxbuild() {
+ int i,j,smallpos,smallval;
+ int *p,*q;
+ int previouscol,startpos;
+
+ previouscol = 0;
+ startpos = 0;
+ for (i = 0; i < netsize; i++) {
+ p = network[i];
+ smallpos = i;
+ smallval = p[FI_RGBA_GREEN]; // index on g
+ // find smallest in i..netsize-1
+ for (j = i+1; j < netsize; j++) {
+ q = network[j];
+ if (q[FI_RGBA_GREEN] < smallval) { // index on g
+ smallpos = j;
+ smallval = q[FI_RGBA_GREEN]; // index on g
+ }
+ }
+ q = network[smallpos];
+ // swap p (i) and q (smallpos) entries
+ if (i != smallpos) {
+ j = q[FI_RGBA_BLUE]; q[FI_RGBA_BLUE] = p[FI_RGBA_BLUE]; p[FI_RGBA_BLUE] = j;
+ j = q[FI_RGBA_GREEN]; q[FI_RGBA_GREEN] = p[FI_RGBA_GREEN]; p[FI_RGBA_GREEN] = j;
+ j = q[FI_RGBA_RED]; q[FI_RGBA_RED] = p[FI_RGBA_RED]; p[FI_RGBA_RED] = j;
+ j = q[3]; q[3] = p[3]; p[3] = j;
+ }
+ // smallval entry is now in position i
+ if (smallval != previouscol) {
+ netindex[previouscol] = (startpos+i)>>1;
+ for (j = previouscol+1; j < smallval; j++)
+ netindex[j] = i;
+ previouscol = smallval;
+ startpos = i;
+ }
+ }
+ netindex[previouscol] = (startpos+maxnetpos)>>1;
+ for (j = previouscol+1; j < 256; j++)
+ netindex[j] = maxnetpos; // really 256
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Search for BGR values 0..255 (after net is unbiased) and return colour index
+// ----------------------------------------------------------------------------
+
+int NNQuantizer::inxsearch(int b, int g, int r) {
+ int i, j, dist, a, bestd;
+ int *p;
+ int best;
+
+ bestd = 1000; // biggest possible dist is 256*3
+ best = -1;
+ i = netindex[g]; // index on g
+ j = i-1; // start at netindex[g] and work outwards
+
+ while ((i < netsize) || (j >= 0)) {
+ if (i < netsize) {
+ p = network[i];
+ dist = p[FI_RGBA_GREEN] - g; // inx key
+ if (dist >= bestd)
+ i = netsize; // stop iter
+ else {
+ i++;
+ if (dist < 0)
+ dist = -dist;
+ a = p[FI_RGBA_BLUE] - b;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ a = p[FI_RGBA_RED] - r;
+ if (a<0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ if (j >= 0) {
+ p = network[j];
+ dist = g - p[FI_RGBA_GREEN]; // inx key - reverse dif
+ if (dist >= bestd)
+ j = -1; // stop iter
+ else {
+ j--;
+ if (dist < 0)
+ dist = -dist;
+ a = p[FI_RGBA_BLUE] - b;
+ if (a<0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ a = p[FI_RGBA_RED] - r;
+ if (a<0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ best = p[3];
+ }
+ }
+ }
+ }
+ }
+ return best;
+}
+
+///////////////////////////////
+// Search for biased BGR values
+// ----------------------------
+
+int NNQuantizer::contest(int b, int g, int r) {
+ // finds closest neuron (min dist) and updates freq
+ // finds best neuron (min dist-bias) and returns position
+ // for frequently chosen neurons, freq[i] is high and bias[i] is negative
+ // bias[i] = gamma*((1/netsize)-freq[i])
+
+ int i,dist,a,biasdist,betafreq;
+ int bestpos,bestbiaspos,bestd,bestbiasd;
+ int *p,*f, *n;
+
+ bestd = ~(((int) 1)<<31);
+ bestbiasd = bestd;
+ bestpos = -1;
+ bestbiaspos = bestpos;
+ p = bias;
+ f = freq;
+
+ for (i = 0; i < netsize; i++) {
+ n = network[i];
+ dist = n[FI_RGBA_BLUE] - b;
+ if (dist < 0)
+ dist = -dist;
+ a = n[FI_RGBA_GREEN] - g;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ a = n[FI_RGBA_RED] - r;
+ if (a < 0)
+ a = -a;
+ dist += a;
+ if (dist < bestd) {
+ bestd = dist;
+ bestpos = i;
+ }
+ biasdist = dist - ((*p)>>(intbiasshift-netbiasshift));
+ if (biasdist < bestbiasd) {
+ bestbiasd = biasdist;
+ bestbiaspos = i;
+ }
+ betafreq = (*f >> betashift);
+ *f++ -= betafreq;
+ *p++ += (betafreq << gammashift);
+ }
+ freq[bestpos] += beta;
+ bias[bestpos] -= betagamma;
+ return bestbiaspos;
+}
+
+///////////////////////////////////////////////////////
+// Move neuron i towards biased (b,g,r) by factor alpha
+// ----------------------------------------------------
+
+void NNQuantizer::altersingle(int alpha, int i, int b, int g, int r) {
+ int *n;
+
+ n = network[i]; // alter hit neuron
+ n[FI_RGBA_BLUE] -= (alpha * (n[FI_RGBA_BLUE] - b)) / initalpha;
+ n[FI_RGBA_GREEN] -= (alpha * (n[FI_RGBA_GREEN] - g)) / initalpha;
+ n[FI_RGBA_RED] -= (alpha * (n[FI_RGBA_RED] - r)) / initalpha;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// Move adjacent neurons by precomputed alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|]
+// ---------------------------------------------------------------------------------
+
+void NNQuantizer::alterneigh(int rad, int i, int b, int g, int r) {
+ int j, k, lo, hi, a;
+ int *p, *q;
+
+ lo = i - rad; if (lo < -1) lo = -1;
+ hi = i + rad; if (hi > netsize) hi = netsize;
+
+ j = i+1;
+ k = i-1;
+ q = radpower;
+ while ((j < hi) || (k > lo)) {
+ a = (*(++q));
+ if (j < hi) {
+ p = network[j];
+ p[FI_RGBA_BLUE] -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias;
+ p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias;
+ p[FI_RGBA_RED] -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias;
+ j++;
+ }
+ if (k > lo) {
+ p = network[k];
+ p[FI_RGBA_BLUE] -= (a * (p[FI_RGBA_BLUE] - b)) / alpharadbias;
+ p[FI_RGBA_GREEN] -= (a * (p[FI_RGBA_GREEN] - g)) / alpharadbias;
+ p[FI_RGBA_RED] -= (a * (p[FI_RGBA_RED] - r)) / alpharadbias;
+ k--;
+ }
+ }
+}
+
+/////////////////////
+// Main Learning Loop
+// ------------------
+
+/**
+ Get a pixel sample at position pos. Handle 4-byte boundary alignment.
+ @param pos pixel position in a WxHx3 pixel buffer
+ @param b blue pixel component
+ @param g green pixel component
+ @param r red pixel component
+*/
+void NNQuantizer::getSample(long pos, int *b, int *g, int *r) {
+ // get equivalent pixel coordinates
+ // - assume it's a 24-bit image -
+ int x = pos % img_line;
+ int y = pos / img_line;
+
+ BYTE *bits = FreeImage_GetScanLine(dib_ptr, y) + x;
+
+ *b = bits[FI_RGBA_BLUE] << netbiasshift;
+ *g = bits[FI_RGBA_GREEN] << netbiasshift;
+ *r = bits[FI_RGBA_RED] << netbiasshift;
+}
+
+void NNQuantizer::learn(int sampling_factor) {
+ int i, j, b, g, r;
+ int radius, rad, alpha, step, delta, samplepixels;
+ int alphadec; // biased by 10 bits
+ long pos, lengthcount;
+
+ // image size as viewed by the scan algorithm
+ lengthcount = img_width * img_height * 3;
+
+ // number of samples used for the learning phase
+ samplepixels = lengthcount / (3 * sampling_factor);
+
+ // decrease learning rate after delta pixel presentations
+ delta = samplepixels / ncycles;
+ if(delta == 0) {
+ // avoid a 'divide by zero' error with very small images
+ delta = 1;
+ }
+
+ // initialize learning parameters
+ alphadec = 30 + ((sampling_factor - 1) / 3);
+ alpha = initalpha;
+ radius = initradius;
+
+ rad = radius >> radiusbiasshift;
+ if (rad <= 1) rad = 0;
+ for (i = 0; i < rad; i++)
+ radpower[i] = alpha*(((rad*rad - i*i)*radbias)/(rad*rad));
+
+ // initialize pseudo-random scan
+ if ((lengthcount % prime1) != 0)
+ step = 3*prime1;
+ else {
+ if ((lengthcount % prime2) != 0)
+ step = 3*prime2;
+ else {
+ if ((lengthcount % prime3) != 0)
+ step = 3*prime3;
+ else
+ step = 3*prime4;
+ }
+ }
+
+ i = 0; // iteration
+ pos = 0; // pixel position
+
+ while (i < samplepixels) {
+ // get next learning sample
+ getSample(pos, &b, &g, &r);
+
+ // find winning neuron
+ j = contest(b, g, r);
+
+ // alter winner
+ altersingle(alpha, j, b, g, r);
+
+ // alter neighbours
+ if (rad) alterneigh(rad, j, b, g, r);
+
+ // next sample
+ pos += step;
+ while (pos >= lengthcount) pos -= lengthcount;
+
+ i++;
+ if (i % delta == 0) {
+ // decrease learning rate and also the neighborhood
+ alpha -= alpha / alphadec;
+ radius -= radius / radiusdec;
+ rad = radius >> radiusbiasshift;
+ if (rad <= 1) rad = 0;
+ for (j = 0; j < rad; j++)
+ radpower[j] = alpha * (((rad*rad - j*j) * radbias) / (rad*rad));
+ }
+ }
+
+}
+
+//////////////
+// Quantizer
+// -----------
+
+FIBITMAP* NNQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette, int sampling) {
+
+ if ((!dib) || (FreeImage_GetBPP(dib) != 24)) {
+ return NULL;
+ }
+
+ // 1) Select a sampling factor in range 1..30 (input parameter 'sampling')
+ // 1 => slower, 30 => faster. Default value is 1
+
+
+ // 2) Get DIB parameters
+
+ dib_ptr = dib;
+
+ img_width = FreeImage_GetWidth(dib); // DIB width
+ img_height = FreeImage_GetHeight(dib); // DIB height
+ img_line = FreeImage_GetLine(dib); // DIB line length in bytes (should be equal to 3 x W)
+
+ // For small images, adjust the sampling factor to avoid a 'divide by zero' error later
+ // (see delta in learn() routine)
+ int adjust = (img_width * img_height) / ncycles;
+ if(sampling >= adjust)
+ sampling = 1;
+
+
+ // 3) Initialize the network and apply the learning algorithm
+
+ if ( netsize > ReserveSize ) {
+ netsize -= ReserveSize;
+ initnet();
+ learn(sampling);
+ unbiasnet();
+ netsize += ReserveSize;
+ }
+
+ // 3.5) Overwrite the last few palette entries with the reserved ones
+ for (int i = 0; i < ReserveSize; i++) {
+ network[netsize - ReserveSize + i][FI_RGBA_BLUE] = ReservePalette[i].rgbBlue;
+ network[netsize - ReserveSize + i][FI_RGBA_GREEN] = ReservePalette[i].rgbGreen;
+ network[netsize - ReserveSize + i][FI_RGBA_RED] = ReservePalette[i].rgbRed;
+ network[netsize - ReserveSize + i][3] = netsize - ReserveSize + i;
+ }
+
+ // 4) Allocate a new 8-bit DIB
+
+ FIBITMAP *new_dib = FreeImage_Allocate(img_width, img_height, 8);
+
+ if (new_dib == NULL)
+ return NULL;
+
+ // 5) Write the quantized palette
+
+ RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+ for (int j = 0; j < netsize; j++) {
+ new_pal[j].rgbBlue = (BYTE)network[j][FI_RGBA_BLUE];
+ new_pal[j].rgbGreen = (BYTE)network[j][FI_RGBA_GREEN];
+ new_pal[j].rgbRed = (BYTE)network[j][FI_RGBA_RED];
+ }
+
+ inxbuild();
+
+ // 6) Write output image using inxsearch(b,g,r)
+
+ for (WORD rows = 0; rows < img_height; rows++) {
+ BYTE *new_bits = FreeImage_GetScanLine(new_dib, rows);
+ BYTE *bits = FreeImage_GetScanLine(dib_ptr, rows);
+
+ for (WORD cols = 0; cols < img_width; cols++) {
+ new_bits[cols] = (BYTE)inxsearch(bits[FI_RGBA_BLUE], bits[FI_RGBA_GREEN], bits[FI_RGBA_RED]);
+
+ bits += 3;
+ }
+ }
+
+ return (FIBITMAP*) new_dib;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PSDParser.cpp b/plugins/AdvaImg/src/FreeImage/PSDParser.cpp
new file mode 100644
index 0000000000..93573d01c0
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PSDParser.cpp
@@ -0,0 +1,1053 @@
+// ==========================================================
+// Photoshop Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// Based on LGPL code created and published by http://sourceforge.net/projects/elynx/
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "PSDParser.h"
+
+// --------------------------------------------------------------------------
+
+// PSD signature (= '8BPS')
+#define PSD_SIGNATURE 0x38425053
+// Image resource block signature (= '8BIM')
+#define PSD_RESOURCE 0x3842494D
+
+// PSD color modes
+#define PSDP_BITMAP 0
+#define PSDP_GRAYSCALE 1
+#define PSDP_INDEXED 2
+#define PSDP_RGB 3
+#define PSDP_CMYK 4
+#define PSDP_MULTICHANNEL 7
+#define PSDP_DUOTONE 8
+#define PSDP_LAB 9
+
+// PSD compression schemes
+#define PSDP_COMPRESSION_NONE 0 // Raw data
+#define PSDP_COMPRESSION_RLE 1 // RLE compression (same as TIFF packed bits)
+
+#define SAFE_DELETE_ARRAY(_p_) { if (NULL != (_p_)) { delete [] (_p_); (_p_) = NULL; } }
+
+// --------------------------------------------------------------------------
+
+static inline int
+psdGetValue(const BYTE * iprBuffer, const int iBytes) {
+ int v = iprBuffer[0];
+ for (int i=1; i<iBytes; ++i) {
+ v = (v << 8) | iprBuffer[i];
+ }
+ return v;
+}
+
+// --------------------------------------------------------------------------
+
+psdHeaderInfo::psdHeaderInfo() : _Channels(-1), _Height(-1), _Width(-1), _BitsPerChannel(-1), _ColourMode(-1) {
+}
+
+psdHeaderInfo::~psdHeaderInfo() {
+}
+
+bool psdHeaderInfo::Read(FreeImageIO *io, fi_handle handle) {
+ psdHeader header;
+
+ const int n = (int)io->read_proc(&header, sizeof(header), 1, handle);
+ if (!n) {
+ return false;
+ }
+
+ // check the signature
+ int nSignature = psdGetValue(header.Signature, sizeof(header.Signature));
+ if (PSD_SIGNATURE == nSignature) {
+ // check the version
+ int nVersion = psdGetValue( header.Version, sizeof(header.Version) );
+ if (1 == nVersion) {
+ // header.Reserved must be zero
+ BYTE psd_reserved[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ if(memcmp(header.Reserved, psd_reserved, 6) != 0) {
+ FreeImage_OutputMessageProc(FIF_PSD, "Warning: file header reserved member is not equal to zero");
+ }
+ // read the header
+ _Channels = (short)psdGetValue( header.Channels, sizeof(header.Channels) );
+ _Height = psdGetValue( header.Rows, sizeof(header.Rows) );
+ _Width = psdGetValue( header.Columns, sizeof(header.Columns) );
+ _BitsPerChannel = (short)psdGetValue( header.Depth, sizeof(header.Depth) );
+ _ColourMode = (short)psdGetValue( header.Mode, sizeof(header.Mode) );
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// --------------------------------------------------------------------------
+
+psdColourModeData::psdColourModeData() : _Length(-1), _plColourData(NULL) {
+}
+
+psdColourModeData::~psdColourModeData() {
+ SAFE_DELETE_ARRAY(_plColourData);
+}
+
+bool psdColourModeData::Read(FreeImageIO *io, fi_handle handle) {
+ if (0 < _Length) {
+ SAFE_DELETE_ARRAY(_plColourData);
+ }
+
+ BYTE Length[4];
+ io->read_proc(&Length, sizeof(Length), 1, handle);
+
+ _Length = psdGetValue( Length, sizeof(_Length) );
+ if (0 < _Length) {
+ _plColourData = new BYTE[_Length];
+ io->read_proc(_plColourData, _Length, 1, handle);
+ }
+
+ return true;
+}
+
+bool psdColourModeData::FillPalette(FIBITMAP *dib) {
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ if(pal) {
+ for (int i = 0; i < 256; i++) {
+ pal[i].rgbRed = _plColourData[i + 0*256];
+ pal[i].rgbGreen = _plColourData[i + 1*256];
+ pal[i].rgbBlue = _plColourData[i + 2*256];
+ }
+ return true;
+ }
+ return false;
+}
+
+// --------------------------------------------------------------------------
+
+psdImageResource::psdImageResource() : _plName (0) {
+ Reset();
+}
+
+psdImageResource::~psdImageResource() {
+ SAFE_DELETE_ARRAY(_plName);
+}
+
+void psdImageResource::Reset() {
+ _Length = -1;
+ memset( _OSType, '\0', sizeof(_OSType) );
+ _ID = -1;
+ SAFE_DELETE_ARRAY(_plName);
+ _Size = -1;
+}
+
+// --------------------------------------------------------------------------
+
+psdResolutionInfo::psdResolutionInfo() : _widthUnit(-1), _heightUnit(-1), _hRes(-1), _vRes(-1), _hResUnit(-1), _vResUnit(-1) {
+}
+
+psdResolutionInfo::~psdResolutionInfo() {
+}
+
+int psdResolutionInfo::Read(FreeImageIO *io, fi_handle handle) {
+ BYTE IntValue[4], ShortValue[2];
+ int nBytes=0, n;
+
+ // Horizontal resolution in pixels per inch.
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _hRes = (short)psdGetValue(ShortValue, sizeof(_hRes) );
+ // 1=display horizontal resolution in pixels per inch; 2=display horizontal resolution in pixels per cm.
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _hResUnit = psdGetValue(IntValue, sizeof(_hResUnit) );
+ // Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _widthUnit = (short)psdGetValue(ShortValue, sizeof(_widthUnit) );
+ // Vertical resolution in pixels per inch.
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _vRes = (short)psdGetValue(ShortValue, sizeof(_vRes) );
+ // 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm.
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _vResUnit = psdGetValue(IntValue, sizeof(_vResUnit) );
+ // Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _heightUnit = (short)psdGetValue(ShortValue, sizeof(_heightUnit) );
+
+ return nBytes;
+}
+
+void psdResolutionInfo::GetResolutionInfo(unsigned &res_x, unsigned &res_y) {
+ if(_hResUnit == 1) {
+ // convert pixels / inch to pixel / m
+ res_x = (unsigned) (_hRes / 0.0254000 + 0.5);
+ } else if(_hResUnit == 2) {
+ // convert pixels / cm to pixel / m
+ res_x = (unsigned) (_hRes * 100.0 + 0.5);
+ }
+ if(_vResUnit == 1) {
+ // convert pixels / inch to pixel / m
+ res_y = (unsigned) (_vRes / 0.0254000 + 0.5);
+ } else if(_vResUnit == 2) {
+ // convert pixels / cm to pixel / m
+ res_y = (unsigned) (_vRes * 100.0 + 0.5);
+ }
+}
+
+// --------------------------------------------------------------------------
+
+psdResolutionInfo_v2::psdResolutionInfo_v2() {
+ _Channels = _Rows = _Columns = _Depth = _Mode = -1;
+}
+
+psdResolutionInfo_v2::~psdResolutionInfo_v2() {
+}
+
+int psdResolutionInfo_v2::Read(FreeImageIO *io, fi_handle handle) {
+ BYTE ShortValue[2];
+ int nBytes=0, n;
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Channels = (short)psdGetValue(ShortValue, sizeof(_Channels) );
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Rows = (short)psdGetValue(ShortValue, sizeof(_Rows) );
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Columns = (short)psdGetValue(ShortValue, sizeof(_Columns) );
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Depth = (short)psdGetValue(ShortValue, sizeof(_Depth) );
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Mode = (short)psdGetValue(ShortValue, sizeof(_Mode) );
+
+ return nBytes;
+}
+
+// --------------------------------------------------------------------------
+
+psdDisplayInfo::psdDisplayInfo() {
+ _Opacity = _ColourSpace = -1;
+ for (unsigned n = 0; n < 4; ++n) {
+ _Colour[n] = 0;
+ }
+ _Kind = 0;
+ _padding = '0';
+}
+
+psdDisplayInfo::~psdDisplayInfo() {
+}
+
+int psdDisplayInfo::Read(FreeImageIO *io, fi_handle handle) {
+ BYTE ShortValue[2];
+ int nBytes=0, n;
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _ColourSpace = (short)psdGetValue(ShortValue, sizeof(_ColourSpace) );
+
+ for (unsigned i = 0; i < 4; ++i) {
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Colour[i] = (short)psdGetValue(ShortValue, sizeof(_Colour[i]) );
+ }
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Opacity = (short)psdGetValue(ShortValue, sizeof(_Opacity) );
+ if ((_Opacity < 0) || (_Opacity > 100)) {
+ throw "Invalid DisplayInfo::Opacity value";
+ }
+
+ BYTE c[1];
+ n = (int)io->read_proc(&c, sizeof(c), 1, handle);
+ nBytes += n * sizeof(c);
+ _Kind = (BYTE)psdGetValue(c, sizeof(c));
+
+ n = (int)io->read_proc(&c, sizeof(c), 1, handle);
+ nBytes += n * sizeof(c);
+ _padding = (BYTE)psdGetValue(c, sizeof(c));
+ if(_padding != 0) {
+ throw "Invalid DisplayInfo::Padding value";
+ }
+
+ return nBytes;
+}
+
+// --------------------------------------------------------------------------
+
+psdThumbnail::psdThumbnail() :
+_Format(-1), _Width(-1), _Height(-1), _WidthBytes(-1), _Size(-1), _CompressedSize(-1), _BitPerPixel(-1), _Planes(-1), _dib(NULL) {
+}
+
+psdThumbnail::~psdThumbnail() {
+ FreeImage_Unload(_dib);
+}
+
+int psdThumbnail::Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR) {
+ BYTE ShortValue[2], IntValue[4];
+ int nBytes=0, n;
+
+ // remove the header size (28 bytes) from the total data size
+ int iTotalData = iResourceSize - 28;
+
+ const long block_end = io->tell_proc(handle) + iTotalData;
+
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _Format = psdGetValue(IntValue, sizeof(_Format) );
+
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _Width = psdGetValue(IntValue, sizeof(_Width) );
+
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _Height = psdGetValue(IntValue, sizeof(_Height) );
+
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _WidthBytes = psdGetValue(IntValue, sizeof(_WidthBytes) );
+
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _Size = psdGetValue(IntValue, sizeof(_Size) );
+
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _CompressedSize = psdGetValue(IntValue, sizeof(_CompressedSize) );
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _BitPerPixel = (short)psdGetValue(ShortValue, sizeof(_BitPerPixel) );
+
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _Planes = (short)psdGetValue(ShortValue, sizeof(_Planes) );
+
+ const long JFIF_startpos = io->tell_proc(handle);
+
+ if(_dib) {
+ FreeImage_Unload(_dib);
+ }
+
+ if(_Format == 1) {
+ // kJpegRGB thumbnail image
+ _dib = FreeImage_LoadFromHandle(FIF_JPEG, io, handle);
+ if(isBGR) {
+ SwapRedBlue32(_dib);
+ }
+ // HACK: manually go to end of thumbnail, because (for some reason) LoadFromHandle consumes more bytes then available!
+ io->seek_proc(handle, block_end, SEEK_SET);
+ }
+ else {
+ // kRawRGB thumbnail image
+ // ### Unimplemented (should be trivial)
+
+ // skip the thumbnail part
+ io->seek_proc(handle, iTotalData, SEEK_CUR);
+ return iResourceSize;
+ }
+
+ nBytes += (block_end - JFIF_startpos);
+
+ return nBytes;
+}
+
+//---------------------------------------------------------------------------
+
+psdICCProfile::psdICCProfile() : _ProfileSize(0), _ProfileData(NULL) {
+}
+
+psdICCProfile::~psdICCProfile() {
+ clear();
+}
+
+void psdICCProfile::clear() { SAFE_DELETE_ARRAY(_ProfileData); _ProfileSize = 0;}
+
+int psdICCProfile::Read(FreeImageIO *io, fi_handle handle, int size) {
+ int nBytes = 0, n;
+
+ clear();
+
+ _ProfileData = new (std::nothrow) BYTE[size];
+ if(NULL != _ProfileData) {
+ n = (int)io->read_proc(_ProfileData, 1, size, handle);
+ _ProfileSize = size;
+ nBytes += n * sizeof(BYTE);
+ }
+
+ return nBytes;
+}
+
+//---------------------------------------------------------------------------
+
+/**
+Invert only color components, skipping Alpha/Black
+(Can be useful as public/utility function)
+*/
+static
+BOOL invertColor(FIBITMAP* dib) {
+ FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib);
+ const unsigned Bpp = FreeImage_GetBPP(dib)/8;
+
+ if ((type == FIT_BITMAP && Bpp == 4) || type == FIT_RGBA16) {
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ BYTE *line_start = FreeImage_GetScanLine(dib, 0);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+ const unsigned triBpp = Bpp - (Bpp == 4 ? 1 : 2);
+
+ for(unsigned y = 0; y < height; y++) {
+ BYTE *line = line_start;
+
+ for(unsigned x = 0; x < width; x++) {
+ for(unsigned b=0; b < triBpp; ++b) {
+ line[b] = ~line[b];
+ }
+
+ line += Bpp;
+ }
+ line_start += pitch;
+ }
+
+ return TRUE;
+ }
+ else {
+ return FreeImage_Invert(dib);
+ }
+}
+
+//---------------------------------------------------------------------------
+
+psdParser::psdParser() {
+ _bThumbnailFilled = false;
+ _bDisplayInfoFilled = false;
+ _bResolutionInfoFilled = false;
+ _bResolutionInfoFilled_v2 = false;
+ _bCopyright = false;
+ _GlobalAngle = 30;
+ _ColourCount = -1;
+ _TransparentIndex = -1;
+ _fi_flags = 0;
+ _fi_format_id = FIF_UNKNOWN;
+}
+
+psdParser::~psdParser() {
+}
+
+bool psdParser::ReadLayerAndMaskInfoSection(FreeImageIO *io, fi_handle handle) {
+ bool bSuccess = false;
+
+ BYTE DataLength[4];
+ int nBytes = 0;
+ int n = (int)io->read_proc(&DataLength, sizeof(DataLength), 1, handle);
+ int nTotalBytes = psdGetValue( DataLength, sizeof(DataLength) );
+
+ BYTE data[1];
+ while( n && ( nBytes < nTotalBytes ) ) {
+ data[0] = '\0';
+ n = (int)io->read_proc(&data, sizeof(data), 1, handle);
+ nBytes += n * sizeof(data);
+ }
+
+ if ( nBytes == nTotalBytes ) {
+ bSuccess = true;
+ }
+
+ return bSuccess;
+}
+
+bool psdParser::ReadImageResources(FreeImageIO *io, fi_handle handle, LONG length) {
+ psdImageResource oResource;
+ bool bSuccess = false;
+
+ if(length > 0) {
+ oResource._Length = length;
+ } else {
+ BYTE Length[4];
+ int n = (int)io->read_proc(&Length, sizeof(Length), 1, handle);
+
+ oResource._Length = psdGetValue( Length, sizeof(oResource._Length) );
+ }
+
+ int nBytes = 0;
+ int nTotalBytes = oResource._Length;
+
+ while(nBytes < nTotalBytes) {
+ int n = 0;
+ oResource.Reset();
+
+ n = (int)io->read_proc(&oResource._OSType, sizeof(oResource._OSType), 1, handle);
+ nBytes += n * sizeof(oResource._OSType);
+
+ if ( (nBytes % 2) != 0 ) {
+ return false;
+ }
+
+ int nOSType = psdGetValue((BYTE*)&oResource._OSType, sizeof(oResource._OSType));
+
+ if ( PSD_RESOURCE == nOSType ) {
+ BYTE ID[2];
+ n = (int)io->read_proc(&ID, sizeof(ID), 1, handle);
+ nBytes += n * sizeof(ID);
+
+ oResource._ID = (short)psdGetValue( ID, sizeof(ID) );
+
+ BYTE SizeOfName;
+ n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle);
+ nBytes += n * sizeof(SizeOfName);
+
+ int nSizeOfName = psdGetValue( &SizeOfName, sizeof(SizeOfName) );
+ if ( 0 < nSizeOfName ) {
+ oResource._plName = new BYTE[nSizeOfName];
+ n = (int)io->read_proc(oResource._plName, nSizeOfName, 1, handle);
+ nBytes += n * nSizeOfName;
+ }
+
+ if ( 0 == (nSizeOfName % 2) ) {
+ n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle);
+ nBytes += n * sizeof(SizeOfName);
+ }
+
+ BYTE Size[4];
+ n = (int)io->read_proc(&Size, sizeof(Size), 1, handle);
+ nBytes += n * sizeof(Size);
+
+ oResource._Size = psdGetValue( Size, sizeof(oResource._Size) );
+
+ if ( 0 != (oResource._Size % 2) ) {
+ // resource data must be even
+ oResource._Size++;
+ }
+ if ( 0 < oResource._Size ) {
+ BYTE IntValue[4];
+ BYTE ShortValue[2];
+
+ switch( oResource._ID ) {
+ case 1000:
+ // Obsolete - Photoshop 2.0
+ _bResolutionInfoFilled_v2 = true;
+ nBytes += _resolutionInfo_v2.Read(io, handle);
+ break;
+
+ // ResolutionInfo structure
+ case 1005:
+ _bResolutionInfoFilled = true;
+ nBytes += _resolutionInfo.Read(io, handle);
+ break;
+
+ // DisplayInfo structure
+ case 1007:
+ _bDisplayInfoFilled = true;
+ nBytes += _displayInfo.Read(io, handle);
+ break;
+
+ // (Photoshop 4.0) Copyright flag
+ // Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...
+ case 1034:
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _bCopyright = (1 == psdGetValue(ShortValue, sizeof(ShortValue)));
+ break;
+
+ // (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only
+ case 1033:
+ // (Photoshop 5.0) Thumbnail resource (supersedes resource 1033)
+ case 1036:
+ {
+ _bThumbnailFilled = true;
+ bool bBGR = (1033==oResource._ID);
+ nBytes += _thumbnail.Read(io, handle, oResource._Size, bBGR);
+ break;
+ }
+
+ // (Photoshop 5.0) Global Angle
+ // 4 bytes that contain an integer between 0 and 359, which is the global
+ // lighting angle for effects layer. If not present, assumed to be 30.
+ case 1037:
+ n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle);
+ nBytes += n * sizeof(IntValue);
+ _GlobalAngle = psdGetValue(IntValue, sizeof(_GlobalAngle) );
+ break;
+
+ // ICC profile
+ case 1039:
+ nBytes += _iccProfile.Read(io, handle, oResource._Size);
+ break;
+
+ // (Photoshop 6.0) Indexed Color Table Count
+ // 2 bytes for the number of colors in table that are actually defined
+ case 1046:
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _ColourCount = (short)psdGetValue(ShortValue, sizeof(ShortValue) );
+ break;
+
+ // (Photoshop 6.0) Transparency Index.
+ // 2 bytes for the index of transparent color, if any.
+ case 1047:
+ n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle);
+ nBytes += n * sizeof(ShortValue);
+ _TransparentIndex = (short)psdGetValue(ShortValue, sizeof(ShortValue) );
+ break;
+
+ default:
+ {
+ // skip resource
+ unsigned skip_length = MIN(oResource._Size, nTotalBytes - nBytes);
+ io->seek_proc(handle, skip_length, SEEK_CUR);
+ nBytes += skip_length;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (nBytes == nTotalBytes) {
+ bSuccess = true;
+ }
+
+ return bSuccess;
+
+}
+
+FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) {
+ if(handle == NULL)
+ return NULL;
+
+ bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ WORD nCompression = 0;
+ io->read_proc(&nCompression, sizeof(nCompression), 1, handle);
+
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapShort(&nCompression);
+#endif
+
+ if ((nCompression != PSDP_COMPRESSION_NONE && nCompression != PSDP_COMPRESSION_RLE)) {
+ FreeImage_OutputMessageProc(_fi_format_id, "Unsupported compression %d", nCompression);
+ return NULL;
+ }
+
+ const unsigned nWidth = _headerInfo._Width;
+ const unsigned nHeight = _headerInfo._Height;
+ const unsigned nChannels = _headerInfo._Channels;
+ const unsigned depth = _headerInfo._BitsPerChannel;
+ const unsigned bytes = (depth == 1) ? 1 : depth / 8;
+
+ // channel(plane) line (BYTE aligned)
+ const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes;
+
+ if(nCompression == PSDP_COMPRESSION_RLE && depth > 16) {
+ FreeImage_OutputMessageProc(_fi_format_id, "Unsupported RLE with depth %d", depth);
+ return NULL;
+ }
+
+ // build output buffer
+
+ FIBITMAP* bitmap = NULL;
+ unsigned dstCh = 0;
+
+ short mode = _headerInfo._ColourMode;
+
+ if(mode == PSDP_MULTICHANNEL && nChannels < 3) {
+ // CM
+ mode = PSDP_GRAYSCALE; // C as gray, M as extra channel
+ }
+
+ bool needPalette = false;
+ switch (mode) {
+ case PSDP_BITMAP:
+ case PSDP_DUOTONE:
+ case PSDP_INDEXED:
+ case PSDP_GRAYSCALE:
+ dstCh = 1;
+ switch(depth) {
+ case 16:
+ bitmap = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, nWidth, nHeight, depth*dstCh);
+ break;
+ case 32:
+ bitmap = FreeImage_AllocateHeaderT(header_only, FIT_FLOAT, nWidth, nHeight, depth*dstCh);
+ break;
+ default: // 1-, 8-
+ needPalette = true;
+ bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh);
+ break;
+ }
+ break;
+ case PSDP_RGB:
+ case PSDP_LAB:
+ case PSDP_CMYK :
+ case PSDP_MULTICHANNEL :
+ // force PSDP_MULTICHANNEL CMY as CMYK
+ dstCh = (mode == PSDP_MULTICHANNEL && !header_only) ? 4 : MIN<unsigned>(nChannels, 4);
+ if(dstCh < 3) {
+ throw "Invalid number of channels";
+ }
+
+ switch(depth) {
+ case 16:
+ bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGB16 : FIT_RGBA16, nWidth, nHeight, depth*dstCh);
+ break;
+ case 32:
+ bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGBF : FIT_RGBAF, nWidth, nHeight, depth*dstCh);
+ break;
+ default:
+ bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh);
+ break;
+ }
+ break;
+ default:
+ throw "Unsupported color mode";
+ break;
+ }
+ if (!bitmap) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // write thumbnail
+ FreeImage_SetThumbnail(bitmap, _thumbnail.getDib());
+
+ // @todo Add some metadata model
+
+ if(header_only) {
+ return bitmap;
+ }
+
+ // Load pixels data
+
+ const unsigned dstChannels = dstCh;
+
+ const unsigned dstBpp = (depth == 1) ? 1 : FreeImage_GetBPP(bitmap)/8;
+ const unsigned dstLineSize = FreeImage_GetPitch(bitmap);
+ BYTE* const dst_first_line = FreeImage_GetScanLine(bitmap, nHeight - 1);//<*** flipped
+
+ BYTE* line_start = new BYTE[lineSize]; //< fileline cache
+
+ switch ( nCompression ) {
+ case PSDP_COMPRESSION_NONE: // raw data
+ {
+ for(unsigned c = 0; c < nChannels; c++) {
+ if(c >= dstChannels) {
+ // @todo write extra channels
+ break;
+ }
+
+ const unsigned channelOffset = c * bytes;
+
+ BYTE* dst_line_start = dst_first_line;
+ for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped
+
+ io->read_proc(line_start, lineSize, 1, handle);
+
+ for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize;
+ line += bytes, dst_line += dstBpp) {
+#ifdef FREEIMAGE_BIGENDIAN
+ memcpy(dst_line + channelOffset, line, bytes);
+#else
+ // reverse copy bytes
+ for (unsigned b = 0; b < bytes; ++b) {
+ dst_line[channelOffset + b] = line[(bytes-1) - b];
+ }
+#endif // FREEIMAGE_BIGENDIAN
+ }
+ } //< h
+ }//< ch
+
+ SAFE_DELETE_ARRAY(line_start);
+
+ }
+ break;
+
+ case PSDP_COMPRESSION_RLE: // RLE compression
+ {
+
+ // The RLE-compressed data is preceeded by a 2-byte line size for each row in the data,
+ // store an array of these
+
+ // later use this array as WORD rleLineSizeList[nChannels][nHeight];
+ WORD *rleLineSizeList = new (std::nothrow) WORD[nChannels*nHeight];
+
+ if (!rleLineSizeList) {
+ FreeImage_Unload(bitmap);
+ SAFE_DELETE_ARRAY(line_start);
+ throw std::bad_alloc();
+ }
+
+ io->read_proc(rleLineSizeList, 2, nChannels * nHeight, handle);
+
+ WORD largestRLELine = 0;
+ for(unsigned ch = 0; ch < nChannels; ++ch) {
+ for(unsigned h = 0; h < nHeight; ++h) {
+ const unsigned index = ch * nHeight + h;
+
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapShort(&rleLineSizeList[index]);
+#endif
+ if(largestRLELine < rleLineSizeList[index]) {
+ largestRLELine = rleLineSizeList[index];
+ }
+ }
+ }
+
+ BYTE* rle_line_start = new (std::nothrow) BYTE[largestRLELine];
+ if (!rle_line_start) {
+ FreeImage_Unload(bitmap);
+ SAFE_DELETE_ARRAY(line_start);
+ SAFE_DELETE_ARRAY(rleLineSizeList);
+ throw std::bad_alloc();
+ }
+
+ // Read the RLE data (assume 8-bit)
+
+ const BYTE* const line_end = line_start + lineSize;
+
+ for (unsigned ch = 0; ch < nChannels; ch++) {
+ const unsigned channelOffset = ch * bytes;
+
+ BYTE* dst_line_start = dst_first_line;
+ for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped
+ const unsigned index = ch * nHeight + h;
+
+ // - read and uncompress line -
+
+ const WORD rleLineSize = rleLineSizeList[index];
+
+ io->read_proc(rle_line_start, rleLineSize, 1, handle);
+
+ for (BYTE* rle_line = rle_line_start, *line = line_start;
+ rle_line < rle_line_start + rleLineSize, line < line_end;) {
+
+ int len = *rle_line++;
+
+ // NOTE len is signed byte in PackBits RLE
+
+ if ( len < 128 ) { //<- MSB is not set
+ // uncompressed packet
+
+ // (len + 1) bytes of data are copied
+
+ ++len;
+
+ // assert we don't write beyound eol
+ memcpy(line, rle_line, line + len > line_end ? line_end - line : len);
+ line += len;
+ rle_line += len;
+ }
+ else if ( len > 128 ) { //< MSB is set
+
+ // RLE compressed packet
+
+ // One byte of data is repeated (–len + 1) times
+
+ len ^= 0xFF; // same as (-len + 1) & 0xFF
+ len += 2; //
+
+ // assert we don't write beyound eol
+ memset(line, *rle_line++, line + len > line_end ? line_end - line : len);
+ line += len;
+
+ }
+ else if ( 128 == len ) {
+ // Do nothing
+ }
+ }//< rle_line
+
+ // - write line to destination -
+
+ if(ch >= dstChannels) {
+ // @todo write to extra channels
+ break;
+ }
+
+ // byte by byte copy a single channel to pixel
+ for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize;
+ line += bytes, dst_line += dstBpp) {
+
+#ifdef FREEIMAGE_BIGENDIAN
+ memcpy(dst_line + channelOffset, line, bytes);
+#else
+ // reverse copy bytes
+ for (unsigned b = 0; b < bytes; ++b) {
+ dst_line[channelOffset + b] = line[(bytes-1) - b];
+ }
+#endif // FREEIMAGE_BIGENDIAN
+ }
+ }//< h
+ }//< ch
+
+ SAFE_DELETE_ARRAY(line_start);
+ SAFE_DELETE_ARRAY(rleLineSizeList);
+ SAFE_DELETE_ARRAY(rle_line_start);
+ }
+ break;
+
+ case 2: // ZIP without prediction, no specification
+ break;
+
+ case 3: // ZIP with prediction, no specification
+ break;
+
+ default: // Unknown format
+ break;
+
+ }
+
+ // --- Further process the bitmap ---
+
+ if ((mode == PSDP_CMYK || mode == PSDP_MULTICHANNEL)) {
+ // CMYK values are "inverted", invert them back
+
+ if(mode == PSDP_MULTICHANNEL) {
+ invertColor(bitmap);
+ } else {
+ FreeImage_Invert(bitmap);
+ }
+
+ if ((_fi_flags & PSD_CMYK) == PSD_CMYK) {
+ // keep as CMYK
+
+ if(mode == PSDP_MULTICHANNEL) {
+ //### we force CMY to be CMYK, but CMY has no ICC.
+ // Create empty profile and add the flag.
+ FreeImage_CreateICCProfile(bitmap, NULL, 0);
+ FreeImage_GetICCProfile(bitmap)->flags |= FIICC_COLOR_IS_CMYK;
+ }
+ }
+ else {
+ // convert to RGB
+
+ ConvertCMYKtoRGBA(bitmap);
+
+ // The ICC Profile is no longer valid
+ _iccProfile.clear();
+
+ // remove the pending A if not present in source
+ if(nChannels == 4 || nChannels == 3 ) {
+ FIBITMAP* t = RemoveAlphaChannel(bitmap);
+ if(t) {
+ FreeImage_Unload(bitmap);
+ bitmap = t;
+ } // else: silently fail
+ }
+ }
+ }
+ else if ( mode == PSDP_LAB && !((_fi_flags & PSD_LAB) == PSD_LAB)) {
+ ConvertLABtoRGB(bitmap);
+ }
+ else {
+ if (needPalette && FreeImage_GetPalette(bitmap)) {
+
+ if(mode == PSDP_BITMAP) {
+ CREATE_GREYSCALE_PALETTE_REVERSE(FreeImage_GetPalette(bitmap), 2);
+ }
+ else if(mode == PSDP_INDEXED) {
+ if (!_colourModeData._plColourData || _colourModeData._Length != 768 || _ColourCount < 0) {
+ FreeImage_OutputMessageProc(_fi_format_id, "Indexed image has no palette. Using the default grayscale one.");
+ } else {
+ _colourModeData.FillPalette(bitmap);
+ }
+ }
+ // GRAYSCALE, DUOTONE - use default grayscale palette
+ }
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ if(FreeImage_GetImageType(bitmap) == FIT_BITMAP) {
+ SwapRedBlue32(bitmap);
+ }
+#endif
+ }
+
+ return bitmap;
+}
+
+FIBITMAP* psdParser::Load(FreeImageIO *io, fi_handle handle, int s_format_id, int flags) {
+ FIBITMAP *Bitmap = NULL;
+
+ _fi_flags = flags;
+ _fi_format_id = s_format_id;
+
+ try {
+ if (NULL == handle) {
+ throw("Cannot open file");
+ }
+
+ if (!_headerInfo.Read(io, handle)) {
+ throw("Error in header");
+ }
+
+ if (!_colourModeData.Read(io, handle)) {
+ throw("Error in ColourMode Data");
+ }
+
+ if (!ReadImageResources(io, handle)) {
+ throw("Error in Image Resource");
+ }
+
+ if (!ReadLayerAndMaskInfoSection(io, handle)) {
+ throw("Error in Mask Info");
+ }
+
+ Bitmap = ReadImageData(io, handle);
+ if (NULL == Bitmap) {
+ throw("Error in Image Data");
+ }
+
+ // set resolution info
+ if(NULL != Bitmap) {
+ unsigned res_x = 2835; // 72 dpi
+ unsigned res_y = 2835; // 72 dpi
+ if (_bResolutionInfoFilled) {
+ _resolutionInfo.GetResolutionInfo(res_x, res_y);
+ }
+ FreeImage_SetDotsPerMeterX(Bitmap, res_x);
+ FreeImage_SetDotsPerMeterY(Bitmap, res_y);
+ }
+
+ // set ICC profile
+ FreeImage_CreateICCProfile(Bitmap, _iccProfile._ProfileData, _iccProfile._ProfileSize);
+ if ((flags & PSD_CMYK) == PSD_CMYK) {
+ short mode = _headerInfo._ColourMode;
+ if ((mode == PSDP_CMYK) || (mode == PSDP_MULTICHANNEL)) {
+ FreeImage_GetICCProfile(Bitmap)->flags |= FIICC_COLOR_IS_CMYK;
+ }
+ }
+
+ } catch(const char *text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+ catch(const std::exception& e) {
+ FreeImage_OutputMessageProc(s_format_id, "%s", e.what());
+ }
+
+ return Bitmap;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PSDParser.h b/plugins/AdvaImg/src/FreeImage/PSDParser.h
new file mode 100644
index 0000000000..15ab5425ca
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PSDParser.h
@@ -0,0 +1,271 @@
+// ==========================================================
+// Photoshop Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// Based on LGPL code created and published by http://sourceforge.net/projects/elynx/
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifndef PSDPARSER_H
+#define PSDPARSER_H
+
+/**
+Table 2-12: File header section.
+The file header contains the basic properties of the image.
+*/
+typedef struct psdHeader {
+ BYTE Signature[4]; //! Always equal 8BPS, do not try to read the file if the signature does not match this value.
+ BYTE Version[2]; //! Always equal 1, do not read file if the version does not match this value.
+ char Reserved[6]; //! Must be zero.
+ BYTE Channels[2]; //! Number of channels including any alpha channels, supported range is 1 to 24.
+ BYTE Rows[4]; //! The height of the image in pixels. Supported range is 1 to 30,000.
+ BYTE Columns[4]; //! The width of the image in pixels. Supported range is 1 to 30,000.
+ BYTE Depth[2]; //! The number of bits per channel. Supported values are 1, 8, and 16.
+ BYTE Mode[2]; //! Colour mode of the file, Bitmap=0, Grayscale=1, Indexed=2, RGB=3, CMYK=4, Multichannel=7, Duotone=8, Lab=9.
+} psdHeader;
+
+/**
+Table 2-12: HeaderInfo Color spaces
+@see psdHeader
+*/
+class psdHeaderInfo {
+public:
+ short _Channels; //! Numer of channels including any alpha channels, supported range is 1 to 24.
+ int _Height; //! The height of the image in pixels. Supported range is 1 to 30,000.
+ int _Width; //! The width of the image in pixels. Supported range is 1 to 30,000.
+ short _BitsPerChannel;//! The number of bits per channel. Supported values are 1, 8, and 16.
+ short _ColourMode; //! Colour mode of the file, Bitmap=0, Grayscale=1, Indexed=2, RGB=3, CMYK=4, Multichannel=7, Duotone=8, Lab=9.
+
+public:
+ psdHeaderInfo();
+ ~psdHeaderInfo();
+ /**
+ @return Returns the number of bytes read
+ */
+ bool Read(FreeImageIO *io, fi_handle handle);
+};
+
+/**
+Table 2-13 Color mode data section
+
+Only indexed color and duotone have color mode data. For all other modes,
+this section is just 4 bytes: the length field, which is set to zero.
+For indexed color images, the length will be equal to 768, and the color data
+will contain the color table for the image, in non-interleaved order.
+For duotone images, the color data will contain the duotone specification,
+the format of which is not documented. Other applications that read
+Photoshop files can treat a duotone image as a grayscale image, and just
+preserve the contents of the duotone information when reading and writing
+the file.
+*/
+class psdColourModeData {
+public:
+ int _Length; //! The length of the following color data
+ BYTE * _plColourData; //! The color data
+
+public:
+ psdColourModeData();
+ ~psdColourModeData();
+ /**
+ @return Returns the number of bytes read
+ */
+ bool Read(FreeImageIO *io, fi_handle handle);
+ bool FillPalette(FIBITMAP *dib);
+};
+
+/**
+Table 2-1: Image resource block
+NB: Resource data is padded to make size even
+*/
+class psdImageResource {
+public:
+ int _Length;
+ char _OSType[4]; //! Photoshop always uses its signature, 8BIM
+ short _ID; //! Unique identifier. Image resource IDs on page 8
+ BYTE * _plName; //! A pascal string, padded to make size even (a null name consists of two bytes of 0)
+ int _Size; //! Actual size of resource data. This does not include the Type, ID, Name or Size fields.
+
+public:
+ psdImageResource();
+ ~psdImageResource();
+ void Reset();
+};
+
+/**
+Table A-6: ResolutionInfo structure
+This structure contains information about the resolution of an image. It is
+written as an image resource. See the Document file formats chapter for more
+details.
+*/
+class psdResolutionInfo {
+public:
+ short _widthUnit; //! Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+ short _heightUnit; //! Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns.
+ short _hRes; //! Horizontal resolution in pixels per inch.
+ short _vRes; //! Vertical resolution in pixels per inch.
+ int _hResUnit; //! 1=display horizontal resolution in pixels per inch; 2=display horizontal resolution in pixels per cm.
+ int _vResUnit; //! 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm.
+
+public:
+ psdResolutionInfo();
+ ~psdResolutionInfo();
+ /**
+ @return Returns the number of bytes read
+ */
+ int Read(FreeImageIO *io, fi_handle handle);
+ /**
+ @param res_x [out] X resolution in pixels/meter
+ @param res_y [out] Y resolution in pixels/meter
+ */
+ void GetResolutionInfo(unsigned &res_x, unsigned &res_y);
+};
+
+// Obsolete - Photoshop 2.0
+class psdResolutionInfo_v2 {
+public:
+ short _Channels;
+ short _Rows;
+ short _Columns;
+ short _Depth;
+ short _Mode;
+
+public:
+ psdResolutionInfo_v2();
+ ~psdResolutionInfo_v2();
+ /**
+ @return Returns the number of bytes read
+ */
+ int Read(FreeImageIO *io, fi_handle handle);
+};
+
+/**
+Table A-7: DisplayInfo Color spaces
+This structure contains display information about each channel. It is written as an image resource.
+*/
+class psdDisplayInfo {
+public:
+ short _ColourSpace;
+ short _Colour[4];
+ short _Opacity; //! 0..100
+ BYTE _Kind; //! selected = 0, protected = 1
+ BYTE _padding; //! should be zero
+
+public:
+ psdDisplayInfo();
+ ~psdDisplayInfo();
+ /**
+ @return Returns the number of bytes read
+ */
+ int Read(FreeImageIO *io, fi_handle handle);
+};
+
+/**
+Table 2-5: Thumbnail resource header
+Adobe Photoshop 5.0 and later stores thumbnail information for preview
+display in an image resource block. These resource blocks consist of an initial
+28 byte header, followed by a JFIF thumbnail in RGB (red, green, blue) order
+for both Macintosh and Windows. Adobe Photoshop 4.0 stored the
+thumbnail information in the same format except the data section is BGR
+(blue, green, red). The Adobe Photoshop 4.0 format is at resource ID 1033
+and the Adobe Photoshop 5.0 format is at resource ID 1036.
+*/
+class psdThumbnail {
+public:
+ int _Format; //! = 1 (kJpegRGB). Also supports kRawRGB (0).
+ int _Width; //! Width of thumbnail in pixels.
+ int _Height; //! Height of thumbnail in pixels.
+ int _WidthBytes; //! Padded row bytes as (width * bitspixel + 31) / 32 * 4.
+ int _Size; //! Total size as widthbytes * height * planes
+ int _CompressedSize; //! Size after compression. Used for consistentcy check.
+ short _BitPerPixel; //! = 24. Bits per pixel.
+ short _Planes; //! = 1. Number of planes.
+ FIBITMAP * _dib; //! JFIF data as uncompressed dib. Note: For resource ID 1033 the data is in BGR format.
+
+public:
+ psdThumbnail();
+ ~psdThumbnail();
+ FIBITMAP* getDib() { return _dib; }
+ /**
+ @return Returns the number of bytes read
+ */
+ int Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR);
+
+private:
+ psdThumbnail(const psdThumbnail&);
+ psdThumbnail& operator=(const psdThumbnail&);
+};
+
+class psdICCProfile {
+public:
+ int _ProfileSize;
+ BYTE * _ProfileData;
+public:
+ psdICCProfile();
+ ~psdICCProfile();
+ void clear();
+ /**
+ @return Returns the number of bytes read
+ */
+ int Read(FreeImageIO *io, fi_handle handle, int size);
+};
+
+/**
+PSD loader
+*/
+class psdParser {
+private:
+ psdHeaderInfo _headerInfo;
+ psdColourModeData _colourModeData;
+ psdResolutionInfo _resolutionInfo;
+ psdResolutionInfo_v2 _resolutionInfo_v2;
+ psdDisplayInfo _displayInfo;
+ psdThumbnail _thumbnail;
+ psdICCProfile _iccProfile;
+
+ short _ColourCount;
+ short _TransparentIndex;
+ int _GlobalAngle;
+ bool _bResolutionInfoFilled;
+ bool _bResolutionInfoFilled_v2;
+ bool _bDisplayInfoFilled;
+ bool _bThumbnailFilled;
+ bool _bCopyright;
+
+ int _fi_flags;
+ int _fi_format_id;
+
+private:
+ /** Actually ignore it */
+ bool ReadLayerAndMaskInfoSection(FreeImageIO *io, fi_handle handle);
+ FIBITMAP* ReadImageData(FreeImageIO *io, fi_handle handle);
+
+public:
+ psdParser();
+ ~psdParser();
+ FIBITMAP* Load(FreeImageIO *io, fi_handle handle, int s_format_id, int flags=0);
+ /** Also used by the TIFF plugin */
+ bool ReadImageResources(FreeImageIO *io, fi_handle handle, LONG length=0);
+ /** Used by the TIFF plugin */
+ FIBITMAP* GetThumbnail() {
+ return _thumbnail.getDib();
+ }
+};
+
+#endif // PSDPARSER_H
+
diff --git a/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp b/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp
new file mode 100644
index 0000000000..158f9997b2
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp
@@ -0,0 +1,209 @@
+// ==========================================================
+// Pixel access functions
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Ryan Rubley (ryan@lostreality.org)
+// - Riley McNiff (rmcniff@marexgroup.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+
+BYTE * DLL_CALLCONV
+FreeImage_GetBits(FIBITMAP *dib) {
+ if (!FreeImage_HasPixels(dib)) {
+ return NULL;
+ }
+ // returns the pixels aligned on a FIBITMAP_ALIGNMENT bytes alignment boundary
+ size_t lp = (size_t)FreeImage_GetInfoHeader(dib);
+ lp += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * FreeImage_GetColorsUsed(dib);
+ lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0);
+ return (BYTE *)lp;
+}
+
+BYTE * DLL_CALLCONV
+FreeImage_GetScanLine(FIBITMAP *dib, int scanline) {
+ if (!FreeImage_HasPixels(dib)) {
+ return NULL;
+ }
+ return CalculateScanLine(FreeImage_GetBits(dib), FreeImage_GetPitch(dib), scanline);
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) {
+ BYTE shift;
+
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+ return FALSE;
+
+ if ((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+ switch(FreeImage_GetBPP(dib)) {
+ case 1:
+ *value = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+ break;
+ case 4:
+ shift = (BYTE)((1 - x % 2) << 2);
+ *value = (bits[x >> 1] & (0x0F << shift)) >> shift;
+ break;
+ case 8:
+ *value = bits[x];
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_GetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) {
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+ return FALSE;
+
+ if ((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+ switch(FreeImage_GetBPP(dib)) {
+ case 16:
+ {
+ bits += 2*x;
+ WORD *pixel = (WORD *)bits;
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ value->rgbBlue = (BYTE)((((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
+ value->rgbGreen = (BYTE)((((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F);
+ value->rgbRed = (BYTE)((((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
+ value->rgbReserved = 0;
+ } else {
+ value->rgbBlue = (BYTE)((((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+ value->rgbGreen = (BYTE)((((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+ value->rgbRed = (BYTE)((((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+ value->rgbReserved = 0;
+ }
+ break;
+ }
+ case 24:
+ bits += 3*x;
+ value->rgbBlue = bits[FI_RGBA_BLUE]; // B
+ value->rgbGreen = bits[FI_RGBA_GREEN]; // G
+ value->rgbRed = bits[FI_RGBA_RED]; // R
+ value->rgbReserved = 0;
+ break;
+ case 32:
+ bits += 4*x;
+ value->rgbBlue = bits[FI_RGBA_BLUE]; // B
+ value->rgbGreen = bits[FI_RGBA_GREEN]; // G
+ value->rgbRed = bits[FI_RGBA_RED]; // R
+ value->rgbReserved = bits[FI_RGBA_ALPHA]; // A
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) {
+ BYTE shift;
+
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+ return FALSE;
+
+ if ((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+ switch(FreeImage_GetBPP(dib)) {
+ case 1:
+ *value ? bits[x >> 3] |= (0x80 >> (x & 0x7)) : bits[x >> 3] &= (0xFF7F >> (x & 0x7));
+ break;
+ case 4:
+ shift = (BYTE)((1 - x % 2) << 2);
+ bits[x >> 1] &= ~(0x0F << shift);
+ bits[x >> 1] |= ((*value & 0x0F) << shift);
+ break;
+ case 8:
+ bits[x] = *value;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) {
+ if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP))
+ return FALSE;
+
+ if ((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+ switch(FreeImage_GetBPP(dib)) {
+ case 16:
+ {
+ bits += 2*x;
+ WORD *pixel = (WORD *)bits;
+ if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
+ *pixel = ((value->rgbBlue >> 3) << FI16_565_BLUE_SHIFT) |
+ ((value->rgbGreen >> 2) << FI16_565_GREEN_SHIFT) |
+ ((value->rgbRed >> 3) << FI16_565_RED_SHIFT);
+ } else {
+ *pixel = ((value->rgbBlue >> 3) << FI16_555_BLUE_SHIFT) |
+ ((value->rgbGreen >> 3) << FI16_555_GREEN_SHIFT) |
+ ((value->rgbRed >> 3) << FI16_555_RED_SHIFT);
+ }
+ break;
+ }
+ case 24:
+ bits += 3*x;
+ bits[FI_RGBA_BLUE] = value->rgbBlue; // B
+ bits[FI_RGBA_GREEN] = value->rgbGreen; // G
+ bits[FI_RGBA_RED] = value->rgbRed; // R
+ break;
+ case 32:
+ bits += 4*x;
+ bits[FI_RGBA_BLUE] = value->rgbBlue; // B
+ bits[FI_RGBA_GREEN] = value->rgbGreen; // G
+ bits[FI_RGBA_RED] = value->rgbRed; // R
+ bits[FI_RGBA_ALPHA] = value->rgbReserved; // A
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/Plugin.cpp b/plugins/AdvaImg/src/FreeImage/Plugin.cpp
new file mode 100644
index 0000000000..ce2863a72e
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/Plugin.cpp
@@ -0,0 +1,747 @@
+// =====================================================================
+// FreeImage Plugin Interface
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Rui Lopes (ruiglopes@yahoo.com)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Petr Pytelka (pyta@lightcomp.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// =====================================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#else
+#include <ctype.h>
+#endif // _WIN32
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "FreeImageIO.h"
+#include "Plugin.h"
+
+// =====================================================================
+
+using namespace std;
+
+// =====================================================================
+// Plugin search list
+// =====================================================================
+
+const char *
+s_search_list[] = {
+ "",
+ "plugins\\",
+};
+
+static int s_search_list_size = sizeof(s_search_list) / sizeof(char *);
+static PluginList *s_plugins = NULL;
+static int s_plugin_reference_count = 0;
+
+
+// =====================================================================
+// Reimplementation of stricmp (it is not supported on some systems)
+// =====================================================================
+
+int
+FreeImage_stricmp(const char *s1, const char *s2) {
+ int c1, c2;
+
+ do {
+ c1 = tolower(*s1++);
+ c2 = tolower(*s2++);
+ } while (c1 && c1 == c2);
+
+ return c1 - c2;
+}
+
+// =====================================================================
+// Implementation of PluginList
+// =====================================================================
+
+PluginList::PluginList() :
+m_plugin_map(),
+m_node_count(0) {
+}
+
+FREE_IMAGE_FORMAT
+PluginList::AddNode(FI_InitProc init_proc, void *instance, const char *format, const char *description, const char *extension, const char *regexpr) {
+ if (init_proc != NULL) {
+ PluginNode *node = new PluginNode;
+ Plugin *plugin = new Plugin;
+
+ memset(plugin, 0, sizeof(Plugin));
+
+ // fill-in the plugin structure
+ // note we have memset to 0, so all unset pointers should be NULL)
+
+ init_proc(plugin, (int)m_plugin_map.size());
+
+ // get the format string (two possible ways)
+
+ const char *the_format = NULL;
+
+ if (format != NULL)
+ the_format = format;
+ else if (plugin->format_proc != NULL)
+ the_format = plugin->format_proc();
+
+ // add the node if it wasn't there already
+
+ if (the_format != NULL) {
+ if (FindNodeFromFormat(the_format) == NULL) {
+ node->m_id = (int)m_plugin_map.size();
+ node->m_instance = instance;
+ node->m_plugin = plugin;
+ node->m_format = format;
+ node->m_description = description;
+ node->m_extension = extension;
+ node->m_regexpr = regexpr;
+ node->m_enabled = TRUE;
+
+ m_plugin_map[(const int)m_plugin_map.size()] = node;
+
+ return (FREE_IMAGE_FORMAT)node->m_id;
+ }
+ }
+
+ // something went wrong while allocating the plugin... cleanup
+
+ delete plugin;
+ delete node;
+ }
+
+ return FIF_UNKNOWN;
+}
+
+PluginNode *
+PluginList::FindNodeFromFormat(const char *format) {
+ int count = 0;
+
+ for (map<int, PluginNode *>::iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) {
+ const char *the_format = ((*i).second->m_format != NULL) ? (*i).second->m_format : (*i).second->m_plugin->format_proc();
+
+ if (FreeImage_stricmp(the_format, format) == 0)
+ return (*i).second;
+
+ count++;
+ }
+
+ return NULL;
+}
+
+PluginNode *
+PluginList::FindNodeFromMime(const char *mime) {
+ int count = 0;
+
+ for (map<int, PluginNode *>::iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) {
+ const char *the_mime = ((*i).second->m_plugin->mime_proc != NULL) ? (*i).second->m_plugin->mime_proc() : "";
+
+ if ((the_mime != NULL) && (strcmp(the_mime, mime) == 0))
+ return (*i).second;
+
+ count++;
+ }
+
+ return NULL;
+}
+
+PluginNode *
+PluginList::FindNodeFromFIF(int node_id) {
+ map<int, PluginNode *>::iterator i = m_plugin_map.find(node_id);
+
+ if (i != m_plugin_map.end())
+ return (*i).second;
+
+ return NULL;
+}
+
+int
+PluginList::Size() const {
+ return (int)m_plugin_map.size();
+}
+
+BOOL
+PluginList::IsEmpty() const {
+ return m_plugin_map.empty();
+}
+
+PluginList::~PluginList() {
+ for (map<int, PluginNode *>::iterator i = m_plugin_map.begin(); i != m_plugin_map.end(); ++i) {
+#ifdef _WIN32
+ if ((*i).second->m_instance != NULL)
+ FreeLibrary((HINSTANCE)(*i).second->m_instance);
+#endif
+ delete (*i).second->m_plugin;
+ delete ((*i).second);
+ }
+}
+
+// =====================================================================
+// Retrieve a pointer to the plugin list container
+// =====================================================================
+
+PluginList * DLL_CALLCONV
+FreeImage_GetPluginList() {
+ return s_plugins;
+}
+
+// =====================================================================
+// Plugin System Initialization
+// =====================================================================
+
+void DLL_CALLCONV
+FreeImage_Initialise(BOOL load_local_plugins_only) {
+ if (s_plugin_reference_count++ == 0) {
+
+ // internal plugin initialization
+
+ s_plugins = new(std::nothrow) PluginList;
+
+ if (s_plugins) {
+ /* NOTE :
+ The order used to initialize internal plugins below MUST BE the same order
+ as the one used to define the FREE_IMAGE_FORMAT enum.
+ */
+ s_plugins->AddNode(InitBMP);
+ s_plugins->AddNode(InitICO);
+ s_plugins->AddNode(InitJPEG);
+ //s_plugins->AddNode(InitJNG);
+ //s_plugins->AddNode(InitKOALA);
+ //s_plugins->AddNode(InitIFF);
+ //s_plugins->AddNode(InitMNG);
+ //s_plugins->AddNode(InitPNM, NULL, "PBM", "Portable Bitmap (ASCII)", "pbm", "^P1");
+ //s_plugins->AddNode(InitPNM, NULL, "PBMRAW", "Portable Bitmap (RAW)", "pbm", "^P4");
+ //s_plugins->AddNode(InitPCD);
+ //s_plugins->AddNode(InitPCX);
+ //s_plugins->AddNode(InitPNM, NULL, "PGM", "Portable Greymap (ASCII)", "pgm", "^P2");
+ //s_plugins->AddNode(InitPNM, NULL, "PGMRAW", "Portable Greymap (RAW)", "pgm", "^P5");
+ s_plugins->AddNode(InitPNG);
+ //s_plugins->AddNode(InitPNM, NULL, "PPM", "Portable Pixelmap (ASCII)", "ppm", "^P3");
+ //s_plugins->AddNode(InitPNM, NULL, "PPMRAW", "Portable Pixelmap (RAW)", "ppm", "^P6");
+ //s_plugins->AddNode(InitRAS);
+ //s_plugins->AddNode(InitTARGA);
+ //s_plugins->AddNode(InitTIFF);
+ //s_plugins->AddNode(InitWBMP);
+ //s_plugins->AddNode(InitPSD);
+ s_plugins->AddNode(InitCUT);
+ //s_plugins->AddNode(InitXBM);
+ //s_plugins->AddNode(InitXPM);
+ //s_plugins->AddNode(InitDDS);
+ s_plugins->AddNode(InitGIF);
+ //s_plugins->AddNode(InitHDR);
+ //s_plugins->AddNode(InitG3);
+ //s_plugins->AddNode(InitSGI);
+ //s_plugins->AddNode(InitEXR);
+ //s_plugins->AddNode(InitJ2K);
+ //s_plugins->AddNode(InitJP2);
+ //s_plugins->AddNode(InitPFM);
+ //s_plugins->AddNode(InitPICT);
+ //s_plugins->AddNode(InitRAW);
+ }
+ }
+}
+
+void DLL_CALLCONV
+FreeImage_DeInitialise() {
+ --s_plugin_reference_count;
+
+ if (s_plugin_reference_count == 0) {
+ delete s_plugins;
+ }
+}
+
+// =====================================================================
+// Open and close a bitmap
+// =====================================================================
+
+void * DLL_CALLCONV
+FreeImage_Open(PluginNode *node, FreeImageIO *io, fi_handle handle, BOOL open_for_reading) {
+ if (node->m_plugin->open_proc != NULL) {
+ return node->m_plugin->open_proc(io, handle, open_for_reading);
+ }
+
+ return NULL;
+}
+
+void DLL_CALLCONV
+FreeImage_Close(PluginNode *node, FreeImageIO *io, fi_handle handle, void *data) {
+ if (node->m_plugin->close_proc != NULL) {
+ node->m_plugin->close_proc(io, handle, data);
+ }
+}
+
+// =====================================================================
+// Plugin System Load/Save Functions
+// =====================================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LoadFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
+ if ((fif >= 0) && (fif < FreeImage_GetFIFCount())) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ if (node != NULL) {
+ if (node->m_enabled) {
+ if(node->m_plugin->load_proc != NULL) {
+ FIBITMAP *bitmap = NULL;
+
+ void *data = FreeImage_Open(node, io, handle, TRUE);
+
+ bitmap = node->m_plugin->load_proc(io, handle, -1, flags, data);
+
+ FreeImage_Close(node, io, handle, data);
+
+ return bitmap;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_Load(FREE_IMAGE_FORMAT fif, const char *filename, int flags) {
+ FreeImageIO io;
+ SetDefaultIO(&io);
+
+ FILE *handle = fopen(filename, "rb");
+
+ if (handle) {
+ FIBITMAP *bitmap = FreeImage_LoadFromHandle(fif, &io, (fi_handle)handle, flags);
+
+ fclose(handle);
+
+ return bitmap;
+ } else {
+ FreeImage_OutputMessageProc((int)fif, "FreeImage_Load: failed to open file %s", filename);
+ }
+
+ return NULL;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_LoadU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, int flags) {
+ FreeImageIO io;
+ SetDefaultIO(&io);
+#ifdef _WIN32
+ FILE *handle = _wfopen(filename, L"rb");
+
+ if (handle) {
+ FIBITMAP *bitmap = FreeImage_LoadFromHandle(fif, &io, (fi_handle)handle, flags);
+
+ fclose(handle);
+
+ return bitmap;
+ } else {
+ FreeImage_OutputMessageProc((int)fif, "FreeImage_LoadU: failed to open input file");
+ }
+#endif
+ return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveToHandle(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, FreeImageIO *io, fi_handle handle, int flags) {
+ // cannot save "header only" formats
+ if(FreeImage_HasPixels(dib) == FALSE) {
+ FreeImage_OutputMessageProc((int)fif, "FreeImage_SaveToHandle: cannot save \"header only\" formats");
+ return FALSE;
+ }
+
+ if ((fif >= 0) && (fif < FreeImage_GetFIFCount())) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ if (node) {
+ if (node->m_enabled) {
+ if(node->m_plugin->save_proc != NULL) {
+ BOOL result = FALSE;
+
+ void *data = FreeImage_Open(node, io, handle, FALSE);
+
+ result = node->m_plugin->save_proc(io, dib, handle, -1, flags, data);
+
+ FreeImage_Close(node, io, handle, data);
+
+ return result;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+BOOL DLL_CALLCONV
+FreeImage_Save(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const char *filename, int flags) {
+ FreeImageIO io;
+ SetDefaultIO(&io);
+
+ FILE *handle = fopen(filename, "w+b");
+
+ if (handle) {
+ BOOL success = FreeImage_SaveToHandle(fif, dib, &io, (fi_handle)handle, flags);
+
+ fclose(handle);
+
+ return success;
+ } else {
+ FreeImage_OutputMessageProc((int)fif, "FreeImage_Save: failed to open file %s", filename);
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_SaveU(FREE_IMAGE_FORMAT fif, FIBITMAP *dib, const wchar_t *filename, int flags) {
+ FreeImageIO io;
+ SetDefaultIO(&io);
+#ifdef _WIN32
+ FILE *handle = _wfopen(filename, L"w+b");
+
+ if (handle) {
+ BOOL success = FreeImage_SaveToHandle(fif, dib, &io, (fi_handle)handle, flags);
+
+ fclose(handle);
+
+ return success;
+ } else {
+ FreeImage_OutputMessageProc((int)fif, "FreeImage_SaveU: failed to open output file");
+ }
+#endif
+ return FALSE;
+}
+
+// =====================================================================
+// Plugin construction + enable/disable functions
+// =====================================================================
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_RegisterLocalPlugin(FI_InitProc proc_address, const char *format, const char *description, const char *extension, const char *regexpr) {
+ return s_plugins->AddNode(proc_address, NULL, format, description, extension, regexpr);
+}
+
+#ifdef _WIN32
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_RegisterExternalPlugin(const char *path, const char *format, const char *description, const char *extension, const char *regexpr) {
+ if (path != NULL) {
+ HINSTANCE instance = LoadLibraryA(path);
+
+ if (instance != NULL) {
+ FARPROC proc_address = GetProcAddress(instance, "_Init@8");
+
+ FREE_IMAGE_FORMAT result = s_plugins->AddNode((FI_InitProc)proc_address, (void *)instance, format, description, extension, regexpr);
+
+ if (result == FIF_UNKNOWN)
+ FreeLibrary(instance);
+
+ return result;
+ }
+ }
+
+ return FIF_UNKNOWN;
+}
+#endif // _WIN32
+
+int DLL_CALLCONV
+FreeImage_SetPluginEnabled(FREE_IMAGE_FORMAT fif, BOOL enable) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ if (node != NULL) {
+ BOOL previous_state = node->m_enabled;
+
+ node->m_enabled = enable;
+
+ return previous_state;
+ }
+ }
+
+ return -1;
+}
+
+int DLL_CALLCONV
+FreeImage_IsPluginEnabled(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? node->m_enabled : FALSE;
+ }
+
+ return -1;
+}
+
+// =====================================================================
+// Plugin Access Functions
+// =====================================================================
+
+int DLL_CALLCONV
+FreeImage_GetFIFCount() {
+ return (s_plugins != NULL) ? s_plugins->Size() : 0;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFIFFromFormat(const char *format) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFormat(format);
+
+ return (node != NULL) ? (node->m_enabled) ? (FREE_IMAGE_FORMAT)node->m_id : FIF_UNKNOWN : FIF_UNKNOWN;
+ }
+
+ return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFIFFromMime(const char *mime) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromMime(mime);
+
+ return (node != NULL) ? (node->m_enabled) ? (FREE_IMAGE_FORMAT)node->m_id : FIF_UNKNOWN : FIF_UNKNOWN;
+ }
+
+ return FIF_UNKNOWN;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFormatFromFIF(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? (node->m_format != NULL) ? node->m_format : node->m_plugin->format_proc() : NULL;
+ }
+
+ return NULL;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFIFMimeType(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? (node->m_plugin != NULL) ? ( node->m_plugin->mime_proc != NULL )? node->m_plugin->mime_proc() : NULL : NULL : NULL;
+ }
+
+ return NULL;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFIFExtensionList(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? (node->m_extension != NULL) ? node->m_extension : (node->m_plugin->extension_proc != NULL) ? node->m_plugin->extension_proc() : NULL : NULL;
+ }
+
+ return NULL;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFIFDescription(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? (node->m_description != NULL) ? node->m_description : (node->m_plugin->description_proc != NULL) ? node->m_plugin->description_proc() : NULL : NULL;
+ }
+
+ return NULL;
+}
+
+const char * DLL_CALLCONV
+FreeImage_GetFIFRegExpr(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? (node->m_regexpr != NULL) ? node->m_regexpr : (node->m_plugin->regexpr_proc != NULL) ? node->m_plugin->regexpr_proc() : NULL : NULL;
+ }
+
+ return NULL;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsReading(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? node->m_plugin->load_proc != NULL : FALSE;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsWriting(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ? node->m_plugin->save_proc != NULL : FALSE ;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsExportBPP(FREE_IMAGE_FORMAT fif, int depth) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ?
+ (node->m_plugin->supports_export_bpp_proc != NULL) ?
+ node->m_plugin->supports_export_bpp_proc(depth) : FALSE : FALSE;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsExportType(FREE_IMAGE_FORMAT fif, FREE_IMAGE_TYPE type) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ?
+ (node->m_plugin->supports_export_type_proc != NULL) ?
+ node->m_plugin->supports_export_type_proc(type) : FALSE : FALSE;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsICCProfiles(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ?
+ (node->m_plugin->supports_icc_profiles_proc != NULL) ?
+ node->m_plugin->supports_icc_profiles_proc() : FALSE : FALSE;
+ }
+
+ return FALSE;
+}
+
+BOOL DLL_CALLCONV
+FreeImage_FIFSupportsNoPixels(FREE_IMAGE_FORMAT fif) {
+ if (s_plugins != NULL) {
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ return (node != NULL) ?
+ (node->m_plugin->supports_no_pixels_proc != NULL) ?
+ node->m_plugin->supports_no_pixels_proc() : FALSE : FALSE;
+ }
+
+ return FALSE;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFIFFromFilename(const char *filename) {
+ if (filename != NULL) {
+ const char *extension;
+
+ // get the proper extension if we received a filename
+
+ char *place = strrchr((char *)filename, '.');
+ extension = (place != NULL) ? ++place : filename;
+
+ // look for the extension in the plugin table
+
+ for (int i = 0; i < FreeImage_GetFIFCount(); ++i) {
+
+ if (s_plugins->FindNodeFromFIF(i)->m_enabled) {
+
+ // compare the format id with the extension
+
+ if (FreeImage_stricmp(FreeImage_GetFormatFromFIF((FREE_IMAGE_FORMAT)i), extension) == 0) {
+ return (FREE_IMAGE_FORMAT)i;
+ } else {
+ // make a copy of the extension list and split it
+
+ char *copy = (char *)malloc(strlen(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i)) + 1);
+ memset(copy, 0, strlen(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i)) + 1);
+ memcpy(copy, FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i), strlen(FreeImage_GetFIFExtensionList((FREE_IMAGE_FORMAT)i)));
+
+ // get the first token
+
+ char *token = strtok(copy, ",");
+
+ while (token != NULL) {
+ if (FreeImage_stricmp(token, extension) == 0) {
+ free(copy);
+
+ return (FREE_IMAGE_FORMAT)i;
+ }
+
+ token = strtok(NULL, ",");
+ }
+
+ // free the copy of the extension list
+
+ free(copy);
+ }
+ }
+ }
+ }
+
+ return FIF_UNKNOWN;
+}
+
+FREE_IMAGE_FORMAT DLL_CALLCONV
+FreeImage_GetFIFFromFilenameU(const wchar_t *filename) {
+#ifdef _WIN32
+ if (filename == NULL) return FIF_UNKNOWN;
+
+ // get the proper extension if we received a filename
+ wchar_t *place = wcsrchr((wchar_t *)filename, '.');
+ if (place == NULL) return FIF_UNKNOWN;
+ // convert to single character - no national chars in extensions
+ char *extension = (char *)malloc(wcslen(place)+1);
+ unsigned int i=0;
+ for (; i < wcslen(place); i++) // convert 16-bit to 8-bit
+ extension[i] = (char)(place[i] & 0x00FF);
+ // set terminating 0
+ extension[i]=0;
+ FREE_IMAGE_FORMAT fRet = FreeImage_GetFIFFromFilename(extension);
+ free(extension);
+
+ return fRet;
+#else
+ return FIF_UNKNOWN;
+#endif // _WIN32
+}
+
+BOOL DLL_CALLCONV
+FreeImage_Validate(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle) {
+ if (s_plugins != NULL) {
+ BOOL validated = FALSE;
+
+ PluginNode *node = s_plugins->FindNodeFromFIF(fif);
+
+ if (node) {
+ long tell = io->tell_proc(handle);
+
+ validated = (node != NULL) ? (node->m_enabled) ? (node->m_plugin->validate_proc != NULL) ? node->m_plugin->validate_proc(io, handle) : FALSE : FALSE : FALSE;
+
+ io->seek_proc(handle, tell, SEEK_SET);
+ }
+
+ return validated;
+ }
+
+ return FALSE;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginBMP.cpp b/plugins/AdvaImg/src/FreeImage/PluginBMP.cpp
new file mode 100644
index 0000000000..d69336f3f5
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginBMP.cpp
@@ -0,0 +1,1480 @@
+// ==========================================================
+// BMP Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Martin Weber (martweb@gmx.net)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Michal Novotny (michal@etc.cz)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+static const BYTE RLE_COMMAND = 0;
+static const BYTE RLE_ENDOFLINE = 0;
+static const BYTE RLE_ENDOFBITMAP = 1;
+static const BYTE RLE_DELTA = 2;
+
+static const BYTE BI_RGB = 0;
+static const BYTE BI_RLE8 = 1;
+static const BYTE BI_RLE4 = 2;
+static const BYTE BI_BITFIELDS = 3;
+
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagBITMAPCOREHEADER {
+ DWORD bcSize;
+ WORD bcWidth;
+ WORD bcHeight;
+ WORD bcPlanes;
+ WORD bcBitCnt;
+} BITMAPCOREHEADER, *PBITMAPCOREHEADER;
+
+typedef struct tagBITMAPINFOOS2_1X_HEADER {
+ DWORD biSize;
+ WORD biWidth;
+ WORD biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+} BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER;
+
+typedef struct tagBITMAPFILEHEADER {
+ WORD bfType;
+ DWORD bfSize;
+ WORD bfReserved1;
+ WORD bfReserved2;
+ DWORD bfOffBits;
+} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapInfoHeader(BITMAPINFOHEADER *header) {
+ SwapLong(&header->biSize);
+ SwapLong((DWORD *)&header->biWidth);
+ SwapLong((DWORD *)&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+ SwapLong(&header->biCompression);
+ SwapLong(&header->biSizeImage);
+ SwapLong((DWORD *)&header->biXPelsPerMeter);
+ SwapLong((DWORD *)&header->biYPelsPerMeter);
+ SwapLong(&header->biClrUsed);
+ SwapLong(&header->biClrImportant);
+}
+
+static void
+SwapCoreHeader(BITMAPCOREHEADER *header) {
+ SwapLong(&header->bcSize);
+ SwapShort(&header->bcWidth);
+ SwapShort(&header->bcHeight);
+ SwapShort(&header->bcPlanes);
+ SwapShort(&header->bcBitCnt);
+}
+
+static void
+SwapOS21XHeader(BITMAPINFOOS2_1X_HEADER *header) {
+ SwapLong(&header->biSize);
+ SwapShort(&header->biWidth);
+ SwapShort(&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+}
+
+static void
+SwapFileHeader(BITMAPFILEHEADER *header) {
+ SwapShort(&header->bfType);
+ SwapLong(&header->bfSize);
+ SwapShort(&header->bfReserved1);
+ SwapShort(&header->bfReserved2);
+ SwapLong(&header->bfOffBits);
+}
+#endif
+
+// --------------------------------------------------------------------------
+
+/**
+Load uncompressed image pixels for 1-, 4-, 8-, 16-, 24- and 32-bit dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param dib Image to be loaded
+@param height Image height
+@param pitch Image pitch
+@param bit_count Image bit-depth (1-, 4-, 8-, 16-, 24- or 32-bit)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+LoadPixelData(FreeImageIO *io, fi_handle handle, FIBITMAP *dib, int height, unsigned pitch, unsigned bit_count) {
+ unsigned count = 0;
+
+ // Load pixel data
+ // NB: height can be < 0 for BMP data
+ if (height > 0) {
+ count = io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle);
+ if(count != 1) {
+ return FALSE;
+ }
+ } else {
+ int positiveHeight = abs(height);
+ for (int c = 0; c < positiveHeight; ++c) {
+ count = io->read_proc((void *)FreeImage_GetScanLine(dib, positiveHeight - c - 1), pitch, 1, handle);
+ if(count != 1) {
+ return FALSE;
+ }
+ }
+ }
+
+ // swap as needed
+#ifdef FREEIMAGE_BIGENDIAN
+ if (bit_count == 16) {
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ SwapShort(pixel);
+ pixel++;
+ }
+ }
+ }
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ if (bit_count == 24 || bit_count == 32) {
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *pixel = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ INPLACESWAP(pixel[0], pixel[2]);
+ pixel += (bit_count >> 3);
+ }
+ }
+ }
+#endif
+
+ return TRUE;
+}
+
+/**
+Load image pixels for 4-bit RLE compressed dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param width Image width
+@param height Image height
+@param dib Image to be loaded
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+LoadPixelDataRLE4(FreeImageIO *io, fi_handle handle, int width, int height, FIBITMAP *dib) {
+ int status_byte = 0;
+ BYTE second_byte = 0;
+ int bits = 0;
+
+ BYTE *pixels = NULL; // temporary 8-bit buffer
+
+ try {
+ height = abs(height);
+
+ pixels = (BYTE*)malloc(width * height * sizeof(BYTE));
+ if (!pixels) throw(1);
+ memset(pixels, 0, width * height * sizeof(BYTE));
+
+ BYTE *q = pixels;
+ BYTE *end = pixels + height * width;
+
+ for (int scanline = 0; scanline < height; ) {
+ if (q < pixels || q >= end) {
+ break;
+ }
+ if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ if (status_byte != 0) {
+ status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+ // Encoded mode
+ if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ for (int i = 0; i < status_byte; i++) {
+ *q++=(BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+ }
+ bits += status_byte;
+ }
+ else {
+ // Escape mode
+ if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ switch (status_byte) {
+ case RLE_ENDOFLINE:
+ {
+ // End of line
+ bits = 0;
+ scanline++;
+ q = pixels + scanline*width;
+ }
+ break;
+
+ case RLE_ENDOFBITMAP:
+ // End of bitmap
+ q = end;
+ break;
+
+ case RLE_DELTA:
+ {
+ // read the delta values
+
+ BYTE delta_x = 0;
+ BYTE delta_y = 0;
+
+ if(io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ if(io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+
+ // apply them
+
+ bits += delta_x;
+ scanline += delta_y;
+ q = pixels + scanline*width+bits;
+ }
+ break;
+
+ default:
+ {
+ // Absolute mode
+ status_byte = (int)MIN((size_t)status_byte, (size_t)(end - q));
+ for (int i = 0; i < status_byte; i++) {
+ if ((i & 0x01) == 0) {
+ if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ }
+ *q++=(BYTE)((i & 0x01) ? (second_byte & 0x0f) : ((second_byte >> 4) & 0x0f));
+ }
+ bits += status_byte;
+ // Read pad byte
+ if (((status_byte & 0x03) == 1) || ((status_byte & 0x03) == 2)) {
+ BYTE padding = 0;
+ if(io->read_proc(&padding, sizeof(BYTE), 1, handle) != 1) {
+ throw(1);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ {
+ // Convert to 4-bit
+ for(int y = 0; y < height; y++) {
+ const BYTE *src = (BYTE*)pixels + y * width;
+ BYTE *dst = FreeImage_GetScanLine(dib, y);
+
+ BOOL hinibble = TRUE;
+
+ for (int cols = 0; cols < width; cols++) {
+ if (hinibble) {
+ dst[cols >> 1] = (src[cols] << 4);
+ } else {
+ dst[cols >> 1] |= src[cols];
+ }
+
+ hinibble = !hinibble;
+ }
+ }
+ }
+
+ free(pixels);
+
+ return TRUE;
+
+ } catch(int) {
+ if(pixels) free(pixels);
+ return FALSE;
+ }
+}
+
+/**
+Load image pixels for 8-bit RLE compressed dib
+@param io FreeImage IO
+@param handle FreeImage IO handle
+@param width Image width
+@param height Image height
+@param dib Image to be loaded
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+LoadPixelDataRLE8(FreeImageIO *io, fi_handle handle, int width, int height, FIBITMAP *dib) {
+ BYTE status_byte = 0;
+ BYTE second_byte = 0;
+ int scanline = 0;
+ int bits = 0;
+
+ for (;;) {
+ if ( io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ switch (status_byte) {
+ case RLE_COMMAND :
+ if(io->read_proc(&status_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ switch (status_byte) {
+ case RLE_ENDOFLINE :
+ bits = 0;
+ scanline++;
+ break;
+
+ case RLE_ENDOFBITMAP :
+ return TRUE;
+
+ case RLE_DELTA :
+ {
+ // read the delta values
+
+ BYTE delta_x = 0;
+ BYTE delta_y = 0;
+
+ if(io->read_proc(&delta_x, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+ if(io->read_proc(&delta_y, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ // apply them
+
+ bits += delta_x;
+ scanline += delta_y;
+
+ break;
+ }
+
+ default :
+ {
+ if(scanline >= abs(height)) {
+ return TRUE;
+ }
+
+ int count = MIN((int)status_byte, width - bits);
+
+ BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+ if(io->read_proc((void *)(sline + bits), sizeof(BYTE) * count, 1, handle) != 1) {
+ return FALSE;
+ }
+
+ // align run length to even number of bytes
+
+ if ((status_byte & 1) == 1) {
+ if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+ }
+
+ bits += status_byte;
+
+ break;
+ }
+ }
+
+ break;
+
+ default :
+ {
+ if(scanline >= abs(height)) {
+ return TRUE;
+ }
+
+ int count = MIN((int)status_byte, width - bits);
+
+ BYTE *sline = FreeImage_GetScanLine(dib, scanline);
+
+ if(io->read_proc(&second_byte, sizeof(BYTE), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ for (int i = 0; i < count; i++) {
+ *(sline + bits) = second_byte;
+
+ bits++;
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *
+LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
+ FIBITMAP *dib = NULL;
+
+ try {
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // load the info header
+
+ BITMAPINFOHEADER bih;
+
+ io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bih);
+#endif
+
+ // keep some general information about the bitmap
+
+ unsigned used_colors = bih.biClrUsed;
+ int width = bih.biWidth;
+ int height = bih.biHeight; // WARNING: height can be < 0 => check each call using 'height' as a parameter
+ unsigned bit_count = bih.biBitCount;
+ unsigned compression = bih.biCompression;
+ unsigned pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1 :
+ case 4 :
+ case 8 :
+ {
+ if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load the palette
+
+ io->read_proc(FreeImage_GetPalette(dib), used_colors * sizeof(RGBQUAD), 1, handle);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ for(int i = 0; i < used_colors; i++) {
+ INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
+ }
+#endif
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // seek to the actual pixel data.
+ // this is needed because sometimes the palette is larger than the entries it contains predicts
+
+ if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * sizeof(RGBQUAD))))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ switch (compression) {
+ case BI_RGB :
+ if ( LoadPixelData(io, handle, dib, height, pitch, bit_count)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding BMP data";
+ }
+ break;
+
+ case BI_RLE4 :
+ if ( LoadPixelDataRLE4(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE4 BMP data";
+ }
+ break;
+
+ case BI_RLE8 :
+ if ( LoadPixelDataRLE8(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE8 BMP data";
+ }
+ break;
+
+ default :
+ throw FI_MSG_ERROR_UNSUPPORTED_COMPRESSION;
+ }
+ }
+ break; // 1-, 4-, 8-bit
+
+ case 16 :
+ {
+ if (bih.biCompression == BI_BITFIELDS) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // load pixel data and swap as needed if OS is Big Endian
+
+ if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) {
+ // seek to the actual pixel data
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+ }
+
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+ break; // 16-bit
+
+ case 24 :
+ case 32 :
+ {
+ if (bih.biCompression == BI_BITFIELDS) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
+ } else {
+ if ( bit_count == 32 ) {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ }
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ if (FreeImage_GetColorsUsed(dib) > 0) {
+ io->seek_proc(handle, FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD), SEEK_CUR);
+ } else if ((bih.biCompression != BI_BITFIELDS) && (bitmap_bits_offset > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) {
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+ }
+
+ // read in the bitmap bits
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
+
+ return dib;
+ }
+ break; // 24-, 32-bit
+ }
+ } catch(const char *message) {
+ if(dib) {
+ FreeImage_Unload(dib);
+ }
+ if(message) {
+ FreeImage_OutputMessageProc(s_format_id, message);
+ }
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *
+LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
+ FIBITMAP *dib = NULL;
+
+ try {
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // load the info header
+
+ BITMAPINFOHEADER bih;
+
+ io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bih);
+#endif
+
+ // keep some general information about the bitmap
+
+ unsigned used_colors = bih.biClrUsed;
+ int width = bih.biWidth;
+ int height = bih.biHeight; // WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
+ unsigned bit_count = bih.biBitCount;
+ unsigned compression = bih.biCompression;
+ unsigned pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1 :
+ case 4 :
+ case 8 :
+ {
+ if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count)))
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ // load the palette
+ // note that it may contain RGB or RGBA values : we will calculate this
+ unsigned pal_size = (bitmap_bits_offset - sizeof(BITMAPFILEHEADER) - bih.biSize) / used_colors;
+
+ io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET);
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ if(pal_size == 4) {
+ for (unsigned count = 0; count < used_colors; count++) {
+ FILE_BGRA bgra;
+
+ io->read_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
+
+ pal[count].rgbRed = bgra.r;
+ pal[count].rgbGreen = bgra.g;
+ pal[count].rgbBlue = bgra.b;
+ }
+ } else if(pal_size == 3) {
+ for (unsigned count = 0; count < used_colors; count++) {
+ FILE_BGR bgr;
+
+ io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+
+ pal[count].rgbRed = bgr.r;
+ pal[count].rgbGreen = bgr.g;
+ pal[count].rgbBlue = bgr.b;
+ }
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // seek to the actual pixel data.
+ // this is needed because sometimes the palette is larger than the entries it contains predicts
+
+ if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ switch (compression) {
+ case BI_RGB :
+ // load pixel data
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+ return dib;
+
+ case BI_RLE4 :
+ if ( LoadPixelDataRLE4(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE4 BMP data";
+ }
+ break;
+
+ case BI_RLE8 :
+ if ( LoadPixelDataRLE8(io, handle, width, height, dib)) {
+ return dib;
+ } else {
+ throw "Error encountered while decoding RLE8 BMP data";
+ }
+ break;
+
+ default :
+ throw FI_MSG_ERROR_UNSUPPORTED_COMPRESSION;
+ }
+ }
+
+ case 16 :
+ {
+ if (bih.biCompression == 3) {
+ DWORD bitfields[3];
+
+ io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle);
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]);
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) {
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+ }
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 24 :
+ case 32 :
+ {
+ if ( bit_count == 32 ) {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information
+ FreeImage_SetDotsPerMeterX(dib, bih.biXPelsPerMeter);
+ FreeImage_SetDotsPerMeterY(dib, bih.biYPelsPerMeter);
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3)))
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read in the bitmap bits
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
+
+ return dib;
+ }
+ }
+ } catch(const char *message) {
+ if(dib)
+ FreeImage_Unload(dib);
+
+ FreeImage_OutputMessageProc(s_format_id, message);
+ }
+
+ return NULL;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP *
+LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) {
+ FIBITMAP *dib = NULL;
+
+ try {
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ BITMAPINFOOS2_1X_HEADER bios2_1x;
+
+ io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapOS21XHeader(&bios2_1x);
+#endif
+ // keep some general information about the bitmap
+
+ unsigned used_colors = 0;
+ unsigned width = bios2_1x.biWidth;
+ unsigned height = bios2_1x.biHeight; // WARNING: height can be < 0 => check each read_proc using 'height' as a parameter
+ unsigned bit_count = bios2_1x.biBitCount;
+ unsigned pitch = CalculatePitch(CalculateLine(width, bit_count));
+
+ switch (bit_count) {
+ case 1 :
+ case 4 :
+ case 8 :
+ {
+ used_colors = CalculateUsedPaletteEntries(bit_count);
+
+ // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ // load the palette
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ for (unsigned count = 0; count < used_colors; count++) {
+ FILE_BGR bgr;
+
+ io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle);
+
+ pal[count].rgbRed = bgr.r;
+ pal[count].rgbGreen = bgr.g;
+ pal[count].rgbBlue = bgr.b;
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ io->seek_proc(handle, bitmap_bits_offset, SEEK_SET);
+
+ // read the pixel data
+
+ // load pixel data
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 16 :
+ {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ return dib;
+ }
+
+ case 24 :
+ case 32 :
+ {
+ if ( bit_count == 32 ) {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // set resolution information to default values (72 dpi in english units)
+ FreeImage_SetDotsPerMeterX(dib, 2835);
+ FreeImage_SetDotsPerMeterY(dib, 2835);
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // Skip over the optional palette
+ // A 24 or 32 bit DIB may contain a palette for faster color reduction
+
+ // load pixel data and swap as needed if OS is Big Endian
+ LoadPixelData(io, handle, dib, height, pitch, bit_count);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA));
+
+ return dib;
+ }
+ }
+ } catch(const char *message) {
+ if(dib)
+ FreeImage_Unload(dib);
+
+ FreeImage_OutputMessageProc(s_format_id, message);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "BMP";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Windows or OS/2 Bitmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "bmp";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return "^BM";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/bmp";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE bmp_signature1[] = { 0x42, 0x4D };
+ BYTE bmp_signature2[] = { 0x42, 0x41 };
+ BYTE signature[2] = { 0, 0 };
+
+ io->read_proc(signature, 1, sizeof(bmp_signature1), handle);
+
+ if (memcmp(bmp_signature1, signature, sizeof(bmp_signature1)) == 0)
+ return TRUE;
+
+ if (memcmp(bmp_signature2, signature, sizeof(bmp_signature2)) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 1) ||
+ (depth == 4) ||
+ (depth == 8) ||
+ (depth == 16) ||
+ (depth == 24) ||
+ (depth == 32)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (handle != NULL) {
+ BITMAPFILEHEADER bitmapfileheader;
+ DWORD type = 0;
+
+ // we use this offset value to make seemingly absolute seeks relative in the file
+
+ long offset_in_file = io->tell_proc(handle);
+
+ // read the fileheader
+
+ io->read_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapFileHeader(&bitmapfileheader);
+#endif
+
+ // check the signature
+
+ if ((bitmapfileheader.bfType != 0x4D42) && (bitmapfileheader.bfType != 0x4142)) {
+ FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_MAGIC_NUMBER);
+ return NULL;
+ }
+
+ // read the first byte of the infoheader
+
+ io->read_proc(&type, sizeof(DWORD), 1, handle);
+ io->seek_proc(handle, 0 - (long)sizeof(DWORD), SEEK_CUR);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapLong(&type);
+#endif
+
+ // call the appropriate load function for the found bitmap type
+
+ switch(type) {
+ case 12:
+ // OS/2 and also all Windows versions since Windows 3.0
+ return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+ case 64:
+ // OS/2
+ return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+ case 40:
+ // BITMAPINFOHEADER - all Windows versions since Windows 3.0
+ return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits);
+ case 52:
+ // BITMAPV2INFOHEADER (undocumented)
+ break;
+ case 56:
+ // BITMAPV3INFOHEADER (undocumented)
+ break;
+ case 108:
+ // BITMAPV4HEADER - all Windows versions since Windows 95/NT4 (not supported)
+ break;
+ case 124:
+ // BITMAPV5HEADER - Windows 98/2000 and newer (not supported)
+ break;
+ default:
+ break;
+ }
+
+ FreeImage_OutputMessageProc(s_format_id, "unknown bmp subtype with id %d", type);
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------
+
+/**
+Encode a 8-bit source buffer into a 8-bit target buffer using a RLE compression algorithm.
+The size of the target buffer must be equal to the size of the source buffer.
+On return, the function will return the real size of the target buffer, which should be less that or equal to the source buffer size.
+@param target 8-bit Target buffer
+@param source 8-bit Source buffer
+@param size Source/Target input buffer size
+@return Returns the target buffer size
+*/
+static int
+RLEEncodeLine(BYTE *target, BYTE *source, int size) {
+ BYTE buffer[256];
+ int buffer_size = 0;
+ int target_pos = 0;
+
+ for (int i = 0; i < size; ++i) {
+ if ((i < size - 1) && (source[i] == source[i + 1])) {
+ // find a solid block of same bytes
+
+ int j = i + 1;
+ int jmax = 254 + i;
+
+ while ((j < size - 1) && (j < jmax) && (source[j] == source[j + 1]))
+ ++j;
+
+ // if the block is larger than 3 bytes, use it
+ // else put the data into the larger pool
+
+ if (((j - i) + 1) > 3) {
+ // don't forget to write what we already have in the buffer
+
+ switch(buffer_size) {
+ case 0 :
+ break;
+
+ case RLE_DELTA :
+ target[target_pos++] = 1;
+ target[target_pos++] = buffer[0];
+ target[target_pos++] = 1;
+ target[target_pos++] = buffer[1];
+ break;
+
+ case RLE_ENDOFBITMAP :
+ target[target_pos++] = (BYTE)buffer_size;
+ target[target_pos++] = buffer[0];
+ break;
+
+ default :
+ target[target_pos++] = RLE_COMMAND;
+ target[target_pos++] = (BYTE)buffer_size;
+ memcpy(target + target_pos, buffer, buffer_size);
+
+ // prepare for next run
+
+ target_pos += buffer_size;
+
+ if ((buffer_size & 1) == 1)
+ target_pos++;
+
+ break;
+ }
+
+ // write the continuous data
+
+ target[target_pos++] = (BYTE)((j - i) + 1);
+ target[target_pos++] = source[i];
+
+ buffer_size = 0;
+ } else {
+ for (int k = 0; k < (j - i) + 1; ++k) {
+ buffer[buffer_size++] = source[i + k];
+
+ if (buffer_size == 254) {
+ // write what we have
+
+ target[target_pos++] = RLE_COMMAND;
+ target[target_pos++] = (BYTE)buffer_size;
+ memcpy(target + target_pos, buffer, buffer_size);
+
+ // prepare for next run
+
+ target_pos += buffer_size;
+ buffer_size = 0;
+ }
+ }
+ }
+
+ i = j;
+ } else {
+ buffer[buffer_size++] = source[i];
+ }
+
+ // write the buffer if it's full
+
+ if (buffer_size == 254) {
+ target[target_pos++] = RLE_COMMAND;
+ target[target_pos++] = (BYTE)buffer_size;
+ memcpy(target + target_pos, buffer, buffer_size);
+
+ // prepare for next run
+
+ target_pos += buffer_size;
+ buffer_size = 0;
+ }
+ }
+
+ // write the last bytes
+
+ switch(buffer_size) {
+ case 0 :
+ break;
+
+ case RLE_DELTA :
+ target[target_pos++] = 1;
+ target[target_pos++] = buffer[0];
+ target[target_pos++] = 1;
+ target[target_pos++] = buffer[1];
+ break;
+
+ case RLE_ENDOFBITMAP :
+ target[target_pos++] = (BYTE)buffer_size;
+ target[target_pos++] = buffer[0];
+ break;
+
+ default :
+ target[target_pos++] = RLE_COMMAND;
+ target[target_pos++] = (BYTE)buffer_size;
+ memcpy(target + target_pos, buffer, buffer_size);
+
+ // prepare for next run
+
+ target_pos += buffer_size;
+
+ if ((buffer_size & 1) == 1)
+ target_pos++;
+
+ break;
+ }
+
+ // write the END_OF_LINE marker
+
+ target[target_pos++] = RLE_COMMAND;
+ target[target_pos++] = RLE_ENDOFLINE;
+
+ // return the written size
+
+ return target_pos;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if ((dib != NULL) && (handle != NULL)) {
+ // write the file header
+
+ BITMAPFILEHEADER bitmapfileheader;
+ bitmapfileheader.bfType = 0x4D42;
+ bitmapfileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD);
+ bitmapfileheader.bfSize = bitmapfileheader.bfOffBits + FreeImage_GetHeight(dib) * FreeImage_GetPitch(dib);
+ bitmapfileheader.bfReserved1 = 0;
+ bitmapfileheader.bfReserved2 = 0;
+
+ // take care of the bit fields data of any
+
+ bool bit_fields = (FreeImage_GetBPP(dib) == 16);
+
+ if (bit_fields) {
+ bitmapfileheader.bfSize += 3 * sizeof(DWORD);
+ bitmapfileheader.bfOffBits += 3 * sizeof(DWORD);
+ }
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapFileHeader(&bitmapfileheader);
+#endif
+ if (io->write_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle) != 1)
+ return FALSE;
+
+ // update the bitmap info header
+
+ BITMAPINFOHEADER bih;
+ memcpy(&bih, FreeImage_GetInfoHeader(dib), sizeof(BITMAPINFOHEADER));
+
+ if (bit_fields)
+ bih.biCompression = BI_BITFIELDS;
+ else if ((bih.biBitCount == 8) && (flags & BMP_SAVE_RLE))
+ bih.biCompression = BI_RLE8;
+ else
+ bih.biCompression = BI_RGB;
+
+ // write the bitmap info header
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bih);
+#endif
+ if (io->write_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle) != 1)
+ return FALSE;
+
+ // write the bit fields when we are dealing with a 16 bit BMP
+
+ if (bit_fields) {
+ DWORD d;
+
+ d = FreeImage_GetRedMask(dib);
+
+ if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
+ return FALSE;
+
+ d = FreeImage_GetGreenMask(dib);
+
+ if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
+ return FALSE;
+
+ d = FreeImage_GetBlueMask(dib);
+
+ if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1)
+ return FALSE;
+ }
+
+ // write the palette
+
+ if (FreeImage_GetPalette(dib) != NULL) {
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ FILE_BGRA bgra;
+ for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++ ) {
+ bgra.b = pal[i].rgbBlue;
+ bgra.g = pal[i].rgbGreen;
+ bgra.r = pal[i].rgbRed;
+ bgra.a = pal[i].rgbReserved;
+ if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+
+ // write the bitmap data... if RLE compression is enable, use it
+
+ unsigned bpp = FreeImage_GetBPP(dib);
+ if ((bpp == 8) && (flags & BMP_SAVE_RLE)) {
+ BYTE *buffer = (BYTE*)malloc(FreeImage_GetPitch(dib) * 2 * sizeof(BYTE));
+
+ for (DWORD i = 0; i < FreeImage_GetHeight(dib); ++i) {
+ int size = RLEEncodeLine(buffer, FreeImage_GetScanLine(dib, i), FreeImage_GetLine(dib));
+
+ if (io->write_proc(buffer, size, 1, handle) != 1) {
+ free(buffer);
+ return FALSE;
+ }
+ }
+
+ buffer[0] = RLE_COMMAND;
+ buffer[1] = RLE_ENDOFBITMAP;
+
+ if (io->write_proc(buffer, 2, 1, handle) != 1) {
+ free(buffer);
+ return FALSE;
+ }
+
+ free(buffer);
+#ifdef FREEIMAGE_BIGENDIAN
+ } else if (bpp == 16) {
+ int padding = FreeImage_GetPitch(dib) - FreeImage_GetWidth(dib) * sizeof(WORD);
+ WORD pad = 0;
+ WORD pixel;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ pixel = ((WORD *)line)[x];
+ SwapShort(&pixel);
+ if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1)
+ return FALSE;
+ }
+ if(padding != 0) {
+ if(io->write_proc(&pad, padding, 1, handle) != 1) {
+ return FALSE;
+ }
+ }
+ }
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ } else if (bpp == 24) {
+ int padding = FreeImage_GetPitch(dib) - FreeImage_GetWidth(dib) * sizeof(FILE_BGR);
+ DWORD pad = 0;
+ FILE_BGR bgr;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
+ bgr.b = triple->rgbtBlue;
+ bgr.g = triple->rgbtGreen;
+ bgr.r = triple->rgbtRed;
+ if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1)
+ return FALSE;
+ }
+ if(padding != 0) {
+ if(io->write_proc(&pad, padding, 1, handle) != 1) {
+ return FALSE;
+ }
+ }
+ }
+ } else if (bpp == 32) {
+ FILE_BGRA bgra;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ RGBQUAD *quad = ((RGBQUAD *)line)+x;
+ bgra.b = quad->rgbBlue;
+ bgra.g = quad->rgbGreen;
+ bgra.r = quad->rgbRed;
+ bgra.a = quad->rgbReserved;
+ if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+#endif
+ } else if (io->write_proc(FreeImage_GetBits(dib), FreeImage_GetHeight(dib) * FreeImage_GetPitch(dib), 1, handle) != 1) {
+ return FALSE;
+ }
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitBMP(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL; // not implemented yet;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginCUT.cpp b/plugins/AdvaImg/src/FreeImage/PluginCUT.cpp
new file mode 100644
index 0000000000..746fa034a6
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginCUT.cpp
@@ -0,0 +1,240 @@
+// ==========================================================
+// CUT Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagCUTHEADER {
+ WORD width;
+ WORD height;
+ LONG dummy;
+} CUTHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "CUT";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Dr. Halo";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "cut";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-cut";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ FIBITMAP *dib = NULL;
+
+ if (!handle) {
+ return NULL;
+ }
+
+ try {
+ CUTHEADER header;
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // read the cut header
+
+ if(io->read_proc(&header, 1, sizeof(CUTHEADER), handle) != sizeof(CUTHEADER)) {
+ throw FI_MSG_ERROR_PARSING;
+ }
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort((WORD *)&header.width);
+ SwapShort((WORD *)&header.height);
+#endif
+
+ if ((header.width == 0) || (header.height == 0)) {
+ return NULL;
+ }
+
+ // allocate a new bitmap
+
+ dib = FreeImage_AllocateHeader(header_only, header.width, header.height, 8);
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // stuff it with a palette
+
+ RGBQUAD *palette = FreeImage_GetPalette(dib);
+
+ for (int j = 0; j < 256; ++j) {
+ palette[j].rgbBlue = palette[j].rgbGreen = palette[j].rgbRed = (BYTE)j;
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // unpack the RLE bitmap bits
+
+ BYTE *bits = FreeImage_GetScanLine(dib, header.height - 1);
+
+ unsigned i = 0, k = 0;
+ unsigned pitch = FreeImage_GetPitch(dib);
+ unsigned size = header.width * header.height;
+ BYTE count = 0, run = 0;
+
+ while (i < size) {
+ if(io->read_proc(&count, 1, sizeof(BYTE), handle) != 1) {
+ throw FI_MSG_ERROR_PARSING;
+ }
+
+ if (count == 0) {
+ k = 0;
+ bits -= pitch;
+
+ // paint shop pro adds two useless bytes here...
+
+ io->read_proc(&count, 1, sizeof(BYTE), handle);
+ io->read_proc(&count, 1, sizeof(BYTE), handle);
+
+ continue;
+ }
+
+ if (count & 0x80) {
+ count &= ~(0x80);
+
+ if(io->read_proc(&run, 1, sizeof(BYTE), handle) != 1) {
+ throw FI_MSG_ERROR_PARSING;
+ }
+
+ if(k + count <= header.width) {
+ memset(bits + k, run, count);
+ } else {
+ throw FI_MSG_ERROR_PARSING;
+ }
+ } else {
+ if(k + count <= header.width) {
+ if(io->read_proc(&bits[k], count, sizeof(BYTE), handle) != 1) {
+ throw FI_MSG_ERROR_PARSING;
+ }
+ } else {
+ throw FI_MSG_ERROR_PARSING;
+ }
+ }
+
+ k += count;
+ i += count;
+ }
+
+ return dib;
+
+ } catch(const char* text) {
+ if(dib) {
+ FreeImage_Unload(dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return NULL;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitCUT(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginDDS.cpp b/plugins/AdvaImg/src/FreeImage/PluginDDS.cpp
new file mode 100644
index 0000000000..0a1667d7f1
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginDDS.cpp
@@ -0,0 +1,651 @@
+// ==========================================================
+// DDS Loader
+//
+// Design and implementation by
+// - Volker Gärtner (volkerg@gmx.at)
+// - Sherman Wilcox
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Definitions for the DDS format
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagDDPIXELFORMAT {
+ DWORD dwSize; // size of this structure (must be 32)
+ DWORD dwFlags; // see DDPF_*
+ DWORD dwFourCC;
+ DWORD dwRGBBitCount; // Total number of bits for RGB formats
+ DWORD dwRBitMask;
+ DWORD dwGBitMask;
+ DWORD dwBBitMask;
+ DWORD dwRGBAlphaBitMask;
+} DDPIXELFORMAT;
+
+// DIRECTDRAW PIXELFORMAT FLAGS
+enum {
+ DDPF_ALPHAPIXELS = 0x00000001l, // surface has alpha channel
+ DDPF_ALPHA = 0x00000002l, // alpha only
+ DDPF_FOURCC = 0x00000004l, // FOURCC available
+ DDPF_RGB = 0x00000040l // RGB(A) bitmap
+};
+
+typedef struct tagDDCAPS2 {
+ DWORD dwCaps1; // Zero or more of the DDSCAPS_* members
+ DWORD dwCaps2; // Zero or more of the DDSCAPS2_* members
+ DWORD dwReserved[2];
+} DDCAPS2;
+
+// DIRECTDRAWSURFACE CAPABILITY FLAGS
+enum {
+ DDSCAPS_ALPHA = 0x00000002l, // alpha only surface
+ DDSCAPS_COMPLEX = 0x00000008l, // complex surface structure
+ DDSCAPS_TEXTURE = 0x00001000l, // used as texture (should always be set)
+ DDSCAPS_MIPMAP = 0x00400000l // Mipmap present
+};
+
+enum {
+ DDSCAPS2_CUBEMAP = 0x00000200L,
+ DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400L,
+ DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800L,
+ DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000L,
+ DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000L,
+ DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000L,
+ DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x00008000L,
+ DDSCAPS2_VOLUME = 0x00200000L
+};
+
+typedef struct tagDDSURFACEDESC2 {
+ DWORD dwSize; // size of this structure (must be 124)
+ DWORD dwFlags; // combination of the DDSS_* flags
+ DWORD dwHeight;
+ DWORD dwWidth;
+ DWORD dwPitchOrLinearSize;
+ DWORD dwDepth; // Depth of a volume texture
+ DWORD dwMipMapCount;
+ DWORD dwReserved1[11];
+ DDPIXELFORMAT ddpfPixelFormat;
+ DDCAPS2 ddsCaps;
+ DWORD dwReserved2;
+} DDSURFACEDESC2;
+
+enum {
+ DDSD_CAPS = 0x00000001l,
+ DDSD_HEIGHT = 0x00000002l,
+ DDSD_WITH = 0x00000004l,
+ DDSD_PITCH = 0x00000008l,
+ DDSD_ALPHABITDEPTH = 0x00000080l,
+ DDSD_PIXELFORMAT = 0x00001000l,
+ DDSD_MIPMAPCOUNT = 0x00020000l,
+ DDSD_LINEARSIZE = 0x00080000l,
+ DDSD_DEPTH = 0x00800000l
+};
+
+typedef struct tagDDSHEADER {
+ DWORD dwMagic; // FOURCC: "DDS "
+ DDSURFACEDESC2 surfaceDesc;
+} DDSHEADER;
+
+#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
+ ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 ))
+
+#define FOURCC_DXT1 MAKEFOURCC('D','X','T','1')
+#define FOURCC_DXT2 MAKEFOURCC('D','X','T','2')
+#define FOURCC_DXT3 MAKEFOURCC('D','X','T','3')
+#define FOURCC_DXT4 MAKEFOURCC('D','X','T','4')
+#define FOURCC_DXT5 MAKEFOURCC('D','X','T','5')
+
+// ----------------------------------------------------------
+// Structures used by DXT textures
+// ----------------------------------------------------------
+
+typedef struct tagColor8888 {
+ BYTE b;
+ BYTE g;
+ BYTE r;
+ BYTE a;
+} Color8888;
+
+typedef struct tagColor565 {
+ WORD b : 5;
+ WORD g : 6;
+ WORD r : 5;
+} Color565;
+
+typedef struct tagDXTColBlock {
+ Color565 colors[2];
+ BYTE row[4];
+} DXTColBlock;
+
+typedef struct tagDXTAlphaBlockExplicit {
+ WORD row[4];
+} DXTAlphaBlockExplicit;
+
+typedef struct tagDXTAlphaBlock3BitLinear {
+ BYTE alpha[2];
+ BYTE data[6];
+} DXTAlphaBlock3BitLinear;
+
+typedef struct tagDXT1Block
+{
+ DXTColBlock color;
+} DXT1Block;
+
+typedef struct tagDXT3Block { // also used by dxt2
+ DXTAlphaBlockExplicit alpha;
+ DXTColBlock color;
+} DXT3Block;
+
+typedef struct tagDXT5Block { // also used by dxt4
+ DXTAlphaBlock3BitLinear alpha;
+ DXTColBlock color;
+} DXT5Block;
+
+#ifdef _WIN32
+# pragma pack(pop)
+#else
+# pragma pack()
+#endif
+
+// ----------------------------------------------------------
+// Internal functions
+// ----------------------------------------------------------
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(DDSHEADER *header) {
+ SwapLong(&header->dwMagic);
+ SwapLong(&header->surfaceDesc.dwSize);
+ SwapLong(&header->surfaceDesc.dwFlags);
+ SwapLong(&header->surfaceDesc.dwHeight);
+ SwapLong(&header->surfaceDesc.dwWidth);
+ SwapLong(&header->surfaceDesc.dwPitchOrLinearSize);
+ SwapLong(&header->surfaceDesc.dwDepth);
+ SwapLong(&header->surfaceDesc.dwMipMapCount);
+ for(int i=0; i<11; i++) {
+ SwapLong(&header->surfaceDesc.dwReserved1[i]);
+ }
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwSize);
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwFlags);
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwFourCC);
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRGBBitCount);
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRBitMask);
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwGBitMask);
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwBBitMask);
+ SwapLong(&header->surfaceDesc.ddpfPixelFormat.dwRGBAlphaBitMask);
+ SwapLong(&header->surfaceDesc.ddsCaps.dwCaps1);
+ SwapLong(&header->surfaceDesc.ddsCaps.dwCaps2);
+ SwapLong(&header->surfaceDesc.ddsCaps.dwReserved[0]);
+ SwapLong(&header->surfaceDesc.ddsCaps.dwReserved[1]);
+ SwapLong(&header->surfaceDesc.dwReserved2);
+}
+#endif
+
+// ==========================================================
+
+// Get the 4 possible colors for a block
+//
+static void
+GetBlockColors (const DXTColBlock &block, Color8888 colors[4], bool isDXT1) {
+ int i;
+ // expand from 565 to 888
+ for (i = 0; i < 2; i++) {
+ colors[i].a = 0xff;
+ /*
+ colors[i].r = (BYTE)(block.colors[i].r * 0xff / 0x1f);
+ colors[i].g = (BYTE)(block.colors[i].g * 0xff / 0x3f);
+ colors[i].b = (BYTE)(block.colors[i].b * 0xff / 0x1f);
+ */
+ colors[i].r = (BYTE)((block.colors[i].r << 3U) | (block.colors[i].r >> 2U));
+ colors[i].g = (BYTE)((block.colors[i].g << 2U) | (block.colors[i].g >> 4U));
+ colors[i].b = (BYTE)((block.colors[i].b << 3U) | (block.colors[i].b >> 2U));
+ }
+
+ WORD *wCol = (WORD *)block.colors;
+ if (wCol[0] > wCol[1] || !isDXT1) {
+ // 4 color block
+ for (i = 0; i < 2; i++) {
+ colors[i + 2].a = 0xff;
+ colors[i + 2].r = (BYTE)((WORD (colors[0].r) * (2 - i) + WORD (colors[1].r) * (1 + i)) / 3);
+ colors[i + 2].g = (BYTE)((WORD (colors[0].g) * (2 - i) + WORD (colors[1].g) * (1 + i)) / 3);
+ colors[i + 2].b = (BYTE)((WORD (colors[0].b) * (2 - i) + WORD (colors[1].b) * (1 + i)) / 3);
+ }
+ }
+ else {
+ // 3 color block, number 4 is transparent
+ colors[2].a = 0xff;
+ colors[2].r = (BYTE)((WORD (colors[0].r) + WORD (colors[1].r)) / 2);
+ colors[2].g = (BYTE)((WORD (colors[0].g) + WORD (colors[1].g)) / 2);
+ colors[2].b = (BYTE)((WORD (colors[0].b) + WORD (colors[1].b)) / 2);
+
+ colors[3].a = 0x00;
+ colors[3].g = 0x00;
+ colors[3].b = 0x00;
+ colors[3].r = 0x00;
+ }
+}
+
+struct DXT_INFO_1 {
+ typedef DXT1Block Block;
+ enum {
+ isDXT1 = 1,
+ bytesPerBlock = 8
+ };
+};
+
+struct DXT_INFO_3 {
+ typedef DXT3Block Block;
+ enum {
+ isDXT1 = 1,
+ bytesPerBlock = 16
+ };
+};
+
+struct DXT_INFO_5 {
+ typedef DXT5Block Block;
+ enum
+ {
+ isDXT1 = 1,
+ bytesPerBlock = 16
+ };
+};
+
+template <class INFO> class DXT_BLOCKDECODER_BASE {
+protected:
+ Color8888 m_colors[4];
+ const typename INFO::Block *m_pBlock;
+ unsigned m_colorRow;
+
+public:
+ void Setup (const BYTE *pBlock) {
+ m_pBlock = (const typename INFO::Block *)pBlock;
+ GetBlockColors (m_pBlock->color, m_colors, INFO::isDXT1);
+ }
+
+ void SetY (int y) {
+ m_colorRow = m_pBlock->color.row[y];
+ }
+
+ void GetColor (int x, int y, Color8888 &color) {
+ unsigned bits = (m_colorRow >> (x * 2)) & 3;
+ color = m_colors[bits];
+ }
+};
+
+class DXT_BLOCKDECODER_1 : public DXT_BLOCKDECODER_BASE <DXT_INFO_1> {
+public:
+ typedef DXT_INFO_1 INFO;
+};
+
+class DXT_BLOCKDECODER_3 : public DXT_BLOCKDECODER_BASE <DXT_INFO_3> {
+public:
+ typedef DXT_BLOCKDECODER_BASE <DXT_INFO_3> base;
+ typedef DXT_INFO_3 INFO;
+
+protected:
+ unsigned m_alphaRow;
+
+public:
+ void SetY (int y) {
+ base::SetY (y);
+ m_alphaRow = m_pBlock->alpha.row[y];
+ }
+
+ void GetColor (int x, int y, Color8888 &color) {
+ base::GetColor (x, y, color);
+ const unsigned bits = (m_alphaRow >> (x * 4)) & 0xF;
+ color.a = (BYTE)((bits * 0xFF) / 0xF);
+ }
+};
+
+class DXT_BLOCKDECODER_5 : public DXT_BLOCKDECODER_BASE <DXT_INFO_5> {
+public:
+ typedef DXT_BLOCKDECODER_BASE <DXT_INFO_5> base;
+ typedef DXT_INFO_5 INFO;
+
+protected:
+ unsigned m_alphas[8];
+ unsigned m_alphaBits;
+ int m_offset;
+
+public:
+ void Setup (const BYTE *pBlock) {
+ base::Setup (pBlock);
+
+ const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha;
+ m_alphas[0] = block.alpha[0];
+ m_alphas[1] = block.alpha[1];
+ if (m_alphas[0] > m_alphas[1]) {
+ // 8 alpha block
+ for (int i = 0; i < 6; i++) {
+ m_alphas[i + 2] = ((6 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 3) / 7;
+ }
+ }
+ else {
+ // 6 alpha block
+ for (int i = 0; i < 4; i++) {
+ m_alphas[i + 2] = ((4 - i) * m_alphas[0] + (1 + i) * m_alphas[1] + 2) / 5;
+ }
+ m_alphas[6] = 0;
+ m_alphas[7] = 0xFF;
+ }
+
+ }
+
+ void SetY (int y) {
+ base::SetY (y);
+ int i = y / 2;
+ const DXTAlphaBlock3BitLinear &block = m_pBlock->alpha;
+ m_alphaBits = unsigned(block.data[0 + i * 3]) | (unsigned(block.data[1 + i * 3]) << 8)
+ | (unsigned(block.data[2 + i * 3]) << 16);
+ m_offset = (y & 1) * 12;
+ }
+
+ void GetColor (int x, int y, Color8888 &color) {
+ base::GetColor (x, y, color);
+ unsigned bits = (m_alphaBits >> (x * 3 + m_offset)) & 7;
+ color.a = (BYTE)m_alphas[bits];
+ }
+};
+
+template <class DECODER> void DecodeDXTBlock (BYTE *dstData, const BYTE *srcBlock, long dstPitch, int bw, int bh) {
+ DECODER decoder;
+ decoder.Setup (srcBlock);
+ for (int y = 0; y < bh; y++) {
+ BYTE *dst = dstData - y * dstPitch;
+ decoder.SetY (y);
+ for (int x = 0; x < bw; x++) {
+ decoder.GetColor (x, y, (Color8888 &)*dst);
+ dst += 4;
+ }
+ }
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static FIBITMAP *
+LoadRGB (DDSURFACEDESC2 &desc, FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ int width = (int)desc.dwWidth & ~3;
+ int height = (int)desc.dwHeight & ~3;
+ int bpp = (int)desc.ddpfPixelFormat.dwRGBBitCount;
+
+ // allocate a new dib
+ FIBITMAP *dib = FreeImage_Allocate (width, height, bpp, desc.ddpfPixelFormat.dwRBitMask,
+ desc.ddpfPixelFormat.dwGBitMask, desc.ddpfPixelFormat.dwBBitMask);
+ if (dib == NULL)
+ return NULL;
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ // Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit)
+ int bytespp = FreeImage_GetLine(dib) / FreeImage_GetWidth(dib);
+#endif
+
+ // read the file
+ int line = CalculateLine(width, bpp);
+ int filePitch = (desc.dwFlags & DDSD_PITCH) ? (int)desc.dwPitchOrLinearSize : line;
+ long delta = (long)filePitch - (long)line;
+ for (int i = 0; i < height; i++) {
+ BYTE *pixels = FreeImage_GetScanLine(dib, height - i - 1);
+ io->read_proc (pixels, 1, line, handle);
+ io->seek_proc (handle, delta, SEEK_CUR);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ for(int x = 0; x < width; x++) {
+ INPLACESWAP(pixels[FI_RGBA_RED],pixels[FI_RGBA_BLUE]);
+ pixels += bytespp;
+ }
+#endif
+ }
+
+ // enable transparency
+ FreeImage_SetTransparent (dib, (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) ? TRUE : FALSE);
+
+ if (!(desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS) && bpp == 32) {
+ // no transparency: convert to 24-bit
+ FIBITMAP *old = dib;
+ dib = FreeImage_ConvertTo24Bits (old);
+ FreeImage_Unload (old);
+ }
+ return dib;
+}
+
+template <class DECODER> static void
+LoadDXT_Helper (FreeImageIO *io, fi_handle handle, int page, int flags, void *data, FIBITMAP *dib, int width, int height, int line) {
+ typedef typename DECODER::INFO INFO;
+ typedef typename INFO::Block Block;
+
+ Block *input_buffer = new(std::nothrow) Block[(width + 3) / 4];
+ if (!input_buffer) return;
+
+ int widthRest = (int) width & 3;
+ int heightRest = (int) height & 3;
+ int inputLine = (width + 3) / 4;
+ int y = 0;
+
+ if (height >= 4) {
+ for (; y < height; y += 4) {
+ io->read_proc (input_buffer, sizeof (typename INFO::Block), inputLine, handle);
+ // TODO: probably need some endian work here
+ BYTE *pbSrc = (BYTE *)input_buffer;
+ BYTE *pbDst = FreeImage_GetScanLine (dib, height - y - 1);
+
+ if (width >= 4) {
+ for (int x = 0; x < width; x += 4) {
+ DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, 4, 4);
+ pbSrc += INFO::bytesPerBlock;
+ pbDst += 4 * 4;
+ }
+ }
+ if (widthRest) {
+ DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, widthRest, 4);
+ }
+ }
+ }
+ if (heightRest) {
+ io->read_proc (input_buffer, sizeof (typename INFO::Block), inputLine, handle);
+ // TODO: probably need some endian work here
+ BYTE *pbSrc = (BYTE *)input_buffer;
+ BYTE *pbDst = FreeImage_GetScanLine (dib, height - y - 1);
+
+ if (width >= 4) {
+ for (int x = 0; x < width; x += 4) {
+ DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, 4, heightRest);
+ pbSrc += INFO::bytesPerBlock;
+ pbDst += 4 * 4;
+ }
+ }
+ if (widthRest) {
+ DecodeDXTBlock <DECODER> (pbDst, pbSrc, line, widthRest, heightRest);
+ }
+
+ }
+
+ delete [] input_buffer;
+}
+
+static FIBITMAP *
+LoadDXT (int type, DDSURFACEDESC2 &desc, FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ int width = (int)desc.dwWidth & ~3;
+ int height = (int)desc.dwHeight & ~3;
+
+ // allocate a 32-bit dib
+ FIBITMAP *dib = FreeImage_Allocate (width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if (dib == NULL)
+ return NULL;
+
+ int bpp = FreeImage_GetBPP (dib);
+ int line = CalculateLine (width, bpp);
+ BYTE *bits = FreeImage_GetBits (dib);
+
+ // select the right decoder
+ switch (type) {
+ case 1:
+ LoadDXT_Helper <DXT_BLOCKDECODER_1> (io, handle, page, flags, data, dib, width, height, line);
+ break;
+ case 3:
+ LoadDXT_Helper <DXT_BLOCKDECODER_3> (io, handle, page, flags, data, dib, width, height, line);
+ break;
+ case 5:
+ LoadDXT_Helper <DXT_BLOCKDECODER_5> (io, handle, page, flags, data, dib, width, height, line);
+ break;
+ }
+
+ return dib;
+}
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "DDS";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "DirectX Surface";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "dds";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-dds";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ DDSHEADER header;
+ memset(&header, 0, sizeof(header));
+ io->read_proc(&header, 1, sizeof(header), handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapHeader(&header);
+#endif
+ if (header.dwMagic != MAKEFOURCC ('D','D','S',' '))
+ return FALSE;
+ if (header.surfaceDesc.dwSize != sizeof (header.surfaceDesc) ||
+ header.surfaceDesc.ddpfPixelFormat.dwSize != sizeof (header.surfaceDesc.ddpfPixelFormat))
+ return FALSE;
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ return NULL;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ DDSHEADER header;
+ FIBITMAP *dib = NULL;
+
+ memset(&header, 0, sizeof(header));
+ io->read_proc(&header, 1, sizeof(header), handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapHeader(&header);
+#endif
+ if (header.surfaceDesc.ddpfPixelFormat.dwFlags & DDPF_RGB) {
+ dib = LoadRGB (header.surfaceDesc, io, handle, page, flags, data);
+ }
+ else if (header.surfaceDesc.ddpfPixelFormat.dwFlags & DDPF_FOURCC) {
+ switch (header.surfaceDesc.ddpfPixelFormat.dwFourCC) {
+ case FOURCC_DXT1:
+ dib = LoadDXT (1, header.surfaceDesc, io, handle, page, flags, data);
+ break;
+ case FOURCC_DXT3:
+ dib = LoadDXT (3, header.surfaceDesc, io, handle, page, flags, data);
+ break;
+ case FOURCC_DXT5:
+ dib = LoadDXT (5, header.surfaceDesc, io, handle, page, flags, data);
+ break;
+ }
+ }
+ return dib;
+}
+
+/*
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ return FALSE;
+}
+*/
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitDDS(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL; //Save; // not implemented (yet?)
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginEXR.cpp b/plugins/AdvaImg/src/FreeImage/PluginEXR.cpp
new file mode 100644
index 0000000000..b9963a11b2
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginEXR.cpp
@@ -0,0 +1,764 @@
+// ==========================================================
+// EXR Loader and writer
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../OpenEXR/IlmImf/ImfIO.h"
+#include "../OpenEXR/Iex/Iex.h"
+#include "../OpenEXR/IlmImf/ImfOutputFile.h"
+#include "../OpenEXR/IlmImf/ImfInputFile.h"
+#include "../OpenEXR/IlmImf/ImfRgbaFile.h"
+#include "../OpenEXR/IlmImf/ImfChannelList.h"
+#include "../OpenEXR/IlmImf/ImfRgba.h"
+#include "../OpenEXR/IlmImf/ImfArray.h"
+#include "../OpenEXR/IlmImf/ImfPreviewImage.h"
+#include "../OpenEXR/Half/half.h"
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+
+/**
+FreeImage input stream wrapper
+*/
+class C_IStream: public Imf::IStream {
+public:
+ C_IStream (FreeImageIO *io, fi_handle handle):
+ IStream(""), _io (io), _handle(handle) {}
+
+ virtual bool read (char c[/*n*/], int n);
+ virtual Imf::Int64 tellg ();
+ virtual void seekg (Imf::Int64 pos);
+ virtual void clear () {};
+
+private:
+ FreeImageIO *_io;
+ fi_handle _handle;
+};
+
+
+/**
+FreeImage output stream wrapper
+*/
+class C_OStream: public Imf::OStream {
+public:
+ C_OStream (FreeImageIO *io, fi_handle handle):
+ OStream(""), _io (io), _handle(handle) {}
+
+ virtual void write (const char c[/*n*/], int n);
+ virtual Imf::Int64 tellp ();
+ virtual void seekp (Imf::Int64 pos);
+
+private:
+ FreeImageIO *_io;
+ fi_handle _handle;
+};
+
+
+bool
+C_IStream::read (char c[/*n*/], int n) {
+ return ((unsigned)n != _io->read_proc(c, 1, n, _handle));
+}
+
+Imf::Int64
+C_IStream::tellg () {
+ return _io->tell_proc(_handle);
+}
+
+void
+C_IStream::seekg (Imf::Int64 pos) {
+ _io->seek_proc(_handle, (unsigned)pos, SEEK_SET);
+}
+
+void
+C_OStream::write (const char c[/*n*/], int n) {
+ if ((unsigned)n != _io->write_proc((void*)&c[0], 1, n, _handle)) {
+ Iex::throwErrnoExc();
+ }
+}
+
+Imf::Int64
+C_OStream::tellp () {
+ return _io->tell_proc(_handle);
+}
+
+void
+C_OStream::seekp (Imf::Int64 pos) {
+ _io->seek_proc(_handle, (unsigned)pos, SEEK_SET);
+}
+
+// ----------------------------------------------------------
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "EXR";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "ILM OpenEXR";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "exr";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-exr";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE exr_signature[] = { 0x76, 0x2F, 0x31, 0x01 };
+ BYTE signature[] = { 0, 0, 0, 0 };
+
+ io->read_proc(signature, 1, 4, handle);
+ return (memcmp(exr_signature, signature, 4) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (
+ (type == FIT_FLOAT) ||
+ (type == FIT_RGBF) ||
+ (type == FIT_RGBAF)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ bool bUseRgbaInterface = false;
+ FIBITMAP *dib = NULL;
+
+ if (!handle) {
+ return NULL;
+ }
+
+ try {
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // save the stream starting point
+ const long stream_start = io->tell_proc(handle);
+
+ // wrap the FreeImage IO stream
+ C_IStream istream(io, handle);
+
+ // open the file
+ Imf::InputFile file(istream);
+
+ // get file info
+ const Imath::Box2i &dataWindow = file.header().dataWindow();
+ int width = dataWindow.max.x - dataWindow.min.x + 1;
+ int height = dataWindow.max.y - dataWindow.min.y + 1;
+
+ //const Imf::Compression &compression = file.header().compression();
+
+ const Imf::ChannelList &channels = file.header().channels();
+
+ // check the number of components and check for a coherent format
+
+ std::string exr_color_model;
+ Imf::PixelType pixel_type = Imf::HALF;
+ FREE_IMAGE_TYPE image_type = FIT_UNKNOWN;
+ int components = 0;
+ bool bMixedComponents = false;
+
+ for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
+ components++;
+ if(components == 1) {
+ exr_color_model += i.name();
+ pixel_type = i.channel().type;
+ } else {
+ exr_color_model += "/";
+ exr_color_model += i.name();
+ if (i.channel().type != pixel_type) {
+ bMixedComponents = true;
+ }
+ }
+ }
+
+ if(bMixedComponents) {
+ bool bHandled = false;
+ // we may have a RGBZ or RGBAZ image ...
+ if(components > 4) {
+ if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B") && channels.findChannel("A")) {
+ std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model";
+ FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+ bHandled = true;
+ }
+ }
+ else if(components > 3) {
+ if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
+ std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model";
+ FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+ bHandled = true;
+ }
+ }
+ if (!bHandled) {
+ THROW (Iex::InputExc, "Unable to handle mixed component types (color model = " << exr_color_model << ")");
+ }
+ }
+
+ switch(pixel_type) {
+ case Imf::UINT:
+ THROW (Iex::InputExc, "Unsupported format: UINT");
+ break;
+ case Imf::HALF:
+ case Imf::FLOAT:
+ default:
+ break;
+ }
+
+ // check for supported image color models
+ // --------------------------------------------------------------
+
+ if ((components == 1) || (components == 2)) {
+ // if the image is gray-alpha (YA), ignore the alpha channel
+ if ((components == 1) && channels.findChannel("Y")) {
+ image_type = FIT_FLOAT;
+ components = 1;
+ } else {
+ std::string msg = "Warning: loading color model " + exr_color_model + " as Y color model";
+ FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+ image_type = FIT_FLOAT;
+ // ignore the other channel
+ components = 1;
+ }
+ } else if(components == 3) {
+ if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
+ image_type = FIT_RGBF;
+ }
+ else if(channels.findChannel("BY") && channels.findChannel("RY") && channels.findChannel("Y")) {
+ image_type = FIT_RGBF;
+ bUseRgbaInterface = true;
+ }
+ } else if(components >= 4) {
+ if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) {
+ if(channels.findChannel("A")) {
+ if(components > 4) {
+ std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model";
+ FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+ }
+ image_type = FIT_RGBAF;
+ // ignore other layers if there is more than one alpha layer
+ components = 4;
+ } else {
+ std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model";
+ FreeImage_OutputMessageProc(s_format_id, msg.c_str());
+
+ image_type = FIT_RGBF;
+ // ignore other channels
+ components = 3;
+ }
+ }
+ }
+
+ if(image_type == FIT_UNKNOWN) {
+ THROW (Iex::InputExc, "Unsupported color model: " << exr_color_model);
+ }
+
+ // allocate a new dib
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, 0);
+ if (!dib) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY);
+
+ // try to load the preview image
+ // --------------------------------------------------------------
+
+ if(file.header().hasPreviewImage()) {
+ const Imf::PreviewImage& preview = file.header().previewImage();
+ const unsigned thWidth = preview.width();
+ const unsigned thHeight = preview.height();
+
+ FIBITMAP* thumbnail = FreeImage_Allocate(thWidth, thHeight, 32);
+ if(thumbnail) {
+ const Imf::PreviewRgba *src_line = preview.pixels();
+ BYTE *dst_line = FreeImage_GetScanLine(thumbnail, thHeight - 1);
+ const unsigned dstPitch = FreeImage_GetPitch(thumbnail);
+
+ for (unsigned y = 0; y < thHeight; ++y) {
+ const Imf::PreviewRgba *src_pixel = src_line;
+ RGBQUAD* dst_pixel = (RGBQUAD*)dst_line;
+
+ for(unsigned x = 0; x < thWidth; ++x) {
+ dst_pixel->rgbRed = src_pixel->r;
+ dst_pixel->rgbGreen = src_pixel->g;
+ dst_pixel->rgbBlue = src_pixel->b;
+ dst_pixel->rgbReserved = src_pixel->a;
+ src_pixel++;
+ dst_pixel++;
+ }
+ src_line += thWidth;
+ dst_line -= dstPitch;
+ }
+ FreeImage_SetThumbnail(dib, thumbnail);
+ FreeImage_Unload(thumbnail);
+ }
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // load pixels
+ // --------------------------------------------------------------
+
+ const BYTE *bits = FreeImage_GetBits(dib); // pointer to our pixel buffer
+ const size_t bytespp = sizeof(float) * components; // size of our pixel in bytes
+ const unsigned pitch = FreeImage_GetPitch(dib); // size of our yStride in bytes
+
+ Imf::PixelType pixelType = Imf::FLOAT; // load as float data type;
+
+ if(bUseRgbaInterface) {
+ // use the RGBA interface (used when loading RY BY Y images )
+
+ const int chunk_size = 16;
+
+ BYTE *scanline = (BYTE*)bits;
+
+ // re-open using the RGBA interface
+ io->seek_proc(handle, stream_start, SEEK_SET);
+ Imf::RgbaInputFile rgbaFile(istream);
+
+ // read the file in chunks
+ Imath::Box2i dw = dataWindow;
+ Imf::Array2D<Imf::Rgba> chunk(chunk_size, width);
+ while (dw.min.y <= dw.max.y) {
+ // read a chunk
+ rgbaFile.setFrameBuffer (&chunk[0][0] - dw.min.x - dw.min.y * width, 1, width);
+ rgbaFile.readPixels (dw.min.y, MIN(dw.min.y + chunk_size - 1, dw.max.y));
+ // fill the dib
+ const int y_max = ((dw.max.y - dw.min.y) <= chunk_size) ? (dw.max.y - dw.min.y) : chunk_size;
+ for(int y = 0; y < y_max; y++) {
+ FIRGBF *pixel = (FIRGBF*)scanline;
+ const Imf::Rgba *half_rgba = chunk[y];
+ for(int x = 0; x < width; x++) {
+ // convert from half to float
+ pixel[x].red = half_rgba[x].r;
+ pixel[x].green = half_rgba[x].g;
+ pixel[x].blue = half_rgba[x].b;
+ }
+ // next line
+ scanline += pitch;
+ }
+ // next chunk
+ dw.min.y += chunk_size;
+ }
+
+ } else {
+ // use the low level interface
+
+ // build a frame buffer (i.e. what we want on output)
+ Imf::FrameBuffer frameBuffer;
+
+ // allow dataWindow with minimal bounds different form zero
+ size_t offset = - dataWindow.min.x * bytespp - dataWindow.min.y * pitch;
+
+ if(components == 1) {
+ frameBuffer.insert ("Y", // name
+ Imf::Slice (pixelType, // type
+ (char*)(bits + offset), // base
+ bytespp, // xStride
+ pitch, // yStride
+ 1, 1, // x/y sampling
+ 0.0)); // fillValue
+ } else if ((components == 3) || (components == 4)) {
+ const char *channel_name[4] = { "R", "G", "B", "A" };
+
+ for(int c = 0; c < components; c++) {
+ frameBuffer.insert (
+ channel_name[c], // name
+ Imf::Slice (pixelType, // type
+ (char*)(bits + c * sizeof(float) + offset), // base
+ bytespp, // xStride
+ pitch, // yStride
+ 1, 1, // x/y sampling
+ 0.0)); // fillValue
+ }
+ }
+
+ // read the file
+ file.setFrameBuffer(frameBuffer);
+ file.readPixels(dataWindow.min.y, dataWindow.max.y);
+ }
+
+ // lastly, flip dib lines
+ FreeImage_FlipVertical(dib);
+
+ }
+ catch(Iex::BaseExc & e) {
+ if(dib != NULL) {
+ FreeImage_Unload(dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, e.what());
+ return NULL;
+ }
+
+ return dib;
+}
+
+/**
+Set the preview image using the dib embedded thumbnail
+*/
+static BOOL
+SetPreviewImage(FIBITMAP *dib, Imf::Header& header) {
+ if (!FreeImage_GetThumbnail(dib)) {
+ return FALSE;
+ }
+ FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+
+ if ((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 32)) {
+ // invalid thumbnail - ignore it
+ FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL);
+ } else {
+ const unsigned thWidth = FreeImage_GetWidth(thumbnail);
+ const unsigned thHeight = FreeImage_GetHeight(thumbnail);
+
+ Imf::PreviewImage preview(thWidth, thHeight);
+
+ // copy thumbnail to 32-bit RGBA preview image
+
+ const BYTE* src_line = FreeImage_GetScanLine(thumbnail, thHeight - 1);
+ Imf::PreviewRgba* dst_line = preview.pixels();
+ const unsigned srcPitch = FreeImage_GetPitch(thumbnail);
+
+ for (unsigned y = 0; y < thHeight; y++) {
+ const RGBQUAD* src_pixel = (RGBQUAD*)src_line;
+ Imf::PreviewRgba* dst_pixel = dst_line;
+
+ for(unsigned x = 0; x < thWidth; x++) {
+ dst_pixel->r = src_pixel->rgbRed;
+ dst_pixel->g = src_pixel->rgbGreen;
+ dst_pixel->b = src_pixel->rgbBlue;
+ dst_pixel->a = src_pixel->rgbReserved;
+
+ src_pixel++;
+ dst_pixel++;
+ }
+
+ src_line -= srcPitch;
+ dst_line += thWidth;
+ }
+
+ header.setPreviewImage(preview);
+ }
+
+ return TRUE;
+}
+
+/**
+Save using EXR_LC compression (works only with RGB[A]F images)
+*/
+static BOOL
+SaveAsEXR_LC(C_OStream& ostream, FIBITMAP *dib, Imf::Header& header, int width, int height) {
+ int x, y;
+ Imf::RgbaChannels rgbaChannels;
+
+ try {
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ // convert from float to half
+ Imf::Array2D<Imf::Rgba> pixels(height, width);
+ switch(image_type) {
+ case FIT_RGBF:
+ rgbaChannels = Imf::WRITE_YC;
+ for(y = 0; y < height; y++) {
+ FIRGBF *src_bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+ for(x = 0; x < width; x++) {
+ Imf::Rgba &dst_bits = pixels[y][x];
+ dst_bits.r = src_bits[x].red;
+ dst_bits.g = src_bits[x].green;
+ dst_bits.b = src_bits[x].blue;
+ }
+ }
+ break;
+ case FIT_RGBAF:
+ rgbaChannels = Imf::WRITE_YCA;
+ for(y = 0; y < height; y++) {
+ FIRGBAF *src_bits = (FIRGBAF*)FreeImage_GetScanLine(dib, height - 1 - y);
+ for(x = 0; x < width; x++) {
+ Imf::Rgba &dst_bits = pixels[y][x];
+ dst_bits.r = src_bits[x].red;
+ dst_bits.g = src_bits[x].green;
+ dst_bits.b = src_bits[x].blue;
+ dst_bits.a = src_bits[x].alpha;
+ }
+ }
+ break;
+ default:
+ THROW (Iex::IoExc, "Bad image type");
+ break;
+ }
+
+ // write the data
+ Imf::RgbaOutputFile file(ostream, header, rgbaChannels);
+ file.setFrameBuffer (&pixels[0][0], 1, width);
+ file.writePixels (height);
+
+ return TRUE;
+
+ } catch(Iex::BaseExc & e) {
+ FreeImage_OutputMessageProc(s_format_id, e.what());
+
+ return FALSE;
+ }
+
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ const char *channel_name[4] = { "R", "G", "B", "A" };
+ BOOL bIsFlipped = FALSE;
+ half *halfData = NULL;
+
+ if (!dib || !handle) return FALSE;
+
+ try {
+ // check for EXR_LC compression and verify that the format is RGB
+ if ((flags & EXR_LC) == EXR_LC) {
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ if (((image_type != FIT_RGBF) && (image_type != FIT_RGBAF)) || ((flags & EXR_FLOAT) == EXR_FLOAT)) {
+ THROW (Iex::IoExc, "EXR_LC compression is only available with RGB[A]F images");
+ }
+ if ((FreeImage_GetWidth(dib) % 2) || (FreeImage_GetHeight(dib) % 2)) {
+ THROW (Iex::IoExc, "EXR_LC compression only works when the width and height are a multiple of 2");
+ }
+ }
+
+ // wrap the FreeImage IO stream
+ C_OStream ostream(io, handle);
+
+ // compression
+ Imf::Compression compress;
+ if ((flags & EXR_NONE) == EXR_NONE) {
+ // no compression
+ compress = Imf::NO_COMPRESSION;
+ } else if ((flags & EXR_ZIP) == EXR_ZIP) {
+ // zlib compression, in blocks of 16 scan lines
+ compress = Imf::ZIP_COMPRESSION;
+ } else if ((flags & EXR_PIZ) == EXR_PIZ) {
+ // piz-based wavelet compression
+ compress = Imf::PIZ_COMPRESSION;
+ } else if ((flags & EXR_PXR24) == EXR_PXR24) {
+ // lossy 24-bit float compression
+ compress = Imf::PXR24_COMPRESSION;
+ } else if ((flags & EXR_B44) == EXR_B44) {
+ // lossy 44% float compression
+ compress = Imf::B44_COMPRESSION;
+ } else {
+ // default value
+ compress = Imf::PIZ_COMPRESSION;
+ }
+
+ // create the header
+ int width = FreeImage_GetWidth(dib);
+ int height = FreeImage_GetHeight(dib);
+ int dx = 0, dy = 0;
+
+ Imath::Box2i dataWindow (Imath::V2i (0, 0), Imath::V2i (width - 1, height - 1));
+ Imath::Box2i displayWindow (Imath::V2i (-dx, -dy), Imath::V2i (width - dx - 1, height - dy - 1));
+
+ Imf::Header header = Imf::Header(displayWindow, dataWindow, 1,
+ Imath::V2f(0,0), 1,
+ Imf::INCREASING_Y, compress);
+
+ // handle thumbnail
+ SetPreviewImage(dib, header);
+
+ // check for EXR_LC compression
+ if ((flags & EXR_LC) == EXR_LC) {
+ return SaveAsEXR_LC(ostream, dib, header, width, height);
+ }
+
+ // output pixel type
+ Imf::PixelType pixelType;
+ if ((flags & EXR_FLOAT) == EXR_FLOAT) {
+ pixelType = Imf::FLOAT; // save as float data type
+ } else {
+ // default value
+ pixelType = Imf::HALF; // save as half data type
+ }
+
+ // check the data type and number of channels
+ int components = 0;
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ switch(image_type) {
+ case FIT_FLOAT:
+ components = 1;
+ // insert luminance channel
+ header.channels().insert ("Y", Imf::Channel(pixelType));
+ break;
+ case FIT_RGBF:
+ components = 3;
+ for(int c = 0; c < components; c++) {
+ // insert R, G and B channels
+ header.channels().insert (channel_name[c], Imf::Channel(pixelType));
+ }
+ break;
+ case FIT_RGBAF:
+ components = 4;
+ for(int c = 0; c < components; c++) {
+ // insert R, G, B and A channels
+ header.channels().insert (channel_name[c], Imf::Channel(pixelType));
+ }
+ break;
+ default:
+ THROW (Iex::ArgExc, "Cannot save: invalid data type.\nConvert the image to float before saving as OpenEXR.");
+ }
+
+ // build a frame buffer (i.e. what we have on input)
+ Imf::FrameBuffer frameBuffer;
+
+ BYTE *bits = NULL; // pointer to our pixel buffer
+ size_t bytespp = 0; // size of our pixel in bytes
+ size_t bytespc = 0; // size of our pixel component in bytes
+ unsigned pitch = 0; // size of our yStride in bytes
+
+
+ if(pixelType == Imf::HALF) {
+ // convert from float to half
+ halfData = new(std::nothrow) half[width * height * components];
+ if (!halfData) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY);
+
+ for(int y = 0; y < height; y++) {
+ float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y);
+ half *dst_bits = halfData + y * width * components;
+ for(int x = 0; x < width; x++) {
+ for(int c = 0; c < components; c++) {
+ dst_bits[c] = src_bits[c];
+ }
+ src_bits += components;
+ dst_bits += components;
+ }
+ }
+ bits = (BYTE*)halfData;
+ bytespc = sizeof(half);
+ bytespp = sizeof(half) * components;
+ pitch = sizeof(half) * width * components;
+ } else if(pixelType == Imf::FLOAT) {
+ // invert dib scanlines
+ bIsFlipped = FreeImage_FlipVertical(dib);
+
+ bits = FreeImage_GetBits(dib);
+ bytespc = sizeof(float);
+ bytespp = sizeof(float) * components;
+ pitch = FreeImage_GetPitch(dib);
+ }
+
+ if(image_type == FIT_FLOAT) {
+ frameBuffer.insert ("Y", // name
+ Imf::Slice (pixelType, // type
+ (char*)(bits), // base
+ bytespp, // xStride
+ pitch)); // yStride
+ } else if ((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) {
+ for(int c = 0; c < components; c++) {
+ char *channel_base = (char*)(bits) + c*bytespc;
+ frameBuffer.insert (channel_name[c],// name
+ Imf::Slice (pixelType, // type
+ channel_base, // base
+ bytespp, // xStride
+ pitch)); // yStride
+ }
+ }
+
+ // write the data
+ Imf::OutputFile file (ostream, header);
+ file.setFrameBuffer (frameBuffer);
+ file.writePixels (height);
+
+ if(halfData != NULL) delete[] halfData;
+ if(bIsFlipped) {
+ // invert dib scanlines
+ FreeImage_FlipVertical(dib);
+ }
+
+ return TRUE;
+
+ } catch(Iex::BaseExc & e) {
+ if(halfData != NULL) delete[] halfData;
+ if(bIsFlipped) {
+ // invert dib scanlines
+ FreeImage_FlipVertical(dib);
+ }
+
+ FreeImage_OutputMessageProc(s_format_id, e.what());
+
+ return FALSE;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitEXR(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginG3.cpp b/plugins/AdvaImg/src/FreeImage/PluginG3.cpp
new file mode 100644
index 0000000000..c383a455b7
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginG3.cpp
@@ -0,0 +1,433 @@
+// ==========================================================
+// G3 Fax Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Petr Pytelka (pyta@lightcomp.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "../LibTIFF4/tiffiop.h"
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Constant/Macro declarations
+// ==========================================================
+
+#define G3_DEFAULT_WIDTH 1728
+
+#define TIFFhowmany8(x) (((x)&0x07)?((uint32)(x)>>3)+1:(uint32)(x)>>3)
+
+// ==========================================================
+// libtiff interface
+// ==========================================================
+
+static tmsize_t
+_g3ReadProc(thandle_t handle, void *buf, tmsize_t size) {
+ // returns an error when reading the TIFF header
+ return 0;
+}
+
+static tmsize_t
+_g3WriteProc(thandle_t handle, void *buf, tmsize_t size) {
+ // returns ok when writing the TIFF header
+ return size;
+}
+
+static toff_t
+_g3SeekProc(thandle_t handle, toff_t off, int whence) {
+ return 0;
+}
+
+static int
+_g3CloseProc(thandle_t handle) {
+ return 0;
+}
+
+static toff_t
+_g3SizeProc(thandle_t handle) {
+ return 0;
+}
+
+static int
+_g3MapProc(thandle_t, void** base, toff_t* size) {
+ return 0;
+}
+
+static void
+_g3UnmapProc(thandle_t, void* base, toff_t size) {
+}
+
+// --------------------------------------------------------------
+
+static tmsize_t
+G3GetFileSize(FreeImageIO *io, fi_handle handle) {
+ long currentPos = io->tell_proc(handle);
+ io->seek_proc(handle, 0, SEEK_END);
+ long fileSize = io->tell_proc(handle);
+ io->seek_proc(handle, currentPos, SEEK_SET);
+ return fileSize;
+}
+
+static BOOL
+G3ReadFile(FreeImageIO *io, fi_handle handle, uint8 *tif_rawdata, tmsize_t tif_rawdatasize) {
+ return ((tmsize_t)(io->read_proc(tif_rawdata, tif_rawdatasize, 1, handle) * tif_rawdatasize) == tif_rawdatasize);
+}
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static int
+copyFaxFile(FreeImageIO *io, fi_handle handle, TIFF* tifin, uint32 xsize, int stretch, FIMEMORY *memory) {
+ BYTE *rowbuf = NULL;
+ BYTE *refbuf = NULL;
+ uint32 row;
+ uint16 badrun;
+ uint16 badfaxrun;
+ uint32 badfaxlines;
+ int ok;
+
+ try {
+
+ uint32 linesize = TIFFhowmany8(xsize);
+ rowbuf = (BYTE*) _TIFFmalloc(linesize);
+ refbuf = (BYTE*) _TIFFmalloc(linesize);
+ if (rowbuf == NULL || refbuf == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ tifin->tif_rawdatasize = G3GetFileSize(io, handle);
+ tifin->tif_rawdata = (tidata_t) _TIFFmalloc(tifin->tif_rawdatasize);
+ if (tifin->tif_rawdata == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ if (!G3ReadFile(io, handle, tifin->tif_rawdata, tifin->tif_rawdatasize)) {
+ throw "Read error at scanline 0";
+ }
+ tifin->tif_rawcp = tifin->tif_rawdata;
+ tifin->tif_rawcc = tifin->tif_rawdatasize;
+
+ (*tifin->tif_setupdecode)(tifin);
+ (*tifin->tif_predecode)(tifin, (uint16) 0);
+ tifin->tif_row = 0;
+ badfaxlines = 0;
+ badfaxrun = 0;
+
+ _TIFFmemset(refbuf, 0, linesize);
+ row = 0;
+ badrun = 0; // current run of bad lines
+ while (tifin->tif_rawcc > 0) {
+ ok = (*tifin->tif_decoderow)(tifin, rowbuf, linesize, 0);
+ if (!ok) {
+ badfaxlines++;
+ badrun++;
+ // regenerate line from previous good line
+ _TIFFmemcpy(rowbuf, refbuf, linesize);
+ } else {
+ if (badrun > badfaxrun)
+ badfaxrun = badrun;
+ badrun = 0;
+ _TIFFmemcpy(refbuf, rowbuf, linesize);
+ }
+ tifin->tif_row++;
+
+ FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
+ row++;
+ if (stretch) {
+ FreeImage_WriteMemory(rowbuf, linesize, 1, memory);
+ row++;
+ }
+ }
+ if (badrun > badfaxrun)
+ badfaxrun = badrun;
+
+ _TIFFfree(tifin->tif_rawdata);
+ tifin->tif_rawdata = NULL;
+
+ _TIFFfree(rowbuf);
+ _TIFFfree(refbuf);
+
+ /*
+ if (verbose) {
+ fprintf(stderr, "%d rows in input\n", rows);
+ fprintf(stderr, "%ld total bad rows\n", (long) badfaxlines);
+ fprintf(stderr, "%d max consecutive bad rows\n", badfaxrun);
+ }
+ */
+
+ } catch(const char *message) {
+ if(rowbuf) _TIFFfree(rowbuf);
+ if(refbuf) _TIFFfree(refbuf);
+ if(tifin->tif_rawdata) {
+ _TIFFfree(tifin->tif_rawdata);
+ tifin->tif_rawdata = NULL;
+ }
+ FreeImage_OutputMessageProc(s_format_id, message);
+
+ return -1;
+ }
+
+ return (row);
+}
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "G3";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Raw fax format CCITT G.3";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "g3";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL; // there is now reasonable regexp for raw G3
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/fax-g3";
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ TIFF *faxTIFF = NULL;
+ FIBITMAP *dib = NULL;
+ FIMEMORY *memory = NULL;
+
+ //int verbose = 0;
+ int stretch = 0;
+ int rows;
+ float resX = 204.0;
+ float resY = 196.0;
+
+ uint32 xsize = G3_DEFAULT_WIDTH;
+ int compression_in = COMPRESSION_CCITTFAX3;
+ int fillorder_in = FILLORDER_LSB2MSB;
+ uint32 group3options_in = 0; // 1d-encoded
+ uint32 group4options_in = 0; // compressed
+ int photometric_in = PHOTOMETRIC_MINISWHITE;
+
+ if(handle==NULL) return NULL;
+
+ try {
+ // set default load options
+
+ compression_in = COMPRESSION_CCITTFAX3; // input is g3-encoded
+ group3options_in &= ~GROUP3OPT_2DENCODING; // input is 1d-encoded (g3 only)
+ fillorder_in = FILLORDER_MSB2LSB; // input has msb-to-lsb fillorder
+
+ /*
+ Original input-related fax2tiff options
+
+ while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) {
+ switch (c) {
+ // input-related options
+ case '3': // input is g3-encoded
+ compression_in = COMPRESSION_CCITTFAX3;
+ break;
+ case '4': // input is g4-encoded
+ compression_in = COMPRESSION_CCITTFAX4;
+ break;
+ case 'U': // input is uncompressed (g3 and g4)
+ group3options_in |= GROUP3OPT_UNCOMPRESSED;
+ group4options_in |= GROUP4OPT_UNCOMPRESSED;
+ break;
+ case '1': // input is 1d-encoded (g3 only)
+ group3options_in &= ~GROUP3OPT_2DENCODING;
+ break;
+ case '2': // input is 2d-encoded (g3 only)
+ group3options_in |= GROUP3OPT_2DENCODING;
+ break;
+ case 'P': // input has not-aligned EOL (g3 only)
+ group3options_in &= ~GROUP3OPT_FILLBITS;
+ break;
+ case 'A': // input has aligned EOL (g3 only)
+ group3options_in |= GROUP3OPT_FILLBITS;
+ break;
+ case 'W': // input has 0 mean white
+ photometric_in = PHOTOMETRIC_MINISWHITE;
+ break;
+ case 'B': // input has 0 mean black
+ photometric_in = PHOTOMETRIC_MINISBLACK;
+ break;
+ case 'L': // input has lsb-to-msb fillorder
+ fillorder_in = FILLORDER_LSB2MSB;
+ break;
+ case 'M': // input has msb-to-lsb fillorder
+ fillorder_in = FILLORDER_MSB2LSB;
+ break;
+ case 'R': // input resolution
+ resY = (float) atof(optarg);
+ break;
+ case 'X': // input width
+ xsize = (uint32) atoi(optarg);
+ break;
+
+ // output-related options
+ case 's': // stretch image by dup'ng scanlines
+ stretch = 1;
+ break;
+ case 'v': // -v for info
+ verbose++;
+ break;
+ }
+ }
+
+ */
+
+ // open a temporary memory buffer to save decoded scanlines
+ memory = FreeImage_OpenMemory();
+ if (!memory) throw FI_MSG_ERROR_MEMORY;
+
+ // wrap the raw fax file
+ faxTIFF = TIFFClientOpen("(FakeInput)", "w",
+ // TIFFClientOpen() fails if we don't set existing value here
+ NULL,
+ _g3ReadProc, _g3WriteProc,
+ _g3SeekProc, _g3CloseProc,
+ _g3SizeProc, _g3MapProc,
+ _g3UnmapProc);
+
+ if (faxTIFF == NULL) {
+ throw "Can not create fake input file";
+ }
+ TIFFSetMode(faxTIFF, O_RDONLY);
+ TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize);
+ TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
+ TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1);
+ TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in);
+ TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in);
+ TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY);
+ TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+
+ // NB: this must be done after directory info is setup
+ TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in);
+ if (compression_in == COMPRESSION_CCITTFAX3)
+ TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in);
+ else if (compression_in == COMPRESSION_CCITTFAX4)
+ TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in);
+
+ resX = 204;
+ if (!stretch) {
+ TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY);
+ } else {
+ resY = 196;
+ }
+
+ // decode the raw fax data
+ rows = copyFaxFile(io, handle, faxTIFF, xsize, stretch, memory);
+ if(rows <= 0) throw "Error when decoding raw fax file : check the decoder options";
+
+
+ // allocate the output dib
+ dib = FreeImage_Allocate(xsize, rows, 1);
+ unsigned pitch = FreeImage_GetPitch(dib);
+ uint32 linesize = TIFFhowmany8(xsize);
+
+ // fill the bitmap structure ...
+ // ... palette
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ if(photometric_in == PHOTOMETRIC_MINISWHITE) {
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0;
+ } else {
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+ }
+ // ... resolution
+ FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX/0.0254000 + 0.5));
+ FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY/0.0254000 + 0.5));
+
+ // read the decoded scanline and fill the bitmap data
+ FreeImage_SeekMemory(memory, 0, SEEK_SET);
+ BYTE *bits = FreeImage_GetScanLine(dib, rows - 1);
+ for(int k = 0; k < rows; k++) {
+ FreeImage_ReadMemory(bits, linesize, 1, memory);
+ bits -= pitch;
+ }
+
+ // free the TIFF wrapper
+ TIFFClose(faxTIFF);
+
+ // free the memory buffer
+ FreeImage_CloseMemory(memory);
+
+ } catch(const char *message) {
+ if(memory) FreeImage_CloseMemory(memory);
+ if(faxTIFF) TIFFClose(faxTIFF);
+ if(dib) FreeImage_Unload(dib);
+ FreeImage_OutputMessageProc(s_format_id, message);
+ return NULL;
+ }
+
+ return dib;
+
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitG3(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = NULL;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = NULL;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp b/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp
new file mode 100644
index 0000000000..7af1d821d8
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp
@@ -0,0 +1,1398 @@
+// ==========================================================
+// GIF Loader and Writer
+//
+// Design and implementation by
+// - Ryan Rubley <ryan@lostreality.org>
+// - Raphaël Gaquer <raphael.gaquer@alcer.com>
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../Metadata/FreeImageTag.h"
+
+// ==========================================================
+// Metadata declarations
+// ==========================================================
+
+#define GIF_DISPOSAL_UNSPECIFIED 0
+#define GIF_DISPOSAL_LEAVE 1
+#define GIF_DISPOSAL_BACKGROUND 2
+#define GIF_DISPOSAL_PREVIOUS 3
+
+// ==========================================================
+// Constant/Typedef declarations
+// ==========================================================
+
+
+struct GIFinfo {
+ BOOL read;
+ //only really used when reading
+ size_t global_color_table_offset;
+ int global_color_table_size;
+ BYTE background_color;
+ std::vector<size_t> application_extension_offsets;
+ std::vector<size_t> comment_extension_offsets;
+ std::vector<size_t> graphic_control_extension_offsets;
+ std::vector<size_t> image_descriptor_offsets;
+
+ GIFinfo() : read(0), global_color_table_offset(0), global_color_table_size(0), background_color(0)
+ {
+ }
+};
+
+struct PageInfo {
+ PageInfo(int d, int l, int t, int w, int h) {
+ disposal_method = d; left = (WORD)l; top = (WORD)t; width = (WORD)w; height = (WORD)h;
+ }
+ int disposal_method;
+ WORD left, top, width, height;
+};
+
+//GIF defines a max of 12 bits per code
+#define MAX_LZW_CODE 4096
+
+class StringTable
+{
+public:
+ StringTable();
+ ~StringTable();
+ void Initialize(int minCodeSize);
+ BYTE *FillInputBuffer(int len);
+ void CompressStart(int bpp, int width);
+ int CompressEnd(BYTE *buf); //0-4 bytes
+ bool Compress(BYTE *buf, int *len);
+ bool Decompress(BYTE *buf, int *len);
+ void Done(void);
+
+protected:
+ bool m_done;
+
+ int m_minCodeSize, m_clearCode, m_endCode, m_nextCode;
+
+ int m_bpp, m_slack; //Compressor information
+
+ int m_prefix; //Compressor state variable
+ int m_codeSize, m_codeMask; //Compressor/Decompressor state variables
+ int m_oldCode; //Decompressor state variable
+ int m_partial, m_partialSize; //Compressor/Decompressor bit buffer
+
+ int firstPixelPassed; // A specific flag that indicates if the first pixel
+ // of the whole image had already been read
+
+ std::string m_strings[MAX_LZW_CODE]; //This is what is really the "string table" data for the Decompressor
+ int* m_strmap;
+
+ //input buffer
+ BYTE *m_buffer;
+ int m_bufferSize, m_bufferRealSize, m_bufferPos, m_bufferShift;
+
+ void ClearCompressorTable(void);
+ void ClearDecompressorTable(void);
+};
+
+#define GIF_PACKED_LSD_HAVEGCT 0x80
+#define GIF_PACKED_LSD_COLORRES 0x70
+#define GIF_PACKED_LSD_GCTSORTED 0x08
+#define GIF_PACKED_LSD_GCTSIZE 0x07
+#define GIF_PACKED_ID_HAVELCT 0x80
+#define GIF_PACKED_ID_INTERLACED 0x40
+#define GIF_PACKED_ID_LCTSORTED 0x20
+#define GIF_PACKED_ID_RESERVED 0x18
+#define GIF_PACKED_ID_LCTSIZE 0x07
+#define GIF_PACKED_GCE_RESERVED 0xE0
+#define GIF_PACKED_GCE_DISPOSAL 0x1C
+#define GIF_PACKED_GCE_WAITINPUT 0x02
+#define GIF_PACKED_GCE_HAVETRANS 0x01
+
+#define GIF_BLOCK_IMAGE_DESCRIPTOR 0x2C
+#define GIF_BLOCK_EXTENSION 0x21
+#define GIF_BLOCK_TRAILER 0x3B
+
+#define GIF_EXT_PLAINTEXT 0x01
+#define GIF_EXT_GRAPHIC_CONTROL 0xF9
+#define GIF_EXT_COMMENT 0xFE
+#define GIF_EXT_APPLICATION 0xFF
+
+#define GIF_INTERLACE_PASSES 4
+static int g_GifInterlaceOffset[GIF_INTERLACE_PASSES] = {0, 4, 2, 1};
+static int g_GifInterlaceIncrement[GIF_INTERLACE_PASSES] = {8, 8, 4, 2};
+
+// ==========================================================
+// Helpers Functions
+// ==========================================================
+
+static BOOL
+FreeImage_SetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, WORD id, FREE_IMAGE_MDTYPE type, DWORD count, DWORD length, const void *value)
+{
+ BOOL bResult = FALSE;
+ FITAG *tag = FreeImage_CreateTag();
+ if(tag) {
+ FreeImage_SetTagKey(tag, key);
+ FreeImage_SetTagID(tag, id);
+ FreeImage_SetTagType(tag, type);
+ FreeImage_SetTagCount(tag, count);
+ FreeImage_SetTagLength(tag, length);
+ FreeImage_SetTagValue(tag, value);
+ if(model == FIMD_ANIMATION) {
+ TagLib& s = TagLib::instance();
+ // get the tag description
+ const char *description = s.getTagDescription(TagLib::ANIMATION, id);
+ FreeImage_SetTagDescription(tag, description);
+ }
+ // store the tag
+ bResult = FreeImage_SetMetadata(model, dib, key, tag);
+ FreeImage_DeleteTag(tag);
+ }
+ return bResult;
+}
+
+static BOOL
+FreeImage_GetMetadataEx(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FREE_IMAGE_MDTYPE type, FITAG **tag)
+{
+ if ( FreeImage_GetMetadata(model, dib, key, tag)) {
+ if ( FreeImage_GetTagType(*tag) == type ) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+StringTable::StringTable()
+{
+ m_buffer = NULL;
+ firstPixelPassed = 0; // Still no pixel read
+ // Maximum number of entries in the map is MAX_LZW_CODE * 256
+ // (aka 2**12 * 2**8 => a 20 bits key)
+ // This Map could be optmized to only handle MAX_LZW_CODE * 2**(m_bpp)
+ m_strmap = new(std::nothrow) int[1<<20];
+}
+
+StringTable::~StringTable()
+{
+ if ( m_buffer != NULL ) {
+ delete [] m_buffer;
+ }
+ if ( m_strmap != NULL ) {
+ delete [] m_strmap;
+ m_strmap = NULL;
+ }
+}
+
+void StringTable::Initialize(int minCodeSize)
+{
+ m_done = false;
+
+ m_bpp = 8;
+ m_minCodeSize = minCodeSize;
+ m_clearCode = 1 << m_minCodeSize;
+ if(m_clearCode > MAX_LZW_CODE) {
+ m_clearCode = MAX_LZW_CODE;
+ }
+ m_endCode = m_clearCode + 1;
+
+ m_partial = 0;
+ m_partialSize = 0;
+
+ m_bufferSize = 0;
+ ClearCompressorTable();
+ ClearDecompressorTable();
+}
+
+BYTE *StringTable::FillInputBuffer(int len)
+{
+ if ( m_buffer == NULL ) {
+ m_buffer = new(std::nothrow) BYTE[len];
+ m_bufferRealSize = len;
+ } else if ( len > m_bufferRealSize ) {
+ delete [] m_buffer;
+ m_buffer = new(std::nothrow) BYTE[len];
+ m_bufferRealSize = len;
+ }
+ m_bufferSize = len;
+ m_bufferPos = 0;
+ m_bufferShift = 8 - m_bpp;
+ return m_buffer;
+}
+
+void StringTable::CompressStart(int bpp, int width)
+{
+ m_bpp = bpp;
+ m_slack = (8 - ((width * bpp) % 8)) % 8;
+
+ m_partial |= m_clearCode << m_partialSize;
+ m_partialSize += m_codeSize;
+ ClearCompressorTable();
+}
+
+int StringTable::CompressEnd(BYTE *buf)
+{
+ int len = 0;
+
+ //output code for remaining prefix
+ m_partial |= m_prefix << m_partialSize;
+ m_partialSize += m_codeSize;
+ while( m_partialSize >= 8 ) {
+ *buf++ = (BYTE)m_partial;
+ m_partial >>= 8;
+ m_partialSize -= 8;
+ len++;
+ }
+
+ //add the end of information code and flush the entire buffer out
+ m_partial |= m_endCode << m_partialSize;
+ m_partialSize += m_codeSize;
+ while( m_partialSize > 0 ) {
+ *buf++ = (BYTE)m_partial;
+ m_partial >>= 8;
+ m_partialSize -= 8;
+ len++;
+ }
+
+ //most this can be is 4 bytes. 7 bits in m_partial to start + 12 for the
+ //last code + 12 for the end code = 31 bits total.
+ return len;
+}
+
+bool StringTable::Compress(BYTE *buf, int *len)
+{
+ if ( m_bufferSize == 0 || m_done ) {
+ return false;
+ }
+
+ int mask = (1 << m_bpp) - 1;
+ BYTE *bufpos = buf;
+ while( m_bufferPos < m_bufferSize ) {
+ //get the current pixel value
+ char ch = (char)((m_buffer[m_bufferPos] >> m_bufferShift) & mask);
+
+ // The next prefix is :
+ // <the previous LZW code (on 12 bits << 8)> | <the code of the current pixel (on 8 bits)>
+ int nextprefix = (((m_prefix)<<8)&0xFFF00) + (ch & 0x000FF);
+ if(firstPixelPassed) {
+
+ if ( m_strmap[nextprefix] > 0) {
+ m_prefix = m_strmap[nextprefix];
+ } else {
+ m_partial |= m_prefix << m_partialSize;
+ m_partialSize += m_codeSize;
+ //grab full bytes for the output buffer
+ while( m_partialSize >= 8 && bufpos - buf < *len ) {
+ *bufpos++ = (BYTE)m_partial;
+ m_partial >>= 8;
+ m_partialSize -= 8;
+ }
+
+ //add the code to the "table map"
+ m_strmap[nextprefix] = m_nextCode;
+
+ //increment the next highest valid code, increase the code size
+ if ( m_nextCode == (1 << m_codeSize)) {
+ m_codeSize++;
+ }
+ m_nextCode++;
+
+ //if we're out of codes, restart the string table
+ if ( m_nextCode == MAX_LZW_CODE ) {
+ m_partial |= m_clearCode << m_partialSize;
+ m_partialSize += m_codeSize;
+ ClearCompressorTable();
+ }
+
+ // Only keep the 8 lowest bits (prevent problems with "negative chars")
+ m_prefix = ch & 0x000FF;
+ }
+
+ //increment to the next pixel
+ if ( m_bufferShift > 0 && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) {
+ m_bufferShift -= m_bpp;
+ } else {
+ m_bufferPos++;
+ m_bufferShift = 8 - m_bpp;
+ }
+
+ //jump out here if the output buffer is full
+ if ( bufpos - buf == *len ) {
+ return true;
+ }
+
+ } else {
+ // Specific behavior for the first pixel of the whole image
+
+ firstPixelPassed=1;
+ // Only keep the 8 lowest bits (prevent problems with "negative chars")
+ m_prefix = ch & 0x000FF;
+
+ //increment to the next pixel
+ if ( m_bufferShift > 0 && !(m_bufferPos + 1 == m_bufferSize && m_bufferShift <= m_slack)) {
+ m_bufferShift -= m_bpp;
+ } else {
+ m_bufferPos++;
+ m_bufferShift = 8 - m_bpp;
+ }
+
+ //jump out here if the output buffer is full
+ if ( bufpos - buf == *len ) {
+ return true;
+ }
+ }
+ }
+
+ m_bufferSize = 0;
+ *len = (int)(bufpos - buf);
+
+ return true;
+}
+
+bool StringTable::Decompress(BYTE *buf, int *len)
+{
+ if ( m_bufferSize == 0 || m_done ) {
+ return false;
+ }
+
+ BYTE *bufpos = buf;
+ for ( ; m_bufferPos < m_bufferSize; m_bufferPos++ ) {
+ m_partial |= (int)m_buffer[m_bufferPos] << m_partialSize;
+ m_partialSize += 8;
+ while( m_partialSize >= m_codeSize ) {
+ int code = m_partial & m_codeMask;
+ m_partial >>= m_codeSize;
+ m_partialSize -= m_codeSize;
+
+ if ( code > m_nextCode || (m_nextCode == MAX_LZW_CODE && code != m_clearCode) || code == m_endCode ) {
+ m_done = true;
+ *len = (int)(bufpos - buf);
+ return true;
+ }
+ if ( code == m_clearCode ) {
+ ClearDecompressorTable();
+ continue;
+ }
+
+ //add new string to string table, if not the first pass since a clear code
+ if ( m_oldCode != MAX_LZW_CODE ) {
+ m_strings[m_nextCode] = m_strings[m_oldCode] + m_strings[code == m_nextCode ? m_oldCode : code][0];
+ }
+
+ if ( (int)m_strings[code].size() > *len - (bufpos - buf)) {
+ //out of space, stuff the code back in for next time
+ m_partial <<= m_codeSize;
+ m_partialSize += m_codeSize;
+ m_partial |= code;
+ m_bufferPos++;
+ *len = (int)(bufpos - buf);
+ return true;
+ }
+
+ //output the string into the buffer
+ memcpy(bufpos, m_strings[code].data(), m_strings[code].size());
+ bufpos += m_strings[code].size();
+
+ //increment the next highest valid code, add a bit to the mask if we need to increase the code size
+ if ( m_oldCode != MAX_LZW_CODE && m_nextCode < MAX_LZW_CODE ) {
+ if ( ++m_nextCode < MAX_LZW_CODE ) {
+ if ( (m_nextCode & m_codeMask) == 0 ) {
+ m_codeSize++;
+ m_codeMask |= m_nextCode;
+ }
+ }
+ }
+
+ m_oldCode = code;
+ }
+ }
+
+ m_bufferSize = 0;
+ *len = (int)(bufpos - buf);
+
+ return true;
+}
+
+void StringTable::Done(void)
+{
+ m_done = true;
+}
+
+void StringTable::ClearCompressorTable(void)
+{
+ if(m_strmap) {
+ memset(m_strmap, 0xFF, sizeof(unsigned int)*(1<<20));
+ }
+ m_nextCode = m_endCode + 1;
+
+ m_prefix = 0;
+ m_codeSize = m_minCodeSize + 1;
+}
+
+void StringTable::ClearDecompressorTable(void)
+{
+ for ( int i = 0; i < m_clearCode; i++ ) {
+ m_strings[i].resize(1);
+ m_strings[i][0] = (char)i;
+ }
+ m_nextCode = m_endCode + 1;
+
+ m_codeSize = m_minCodeSize + 1;
+ m_codeMask = (1 << m_codeSize) - 1;
+ m_oldCode = MAX_LZW_CODE;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "GIF";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Graphics Interchange Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "gif";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return "^GIF";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/gif";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ char buf[6];
+ if ( io->read_proc(buf, 6, 1, handle) < 1 ) {
+ return FALSE;
+ }
+
+ BOOL bResult = FALSE;
+ if ( !strncmp(buf, "GIF", 3)) {
+ if ( buf[3] >= '0' && buf[3] <= '9' && buf[4] >= '0' && buf[4] <= '9' && buf[5] >= 'a' && buf[5] <= 'z' ) {
+ bResult = TRUE;
+ }
+ }
+
+ io->seek_proc(handle, -6, SEEK_CUR);
+
+ return bResult;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (depth == 1) ||
+ (depth == 4) ||
+ (depth == 8);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+// ----------------------------------------------------------
+
+static void *DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ GIFinfo *info = new(std::nothrow) GIFinfo;
+ if ( info == NULL ) {
+ return NULL;
+ }
+
+ // 25/02/2008 MDA: Not safe to memset GIFinfo structure with VS 2008 (safe iterators),
+ // perform initialization in constructor instead.
+ // memset(info, 0, sizeof(GIFinfo));
+
+ info->read = read;
+ if ( read ) {
+ try {
+ //Header
+ if ( !Validate(io, handle)) {
+ throw FI_MSG_ERROR_MAGIC_NUMBER;
+ }
+ io->seek_proc(handle, 6, SEEK_CUR);
+
+ //Logical Screen Descriptor
+ io->seek_proc(handle, 4, SEEK_CUR);
+ BYTE packed;
+ if ( io->read_proc(&packed, 1, 1, handle) < 1 ) {
+ throw "EOF reading Logical Screen Descriptor";
+ }
+ if ( io->read_proc(&info->background_color, 1, 1, handle) < 1 ) {
+ throw "EOF reading Logical Screen Descriptor";
+ }
+ io->seek_proc(handle, 1, SEEK_CUR);
+
+ //Global Color Table
+ if ( packed & GIF_PACKED_LSD_HAVEGCT ) {
+ info->global_color_table_offset = io->tell_proc(handle);
+ info->global_color_table_size = 2 << (packed & GIF_PACKED_LSD_GCTSIZE);
+ io->seek_proc(handle, 3 * info->global_color_table_size, SEEK_CUR);
+ }
+
+ //Scan through all the rest of the blocks, saving offsets
+ size_t gce_offset = 0;
+ BYTE block = 0;
+ while( block != GIF_BLOCK_TRAILER ) {
+ if ( io->read_proc(&block, 1, 1, handle) < 1 ) {
+ throw "EOF reading blocks";
+ }
+ if ( block == GIF_BLOCK_IMAGE_DESCRIPTOR ) {
+ info->image_descriptor_offsets.push_back(io->tell_proc(handle));
+ //GCE may be 0, meaning no GCE preceded this ID
+ info->graphic_control_extension_offsets.push_back(gce_offset);
+ gce_offset = 0;
+
+ io->seek_proc(handle, 8, SEEK_CUR);
+ if ( io->read_proc(&packed, 1, 1, handle) < 1 ) {
+ throw "EOF reading Image Descriptor";
+ }
+
+ //Local Color Table
+ if ( packed & GIF_PACKED_ID_HAVELCT ) {
+ io->seek_proc(handle, 3 * (2 << (packed & GIF_PACKED_ID_LCTSIZE)), SEEK_CUR);
+ }
+
+ //LZW Minimum Code Size
+ io->seek_proc(handle, 1, SEEK_CUR);
+ } else if ( block == GIF_BLOCK_EXTENSION ) {
+ BYTE ext;
+ if ( io->read_proc(&ext, 1, 1, handle) < 1 ) {
+ throw "EOF reading extension";
+ }
+
+ if ( ext == GIF_EXT_GRAPHIC_CONTROL ) {
+ //overwrite previous offset if more than one GCE found before an ID
+ gce_offset = io->tell_proc(handle);
+ } else if ( ext == GIF_EXT_COMMENT ) {
+ info->comment_extension_offsets.push_back(io->tell_proc(handle));
+ } else if ( ext == GIF_EXT_APPLICATION ) {
+ info->application_extension_offsets.push_back(io->tell_proc(handle));
+ }
+ } else if ( block == GIF_BLOCK_TRAILER ) {
+ continue;
+ } else {
+ throw "Invalid GIF block found";
+ }
+
+ //Data Sub-blocks
+ BYTE len;
+ if ( io->read_proc(&len, 1, 1, handle) < 1 ) {
+ throw "EOF reading sub-block";
+ }
+ while( len != 0 ) {
+ io->seek_proc(handle, len, SEEK_CUR);
+ if ( io->read_proc(&len, 1, 1, handle) < 1 ) {
+ throw "EOF reading sub-block";
+ }
+ }
+ }
+ } catch (const char *msg) {
+ FreeImage_OutputMessageProc(s_format_id, msg);
+ delete info;
+ return NULL;
+ }
+ } else {
+ //Header
+ io->write_proc((void *)"GIF89a", 6, 1, handle);
+ }
+
+ return info;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+ if ( data == NULL ) {
+ return;
+ }
+ GIFinfo *info = (GIFinfo *)data;
+
+ if ( !info->read ) {
+ //Trailer
+ BYTE b = GIF_BLOCK_TRAILER;
+ io->write_proc(&b, 1, 1, handle);
+ }
+
+ delete info;
+}
+
+static int DLL_CALLCONV
+PageCount(FreeImageIO *io, fi_handle handle, void *data) {
+ if ( data == NULL ) {
+ return 0;
+ }
+ GIFinfo *info = (GIFinfo *)data;
+
+ return (int) info->image_descriptor_offsets.size();
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if ( data == NULL ) {
+ return NULL;
+ }
+ GIFinfo *info = (GIFinfo *)data;
+
+ if ( page == -1 ) {
+ page = 0;
+ }
+ if ( page < 0 || page >= (int)info->image_descriptor_offsets.size()) {
+ return NULL;
+ }
+
+ FIBITMAP *dib = NULL;
+ try {
+ bool have_transparent = false, no_local_palette = false, interlaced = false;
+ int disposal_method = GIF_DISPOSAL_LEAVE, delay_time = 0, transparent_color = 0;
+ WORD left, top, width, height;
+ BYTE packed, b;
+ WORD w;
+
+ //playback pages to generate what the user would see for this frame
+ if ( (flags & GIF_PLAYBACK) == GIF_PLAYBACK ) {
+ //Logical Screen Descriptor
+ io->seek_proc(handle, 6, SEEK_SET);
+ WORD logicalwidth, logicalheight;
+ io->read_proc(&logicalwidth, 2, 1, handle);
+ io->read_proc(&logicalheight, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&logicalwidth);
+ SwapShort(&logicalheight);
+#endif
+ //set the background color with 0 alpha
+ RGBQUAD background;
+ if ( info->global_color_table_offset != 0 && info->background_color < info->global_color_table_size ) {
+ io->seek_proc(handle, (long)(info->global_color_table_offset + (info->background_color * 3)), SEEK_SET);
+ io->read_proc(&background.rgbRed, 1, 1, handle);
+ io->read_proc(&background.rgbGreen, 1, 1, handle);
+ io->read_proc(&background.rgbBlue, 1, 1, handle);
+ } else {
+ background.rgbRed = 0;
+ background.rgbGreen = 0;
+ background.rgbBlue = 0;
+ }
+ background.rgbReserved = 0;
+
+ //allocate entire logical area
+ dib = FreeImage_Allocate(logicalwidth, logicalheight, 32);
+ if ( dib == NULL ) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ //fill with background color to start
+ int x, y;
+ RGBQUAD *scanline;
+ for ( y = 0; y < logicalheight; y++ ) {
+ scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, y);
+ for ( x = 0; x < logicalwidth; x++ ) {
+ *scanline++ = background;
+ }
+ }
+
+ //cache some info about each of the pages so we can avoid decoding as many of them as possible
+ std::vector<PageInfo> pageinfo;
+ int start = page, end = page;
+ while( start >= 0 ) {
+ //Graphic Control Extension
+ io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[start] + 1), SEEK_SET);
+ io->read_proc(&packed, 1, 1, handle);
+ have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
+ disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
+ //Image Descriptor
+ io->seek_proc(handle, (long)(info->image_descriptor_offsets[start]), SEEK_SET);
+ io->read_proc(&left, 2, 1, handle);
+ io->read_proc(&top, 2, 1, handle);
+ io->read_proc(&width, 2, 1, handle);
+ io->read_proc(&height, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&left);
+ SwapShort(&top);
+ SwapShort(&width);
+ SwapShort(&height);
+#endif
+
+ pageinfo.push_back(PageInfo(disposal_method, left, top, width, height));
+
+ if ( start != end ) {
+ if ( left == 0 && top == 0 && width == logicalwidth && height == logicalheight ) {
+ if ( disposal_method == GIF_DISPOSAL_BACKGROUND ) {
+ pageinfo.pop_back();
+ start++;
+ break;
+ } else if ( disposal_method != GIF_DISPOSAL_PREVIOUS ) {
+ if ( !have_transparent ) {
+ break;
+ }
+ }
+ }
+ }
+ start--;
+ }
+ if ( start < 0 ) {
+ start = 0;
+ }
+
+ //draw each page into the logical area
+ delay_time = 0;
+ for ( page = start; page <= end; page++ ) {
+ PageInfo &info = pageinfo[end - page];
+ //things we can skip having to decode
+ if ( page != end ) {
+ if ( info.disposal_method == GIF_DISPOSAL_PREVIOUS ) {
+ continue;
+ }
+ if ( info.disposal_method == GIF_DISPOSAL_BACKGROUND ) {
+ for ( y = 0; y < info.height; y++ ) {
+ scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left;
+ for ( x = 0; x < info.width; x++ ) {
+ *scanline++ = background;
+ }
+ }
+ continue;
+ }
+ }
+
+ //decode page
+ FIBITMAP *pagedib = Load(io, handle, page, GIF_LOAD256, data);
+ if ( pagedib != NULL ) {
+ RGBQUAD *pal = FreeImage_GetPalette(pagedib);
+ have_transparent = false;
+ if ( FreeImage_IsTransparent(pagedib)) {
+ int count = FreeImage_GetTransparencyCount(pagedib);
+ BYTE *table = FreeImage_GetTransparencyTable(pagedib);
+ for ( int i = 0; i < count; i++ ) {
+ if ( table[i] == 0 ) {
+ have_transparent = true;
+ transparent_color = i;
+ break;
+ }
+ }
+ }
+ //copy page data into logical buffer, with full alpha opaqueness
+ for ( y = 0; y < info.height; y++ ) {
+ scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left;
+ BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1);
+ for ( x = 0; x < info.width; x++ ) {
+ if ( !have_transparent || *pageline != transparent_color ) {
+ *scanline = pal[*pageline];
+ scanline->rgbReserved = 255;
+ }
+ scanline++;
+ pageline++;
+ }
+ }
+ //copy frame time
+ if ( page == end ) {
+ FITAG *tag;
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, pagedib, "FrameTime", FIDT_LONG, &tag)) {
+ delay_time = *(LONG *)FreeImage_GetTagValue(tag);
+ }
+ }
+ FreeImage_Unload(pagedib);
+ }
+ }
+
+ //setup frame time
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
+ return dib;
+ }
+
+ //get the actual frame image data for a single frame
+
+ //Image Descriptor
+ io->seek_proc(handle, (long)info->image_descriptor_offsets[page], SEEK_SET);
+ io->read_proc(&left, 2, 1, handle);
+ io->read_proc(&top, 2, 1, handle);
+ io->read_proc(&width, 2, 1, handle);
+ io->read_proc(&height, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&left);
+ SwapShort(&top);
+ SwapShort(&width);
+ SwapShort(&height);
+#endif
+ io->read_proc(&packed, 1, 1, handle);
+ interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false;
+ no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true;
+
+ int bpp = 8;
+ if ( (flags & GIF_LOAD256) == 0 ) {
+ if ( !no_local_palette ) {
+ int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
+ if ( size <= 2 ) bpp = 1;
+ else if ( size <= 16 ) bpp = 4;
+ } else if ( info->global_color_table_offset != 0 ) {
+ if ( info->global_color_table_size <= 2 ) bpp = 1;
+ else if ( info->global_color_table_size <= 16 ) bpp = 4;
+ }
+ }
+ dib = FreeImage_Allocate(width, height, bpp);
+ if ( dib == NULL ) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", ANIMTAG_FRAMELEFT, FIDT_SHORT, 1, 2, &left);
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", ANIMTAG_FRAMETOP, FIDT_SHORT, 1, 2, &top);
+ b = no_local_palette ? 1 : 0;
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", ANIMTAG_NOLOCALPALETTE, FIDT_BYTE, 1, 1, &b);
+ b = interlaced ? 1 : 0;
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", ANIMTAG_INTERLACED, FIDT_BYTE, 1, 1, &b);
+
+ //Palette
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ if ( !no_local_palette ) {
+ int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE);
+
+ int i = 0;
+ while( i < size ) {
+ io->read_proc(&pal[i].rgbRed, 1, 1, handle);
+ io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
+ io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
+ i++;
+ }
+ } else if ( info->global_color_table_offset != 0 ) {
+ long pos = io->tell_proc(handle);
+ io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);
+
+ int i = 0;
+ while( i < info->global_color_table_size ) {
+ io->read_proc(&pal[i].rgbRed, 1, 1, handle);
+ io->read_proc(&pal[i].rgbGreen, 1, 1, handle);
+ io->read_proc(&pal[i].rgbBlue, 1, 1, handle);
+ i++;
+ }
+
+ io->seek_proc(handle, pos, SEEK_SET);
+ } else {
+ //its legal to have no palette, but we're going to generate *something*
+ for ( int i = 0; i < 256; i++ ) {
+ pal[i].rgbRed = (BYTE)i;
+ pal[i].rgbGreen = (BYTE)i;
+ pal[i].rgbBlue = (BYTE)i;
+ }
+ }
+
+ //LZW Minimum Code Size
+ io->read_proc(&b, 1, 1, handle);
+ StringTable *stringtable = new(std::nothrow) StringTable;
+ stringtable->Initialize(b);
+
+ //Image Data Sub-blocks
+ int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0;
+ BYTE *scanline = FreeImage_GetScanLine(dib, height - 1);
+ BYTE buf[4096];
+ io->read_proc(&b, 1, 1, handle);
+ while( b ) {
+ io->read_proc(stringtable->FillInputBuffer(b), b, 1, handle);
+ int size = sizeof(buf);
+ while( stringtable->Decompress(buf, &size)) {
+ for ( int i = 0; i < size; i++ ) {
+ scanline[xpos] |= (buf[i] & mask) << shift;
+ if ( shift > 0 ) {
+ shift -= bpp;
+ } else {
+ xpos++;
+ shift = 8 - bpp;
+ }
+ if ( ++x >= width ) {
+ if ( interlaced ) {
+ y += g_GifInterlaceIncrement[interlacepass];
+ if ( y >= height && ++interlacepass < GIF_INTERLACE_PASSES ) {
+ y = g_GifInterlaceOffset[interlacepass];
+ }
+ } else {
+ y++;
+ }
+ if ( y >= height ) {
+ stringtable->Done();
+ break;
+ }
+ x = xpos = 0;
+ shift = 8 - bpp;
+ scanline = FreeImage_GetScanLine(dib, height - y - 1);
+ }
+ }
+ size = sizeof(buf);
+ }
+ io->read_proc(&b, 1, 1, handle);
+ }
+
+ if ( page == 0 ) {
+ size_t idx;
+
+ //Logical Screen Descriptor
+ io->seek_proc(handle, 6, SEEK_SET);
+ WORD logicalwidth, logicalheight;
+ io->read_proc(&logicalwidth, 2, 1, handle);
+ io->read_proc(&logicalheight, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&logicalwidth);
+ SwapShort(&logicalheight);
+#endif
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", ANIMTAG_LOGICALWIDTH, FIDT_SHORT, 1, 2, &logicalwidth);
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", ANIMTAG_LOGICALHEIGHT, FIDT_SHORT, 1, 2, &logicalheight);
+
+ //Global Color Table
+ if ( info->global_color_table_offset != 0 ) {
+ RGBQUAD globalpalette[256];
+ io->seek_proc(handle, (long)info->global_color_table_offset, SEEK_SET);
+ int i = 0;
+ while( i < info->global_color_table_size ) {
+ io->read_proc(&globalpalette[i].rgbRed, 1, 1, handle);
+ io->read_proc(&globalpalette[i].rgbGreen, 1, 1, handle);
+ io->read_proc(&globalpalette[i].rgbBlue, 1, 1, handle);
+ globalpalette[i].rgbReserved = 0;
+ i++;
+ }
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", ANIMTAG_GLOBALPALETTE, FIDT_PALETTE, info->global_color_table_size, info->global_color_table_size * 4, globalpalette);
+ //background color
+ if ( info->background_color < info->global_color_table_size ) {
+ FreeImage_SetBackgroundColor(dib, &globalpalette[info->background_color]);
+ }
+ }
+
+ //Application Extension
+ LONG loop = 1; //If no AE with a loop count is found, the default must be 1
+ for ( idx = 0; idx < info->application_extension_offsets.size(); idx++ ) {
+ io->seek_proc(handle, (long)info->application_extension_offsets[idx], SEEK_SET);
+ io->read_proc(&b, 1, 1, handle);
+ if ( b == 11 ) { //All AEs start with an 11 byte sub-block to determine what type of AE it is
+ char buf[11];
+ io->read_proc(buf, 11, 1, handle);
+ if ( !memcmp(buf, "NETSCAPE2.0", 11) || !memcmp(buf, "ANIMEXTS1.0", 11)) { //Not everybody recognizes ANIMEXTS1.0 but it is valid
+ io->read_proc(&b, 1, 1, handle);
+ if ( b == 3 ) { //we're supposed to have a 3 byte sub-block now
+ io->read_proc(&b, 1, 1, handle); //this should be 0x01 but isn't really important
+ io->read_proc(&w, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&w);
+#endif
+ loop = w;
+ if ( loop > 0 ) loop++;
+ break;
+ }
+ }
+ }
+ }
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Loop", ANIMTAG_LOOP, FIDT_LONG, 1, 4, &loop);
+
+ //Comment Extension
+ for ( idx = 0; idx < info->comment_extension_offsets.size(); idx++ ) {
+ io->seek_proc(handle, (long)info->comment_extension_offsets[idx], SEEK_SET);
+ std::string comment;
+ char buf[255];
+ io->read_proc(&b, 1, 1, handle);
+ while( b ) {
+ io->read_proc(buf, b, 1, handle);
+ comment.append(buf, b);
+ io->read_proc(&b, 1, 1, handle);
+ }
+ comment.append(1, '\0');
+ sprintf(buf, "Comment%d", idx);
+ DWORD comment_size = (DWORD)comment.size();
+ FreeImage_SetMetadataEx(FIMD_COMMENTS, dib, buf, 1, FIDT_ASCII, comment_size, comment_size, comment.c_str());
+ }
+ }
+
+ //Graphic Control Extension
+ if ( info->graphic_control_extension_offsets[page] != 0 ) {
+ io->seek_proc(handle, (long)(info->graphic_control_extension_offsets[page] + 1), SEEK_SET);
+ io->read_proc(&packed, 1, 1, handle);
+ io->read_proc(&w, 2, 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&w);
+#endif
+ io->read_proc(&b, 1, 1, handle);
+ have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false;
+ disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2;
+ delay_time = w * 10; //convert cs to ms
+ transparent_color = b;
+ if ( have_transparent ) {
+ int size = 1 << bpp;
+ if ( transparent_color <= size ) {
+ BYTE table[256];
+ memset(table, 0xFF, size);
+ table[transparent_color] = 0;
+ FreeImage_SetTransparencyTable(dib, table, size);
+ }
+ }
+ }
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", ANIMTAG_FRAMETIME, FIDT_LONG, 1, 4, &delay_time);
+ b = (BYTE)disposal_method;
+ FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", ANIMTAG_DISPOSALMETHOD, FIDT_BYTE, 1, 1, &b);
+
+ delete stringtable;
+
+ } catch (const char *msg) {
+ if ( dib != NULL ) {
+ FreeImage_Unload(dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, msg);
+ return NULL;
+ }
+
+ return dib;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if ( data == NULL ) {
+ return FALSE;
+ }
+ //GIFinfo *info = (GIFinfo *)data;
+
+ if ( page == -1 ) {
+ page = 0;
+ }
+
+ try {
+ BYTE packed, b;
+ WORD w;
+ FITAG *tag;
+
+ int bpp = FreeImage_GetBPP(dib);
+ if ( bpp != 1 && bpp != 4 && bpp != 8 ) {
+ throw "Only 1, 4, or 8 bpp images supported";
+ }
+
+ bool have_transparent = false, no_local_palette = false, interlaced = false;
+ int disposal_method = GIF_DISPOSAL_BACKGROUND, delay_time = 100, transparent_color = 0;
+ WORD left = 0, top = 0, width = (WORD)FreeImage_GetWidth(dib), height = (WORD)FreeImage_GetHeight(dib);
+ WORD output_height = height;
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", FIDT_SHORT, &tag)) {
+ left = *(WORD *)FreeImage_GetTagValue(tag);
+ }
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", FIDT_SHORT, &tag)) {
+ top = *(WORD *)FreeImage_GetTagValue(tag);
+ }
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", FIDT_BYTE, &tag)) {
+ no_local_palette = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
+ }
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", FIDT_BYTE, &tag)) {
+ interlaced = *(BYTE *)FreeImage_GetTagValue(tag) ? true : false;
+ }
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "FrameTime", FIDT_LONG, &tag)) {
+ delay_time = *(LONG *)FreeImage_GetTagValue(tag);
+ }
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "DisposalMethod", FIDT_BYTE, &tag)) {
+ disposal_method = *(BYTE *)FreeImage_GetTagValue(tag);
+ }
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&left);
+ SwapShort(&top);
+ SwapShort(&width);
+ SwapShort(&height);
+#endif
+
+ if ( page == 0 ) {
+ //gather some info
+ WORD logicalwidth = width; // width has already been swapped...
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", FIDT_SHORT, &tag)) {
+ logicalwidth = *(WORD *)FreeImage_GetTagValue(tag);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&logicalwidth);
+#endif
+ }
+ WORD logicalheight = height; // height has already been swapped...
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", FIDT_SHORT, &tag)) {
+ logicalheight = *(WORD *)FreeImage_GetTagValue(tag);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&logicalheight);
+#endif
+ }
+ RGBQUAD *globalpalette = NULL;
+ int globalpalette_size = 0;
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", FIDT_PALETTE, &tag)) {
+ globalpalette_size = FreeImage_GetTagCount(tag);
+ if ( globalpalette_size >= 2 ) {
+ globalpalette = (RGBQUAD *)FreeImage_GetTagValue(tag);
+ }
+ }
+
+ //Logical Screen Descriptor
+ io->write_proc(&logicalwidth, 2, 1, handle);
+ io->write_proc(&logicalheight, 2, 1, handle);
+ packed = GIF_PACKED_LSD_COLORRES;
+ b = 0;
+ RGBQUAD background_color;
+ if ( globalpalette != NULL ) {
+ packed |= GIF_PACKED_LSD_HAVEGCT;
+ if ( globalpalette_size < 4 ) {
+ globalpalette_size = 2;
+ packed |= 0 & GIF_PACKED_LSD_GCTSIZE;
+ } else if ( globalpalette_size < 8 ) {
+ globalpalette_size = 4;
+ packed |= 1 & GIF_PACKED_LSD_GCTSIZE;
+ } else if ( globalpalette_size < 16 ) {
+ globalpalette_size = 8;
+ packed |= 2 & GIF_PACKED_LSD_GCTSIZE;
+ } else if ( globalpalette_size < 32 ) {
+ globalpalette_size = 16;
+ packed |= 3 & GIF_PACKED_LSD_GCTSIZE;
+ } else if ( globalpalette_size < 64 ) {
+ globalpalette_size = 32;
+ packed |= 4 & GIF_PACKED_LSD_GCTSIZE;
+ } else if ( globalpalette_size < 128 ) {
+ globalpalette_size = 64;
+ packed |= 5 & GIF_PACKED_LSD_GCTSIZE;
+ } else if ( globalpalette_size < 256 ) {
+ globalpalette_size = 128;
+ packed |= 6 & GIF_PACKED_LSD_GCTSIZE;
+ } else {
+ globalpalette_size = 256;
+ packed |= 7 & GIF_PACKED_LSD_GCTSIZE;
+ }
+ if ( FreeImage_GetBackgroundColor(dib, &background_color)) {
+ for ( int i = 0; i < globalpalette_size; i++ ) {
+ if ( background_color.rgbRed == globalpalette[i].rgbRed &&
+ background_color.rgbGreen == globalpalette[i].rgbGreen &&
+ background_color.rgbBlue == globalpalette[i].rgbBlue ) {
+
+ b = (BYTE)i;
+ break;
+ }
+ }
+ }
+ } else {
+ packed |= (bpp - 1) & GIF_PACKED_LSD_GCTSIZE;
+ }
+ io->write_proc(&packed, 1, 1, handle);
+ io->write_proc(&b, 1, 1, handle);
+ b = 0;
+ io->write_proc(&b, 1, 1, handle);
+
+ //Global Color Table
+ if ( globalpalette != NULL ) {
+ int i = 0;
+ while( i < globalpalette_size ) {
+ io->write_proc(&globalpalette[i].rgbRed, 1, 1, handle);
+ io->write_proc(&globalpalette[i].rgbGreen, 1, 1, handle);
+ io->write_proc(&globalpalette[i].rgbBlue, 1, 1, handle);
+ i++;
+ }
+ }
+
+ //Application Extension
+ LONG loop = 0;
+ if ( FreeImage_GetMetadataEx(FIMD_ANIMATION, dib, "Loop", FIDT_LONG, &tag)) {
+ loop = *(LONG *)FreeImage_GetTagValue(tag);
+ }
+ if ( loop != 1 ) {
+ //the Netscape extension is really "repeats" not "loops"
+ if ( loop > 1 ) loop--;
+ if ( loop > 0xFFFF ) loop = 0xFFFF;
+ w = (WORD)loop;
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&w);
+#endif
+ io->write_proc((void *)"\x21\xFF\x0BNETSCAPE2.0\x03\x01", 16, 1, handle);
+ io->write_proc(&w, 2, 1, handle);
+ b = 0;
+ io->write_proc(&b, 1, 1, handle);
+ }
+
+ //Comment Extension
+ FIMETADATA *mdhandle = NULL;
+ FITAG *tag = NULL;
+ mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag);
+ if ( mdhandle ) {
+ do {
+ if ( FreeImage_GetTagType(tag) == FIDT_ASCII ) {
+ int length = FreeImage_GetTagLength(tag) - 1;
+ char *value = (char *)FreeImage_GetTagValue(tag);
+ io->write_proc((void *)"\x21\xFE", 2, 1, handle);
+ while( length > 0 ) {
+ b = (BYTE)(length >= 255 ? 255 : length);
+ io->write_proc(&b, 1, 1, handle);
+ io->write_proc(value, b, 1, handle);
+ value += b;
+ length -= b;
+ }
+ b = 0;
+ io->write_proc(&b, 1, 1, handle);
+ }
+ } while(FreeImage_FindNextMetadata(mdhandle, &tag));
+
+ FreeImage_FindCloseMetadata(mdhandle);
+ }
+ }
+
+ //Graphic Control Extension
+ if ( FreeImage_IsTransparent(dib)) {
+ int count = FreeImage_GetTransparencyCount(dib);
+ BYTE *table = FreeImage_GetTransparencyTable(dib);
+ for ( int i = 0; i < count; i++ ) {
+ if ( table[i] == 0 ) {
+ have_transparent = true;
+ transparent_color = i;
+ break;
+ }
+ }
+ }
+ io->write_proc((void *)"\x21\xF9\x04", 3, 1, handle);
+ b = (BYTE)((disposal_method << 2) & GIF_PACKED_GCE_DISPOSAL);
+ if ( have_transparent ) b |= GIF_PACKED_GCE_HAVETRANS;
+ io->write_proc(&b, 1, 1, handle);
+ //Notes about delay time for GIFs:
+ //IE5/IE6 have a minimum and default of 100ms
+ //Mozilla/Firefox/Netscape 6+/Opera have a minimum of 20ms and a default of 100ms if <20ms is specified or the GCE is absent
+ //Netscape 4 has a minimum of 10ms if 0ms is specified, but will use 0ms if the GCE is absent
+ w = (WORD)(delay_time / 10); //convert ms to cs
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&w);
+#endif
+ io->write_proc(&w, 2, 1, handle);
+ b = (BYTE)transparent_color;
+ io->write_proc(&b, 1, 1, handle);
+ b = 0;
+ io->write_proc(&b, 1, 1, handle);
+
+ //Image Descriptor
+ b = GIF_BLOCK_IMAGE_DESCRIPTOR;
+ io->write_proc(&b, 1, 1, handle);
+ io->write_proc(&left, 2, 1, handle);
+ io->write_proc(&top, 2, 1, handle);
+ io->write_proc(&width, 2, 1, handle);
+ io->write_proc(&height, 2, 1, handle);
+ packed = 0;
+ if ( !no_local_palette ) packed |= GIF_PACKED_ID_HAVELCT | ((bpp - 1) & GIF_PACKED_ID_LCTSIZE);
+ if ( interlaced ) packed |= GIF_PACKED_ID_INTERLACED;
+ io->write_proc(&packed, 1, 1, handle);
+
+ //Local Color Table
+ if ( !no_local_palette ) {
+ int palsize = 1 << bpp;
+ for ( int i = 0; i < palsize; i++ ) {
+ io->write_proc(&pal[i].rgbRed, 1, 1, handle);
+ io->write_proc(&pal[i].rgbGreen, 1, 1, handle);
+ io->write_proc(&pal[i].rgbBlue, 1, 1, handle);
+ }
+ }
+
+
+ //LZW Minimum Code Size
+ b = (BYTE)(bpp == 1 ? 2 : bpp);
+ io->write_proc(&b, 1, 1, handle);
+ StringTable *stringtable = new(std::nothrow) StringTable;
+ stringtable->Initialize(b);
+ stringtable->CompressStart(bpp, width);
+
+ //Image Data Sub-blocks
+ int y = 0, interlacepass = 0, line = FreeImage_GetLine(dib);
+ BYTE buf[255], *bufptr = buf; //255 is the max sub-block length
+ int size = sizeof(buf);
+ b = sizeof(buf);
+ while( y < output_height ) {
+ memcpy(stringtable->FillInputBuffer(line), FreeImage_GetScanLine(dib, output_height - y - 1), line);
+ while( stringtable->Compress(bufptr, &size)) {
+ bufptr += size;
+ if ( bufptr - buf == sizeof(buf)) {
+ io->write_proc(&b, 1, 1, handle);
+ io->write_proc(buf, sizeof(buf), 1, handle);
+ size = sizeof(buf);
+ bufptr = buf;
+ } else {
+ size = (int)(sizeof(buf) - (bufptr - buf));
+ }
+ }
+ if ( interlaced ) {
+ y += g_GifInterlaceIncrement[interlacepass];
+ if ( y >= output_height && ++interlacepass < GIF_INTERLACE_PASSES ) {
+ y = g_GifInterlaceOffset[interlacepass];
+ }
+ } else {
+ y++;
+ }
+ }
+ size = (int)(bufptr - buf);
+ BYTE last[4];
+ w = (WORD)stringtable->CompressEnd(last);
+ if ( size + w >= sizeof(buf)) {
+ //one last full size sub-block
+ io->write_proc(&b, 1, 1, handle);
+ io->write_proc(buf, size, 1, handle);
+ io->write_proc(last, sizeof(buf) - size, 1, handle);
+ //and possibly a tiny additional sub-block
+ b = (BYTE)(w - (sizeof(buf) - size));
+ if ( b > 0 ) {
+ io->write_proc(&b, 1, 1, handle);
+ io->write_proc(last + w - b, b, 1, handle);
+ }
+ } else {
+ //last sub-block less than full size
+ b = (BYTE)(size + w);
+ io->write_proc(&b, 1, 1, handle);
+ io->write_proc(buf, size, 1, handle);
+ io->write_proc(last, w, 1, handle);
+ }
+
+ //Block Terminator
+ b = 0;
+ io->write_proc(&b, 1, 1, handle);
+
+ delete stringtable;
+
+ } catch (const char *msg) {
+ FreeImage_OutputMessageProc(s_format_id, msg);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitGIF(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = PageCount;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp b/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp
new file mode 100644
index 0000000000..a0334e1143
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp
@@ -0,0 +1,719 @@
+// ==========================================================
+// HDR Loader and writer
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// RGBE library
+// ==========================================================
+
+// ----------------------------------------------------------
+
+// maximum size of a line in the header
+#define HDR_MAXLINE 256
+
+// flags indicating which fields in an rgbeHeaderInfo are valid
+#define RGBE_VALID_PROGRAMTYPE 0x01
+#define RGBE_VALID_COMMENT 0x02
+#define RGBE_VALID_GAMMA 0x04
+#define RGBE_VALID_EXPOSURE 0x08
+
+// offsets to red, green, and blue components in a data (float) pixel
+#define RGBE_DATA_RED 0
+#define RGBE_DATA_GREEN 1
+#define RGBE_DATA_BLUE 2
+
+// ----------------------------------------------------------
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagHeaderInfo {
+ int valid; // indicate which fields are valid
+ char programtype[16]; // listed at beginning of file to identify it after "#?". defaults to "RGBE"
+ char comment[HDR_MAXLINE]; // comment beginning with "# "
+ float gamma; // image has already been gamma corrected with given gamma. defaults to 1.0 (no correction)
+ float exposure; // a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0
+} rgbeHeaderInfo;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+typedef enum {
+ rgbe_read_error,
+ rgbe_write_error,
+ rgbe_format_error,
+ rgbe_memory_error,
+} rgbe_error_code;
+
+// ----------------------------------------------------------
+// Prototypes
+// ----------------------------------------------------------
+
+static BOOL rgbe_Error(rgbe_error_code error_code, const char *msg);
+static BOOL rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length);
+static inline void rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf);
+static inline void rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]);
+static BOOL rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info);
+static BOOL rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info);
+static BOOL rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
+static BOOL rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels);
+static BOOL rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines);
+static BOOL rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes);
+static BOOL rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines);
+static BOOL rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
+static BOOL rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info);
+
+// ----------------------------------------------------------
+
+/**
+Default error routine. change this to change error handling
+*/
+static BOOL
+rgbe_Error(rgbe_error_code error_code, const char *msg) {
+ switch (error_code) {
+ case rgbe_read_error:
+ FreeImage_OutputMessageProc(s_format_id, "RGBE read error");
+ break;
+ case rgbe_write_error:
+ FreeImage_OutputMessageProc(s_format_id, "RGBE write error");
+ break;
+ case rgbe_format_error:
+ FreeImage_OutputMessageProc(s_format_id, "RGBE bad file format: %s\n", msg);
+ break;
+ default:
+ case rgbe_memory_error:
+ FreeImage_OutputMessageProc(s_format_id, "RGBE error: %s\n",msg);
+ }
+
+ return FALSE;
+}
+
+/**
+Get a line from a ASCII io stream
+*/
+static BOOL
+rgbe_GetLine(FreeImageIO *io, fi_handle handle, char *buffer, int length) {
+ int i;
+ memset(buffer, 0, length);
+ for(i = 0; i < length; i++) {
+ if (!io->read_proc(&buffer[i], 1, 1, handle))
+ return FALSE;
+ if(buffer[i] == 0x0A)
+ break;
+ }
+
+ return (i < length) ? TRUE : FALSE;
+}
+
+/**
+Standard conversion from float pixels to rgbe pixels.
+Note: you can remove the "inline"s if your compiler complains about it
+*/
+static inline void
+rgbe_FloatToRGBE(BYTE rgbe[4], FIRGBF *rgbf) {
+ float v;
+ int e;
+
+ v = rgbf->red;
+ if (rgbf->green > v) v = rgbf->green;
+ if (rgbf->blue > v) v = rgbf->blue;
+ if (v < 1e-32) {
+ rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
+ }
+ else {
+ v = (float)(frexp(v, &e) * 256.0 / v);
+ rgbe[0] = (BYTE) (rgbf->red * v);
+ rgbe[1] = (BYTE) (rgbf->green * v);
+ rgbe[2] = (BYTE) (rgbf->blue * v);
+ rgbe[3] = (BYTE) (e + 128);
+ }
+}
+
+/**
+Standard conversion from rgbe to float pixels.
+Note: Ward uses ldexp(col+0.5,exp-(128+8)).
+However we wanted pixels in the range [0,1] to map back into the range [0,1].
+*/
+static inline void
+rgbe_RGBEToFloat(FIRGBF *rgbf, BYTE rgbe[4]) {
+ if (rgbe[3]) { // nonzero pixel
+ float f = (float)(ldexp(1.0, rgbe[3] - (int)(128+8)));
+ rgbf->red = rgbe[0] * f;
+ rgbf->green = rgbe[1] * f;
+ rgbf->blue = rgbe[2] * f;
+
+ }
+ else {
+ rgbf->red = rgbf->green = rgbf->blue = 0;
+ }
+}
+
+/**
+Minimal header reading. Modify if you want to parse more information
+*/
+static BOOL
+rgbe_ReadHeader(FreeImageIO *io, fi_handle handle, unsigned *width, unsigned *height, rgbeHeaderInfo *header_info) {
+ char buf[HDR_MAXLINE];
+ float tempf;
+ int i;
+ BOOL bFormatFound = FALSE;
+ BOOL bHeaderFound = FALSE;
+
+ header_info->valid = 0;
+ header_info->programtype[0] = 0;
+ header_info->gamma = 1.0;
+ header_info->exposure = 1.0;
+
+ // get the first line
+ if (!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
+ return rgbe_Error(rgbe_read_error, NULL);
+
+ // check the signature
+
+ if ((buf[0] != '#')||(buf[1] != '?')) {
+ // if you don't want to require the magic token then comment the next line
+ return rgbe_Error(rgbe_format_error,"bad initial token");
+ }
+ else {
+ header_info->valid |= RGBE_VALID_PROGRAMTYPE;
+ for(i = 0; i < sizeof(header_info->programtype) - 1; i++) {
+ if ((buf[i+2] == 0) || isspace(buf[i+2]))
+ break;
+ header_info->programtype[i] = buf[i+2];
+ }
+ header_info->programtype[i] = 0;
+ }
+
+ for (;;) {
+ // get next line
+ if (!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
+ return rgbe_Error(rgbe_read_error, NULL);
+
+ if ((buf[0] == 0) || (buf[0] == '\n')) {
+ // end of header so break out of loop
+ bHeaderFound = TRUE;
+ break;
+ }
+ else if(strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0) {
+ bFormatFound = TRUE;
+ }
+ else if(sscanf(buf, "GAMMA=%g", &tempf) == 1) {
+ header_info->gamma = tempf;
+ header_info->valid |= RGBE_VALID_GAMMA;
+ }
+ else if(sscanf(buf,"EXPOSURE=%g",&tempf) == 1) {
+ header_info->exposure = tempf;
+ header_info->valid |= RGBE_VALID_EXPOSURE;
+ }
+ else if ((buf[0] == '#') && (buf[1] == 0x20)) {
+ header_info->valid |= RGBE_VALID_COMMENT;
+ strcpy(header_info->comment, buf);
+ }
+ }
+ if (!bHeaderFound || !bFormatFound) {
+ return rgbe_Error(rgbe_format_error, "invalid header");
+ }
+
+ // get next line
+ if (!rgbe_GetLine(io, handle, buf, HDR_MAXLINE))
+ return rgbe_Error(rgbe_read_error, NULL);
+
+ // get the image width & height
+ if(sscanf(buf,"-Y %d +X %d", height, width) < 2) {
+ if(sscanf(buf,"+X %d +Y %d", height, width) < 2) {
+ return rgbe_Error(rgbe_format_error, "missing image size specifier");
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ default minimal header. modify if you want more information in header
+*/
+static BOOL
+rgbe_WriteHeader(FreeImageIO *io, fi_handle handle, unsigned width, unsigned height, rgbeHeaderInfo *info) {
+ char buffer[HDR_MAXLINE];
+
+ const char *programtype = "RADIANCE";
+
+ if(info && (info->valid & RGBE_VALID_PROGRAMTYPE)) {
+ programtype = info->programtype;
+ }
+ // The #? is to identify file type, the programtype is optional
+ sprintf(buffer, "#?%s\n", programtype);
+ if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+ sprintf(buffer, "%s\n", info->comment);
+ if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+ sprintf(buffer, "FORMAT=32-bit_rle_rgbe\n");
+ if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+ if(info && (info->valid & RGBE_VALID_GAMMA)) {
+ sprintf(buffer, "GAMMA=%g\n", info->gamma);
+ if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+ }
+ if(info && (info->valid & RGBE_VALID_EXPOSURE)) {
+ sprintf(buffer,"EXPOSURE=%g\n", info->exposure);
+ if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+ }
+ sprintf(buffer, "\n-Y %d +X %d\n", height, width);
+ if(io->write_proc(buffer, 1, (unsigned int)strlen(buffer), handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+
+ return TRUE;
+}
+
+static BOOL
+rgbe_ReadMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
+ return TRUE;
+}
+static BOOL
+rgbe_WriteMetadata(FIBITMAP *dib, rgbeHeaderInfo *header_info) {
+ header_info->gamma = 1;
+ header_info->valid |= RGBE_VALID_GAMMA;
+ header_info->exposure = 0;
+ header_info->valid |= RGBE_VALID_EXPOSURE;
+
+ return TRUE;
+}
+
+/**
+Simple read routine. Will not correctly handle run length encoding
+*/
+static BOOL
+rgbe_ReadPixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
+ BYTE rgbe[4];
+
+ for(unsigned x = 0; x < numpixels; x++) {
+ if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) {
+ return rgbe_Error(rgbe_read_error, NULL);
+ }
+ rgbe_RGBEToFloat(&data[x], rgbe);
+ }
+
+ return TRUE;
+}
+
+/**
+ Simple write routine that does not use run length encoding.
+ These routines can be made faster by allocating a larger buffer and
+ fread-ing and fwrite-ing the data in larger chunks.
+*/
+static BOOL
+rgbe_WritePixels(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned numpixels) {
+ BYTE rgbe[4];
+
+ for(unsigned x = 0; x < numpixels; x++) {
+ rgbe_FloatToRGBE(rgbe, &data[x]);
+ if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+ }
+
+ return TRUE;
+}
+
+static BOOL
+rgbe_ReadPixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, int scanline_width, unsigned num_scanlines) {
+ BYTE rgbe[4], *scanline_buffer, *ptr, *ptr_end;
+ int i, count;
+ BYTE buf[2];
+
+ if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
+ // run length encoding is not allowed so read flat
+ return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines);
+ }
+ scanline_buffer = NULL;
+ // read in each successive scanline
+ while(num_scanlines > 0) {
+ if(io->read_proc(rgbe, 1, sizeof(rgbe), handle) < 1) {
+ free(scanline_buffer);
+ return rgbe_Error(rgbe_read_error,NULL);
+ }
+ if ((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) {
+ // this file is not run length encoded
+ rgbe_RGBEToFloat(data, rgbe);
+ data ++;
+ free(scanline_buffer);
+ return rgbe_ReadPixels(io, handle, data, scanline_width * num_scanlines - 1);
+ }
+ if ((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
+ free(scanline_buffer);
+ return rgbe_Error(rgbe_format_error,"wrong scanline width");
+ }
+ if(scanline_buffer == NULL) {
+ scanline_buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width);
+ if(scanline_buffer == NULL) {
+ return rgbe_Error(rgbe_memory_error, "unable to allocate buffer space");
+ }
+ }
+
+ ptr = &scanline_buffer[0];
+ // read each of the four channels for the scanline into the buffer
+ for(i = 0; i < 4; i++) {
+ ptr_end = &scanline_buffer[(i+1)*scanline_width];
+ while(ptr < ptr_end) {
+ if(io->read_proc(buf, 1, 2 * sizeof(BYTE), handle) < 1) {
+ free(scanline_buffer);
+ return rgbe_Error(rgbe_read_error, NULL);
+ }
+ if(buf[0] > 128) {
+ // a run of the same value
+ count = buf[0] - 128;
+ if ((count == 0) || (count > ptr_end - ptr)) {
+ free(scanline_buffer);
+ return rgbe_Error(rgbe_format_error, "bad scanline data");
+ }
+ while(count-- > 0)
+ *ptr++ = buf[1];
+ }
+ else {
+ // a non-run
+ count = buf[0];
+ if ((count == 0) || (count > ptr_end - ptr)) {
+ free(scanline_buffer);
+ return rgbe_Error(rgbe_format_error, "bad scanline data");
+ }
+ *ptr++ = buf[1];
+ if (--count > 0) {
+ if(io->read_proc(ptr, 1, sizeof(BYTE) * count, handle) < 1) {
+ free(scanline_buffer);
+ return rgbe_Error(rgbe_read_error, NULL);
+ }
+ ptr += count;
+ }
+ }
+ }
+ }
+ // now convert data from buffer into floats
+ for(i = 0; i < scanline_width; i++) {
+ rgbe[0] = scanline_buffer[i];
+ rgbe[1] = scanline_buffer[i+scanline_width];
+ rgbe[2] = scanline_buffer[i+2*scanline_width];
+ rgbe[3] = scanline_buffer[i+3*scanline_width];
+ rgbe_RGBEToFloat(data, rgbe);
+ data ++;
+ }
+
+ num_scanlines--;
+ }
+
+ free(scanline_buffer);
+
+ return TRUE;
+}
+
+/**
+ The code below is only needed for the run-length encoded files.
+ Run length encoding adds considerable complexity but does
+ save some space. For each scanline, each channel (r,g,b,e) is
+ encoded separately for better compression.
+ @return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes) {
+ static const int MINRUNLENGTH = 4;
+ int cur, beg_run, run_count, old_run_count, nonrun_count;
+ BYTE buf[2];
+
+ cur = 0;
+ while(cur < numbytes) {
+ beg_run = cur;
+ // find next run of length at least 4 if one exists
+ run_count = old_run_count = 0;
+ while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
+ beg_run += run_count;
+ old_run_count = run_count;
+ run_count = 1;
+ while((data[beg_run] == data[beg_run + run_count]) && (beg_run + run_count < numbytes) && (run_count < 127))
+ run_count++;
+ }
+ // if data before next big run is a short run then write it as such
+ if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
+ buf[0] = (BYTE)(128 + old_run_count); // write short run
+ buf[1] = data[cur];
+ if(io->write_proc(buf, 2 * sizeof(BYTE), 1, handle) < 1)
+ return rgbe_Error(rgbe_write_error, NULL);
+ cur = beg_run;
+ }
+ // write out bytes until we reach the start of the next run
+ while(cur < beg_run) {
+ nonrun_count = beg_run - cur;
+ if (nonrun_count > 128)
+ nonrun_count = 128;
+ buf[0] = (BYTE)nonrun_count;
+ if(io->write_proc(buf, sizeof(buf[0]), 1, handle) < 1)
+ return rgbe_Error(rgbe_write_error,NULL);
+ if(io->write_proc(&data[cur], sizeof(data[0]) * nonrun_count, 1, handle) < 1)
+ return rgbe_Error(rgbe_write_error,NULL);
+ cur += nonrun_count;
+ }
+ // write out next run if one was found
+ if (run_count >= MINRUNLENGTH) {
+ buf[0] = (BYTE)(128 + run_count);
+ buf[1] = data[beg_run];
+ if(io->write_proc(buf, sizeof(buf[0]) * 2, 1, handle) < 1)
+ return rgbe_Error(rgbe_write_error,NULL);
+ cur += run_count;
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL
+rgbe_WritePixels_RLE(FreeImageIO *io, fi_handle handle, FIRGBF *data, unsigned scanline_width, unsigned num_scanlines) {
+ BYTE rgbe[4];
+ BYTE *buffer;
+
+ if ((scanline_width < 8)||(scanline_width > 0x7fff)) {
+ // run length encoding is not allowed so write flat
+ return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
+ }
+ buffer = (BYTE*)malloc(sizeof(BYTE) * 4 * scanline_width);
+ if (buffer == NULL) {
+ // no buffer space so write flat
+ return rgbe_WritePixels(io, handle, data, scanline_width * num_scanlines);
+ }
+ while(num_scanlines-- > 0) {
+ rgbe[0] = (BYTE)2;
+ rgbe[1] = (BYTE)2;
+ rgbe[2] = (BYTE)(scanline_width >> 8);
+ rgbe[3] = (BYTE)(scanline_width & 0xFF);
+ if(io->write_proc(rgbe, sizeof(rgbe), 1, handle) < 1) {
+ free(buffer);
+ return rgbe_Error(rgbe_write_error, NULL);
+ }
+ for(unsigned x = 0; x < scanline_width; x++) {
+ rgbe_FloatToRGBE(rgbe, data);
+ buffer[x] = rgbe[0];
+ buffer[x+scanline_width] = rgbe[1];
+ buffer[x+2*scanline_width] = rgbe[2];
+ buffer[x+3*scanline_width] = rgbe[3];
+ data ++;
+ }
+ // write out each of the four channels separately run length encoded
+ // first red, then green, then blue, then exponent
+ for(int i = 0; i < 4; i++) {
+ BOOL bOK = rgbe_WriteBytes_RLE(io, handle, &buffer[i*scanline_width], scanline_width);
+ if (!bOK) {
+ free(buffer);
+ return bOK;
+ }
+ }
+ }
+ free(buffer);
+
+ return TRUE;
+}
+
+
+// ----------------------------------------------------------
+
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "HDR";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "High Dynamic Range Image";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "hdr";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/vnd.radiance";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE hdr_signature[] = { '#', '?' };
+ BYTE signature[] = { 0, 0 };
+
+ io->read_proc(signature, 1, 2, handle);
+
+ return (memcmp(hdr_signature, signature, 2) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_RGBF) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ FIBITMAP *dib = NULL;
+
+ if (!handle) {
+ return NULL;
+ }
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+
+ rgbeHeaderInfo header_info;
+ unsigned width, height;
+
+ // Read the header
+ if(rgbe_ReadHeader(io, handle, &width, &height, &header_info) == FALSE) {
+ return NULL;
+ }
+
+ // allocate a RGBF image
+ dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBF, width, height);
+ if (!dib) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // set the metadata as comments
+ rgbe_ReadMetadata(dib, &header_info);
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // read the image pixels and fill the dib
+
+ for(unsigned y = 0; y < height; y++) {
+ FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+ if (!rgbe_ReadPixels_RLE(io, handle, scanline, width, 1)) {
+ FreeImage_Unload(dib);
+ return NULL;
+ }
+ }
+
+ }
+ catch(const char *text) {
+ if(dib != NULL) {
+ FreeImage_Unload(dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+
+ return dib;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if (!dib) return FALSE;
+
+ if(FreeImage_GetImageType(dib) != FIT_RGBF) {
+ return FALSE;
+ }
+
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+
+ // write the header
+
+ rgbeHeaderInfo header_info;
+ memset(&header_info, 0, sizeof(rgbeHeaderInfo));
+ // fill the header with correct gamma and exposure
+ rgbe_WriteMetadata(dib, &header_info);
+ // fill a comment
+ sprintf(header_info.comment, "# Made with FreeImage %s", FreeImage_GetVersion());
+ if (!rgbe_WriteHeader(io, handle, width, height, &header_info)) {
+ return FALSE;
+ }
+
+ // write each scanline
+
+ for(unsigned y = 0; y < height; y++) {
+ FIRGBF *scanline = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+ if (!rgbe_WritePixels_RLE(io, handle, scanline, width, 1)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitHDR(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginICO.cpp b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp
new file mode 100644
index 0000000000..d0ece3514c
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp
@@ -0,0 +1,805 @@
+// ==========================================================
+// ICO Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+// These next two structs represent how the icon information is stored
+// in an ICO file.
+
+typedef struct tagICONHEADER {
+ WORD idReserved; // reserved
+ WORD idType; // resource type (1 for icons)
+ WORD idCount; // how many images?
+} ICONHEADER;
+
+typedef struct tagICONDIRECTORYENTRY {
+ BYTE bWidth; // width of the image
+ BYTE bHeight; // height of the image (times 2)
+ BYTE bColorCount; // number of colors in image (0 if >=8bpp)
+ BYTE bReserved; // reserved
+ WORD wPlanes; // color Planes
+ WORD wBitCount; // bits per pixel
+ DWORD dwBytesInRes; // how many bytes in this resource?
+ DWORD dwImageOffset; // where in the file is this image
+} ICONDIRENTRY;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Static helpers
+// ==========================================================
+
+/** How wide, in bytes, would this many bits be, DWORD aligned ?
+*/
+static int
+WidthBytes(int bits) {
+ return ((((bits) + 31)>>5)<<2);
+}
+
+/** Calculates the size of a single icon image
+@return Returns the size for that image
+*/
+static DWORD
+CalculateImageSize(FIBITMAP* icon_dib) {
+ DWORD dwNumBytes = 0;
+
+ unsigned colors = FreeImage_GetColorsUsed(icon_dib);
+ unsigned width = FreeImage_GetWidth(icon_dib);
+ unsigned height = FreeImage_GetHeight(icon_dib);
+ unsigned pitch = FreeImage_GetPitch(icon_dib);
+
+ dwNumBytes = sizeof( BITMAPINFOHEADER ); // header
+ dwNumBytes += colors * sizeof(RGBQUAD); // palette
+ dwNumBytes += height * pitch; // XOR mask
+ dwNumBytes += height * WidthBytes(width); // AND mask
+
+ return dwNumBytes;
+}
+
+/** Calculates the file offset for an icon image
+@return Returns the file offset for that image
+*/
+static DWORD
+CalculateImageOffset(std::vector<FIBITMAP*>& vPages, int nIndex ) {
+ DWORD dwSize;
+
+ // calculate the ICO header size
+ dwSize = sizeof(ICONHEADER);
+ // add the ICONDIRENTRY's
+ dwSize += (DWORD)( vPages.size() * sizeof(ICONDIRENTRY));
+ // add the sizes of the previous images
+ for(int k = 0; k < nIndex; k++) {
+ FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
+ dwSize += CalculateImageSize(icon_dib);
+ }
+
+ return dwSize;
+}
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapInfoHeader(BITMAPINFOHEADER *header) {
+ SwapLong(&header->biSize);
+ SwapLong((DWORD *)&header->biWidth);
+ SwapLong((DWORD *)&header->biHeight);
+ SwapShort(&header->biPlanes);
+ SwapShort(&header->biBitCount);
+ SwapLong(&header->biCompression);
+ SwapLong(&header->biSizeImage);
+ SwapLong((DWORD *)&header->biXPelsPerMeter);
+ SwapLong((DWORD *)&header->biYPelsPerMeter);
+ SwapLong(&header->biClrUsed);
+ SwapLong(&header->biClrImportant);
+}
+
+static void
+SwapIconHeader(ICONHEADER *header) {
+ SwapShort(&header->idReserved);
+ SwapShort(&header->idType);
+ SwapShort(&header->idCount);
+}
+
+static void
+SwapIconDirEntries(ICONDIRENTRY *ent, int num) {
+ while(num) {
+ SwapShort(&ent->wPlanes);
+ SwapShort(&ent->wBitCount);
+ SwapLong(&ent->dwBytesInRes);
+ SwapLong(&ent->dwImageOffset);
+ num--;
+ ent++;
+ }
+}
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "ICO";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Windows Icon";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "ico";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/vnd.microsoft.icon";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ ICONHEADER icon_header;
+
+ io->read_proc(&icon_header, sizeof(ICONHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(&icon_header);
+#endif
+
+ return ((icon_header.idReserved == 0) && (icon_header.idType == 1) && (icon_header.idCount > 0));
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 1) ||
+ (depth == 4) ||
+ (depth == 8) ||
+ (depth == 16) ||
+ (depth == 24) ||
+ (depth == 32)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ // Allocate memory for the header structure
+ ICONHEADER *lpIH = (ICONHEADER*)malloc(sizeof(ICONHEADER));
+ if(lpIH == NULL) {
+ return NULL;
+ }
+
+ if (read) {
+ // Read in the header
+ io->read_proc(lpIH, 1, sizeof(ICONHEADER), handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(lpIH);
+#endif
+
+ if (!(lpIH->idReserved == 0) || !(lpIH->idType == 1)) {
+ // Not an ICO file
+ free(lpIH);
+ return NULL;
+ }
+ }
+ else {
+ // Fill the header
+ lpIH->idReserved = 0;
+ lpIH->idType = 1;
+ lpIH->idCount = 0;
+ }
+
+ return lpIH;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+ // free the header structure
+ ICONHEADER *lpIH = (ICONHEADER*)data;
+ free(lpIH);
+}
+
+// ----------------------------------------------------------
+
+static int DLL_CALLCONV
+PageCount(FreeImageIO *io, fi_handle handle, void *data) {
+ ICONHEADER *lpIH = (ICONHEADER*)data;
+
+ if(lpIH) {
+ return lpIH->idCount;
+ }
+ return 1;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP*
+LoadStandardIcon(FreeImageIO *io, fi_handle handle, int flags, BOOL header_only) {
+ FIBITMAP *dib = NULL;
+
+ // load the BITMAPINFOHEADER
+ BITMAPINFOHEADER bmih;
+ io->read_proc(&bmih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(&bmih);
+#endif
+
+ // allocate the bitmap
+ int width = bmih.biWidth;
+ int height = bmih.biHeight / 2; // height == xor + and mask
+ unsigned bit_count = bmih.biBitCount;
+ unsigned line = CalculateLine(width, bit_count);
+ unsigned pitch = CalculatePitch(line);
+
+ // allocate memory for one icon
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, bit_count);
+
+ if (dib == NULL) {
+ return NULL;
+ }
+
+ if ( bmih.biBitCount <= 8 ) {
+ // read the palette data
+ io->read_proc(FreeImage_GetPalette(dib), CalculateUsedPaletteEntries(bit_count) * sizeof(RGBQUAD), 1, handle);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ for(unsigned i = 0; i < CalculateUsedPaletteEntries(bit_count); i++) {
+ INPLACESWAP(pal[i].rgbRed, pal[i].rgbBlue);
+ }
+#endif
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // read the icon
+ io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+ if (bit_count == 16) {
+ for(int y = 0; y < height; y++) {
+ WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ SwapShort(pixel);
+ pixel++;
+ }
+ }
+ }
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ if (bit_count == 24 || bit_count == 32) {
+ for(int y = 0; y < height; y++) {
+ BYTE *pixel = FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ INPLACESWAP(pixel[0], pixel[2]);
+ pixel += (bit_count>>3);
+ }
+ }
+ }
+#endif
+ // bitmap has been loaded successfully!
+
+ // convert to 32bpp and generate an alpha channel
+ if ((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) {
+ FIBITMAP *dib32 = FreeImage_ConvertTo32Bits(dib);
+ FreeImage_Unload(dib);
+
+ if (dib32 == NULL) {
+ return NULL;
+ }
+
+ int width_and = WidthBytes(width);
+ BYTE *line_and = (BYTE *)malloc(width_and);
+
+ if ( line_and == NULL ) {
+ FreeImage_Unload(dib32);
+ return NULL;
+ }
+
+ //loop through each line of the AND-mask generating the alpha channel, invert XOR-mask
+ for(int y = 0; y < height; y++) {
+ RGBQUAD *quad = (RGBQUAD *)FreeImage_GetScanLine(dib32, y);
+ io->read_proc(line_and, width_and, 1, handle);
+ for(int x = 0; x < width; x++) {
+ quad->rgbReserved = (line_and[x>>3] & (0x80 >> (x & 0x07))) != 0 ? 0 : 0xFF;
+ if ( quad->rgbReserved == 0 ) {
+ quad->rgbBlue ^= 0xFF;
+ quad->rgbGreen ^= 0xFF;
+ quad->rgbRed ^= 0xFF;
+ }
+ quad++;
+ }
+ }
+ free(line_and);
+
+ return dib32;
+ }
+
+ return dib;
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (page == -1) {
+ page = 0;
+ }
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ if (handle != NULL) {
+ FIBITMAP *dib = NULL;
+
+ // get the icon header
+ ICONHEADER *icon_header = (ICONHEADER*)data;
+
+ if (icon_header) {
+ // load the icon descriptions
+ ICONDIRENTRY *icon_list = (ICONDIRENTRY*)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
+ if(icon_list == NULL) {
+ return NULL;
+ }
+ io->seek_proc(handle, sizeof(ICONHEADER), SEEK_SET);
+ io->read_proc(icon_list, icon_header->idCount * sizeof(ICONDIRENTRY), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconDirEntries(icon_list, icon_header->idCount);
+#endif
+
+ // load the requested icon
+ if (page < icon_header->idCount) {
+ // seek to the start of the bitmap data for the icon
+ io->seek_proc(handle, 0, SEEK_SET);
+ io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_CUR);
+
+ if ((icon_list[page].bWidth == 0) && (icon_list[page].bHeight == 0)) {
+ // Vista icon support
+ dib = FreeImage_LoadFromHandle(FIF_PNG, io, handle, header_only ? FIF_LOAD_NOPIXELS : PNG_DEFAULT);
+ }
+ else {
+ // standard icon support
+ // see http://msdn.microsoft.com/en-us/library/ms997538.aspx
+ dib = LoadStandardIcon(io, handle, flags, header_only);
+ }
+
+ free(icon_list);
+
+ return dib;
+
+ } else {
+ free(icon_list);
+ FreeImage_OutputMessageProc(s_format_id, "Page doesn't exist");
+ }
+ } else {
+ FreeImage_OutputMessageProc(s_format_id, "File is not an ICO file");
+ }
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------
+
+static BOOL
+SaveStandardIcon(FreeImageIO *io, FIBITMAP *dib, fi_handle handle) {
+ BITMAPINFOHEADER *bmih = NULL;
+
+ // write the BITMAPINFOHEADER
+ bmih = FreeImage_GetInfoHeader(dib);
+ bmih->biHeight *= 2; // height == xor + and mask
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(bmih);
+#endif
+ io->write_proc(bmih, sizeof(BITMAPINFOHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapInfoHeader(bmih);
+#endif
+ bmih->biHeight /= 2;
+
+ // write the palette data
+ if (FreeImage_GetPalette(dib) != NULL) {
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ FILE_BGRA bgra;
+ for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
+ bgra.b = pal[i].rgbBlue;
+ bgra.g = pal[i].rgbGreen;
+ bgra.r = pal[i].rgbRed;
+ bgra.a = pal[i].rgbReserved;
+ io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle);
+ }
+ }
+
+ // write the bits
+ int width = bmih->biWidth;
+ int height = bmih->biHeight;
+ unsigned bit_count = bmih->biBitCount;
+ unsigned line = CalculateLine(width, bit_count);
+ unsigned pitch = CalculatePitch(line);
+ int size_xor = height * pitch;
+ int size_and = height * WidthBytes(width);
+
+ // XOR mask
+#ifdef FREEIMAGE_BIGENDIAN
+ if (bit_count == 16) {
+ WORD pixel;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ pixel = ((WORD *)line)[x];
+ SwapShort(&pixel);
+ if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+ } else
+#endif
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ if (bit_count == 24) {
+ FILE_BGR bgr;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x;
+ bgr.b = triple->rgbtBlue;
+ bgr.g = triple->rgbtGreen;
+ bgr.r = triple->rgbtRed;
+ if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+ } else if (bit_count == 32) {
+ FILE_BGRA bgra;
+ for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) {
+ BYTE *line = FreeImage_GetScanLine(dib, y);
+ for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) {
+ RGBQUAD *quad = ((RGBQUAD *)line)+x;
+ bgra.b = quad->rgbBlue;
+ bgra.g = quad->rgbGreen;
+ bgra.r = quad->rgbRed;
+ bgra.a = quad->rgbReserved;
+ if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1)
+ return FALSE;
+ }
+ }
+ } else
+#endif
+#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ {
+#endif
+ BYTE *xor_mask = FreeImage_GetBits(dib);
+ io->write_proc(xor_mask, size_xor, 1, handle);
+#if defined(FREEIMAGE_BIGENDIAN) || FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ }
+#endif
+ // AND mask
+ BYTE *and_mask = (BYTE*)malloc(size_and);
+ if (!and_mask) {
+ return FALSE;
+ }
+
+ if(FreeImage_IsTransparent(dib)) {
+
+ if(bit_count == 32) {
+ // create the AND mask from the alpha channel
+
+ int width_and = WidthBytes(width);
+ BYTE *and_bits = and_mask;
+
+ // clear the mask
+ memset(and_mask, 0, size_and);
+
+ for(int y = 0; y < height; y++) {
+ RGBQUAD *bits = (RGBQUAD*)FreeImage_GetScanLine(dib, y);
+
+ for(int x = 0; x < width; x++) {
+ if(bits[x].rgbReserved != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+
+ and_bits += width_and;
+ }
+ }
+ else if(bit_count <= 8) {
+ // create the AND mask from the transparency table
+
+ BYTE *trns = FreeImage_GetTransparencyTable(dib);
+
+ int width_and = WidthBytes(width);
+ BYTE *and_bits = and_mask;
+
+ // clear the mask
+ memset(and_mask, 0, size_and);
+
+ switch(FreeImage_GetBPP(dib)) {
+ case 1:
+ {
+ for(int y = 0; y < height; y++) {
+ BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ // get pixel at (x, y)
+ BYTE index = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+ if(trns[index] != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+ and_bits += width_and;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ for(int y = 0; y < height; y++) {
+ BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ // get pixel at (x, y)
+ BYTE shift = (BYTE)((1 - x % 2) << 2);
+ BYTE index = (bits[x >> 1] & (0x0F << shift)) >> shift;
+ if(trns[index] != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+ and_bits += width_and;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ for(int y = 0; y < height; y++) {
+ BYTE *bits = (BYTE*)FreeImage_GetScanLine(dib, y);
+ for(int x = 0; x < width; x++) {
+ // get pixel at (x, y)
+ BYTE index = bits[x];
+ if(trns[index] != 0xFF) {
+ // set any transparent color to full transparency
+ and_bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ }
+ and_bits += width_and;
+ }
+ }
+ break;
+
+ }
+ }
+ }
+ else {
+ // empty AND mask
+ memset(and_mask, 0, size_and);
+ }
+
+ io->write_proc(and_mask, size_and, 1, handle);
+ free(and_mask);
+
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ ICONHEADER *icon_header = NULL;
+ std::vector<FIBITMAP*> vPages;
+ int k;
+
+ if (!dib || !handle || !data) {
+ return FALSE;
+ }
+
+ // check format limits
+ unsigned w = FreeImage_GetWidth(dib);
+ unsigned h = FreeImage_GetHeight(dib);
+ if ((w < 16) || (w > 256) || (h < 16) || (h > 256) || (w != h)) {
+ FreeImage_OutputMessageProc(s_format_id, "Unsupported icon size: width x height = %d x %d", w, h);
+ return FALSE;
+ }
+
+ if (page == -1) {
+ page = 0;
+ }
+
+ // get the icon header
+ icon_header = (ICONHEADER*)data;
+
+ try {
+ FIBITMAP *icon_dib = NULL;
+
+ // load all icons
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = Load(io, handle, k, flags, data);
+ if (!icon_dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+ vPages.push_back(icon_dib);
+ }
+
+ // add the page
+ icon_dib = FreeImage_Clone(dib);
+ vPages.push_back(icon_dib);
+ icon_header->idCount++;
+
+ // write the header
+ io->seek_proc(handle, 0, SEEK_SET);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(icon_header);
+#endif
+ io->write_proc(icon_header, sizeof(ICONHEADER), 1, handle);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconHeader(icon_header);
+#endif
+
+ // write all icons
+ // ...
+
+ // save the icon descriptions
+
+ ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header->idCount * sizeof(ICONDIRENTRY));
+ if (!icon_list) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ memset(icon_list, 0, icon_header->idCount * sizeof(ICONDIRENTRY));
+
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = (FIBITMAP*)vPages[k];
+
+ // convert internal format to ICONDIRENTRY
+ // take into account Vista icons whose size is 256x256
+ const BITMAPINFOHEADER *bmih = FreeImage_GetInfoHeader(icon_dib);
+ icon_list[k].bWidth = (bmih->biWidth > 255) ? 0 : (BYTE)bmih->biWidth;
+ icon_list[k].bHeight = (bmih->biHeight > 255) ? 0 : (BYTE)bmih->biHeight;
+ icon_list[k].bReserved = 0;
+ icon_list[k].wPlanes = bmih->biPlanes;
+ icon_list[k].wBitCount = bmih->biBitCount;
+ if ( (icon_list[k].wPlanes * icon_list[k].wBitCount) >= 8 ) {
+ icon_list[k].bColorCount = 0;
+ } else {
+ icon_list[k].bColorCount = (BYTE)(1 << (icon_list[k].wPlanes * icon_list[k].wBitCount));
+ }
+ // initial guess (correct only for standard icons)
+ icon_list[k].dwBytesInRes = CalculateImageSize(icon_dib);
+ icon_list[k].dwImageOffset = CalculateImageOffset(vPages, k);
+ }
+
+ // make a room for icon dir entries, until later update
+ const long directory_start = io->tell_proc(handle);
+ io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
+
+ // write the image bits for each image
+
+ DWORD dwImageOffset = (DWORD)io->tell_proc(handle);
+
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = (FIBITMAP*)vPages[k];
+
+ if ((icon_list[k].bWidth == 0) && (icon_list[k].bHeight == 0)) {
+ // Vista icon support
+ FreeImage_SaveToHandle(FIF_PNG, icon_dib, io, handle, PNG_DEFAULT);
+ }
+ else {
+ // standard icon support
+ // see http://msdn.microsoft.com/en-us/library/ms997538.aspx
+ SaveStandardIcon(io, icon_dib, handle);
+ }
+
+ // update ICONDIRENTRY members
+ DWORD dwBytesInRes = (DWORD)io->tell_proc(handle) - dwImageOffset;
+ icon_list[k].dwImageOffset = dwImageOffset;
+ icon_list[k].dwBytesInRes = dwBytesInRes;
+ dwImageOffset += dwBytesInRes;
+ }
+
+ // update the icon descriptions
+ const long current_pos = io->tell_proc(handle);
+ io->seek_proc(handle, directory_start, SEEK_SET);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapIconDirEntries(icon_list, icon_header->idCount);
+#endif
+ io->write_proc(icon_list, sizeof(ICONDIRENTRY) * icon_header->idCount, 1, handle);
+ io->seek_proc(handle, current_pos, SEEK_SET);
+
+ free(icon_list);
+
+ // free the vector class
+ for(k = 0; k < icon_header->idCount; k++) {
+ icon_dib = (FIBITMAP*)vPages[k];
+ FreeImage_Unload(icon_dib);
+ }
+
+ return TRUE;
+
+ } catch(const char *text) {
+ // free the vector class
+ for(size_t k = 0; k < vPages.size(); k++) {
+ FIBITMAP *icon_dib = (FIBITMAP*)vPages[k];
+ FreeImage_Unload(icon_dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return FALSE;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitICO(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = PageCount;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginIFF.cpp b/plugins/AdvaImg/src/FreeImage/PluginIFF.cpp
new file mode 100644
index 0000000000..b6cd45d6db
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginIFF.cpp
@@ -0,0 +1,459 @@
+// ==========================================================
+// Deluxe Paint Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Mark Sibly (marksibly@blitzbasic.com)
+// - Aaron Shumate (trek@startreker.com)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Internal typedefs and structures
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct {
+ WORD w, h; /* raster width & height in pixels */
+ WORD x, y; /* position for this image */
+ BYTE nPlanes; /* # source bitplanes */
+ BYTE masking; /* masking technique */
+ BYTE compression; /* compression algorithm */
+ BYTE pad1; /* UNUSED. For consistency, put 0 here.*/
+ WORD transparentColor; /* transparent "color number" */
+ BYTE xAspect, yAspect; /* aspect ratio, a rational number x/y */
+ WORD pageWidth, pageHeight; /* source "page" size in pixels */
+} BMHD;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+#ifndef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(BMHD *header) {
+ SwapShort(&header->w);
+ SwapShort(&header->h);
+ SwapShort(&header->x);
+ SwapShort(&header->y);
+ SwapShort(&header->transparentColor);
+ SwapShort(&header->pageWidth);
+ SwapShort(&header->pageHeight);
+}
+#endif
+
+// ----------------------------------------------------------
+
+/* IFF chunk IDs */
+
+typedef DWORD IFF_ID;
+
+#define MAKE_ID(a, b, c, d) ((IFF_ID)(a)<<24 | (IFF_ID)(b)<<16 | (IFF_ID)(c)<<8 | (IFF_ID)(d))
+
+#define ID_FORM MAKE_ID('F', 'O', 'R', 'M') /* EA IFF 85 group identifier */
+#define ID_CAT MAKE_ID('C', 'A', 'T', ' ') /* EA IFF 85 group identifier */
+#define ID_LIST MAKE_ID('L', 'I', 'S', 'T') /* EA IFF 85 group identifier */
+#define ID_PROP MAKE_ID('P', 'R', 'O', 'P') /* EA IFF 85 group identifier */
+#define ID_END MAKE_ID('E', 'N', 'D', ' ') /* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3 page 376) */
+
+#define ID_ILBM MAKE_ID('I', 'L', 'B', 'M') /* EA IFF 85 raster bitmap form */
+#define ID_DEEP MAKE_ID('D', 'E', 'E', 'P') /* Chunky pixel image files (Used in TV Paint) */
+#define ID_RGB8 MAKE_ID('R', 'G', 'B', '8') /* RGB image forms, Turbo Silver (Impulse) */
+#define ID_RGBN MAKE_ID('R', 'G', 'B', 'N') /* RGB image forms, Turbo Silver (Impulse) */
+#define ID_PBM MAKE_ID('P', 'B', 'M', ' ') /* 256-color chunky format (DPaint 2 ?) */
+#define ID_ACBM MAKE_ID('A', 'C', 'B', 'M') /* Amiga Contiguous Bitmap (AmigaBasic) */
+/* generic */
+#define ID_FVER MAKE_ID('F', 'V', 'E', 'R') /* AmigaOS version string */
+#define ID_JUNK MAKE_ID('J', 'U', 'N', 'K') /* always ignore this chunk */
+#define ID_ANNO MAKE_ID('A', 'N', 'N', 'O') /* EA IFF 85 Generic Annotation chunk */
+#define ID_AUTH MAKE_ID('A', 'U', 'T', 'H') /* EA IFF 85 Generic Author chunk */
+#define ID_CHRS MAKE_ID('C', 'H', 'R', 'S') /* EA IFF 85 Generic character string chunk */
+#define ID_NAME MAKE_ID('N', 'A', 'M', 'E') /* EA IFF 85 Generic Name of art, music, etc. chunk */
+#define ID_TEXT MAKE_ID('T', 'E', 'X', 'T') /* EA IFF 85 Generic unformatted ASCII text chunk */
+#define ID_copy MAKE_ID('(', 'c', ')', ' ') /* EA IFF 85 Generic Copyright text chunk */
+/* ILBM chunks */
+#define ID_BMHD MAKE_ID('B', 'M', 'H', 'D') /* ILBM BitmapHeader */
+#define ID_CMAP MAKE_ID('C', 'M', 'A', 'P') /* ILBM 8bit RGB colormap */
+#define ID_GRAB MAKE_ID('G', 'R', 'A', 'B') /* ILBM "hotspot" coordiantes */
+#define ID_DEST MAKE_ID('D', 'E', 'S', 'T') /* ILBM destination image info */
+#define ID_SPRT MAKE_ID('S', 'P', 'R', 'T') /* ILBM sprite identifier */
+#define ID_CAMG MAKE_ID('C', 'A', 'M', 'G') /* Amiga viewportmodes */
+#define ID_BODY MAKE_ID('B', 'O', 'D', 'Y') /* ILBM image data */
+#define ID_CRNG MAKE_ID('C', 'R', 'N', 'G') /* color cycling */
+#define ID_CCRT MAKE_ID('C', 'C', 'R', 'T') /* color cycling */
+#define ID_CLUT MAKE_ID('C', 'L', 'U', 'T') /* Color Lookup Table chunk */
+#define ID_DPI MAKE_ID('D', 'P', 'I', ' ') /* Dots per inch chunk */
+#define ID_DPPV MAKE_ID('D', 'P', 'P', 'V') /* DPaint perspective chunk (EA) */
+#define ID_DRNG MAKE_ID('D', 'R', 'N', 'G') /* DPaint IV enhanced color cycle chunk (EA) */
+#define ID_EPSF MAKE_ID('E', 'P', 'S', 'F') /* Encapsulated Postscript chunk */
+#define ID_CMYK MAKE_ID('C', 'M', 'Y', 'K') /* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
+#define ID_CNAM MAKE_ID('C', 'N', 'A', 'M') /* Color naming chunk (Soft-Logik) */
+#define ID_PCHG MAKE_ID('P', 'C', 'H', 'G') /* Line by line palette control information (Sebastiano Vigna) */
+#define ID_PRVW MAKE_ID('P', 'R', 'V', 'W') /* A mini duplicate ILBM used for preview (Gary Bonham) */
+#define ID_XBMI MAKE_ID('X', 'B', 'M', 'I') /* eXtended BitMap Information (Soft-Logik) */
+#define ID_CTBL MAKE_ID('C', 'T', 'B', 'L') /* Newtek Dynamic Ham color chunk */
+#define ID_DYCP MAKE_ID('D', 'Y', 'C', 'P') /* Newtek Dynamic Ham chunk */
+#define ID_SHAM MAKE_ID('S', 'H', 'A', 'M') /* Sliced HAM color chunk */
+#define ID_ABIT MAKE_ID('A', 'B', 'I', 'T') /* ACBM body chunk */
+#define ID_DCOL MAKE_ID('D', 'C', 'O', 'L') /* unofficial direct color */
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "IFF";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "IFF Interleaved Bitmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "iff,lbm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-iff";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ DWORD type = 0;
+
+ // read chunk type
+ io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(&type);
+#endif
+
+ if(type != ID_FORM)
+ return FALSE;
+
+ // skip 4 bytes
+ io->read_proc(&type, 4, 1, handle);
+
+ // read chunk type
+ io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(&type);
+#endif
+
+ // File format : ID_PBM = Packed Bitmap, ID_ILBM = Interleaved Bitmap
+ return (type == ID_ILBM) || (type == ID_PBM);
+}
+
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (handle != NULL) {
+ FIBITMAP *dib = NULL;
+
+ DWORD type, size;
+
+ io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(&type);
+#endif
+
+ if(type != ID_FORM)
+ return NULL;
+
+ io->read_proc(&size, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(&size);
+#endif
+
+ io->read_proc(&type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(&type);
+#endif
+
+ if ((type != ID_ILBM) && (type != ID_PBM))
+ return NULL;
+
+ size -= 4;
+
+ unsigned width = 0, height = 0, planes = 0, depth = 0, comp = 0;
+
+ while (size) {
+ DWORD ch_type,ch_size;
+
+ io->read_proc(&ch_type, 4, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(&ch_type);
+#endif
+
+ io->read_proc(&ch_size,4,1,handle );
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(&ch_size);
+#endif
+
+ unsigned ch_end = io->tell_proc(handle) + ch_size;
+
+ if (ch_type == ID_BMHD) { // Bitmap Header
+ if (dib)
+ FreeImage_Unload(dib);
+
+ BMHD bmhd;
+
+ io->read_proc(&bmhd, sizeof(bmhd), 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapHeader(&bmhd);
+#endif
+
+ width = bmhd.w;
+ height = bmhd.h;
+ planes = bmhd.nPlanes;
+ comp = bmhd.compression;
+
+ if(bmhd.masking & 1)
+ planes++; // there is a mask ( 'stencil' )
+
+ if (planes > 8 && planes != 24)
+ return NULL;
+
+ depth = planes > 8 ? 24 : 8;
+
+ if ( depth == 24 ) {
+ dib = FreeImage_Allocate(width, height, depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_Allocate(width, height, depth);
+ }
+ } else if (ch_type == ID_CMAP) { // Palette (Color Map)
+ if (!dib)
+ return NULL;
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ if(pal != NULL) {
+ unsigned palette_entries = MIN((unsigned)ch_size / 3, FreeImage_GetColorsUsed(dib));
+ for (unsigned k = 0; k < palette_entries; k++) {
+ io->read_proc(&pal[k].rgbRed, 1, 1, handle );
+ io->read_proc(&pal[k].rgbGreen, 1, 1, handle );
+ io->read_proc(&pal[k].rgbBlue, 1, 1, handle );
+ }
+ }
+ } else if (ch_type == ID_BODY) {
+ if (!dib)
+ return NULL;
+
+ if (type == ID_PBM) {
+ // NON INTERLACED (LBM)
+
+ unsigned line = FreeImage_GetLine(dib) + 1 & ~1;
+
+ for (unsigned i = 0; i < FreeImage_GetHeight(dib); i++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - i - 1);
+
+ if (comp == 1) {
+ // use RLE compression
+
+ DWORD number_of_bytes_written = 0;
+ BYTE rle_count;
+ BYTE byte;
+
+ while (number_of_bytes_written < line) {
+ io->read_proc(&rle_count, 1, 1, handle);
+
+ if (rle_count < 128) {
+ for (int k = 0; k < rle_count + 1; k++) {
+ io->read_proc(&byte, 1, 1, handle);
+
+ bits[number_of_bytes_written++] += byte;
+ }
+ } else if (rle_count > 128) {
+ io->read_proc(&byte, 1, 1, handle);
+
+ for (int k = 0; k < 257 - rle_count; k++) {
+ bits[number_of_bytes_written++] += byte;
+ }
+ }
+ }
+ } else {
+ // don't use compression
+
+ io->read_proc(bits, line, 1, handle);
+ }
+ }
+
+ return dib;
+ } else {
+ // INTERLACED (ILBM)
+
+ unsigned pixel_size = depth/8;
+ unsigned n_width=(width+15)&~15;
+ unsigned plane_size = n_width/8;
+ unsigned src_size = plane_size * planes;
+ BYTE *src = (BYTE*)malloc(src_size);
+ BYTE *dest = FreeImage_GetBits(dib);
+
+ dest += FreeImage_GetPitch(dib) * height;
+
+ for (unsigned y = 0; y < height; y++) {
+ dest -= FreeImage_GetPitch(dib);
+
+ // read all planes in one hit,
+ // 'coz PSP compresses across planes...
+
+ if (comp) {
+ // unpacker algorithm
+
+ for(unsigned x = 0; x < src_size;) {
+ // read the next source byte into t
+ signed char t = 0;
+ io->read_proc(&t, 1, 1, handle);
+
+ if (t >= 0) {
+ // t = [0..127] => copy the next t+1 bytes literally
+ unsigned size_to_read = t + 1;
+
+ if ((size_to_read + x) > src_size) {
+ // sanity check for buffer overruns
+ size_to_read = src_size - x;
+ io->read_proc(src + x, size_to_read, 1, handle);
+ x += (t + 1);
+ } else {
+ io->read_proc(src + x, size_to_read, 1, handle);
+ x += size_to_read;
+ }
+ } else if (t != -128) {
+ // t = [-1..-127] => replicate the next byte -t+1 times
+ BYTE b = 0;
+ io->read_proc(&b, 1, 1, handle);
+ unsigned size_to_copy = (unsigned)(-(int)t + 1);
+
+ if ((size_to_copy + x) > src_size) {
+ // sanity check for buffer overruns
+ size_to_copy = src_size - x;
+ memset(src + x, b, size_to_copy);
+ x += (unsigned)(-(int)t + 1);
+ } else {
+ memset(src + x, b, size_to_copy);
+ x += size_to_copy;
+ }
+ }
+ // t = -128 => noop
+ }
+ } else {
+ io->read_proc(src, src_size, 1, handle);
+ }
+
+ // lazy planar->chunky...
+
+ for (unsigned x = 0; x < width; x++) {
+ for (unsigned n = 0; n < planes; n++) {
+ BYTE bit = (BYTE)( src[n * plane_size + (x / 8)] >> ((x^7) & 7) );
+
+ dest[x * pixel_size + (n / 8)] |= (bit & 1) << (n & 7);
+ }
+ }
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ if (depth == 24) {
+ for (unsigned x = 0; x < width; ++x){
+ INPLACESWAP(dest[x * 3], dest[x * 3 + 2]);
+ }
+ }
+#endif
+ }
+
+ free(src);
+
+ return dib;
+ }
+ }
+
+ // Every odd-length chunk is followed by a 0 pad byte. This pad
+ // byte is not counted in ch_size.
+ if (ch_size & 1) {
+ ch_size++;
+ ch_end++;
+ }
+
+ io->seek_proc(handle, ch_end - io->tell_proc(handle), SEEK_CUR);
+
+ size -= ch_size + 8;
+ }
+
+ if (dib)
+ FreeImage_Unload(dib);
+ }
+
+ return 0;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitIFF(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp b/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp
new file mode 100644
index 0000000000..6e772afdaa
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp
@@ -0,0 +1,339 @@
+// ==========================================================
+// JPEG2000 J2K codestream Loader and Writer
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../LibOpenJPEG/openjpeg.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Helper functions (see J2KHelper.cpp)
+// ==========================================================
+
+FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image);
+opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters);
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/**
+OpenJPEG Error callback
+*/
+static void j2k_error_callback(const char *msg, void *client_data) {
+ FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg);
+}
+/**
+OpenJPEG Warning callback
+*/
+static void j2k_warning_callback(const char *msg, void *client_data) {
+ FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg);
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "J2K";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "JPEG-2000 codestream";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "j2k,j2c";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/j2k";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE jpc_signature[] = { 0xFF, 0x4F };
+ BYTE signature[2] = { 0, 0 };
+
+ long tell = io->tell_proc(handle);
+ io->read_proc(signature, 1, sizeof(jpc_signature), handle);
+ io->seek_proc(handle, tell, SEEK_SET);
+
+ return (memcmp(jpc_signature, signature, sizeof(jpc_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 8) ||
+ (depth == 24) ||
+ (depth == 32)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (
+ (type == FIT_BITMAP) ||
+ (type == FIT_UINT16) ||
+ (type == FIT_RGB16) ||
+ (type == FIT_RGBA16)
+ );
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ return NULL;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (handle) {
+ opj_dparameters_t parameters; // decompression parameters
+ opj_event_mgr_t event_mgr; // event manager
+ opj_image_t *image = NULL; // decoded image
+
+ BYTE *src = NULL;
+ long file_length;
+
+ opj_dinfo_t* dinfo = NULL; // handle to a decompressor
+ opj_cio_t *cio = NULL;
+
+ FIBITMAP *dib = NULL;
+
+ // check the file format
+ if (!Validate(io, handle)) {
+ return NULL;
+ }
+
+ // configure the event callbacks
+ memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+ event_mgr.error_handler = j2k_error_callback;
+ event_mgr.warning_handler = j2k_warning_callback;
+ event_mgr.info_handler = NULL;
+
+ // set decoding parameters to default values
+ opj_set_default_decoder_parameters(&parameters);
+
+ try {
+ // read the input file and put it in memory
+
+ long start_pos = io->tell_proc(handle);
+ io->seek_proc(handle, 0, SEEK_END);
+ file_length = io->tell_proc(handle) - start_pos;
+ io->seek_proc(handle, start_pos, SEEK_SET);
+ src = (BYTE*)malloc(file_length * sizeof(BYTE));
+ if (!src) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ if(io->read_proc(src, 1, file_length, handle) < 1) {
+ throw "Error while reading input stream";
+ }
+
+ // decode the JPEG-2000 codestream
+
+ // get a decoder handle
+ dinfo = opj_create_decompress(CODEC_J2K);
+
+ // catch events using our callbacks
+ opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, NULL);
+
+ // setup the decoder decoding parameters using user parameters
+ opj_setup_decoder(dinfo, &parameters);
+
+ // open a byte stream
+ cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length);
+
+ // decode the stream and fill the image structure
+ image = opj_decode(dinfo, cio);
+ if (!image) {
+ throw "Failed to decode image!\n";
+ }
+
+ // close the byte stream
+ opj_cio_close(cio);
+ cio = NULL;
+
+ // free the memory containing the code-stream
+ free(src);
+ src = NULL;
+
+ // free the codec context
+ opj_destroy_decompress(dinfo);
+
+ // create output image
+ dib = J2KImageToFIBITMAP(s_format_id, image);
+ if (!dib) throw "Failed to import JPEG2000 image";
+
+ // free image data structure
+ opj_image_destroy(image);
+
+ return dib;
+
+ } catch (const char *text) {
+ if(src) free(src);
+ if(dib) FreeImage_Unload(dib);
+ // free remaining structures
+ opj_destroy_decompress(dinfo);
+ opj_image_destroy(image);
+ // close the byte stream
+ if(cio) opj_cio_close(cio);
+
+ FreeImage_OutputMessageProc(s_format_id, text);
+
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if ((dib) && (handle)) {
+ BOOL bSuccess;
+ opj_cparameters_t parameters; // compression parameters
+ opj_event_mgr_t event_mgr; // event manager
+ opj_image_t *image = NULL; // image to encode
+ opj_cinfo_t* cinfo = NULL; // codec context
+ opj_cio_t *cio = NULL; // memory byte stream
+
+ // configure the event callbacks
+ memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+ event_mgr.error_handler = j2k_error_callback;
+ event_mgr.warning_handler = j2k_warning_callback;
+ event_mgr.info_handler = NULL;
+
+ // set encoding parameters to default values
+ opj_set_default_encoder_parameters(&parameters);
+
+ parameters.tcp_numlayers = 0;
+ // if no rate entered, apply a 16:1 rate by default
+ if(flags == J2K_DEFAULT) {
+ parameters.tcp_rates[0] = (float)16;
+ } else {
+ // for now, the flags parameter is only used to specify the rate
+ parameters.tcp_rates[0] = (float)flags;
+ }
+ parameters.tcp_numlayers++;
+ parameters.cp_disto_alloc = 1;
+
+ try {
+ // convert the dib to a OpenJPEG image
+ image = FIBITMAPToJ2KImage(s_format_id, dib, &parameters);
+ if (!image) return FALSE;
+
+ // decide if MCT should be used
+ parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0;
+
+ // encode the destination image
+
+ // get a J2K compressor handle
+ cinfo = opj_create_compress(CODEC_J2K);
+
+ // catch events using our callbacks
+ opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, NULL);
+
+ // setup the encoder parameters using the current image and using user parameters
+ opj_setup_encoder(cinfo, &parameters, image);
+
+ // open a byte stream for writing, allocate memory for all tiles
+ cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0);
+
+ // encode the image
+ bSuccess = opj_encode(cinfo, cio, image, NULL/*parameters.index*/);
+ if (!bSuccess) {
+ throw "Failed to encode image";
+ }
+ int codestream_length = cio_tell(cio);
+
+ // write the buffer to user's IO handle
+ io->write_proc(cio->buffer, 1, codestream_length, handle);
+
+ // close and free the byte stream
+ opj_cio_close(cio);
+
+ // free remaining compression structures
+ opj_destroy_compress(cinfo);
+
+ // free image data
+ opj_image_destroy(image);
+
+ return TRUE;
+
+ } catch (const char *text) {
+ if(cio) opj_cio_close(cio);
+ if(cinfo) opj_destroy_compress(cinfo);
+ if(image) opj_image_destroy(image);
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJ2K(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp b/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp
new file mode 100644
index 0000000000..ed1cb267bb
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp
@@ -0,0 +1,339 @@
+// ==========================================================
+// JPEG2000 JP2 file format Loader and Writer
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../LibOpenJPEG/openjpeg.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Helper functions (see J2KHelper.cpp)
+// ==========================================================
+
+FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image);
+opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters);
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/**
+OpenJPEG Error callback
+*/
+static void jp2_error_callback(const char *msg, void *client_data) {
+ FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg);
+}
+/**
+OpenJPEG Warning callback
+*/
+static void jp2_warning_callback(const char *msg, void *client_data) {
+ FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg);
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "JP2";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "JPEG-2000 File Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "jp2";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/jp2";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE jp2_signature[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A };
+ BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ long tell = io->tell_proc(handle);
+ io->read_proc(signature, 1, sizeof(jp2_signature), handle);
+ io->seek_proc(handle, tell, SEEK_SET);
+
+ return (memcmp(jp2_signature, signature, sizeof(jp2_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 8) ||
+ (depth == 24) ||
+ (depth == 32)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (
+ (type == FIT_BITMAP) ||
+ (type == FIT_UINT16) ||
+ (type == FIT_RGB16) ||
+ (type == FIT_RGBA16)
+ );
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ return NULL;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (handle) {
+ opj_dparameters_t parameters; // decompression parameters
+ opj_event_mgr_t event_mgr; // event manager
+ opj_image_t *image = NULL; // decoded image
+
+ BYTE *src = NULL;
+ long file_length;
+
+ opj_dinfo_t* dinfo = NULL; // handle to a decompressor
+ opj_cio_t *cio = NULL;
+
+ FIBITMAP *dib = NULL;
+
+ // check the file format
+ if (!Validate(io, handle)) {
+ return NULL;
+ }
+
+ // configure the event callbacks
+ memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+ event_mgr.error_handler = jp2_error_callback;
+ event_mgr.warning_handler = jp2_warning_callback;
+ event_mgr.info_handler = NULL;
+
+ // set decoding parameters to default values
+ opj_set_default_decoder_parameters(&parameters);
+
+ try {
+ // read the input file and put it in memory
+
+ long start_pos = io->tell_proc(handle);
+ io->seek_proc(handle, 0, SEEK_END);
+ file_length = io->tell_proc(handle) - start_pos;
+ io->seek_proc(handle, start_pos, SEEK_SET);
+ src = (BYTE*)malloc(file_length * sizeof(BYTE));
+ if (!src) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ if(io->read_proc(src, 1, file_length, handle) < 1) {
+ throw "Error while reading input stream";
+ }
+
+ // decode the JPEG-2000 file
+
+ // get a decoder handle
+ dinfo = opj_create_decompress(CODEC_JP2);
+
+ // catch events using our callbacks
+ opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, NULL);
+
+ // setup the decoder decoding parameters using user parameters
+ opj_setup_decoder(dinfo, &parameters);
+
+ // open a byte stream
+ cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length);
+
+ // decode the stream and fill the image structure
+ image = opj_decode(dinfo, cio);
+ if (!image) {
+ throw "Failed to decode image!\n";
+ }
+
+ // close the byte stream
+ opj_cio_close(cio);
+ cio = NULL;
+
+ // free the memory containing the code-stream
+ free(src);
+ src = NULL;
+
+ // free the codec context
+ opj_destroy_decompress(dinfo);
+
+ // create output image
+ dib = J2KImageToFIBITMAP(s_format_id, image);
+ if (!dib) throw "Failed to import JPEG2000 image";
+
+ // free image data structure
+ opj_image_destroy(image);
+
+ return dib;
+
+ } catch (const char *text) {
+ if(src) free(src);
+ if(dib) FreeImage_Unload(dib);
+ // free remaining structures
+ opj_destroy_decompress(dinfo);
+ opj_image_destroy(image);
+ // close the byte stream
+ if(cio) opj_cio_close(cio);
+
+ FreeImage_OutputMessageProc(s_format_id, text);
+
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if ((dib) && (handle)) {
+ BOOL bSuccess;
+ opj_cparameters_t parameters; // compression parameters
+ opj_event_mgr_t event_mgr; // event manager
+ opj_image_t *image = NULL; // image to encode
+ opj_cinfo_t* cinfo = NULL; // codec context
+ opj_cio_t *cio = NULL; // memory byte stream
+
+ // configure the event callbacks
+ memset(&event_mgr, 0, sizeof(opj_event_mgr_t));
+ event_mgr.error_handler = jp2_error_callback;
+ event_mgr.warning_handler = jp2_warning_callback;
+ event_mgr.info_handler = NULL;
+
+ // set encoding parameters to default values
+ opj_set_default_encoder_parameters(&parameters);
+
+ parameters.tcp_numlayers = 0;
+ // if no rate entered, apply a 16:1 rate by default
+ if(flags == JP2_DEFAULT) {
+ parameters.tcp_rates[0] = (float)16;
+ } else {
+ // for now, the flags parameter is only used to specify the rate
+ parameters.tcp_rates[0] = (float)flags;
+ }
+ parameters.tcp_numlayers++;
+ parameters.cp_disto_alloc = 1;
+
+ try {
+ // convert the dib to a OpenJPEG image
+ image = FIBITMAPToJ2KImage(s_format_id, dib, &parameters);
+ if (!image) return FALSE;
+
+ // decide if MCT should be used
+ parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0;
+
+ // encode the destination image
+
+ // get a J2K compressor handle
+ cinfo = opj_create_compress(CODEC_JP2);
+
+ // catch events using our callbacks
+ opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, NULL);
+
+ // setup the encoder parameters using the current image and using user parameters
+ opj_setup_encoder(cinfo, &parameters, image);
+
+ // open a byte stream for writing, allocate memory for all tiles
+ cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0);
+
+ // encode the image
+ bSuccess = opj_encode(cinfo, cio, image, NULL/*parameters.index*/);
+ if (!bSuccess) {
+ throw "Failed to encode image";
+ }
+ int codestream_length = cio_tell(cio);
+
+ // write the buffer to user's IO handle
+ io->write_proc(cio->buffer, 1, codestream_length, handle);
+
+ // close and free the byte stream
+ opj_cio_close(cio);
+
+ // free remaining compression structures
+ opj_destroy_compress(cinfo);
+
+ // free image data
+ opj_image_destroy(image);
+
+ return TRUE;
+
+ } catch (const char *text) {
+ if(cio) opj_cio_close(cio);
+ if(cinfo) opj_destroy_compress(cinfo);
+ if(image) opj_image_destroy(image);
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJP2(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp b/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp
new file mode 100644
index 0000000000..b69ace95bc
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp
@@ -0,0 +1,1801 @@
+// ==========================================================
+// JPEG Loader and writer
+// Based on code developed by The Independent JPEG Group
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Jan L. Nauta (jln@magentammt.com)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Karl-Heinz Bussian (khbussian@moss.de)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Jascha Wetzel (jascha@mainia.de)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+extern "C" {
+#define XMD_H
+#undef FAR
+#include <setjmp.h>
+
+#include "../LibJPEG/jinclude.h"
+#include "../LibJPEG/jpeglib.h"
+#include "../LibJPEG/jerror.h"
+}
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+// Constant declarations
+// ----------------------------------------------------------
+
+#define INPUT_BUF_SIZE 4096 // choose an efficiently fread'able size
+#define OUTPUT_BUF_SIZE 4096 // choose an efficiently fwrite'able size
+
+#define EXIF_MARKER (JPEG_APP0+1) // EXIF marker / Adobe XMP marker
+#define ICC_MARKER (JPEG_APP0+2) // ICC profile marker
+#define IPTC_MARKER (JPEG_APP0+13) // IPTC marker / BIM marker
+
+#define ICC_HEADER_SIZE 14 // size of non-profile data in APP2
+#define MAX_BYTES_IN_MARKER 65533L // maximum data length of a JPEG marker
+#define MAX_DATA_BYTES_IN_MARKER 65519L // maximum data length of a JPEG APP2 marker
+
+#define MAX_JFXX_THUMB_SIZE (MAX_BYTES_IN_MARKER - 5 - 1)
+
+#define JFXX_TYPE_JPEG 0x10 // JFIF extension marker: JPEG-compressed thumbnail image
+#define JFXX_TYPE_8bit 0x11 // JFIF extension marker: palette thumbnail image
+#define JFXX_TYPE_24bit 0x13 // JFIF extension marker: RGB thumbnail image
+
+// ----------------------------------------------------------
+// Typedef declarations
+// ----------------------------------------------------------
+
+typedef struct tagErrorManager {
+ /// "public" fields
+ struct jpeg_error_mgr pub;
+ /// for return to caller
+ jmp_buf setjmp_buffer;
+} ErrorManager;
+
+typedef struct tagSourceManager {
+ /// public fields
+ struct jpeg_source_mgr pub;
+ /// source stream
+ fi_handle infile;
+ FreeImageIO *m_io;
+ /// start of buffer
+ JOCTET * buffer;
+ /// have we gotten any data yet ?
+ boolean start_of_file;
+} SourceManager;
+
+typedef struct tagDestinationManager {
+ /// public fields
+ struct jpeg_destination_mgr pub;
+ /// destination stream
+ fi_handle outfile;
+ FreeImageIO *m_io;
+ /// start of buffer
+ JOCTET * buffer;
+} DestinationManager;
+
+typedef SourceManager* freeimage_src_ptr;
+typedef DestinationManager* freeimage_dst_ptr;
+typedef ErrorManager* freeimage_error_ptr;
+
+// ----------------------------------------------------------
+// Error handling
+// ----------------------------------------------------------
+
+/** Fatal errors (print message and exit) */
+static inline void
+JPEG_EXIT(j_common_ptr cinfo, int code) {
+ freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+ error_ptr->pub.msg_code = code;
+ error_ptr->pub.error_exit(cinfo);
+}
+
+/** Nonfatal errors (we can keep going, but the data is probably corrupt) */
+static inline void
+JPEG_WARNING(j_common_ptr cinfo, int code) {
+ freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+ error_ptr->pub.msg_code = code;
+ error_ptr->pub.emit_message(cinfo, -1);
+}
+
+/**
+ Receives control for a fatal error. Information sufficient to
+ generate the error message has been stored in cinfo->err; call
+ output_message to display it. Control must NOT return to the caller;
+ generally this routine will exit() or longjmp() somewhere.
+*/
+METHODDEF(void)
+jpeg_error_exit (j_common_ptr cinfo) {
+ freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+
+ // always display the message
+ error_ptr->pub.output_message(cinfo);
+
+ // allow JPEG with unknown markers
+ if(error_ptr->pub.msg_code != JERR_UNKNOWN_MARKER) {
+
+ // let the memory manager delete any temp files before we die
+ jpeg_destroy(cinfo);
+
+ // return control to the setjmp point
+ longjmp(error_ptr->setjmp_buffer, 1);
+ }
+}
+
+/**
+ Actual output of any JPEG message. Note that this method does not know
+ how to generate a message, only where to send it.
+*/
+METHODDEF(void)
+jpeg_output_message (j_common_ptr cinfo) {
+ char buffer[JMSG_LENGTH_MAX];
+ freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
+
+ // create the message
+ error_ptr->pub.format_message(cinfo, buffer);
+ // send it to user's message proc
+ FreeImage_OutputMessageProc(s_format_id, buffer);
+}
+
+// ----------------------------------------------------------
+// Destination manager
+// ----------------------------------------------------------
+
+/**
+ Initialize destination. This is called by jpeg_start_compress()
+ before any data is actually written. It must initialize
+ next_output_byte and free_in_buffer. free_in_buffer must be
+ initialized to a positive value.
+*/
+METHODDEF(void)
+init_destination (j_compress_ptr cinfo) {
+ freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+
+ dest->buffer = (JOCTET *)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+ OUTPUT_BUF_SIZE * sizeof(JOCTET));
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+}
+
+/**
+ This is called whenever the buffer has filled (free_in_buffer
+ reaches zero). In typical applications, it should write out the
+ *entire* buffer (use the saved start address and buffer length;
+ ignore the current state of next_output_byte and free_in_buffer).
+ Then reset the pointer & count to the start of the buffer, and
+ return TRUE indicating that the buffer has been dumped.
+ free_in_buffer must be set to a positive value when TRUE is
+ returned. A FALSE return should only be used when I/O suspension is
+ desired.
+*/
+METHODDEF(boolean)
+empty_output_buffer (j_compress_ptr cinfo) {
+ freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+
+ if (dest->m_io->write_proc(dest->buffer, 1, OUTPUT_BUF_SIZE, dest->outfile) != OUTPUT_BUF_SIZE) {
+ // let the memory manager delete any temp files before we die
+ jpeg_destroy((j_common_ptr)cinfo);
+
+ JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE);
+ }
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+
+ return TRUE;
+}
+
+/**
+ Terminate destination --- called by jpeg_finish_compress() after all
+ data has been written. In most applications, this must flush any
+ data remaining in the buffer. Use either next_output_byte or
+ free_in_buffer to determine how much data is in the buffer.
+*/
+METHODDEF(void)
+term_destination (j_compress_ptr cinfo) {
+ freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+
+ size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+
+ // write any data remaining in the buffer
+
+ if (datacount > 0) {
+ if (dest->m_io->write_proc(dest->buffer, 1, (unsigned int)datacount, dest->outfile) != datacount) {
+ // let the memory manager delete any temp files before we die
+ jpeg_destroy((j_common_ptr)cinfo);
+
+ JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE);
+ }
+ }
+}
+
+// ----------------------------------------------------------
+// Source manager
+// ----------------------------------------------------------
+
+/**
+ Initialize source. This is called by jpeg_read_header() before any
+ data is actually read. Unlike init_destination(), it may leave
+ bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
+ will occur immediately).
+*/
+METHODDEF(void)
+init_source (j_decompress_ptr cinfo) {
+ freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
+
+ /* We reset the empty-input-file flag for each image,
+ * but we don't clear the input buffer.
+ * This is correct behavior for reading a series of images from one source.
+ */
+
+ src->start_of_file = TRUE;
+}
+
+/**
+ This is called whenever bytes_in_buffer has reached zero and more
+ data is wanted. In typical applications, it should read fresh data
+ into the buffer (ignoring the current state of next_input_byte and
+ bytes_in_buffer), reset the pointer & count to the start of the
+ buffer, and return TRUE indicating that the buffer has been reloaded.
+ It is not necessary to fill the buffer entirely, only to obtain at
+ least one more byte. bytes_in_buffer MUST be set to a positive value
+ if TRUE is returned. A FALSE return should only be used when I/O
+ suspension is desired.
+*/
+METHODDEF(boolean)
+fill_input_buffer (j_decompress_ptr cinfo) {
+ freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
+
+ size_t nbytes = src->m_io->read_proc(src->buffer, 1, INPUT_BUF_SIZE, src->infile);
+
+ if (nbytes <= 0) {
+ if (src->start_of_file) {
+ // treat empty input file as fatal error
+
+ // let the memory manager delete any temp files before we die
+ jpeg_destroy((j_common_ptr)cinfo);
+
+ JPEG_EXIT((j_common_ptr)cinfo, JERR_INPUT_EMPTY);
+ }
+
+ JPEG_WARNING((j_common_ptr)cinfo, JWRN_JPEG_EOF);
+
+ /* Insert a fake EOI marker */
+
+ src->buffer[0] = (JOCTET) 0xFF;
+ src->buffer[1] = (JOCTET) JPEG_EOI;
+
+ nbytes = 2;
+ }
+
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = nbytes;
+ src->start_of_file = FALSE;
+
+ return TRUE;
+}
+
+/**
+ Skip num_bytes worth of data. The buffer pointer and count should
+ be advanced over num_bytes input bytes, refilling the buffer as
+ needed. This is used to skip over a potentially large amount of
+ uninteresting data (such as an APPn marker). In some applications
+ it may be possible to optimize away the reading of the skipped data,
+ but it's not clear that being smart is worth much trouble; large
+ skips are uncommon. bytes_in_buffer may be zero on return.
+ A zero or negative skip count should be treated as a no-op.
+*/
+METHODDEF(void)
+skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
+ freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
+
+ /* Just a dumb implementation for now. Could use fseek() except
+ * it doesn't work on pipes. Not clear that being smart is worth
+ * any trouble anyway --- large skips are infrequent.
+ */
+
+ if (num_bytes > 0) {
+ while (num_bytes > (long) src->pub.bytes_in_buffer) {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+
+ (void) fill_input_buffer(cinfo);
+
+ /* note we assume that fill_input_buffer will never return FALSE,
+ * so suspension need not be handled.
+ */
+ }
+
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+/**
+ Terminate source --- called by jpeg_finish_decompress
+ after all data has been read. Often a no-op.
+
+ NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ application must deal with any cleanup that should happen even
+ for error exit.
+*/
+METHODDEF(void)
+term_source (j_decompress_ptr cinfo) {
+ // no work necessary here
+}
+
+// ----------------------------------------------------------
+// Source manager & Destination manager setup
+// ----------------------------------------------------------
+
+/**
+ Prepare for input from a stdio stream.
+ The caller must have already opened the stream, and is responsible
+ for closing it after finishing decompression.
+*/
+GLOBAL(void)
+jpeg_freeimage_src (j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io) {
+ freeimage_src_ptr src;
+
+ // allocate memory for the buffer. is released automatically in the end
+
+ if (cinfo->src == NULL) {
+ cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
+ ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(SourceManager));
+
+ src = (freeimage_src_ptr) cinfo->src;
+
+ src->buffer = (JOCTET *) (*cinfo->mem->alloc_small)
+ ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET));
+ }
+
+ // initialize the jpeg pointer struct with pointers to functions
+
+ src = (freeimage_src_ptr) cinfo->src;
+ src->pub.init_source = init_source;
+ src->pub.fill_input_buffer = fill_input_buffer;
+ src->pub.skip_input_data = skip_input_data;
+ src->pub.resync_to_restart = jpeg_resync_to_restart; // use default method
+ src->pub.term_source = term_source;
+ src->infile = infile;
+ src->m_io = io;
+ src->pub.bytes_in_buffer = 0; // forces fill_input_buffer on first read
+ src->pub.next_input_byte = NULL; // until buffer loaded
+}
+
+/**
+ Prepare for output to a stdio stream.
+ The caller must have already opened the stream, and is responsible
+ for closing it after finishing compression.
+*/
+GLOBAL(void)
+jpeg_freeimage_dst (j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io) {
+ freeimage_dst_ptr dest;
+
+ if (cinfo->dest == NULL) {
+ cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small)
+ ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(DestinationManager));
+ }
+
+ dest = (freeimage_dst_ptr) cinfo->dest;
+ dest->pub.init_destination = init_destination;
+ dest->pub.empty_output_buffer = empty_output_buffer;
+ dest->pub.term_destination = term_destination;
+ dest->outfile = outfile;
+ dest->m_io = io;
+}
+
+// ----------------------------------------------------------
+// Special markers read functions
+// ----------------------------------------------------------
+
+/**
+ Read JPEG_COM marker (comment)
+*/
+static BOOL
+jpeg_read_comment(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+ size_t length = datalen;
+ BYTE *profile = (BYTE*)dataptr;
+
+ // read the comment
+ char *value = (char*)malloc((length + 1) * sizeof(char));
+ if(value == NULL) return FALSE;
+ memcpy(value, profile, length);
+ value[length] = '\0';
+
+ // create a tag
+ FITAG *tag = FreeImage_CreateTag();
+ if(tag) {
+ unsigned int count = (unsigned int)length + 1; // includes the null value
+
+ FreeImage_SetTagID(tag, JPEG_COM);
+ FreeImage_SetTagKey(tag, "Comment");
+ FreeImage_SetTagLength(tag, count);
+ FreeImage_SetTagCount(tag, count);
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagValue(tag, value);
+
+ // store the tag
+ FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+
+ // destroy the tag
+ FreeImage_DeleteTag(tag);
+ }
+
+ free(value);
+
+ return TRUE;
+}
+
+/**
+ Read JPEG_APP2 marker (ICC profile)
+*/
+
+/**
+Handy subroutine to test whether a saved marker is an ICC profile marker.
+*/
+static BOOL
+marker_is_icc(jpeg_saved_marker_ptr marker) {
+ // marker identifying string "ICC_PROFILE" (null-terminated)
+ const BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 };
+
+ if(marker->marker == ICC_MARKER) {
+ // verify the identifying string
+ if(marker->data_length >= ICC_HEADER_SIZE) {
+ if(memcmp(icc_signature, marker->data, sizeof(icc_signature)) == 0) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ See if there was an ICC profile in the JPEG file being read;
+ if so, reassemble and return the profile data.
+
+ TRUE is returned if an ICC profile was found, FALSE if not.
+ If TRUE is returned, *icc_data_ptr is set to point to the
+ returned data, and *icc_data_len is set to its length.
+
+ IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
+ and must be freed by the caller with free() when the caller no longer
+ needs it. (Alternatively, we could write this routine to use the
+ IJG library's memory allocator, so that the data would be freed implicitly
+ at jpeg_finish_decompress() time. But it seems likely that many apps
+ will prefer to have the data stick around after decompression finishes.)
+
+ NOTE: if the file contains invalid ICC APP2 markers, we just silently
+ return FALSE. You might want to issue an error message instead.
+*/
+static BOOL
+jpeg_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned *icc_data_len) {
+ jpeg_saved_marker_ptr marker;
+ int num_markers = 0;
+ int seq_no;
+ JOCTET *icc_data;
+ unsigned total_length;
+
+ const int MAX_SEQ_NO = 255; // sufficient since marker numbers are bytes
+ BYTE marker_present[MAX_SEQ_NO+1]; // 1 if marker found
+ unsigned data_length[MAX_SEQ_NO+1]; // size of profile data in marker
+ unsigned data_offset[MAX_SEQ_NO+1]; // offset for data in marker
+
+ *icc_data_ptr = NULL; // avoid confusion if FALSE return
+ *icc_data_len = 0;
+
+ /**
+ this first pass over the saved markers discovers whether there are
+ any ICC markers and verifies the consistency of the marker numbering.
+ */
+
+ memset(marker_present, 0, (MAX_SEQ_NO + 1));
+
+ for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ if (num_markers == 0) {
+ // number of markers
+ num_markers = GETJOCTET(marker->data[13]);
+ }
+ else if (num_markers != GETJOCTET(marker->data[13])) {
+ return FALSE; // inconsistent num_markers fields
+ }
+ // sequence number
+ seq_no = GETJOCTET(marker->data[12]);
+ if (seq_no <= 0 || seq_no > num_markers) {
+ return FALSE; // bogus sequence number
+ }
+ if (marker_present[seq_no]) {
+ return FALSE; // duplicate sequence numbers
+ }
+ marker_present[seq_no] = 1;
+ data_length[seq_no] = marker->data_length - ICC_HEADER_SIZE;
+ }
+ }
+
+ if (num_markers == 0)
+ return FALSE;
+
+ /**
+ check for missing markers, count total space needed,
+ compute offset of each marker's part of the data.
+ */
+
+ total_length = 0;
+ for(seq_no = 1; seq_no <= num_markers; seq_no++) {
+ if (marker_present[seq_no] == 0) {
+ return FALSE; // missing sequence number
+ }
+ data_offset[seq_no] = total_length;
+ total_length += data_length[seq_no];
+ }
+
+ if (total_length <= 0)
+ return FALSE; // found only empty markers ?
+
+ // allocate space for assembled data
+ icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
+ if (icc_data == NULL)
+ return FALSE; // out of memory
+
+ // and fill it in
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ JOCTET FAR *src_ptr;
+ JOCTET *dst_ptr;
+ unsigned length;
+ seq_no = GETJOCTET(marker->data[12]);
+ dst_ptr = icc_data + data_offset[seq_no];
+ src_ptr = marker->data + ICC_HEADER_SIZE;
+ length = data_length[seq_no];
+ while (length--) {
+ *dst_ptr++ = *src_ptr++;
+ }
+ }
+ }
+
+ *icc_data_ptr = icc_data;
+ *icc_data_len = total_length;
+
+ return TRUE;
+}
+
+/**
+ Read JPEG_APPD marker (IPTC or Adobe Photoshop profile)
+*/
+static BOOL
+jpeg_read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+ return read_iptc_profile(dib, dataptr, datalen);
+}
+
+/**
+ Read JPEG_APP1 marker (XMP profile)
+ @param dib Input FIBITMAP
+ @param dataptr Pointer to the APP1 marker
+ @param datalen APP1 marker length
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+jpeg_read_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+ // marker identifying string for XMP (null terminated)
+ const char *xmp_signature = "http://ns.adobe.com/xap/1.0/";
+ // XMP signature is 29 bytes long
+ const size_t xmp_signature_size = strlen(xmp_signature) + 1;
+
+ size_t length = datalen;
+ BYTE *profile = (BYTE*)dataptr;
+
+ if(length <= xmp_signature_size) {
+ // avoid reading corrupted or empty data
+ return FALSE;
+ }
+
+ // verify the identifying string
+
+ if(memcmp(xmp_signature, profile, strlen(xmp_signature)) == 0) {
+ // XMP profile
+
+ profile += xmp_signature_size;
+ length -= xmp_signature_size;
+
+ // create a tag
+ FITAG *tag = FreeImage_CreateTag();
+ if(tag) {
+ FreeImage_SetTagID(tag, JPEG_APP0+1); // 0xFFE1
+ FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+ FreeImage_SetTagLength(tag, (DWORD)length);
+ FreeImage_SetTagCount(tag, (DWORD)length);
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagValue(tag, profile);
+
+ // store the tag
+ FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+
+ // destroy the tag
+ FreeImage_DeleteTag(tag);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Read JPEG_APP1 marker (Exif profile)
+ @param dib Input FIBITMAP
+ @param dataptr Pointer to the APP1 marker
+ @param datalen APP1 marker length
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned int length) {
+ // marker identifying string for Exif = "Exif\0\0"
+ BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
+
+ // verify the identifying string
+ if(memcmp(exif_signature, profile, sizeof(exif_signature)) != 0) {
+ // not an Exif profile
+ return FALSE;
+ }
+
+ // create a tag
+ FITAG *tag = FreeImage_CreateTag();
+ if(tag) {
+ FreeImage_SetTagID(tag, EXIF_MARKER); // (JPEG_APP0 + 1) => EXIF marker / Adobe XMP marker
+ FreeImage_SetTagKey(tag, g_TagLib_ExifRawFieldName);
+ FreeImage_SetTagLength(tag, (DWORD)length);
+ FreeImage_SetTagCount(tag, (DWORD)length);
+ FreeImage_SetTagType(tag, FIDT_BYTE);
+ FreeImage_SetTagValue(tag, profile);
+
+ // store the tag
+ FreeImage_SetMetadata(FIMD_EXIF_RAW, dib, FreeImage_GetTagKey(tag), tag);
+
+ // destroy the tag
+ FreeImage_DeleteTag(tag);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Read JFIF "JFXX" extension APP0 marker
+ @param dib Input FIBITMAP
+ @param dataptr Pointer to the APP0 marker
+ @param datalen APP0 marker length
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+jpeg_read_jfxx(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
+ if(datalen < 6) {
+ return FALSE;
+ }
+
+ const int id_length = 5;
+ const BYTE *data = dataptr + id_length;
+ unsigned remaining = datalen - id_length;
+
+ const BYTE type = *data;
+ ++data, --remaining;
+
+ switch(type) {
+ case JFXX_TYPE_JPEG:
+ {
+ // load the thumbnail
+ FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(data), remaining);
+ FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem);
+ FreeImage_CloseMemory(hmem);
+ // store the thumbnail
+ FreeImage_SetThumbnail(dib, thumbnail);
+ // then delete it
+ FreeImage_Unload(thumbnail);
+ break;
+ }
+ case JFXX_TYPE_8bit:
+ // colormapped uncompressed thumbnail (no supported)
+ break;
+ case JFXX_TYPE_24bit:
+ // truecolor uncompressed thumbnail (no supported)
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Read JPEG special markers
+*/
+static BOOL
+read_markers(j_decompress_ptr cinfo, FIBITMAP *dib) {
+ jpeg_saved_marker_ptr marker;
+
+ for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ switch(marker->marker) {
+ case JPEG_APP0:
+ // JFIF is handled by libjpeg already, handle JFXX
+ if(memcmp(marker->data, "JFIF" , 5) == 0) {
+ continue;
+ }
+ if(memcmp(marker->data, "JFXX" , 5) == 0) {
+ if (!cinfo->saw_JFIF_marker || cinfo->JFIF_minor_version < 2) {
+ FreeImage_OutputMessageProc(s_format_id, "Warning: non-standard JFXX segment");
+ }
+ jpeg_read_jfxx(dib, marker->data, marker->data_length);
+ }
+ // other values such as 'Picasa' : ignore safely unknown APP0 marker
+ break;
+ case JPEG_COM:
+ // JPEG comment
+ jpeg_read_comment(dib, marker->data, marker->data_length);
+ break;
+ case EXIF_MARKER:
+ // Exif or Adobe XMP profile
+ jpeg_read_exif_profile(dib, marker->data, marker->data_length);
+ jpeg_read_xmp_profile(dib, marker->data, marker->data_length);
+ jpeg_read_exif_profile_raw(dib, marker->data, marker->data_length);
+ break;
+ case IPTC_MARKER:
+ // IPTC/NAA or Adobe Photoshop profile
+ jpeg_read_iptc_profile(dib, marker->data, marker->data_length);
+ break;
+ }
+ }
+
+ // ICC profile
+ BYTE *icc_profile = NULL;
+ unsigned icc_length = 0;
+
+ if ( jpeg_read_icc_profile(cinfo, &icc_profile, &icc_length)) {
+ // copy ICC profile data
+ FreeImage_CreateICCProfile(dib, icc_profile, icc_length);
+ // clean up
+ free(icc_profile);
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// Special markers write functions
+// ----------------------------------------------------------
+
+/**
+ Write JPEG_COM marker (comment)
+*/
+static BOOL
+jpeg_write_comment(j_compress_ptr cinfo, FIBITMAP *dib) {
+ FITAG *tag = NULL;
+
+ // write user comment as a JPEG_COM marker
+ FreeImage_GetMetadata(FIMD_COMMENTS, dib, "Comment", &tag);
+ if(tag) {
+ const char *tag_value = (char*)FreeImage_GetTagValue(tag);
+
+ if(NULL != tag_value) {
+ for(long i = 0; i < (long)strlen(tag_value); i+= MAX_BYTES_IN_MARKER) {
+ jpeg_write_marker(cinfo, JPEG_COM, (BYTE*)tag_value + i, MIN((long)strlen(tag_value + i), MAX_BYTES_IN_MARKER));
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ Write JPEG_APP2 marker (ICC profile)
+*/
+static BOOL
+jpeg_write_icc_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
+ // marker identifying string "ICC_PROFILE" (null-terminated)
+ BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 };
+
+ FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
+
+ if (iccProfile->size && iccProfile->data) {
+ // ICC_HEADER_SIZE: ICC signature is 'ICC_PROFILE' + 2 bytes
+
+ BYTE *profile = (BYTE*)malloc((iccProfile->size + ICC_HEADER_SIZE) * sizeof(BYTE));
+ if(profile == NULL) return FALSE;
+ memcpy(profile, icc_signature, 12);
+
+ for(long i = 0; i < (long)iccProfile->size; i += MAX_DATA_BYTES_IN_MARKER) {
+ unsigned length = MIN((long)(iccProfile->size - i), MAX_DATA_BYTES_IN_MARKER);
+ // sequence number
+ profile[12] = (BYTE) ((i / MAX_DATA_BYTES_IN_MARKER) + 1);
+ // number of markers
+ profile[13] = (BYTE) (iccProfile->size / MAX_DATA_BYTES_IN_MARKER + 1);
+
+ memcpy(profile + ICC_HEADER_SIZE, (BYTE*)iccProfile->data + i, length);
+ jpeg_write_marker(cinfo, ICC_MARKER, profile, (length + ICC_HEADER_SIZE));
+ }
+
+ free(profile);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Write JPEG_APPD marker (IPTC or Adobe Photoshop profile)
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+jpeg_write_iptc_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
+ //const char *ps_header = "Photoshop 3.0\x08BIM\x04\x04\x0\x0\x0\x0";
+ const unsigned tag_length = 26;
+
+ if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) {
+ BYTE *profile = NULL;
+ unsigned profile_size = 0;
+
+ // create a binary profile
+ if(write_iptc_profile(dib, &profile, &profile_size)) {
+
+ // write the profile
+ for(long i = 0; i < (long)profile_size; i += 65517L) {
+ unsigned length = MIN((long)profile_size - i, 65517L);
+ unsigned roundup = length & 0x01; // needed for Photoshop
+ BYTE *iptc_profile = (BYTE*)malloc(length + roundup + tag_length);
+ if(iptc_profile == NULL) break;
+ // Photoshop identification string
+ memcpy(&iptc_profile[0], "Photoshop 3.0\x0", 14);
+ // 8BIM segment type
+ memcpy(&iptc_profile[14], "8BIM\x04\x04\x0\x0\x0\x0", 10);
+ // segment size
+ iptc_profile[24] = (BYTE)(length >> 8);
+ iptc_profile[25] = (BYTE)(length & 0xFF);
+ // segment data
+ memcpy(&iptc_profile[tag_length], &profile[i], length);
+ if(roundup)
+ iptc_profile[length + tag_length] = 0;
+ jpeg_write_marker(cinfo, IPTC_MARKER, iptc_profile, length + roundup + tag_length);
+ free(iptc_profile);
+ }
+
+ // release profile
+ free(profile);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Write JPEG_APP1 marker (XMP profile)
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+jpeg_write_xmp_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
+ // marker identifying string for XMP (null terminated)
+ const char *xmp_signature = "http://ns.adobe.com/xap/1.0/";
+
+ FITAG *tag_xmp = NULL;
+ FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp);
+
+ if(tag_xmp) {
+ const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_xmp);
+
+ if(NULL != tag_value) {
+ // XMP signature is 29 bytes long
+ unsigned int xmp_header_size = (unsigned int)strlen(xmp_signature) + 1;
+
+ DWORD tag_length = FreeImage_GetTagLength(tag_xmp);
+
+ BYTE *profile = (BYTE*)malloc((tag_length + xmp_header_size) * sizeof(BYTE));
+ if(profile == NULL) return FALSE;
+ memcpy(profile, xmp_signature, xmp_header_size);
+
+ for(DWORD i = 0; i < tag_length; i += 65504L) {
+ unsigned length = MIN((long)(tag_length - i), 65504L);
+
+ memcpy(profile + xmp_header_size, tag_value + i, length);
+ jpeg_write_marker(cinfo, EXIF_MARKER, profile, (length + xmp_header_size));
+ }
+
+ free(profile);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Write JPEG_APP1 marker (Exif profile)
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+jpeg_write_exif_profile_raw(j_compress_ptr cinfo, FIBITMAP *dib) {
+ // marker identifying string for Exif = "Exif\0\0"
+ BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
+
+ FITAG *tag_exif = NULL;
+ FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag_exif);
+
+ if(tag_exif) {
+ const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_exif);
+
+ // verify the identifying string
+ if(memcmp(exif_signature, tag_value, sizeof(exif_signature)) != 0) {
+ // not an Exif profile
+ return FALSE;
+ }
+
+ if(NULL != tag_value) {
+ DWORD tag_length = FreeImage_GetTagLength(tag_exif);
+
+ BYTE *profile = (BYTE*)malloc(tag_length * sizeof(BYTE));
+ if(profile == NULL) return FALSE;
+
+ for(DWORD i = 0; i < tag_length; i += 65504L) {
+ unsigned length = MIN((long)(tag_length - i), 65504L);
+
+ memcpy(profile, tag_value + i, length);
+ jpeg_write_marker(cinfo, EXIF_MARKER, profile, length);
+ }
+
+ free(profile);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Write thumbnail (JFXX segment, JPEG compressed)
+*/
+static BOOL
+jpeg_write_jfxx(j_compress_ptr cinfo, FIBITMAP *dib) {
+ // get the thumbnail to be stored
+ FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+ if (!thumbnail) {
+ return TRUE;
+ }
+ // check for a compatible output format
+ if ((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 8) && (FreeImage_GetBPP(thumbnail) != 24)) {
+ FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL);
+ return FALSE;
+ }
+
+ // stores the thumbnail as a baseline JPEG into a memory block
+ // return the memory block only if its size is within JFXX marker size limit!
+ FIMEMORY *stream = FreeImage_OpenMemory();
+
+ if(FreeImage_SaveToMemory(FIF_JPEG, thumbnail, stream, JPEG_BASELINE)) {
+ // check that the memory block size is within JFXX marker size limit
+ FreeImage_SeekMemory(stream, 0, SEEK_END);
+ const long eof = FreeImage_TellMemory(stream);
+ if(eof > MAX_JFXX_THUMB_SIZE) {
+ FreeImage_OutputMessageProc(s_format_id, "Warning: attached thumbnail is %d bytes larger than maximum supported size - Thumbnail saving aborted", eof - MAX_JFXX_THUMB_SIZE);
+ FreeImage_CloseMemory(stream);
+ return FALSE;
+ }
+ } else {
+ FreeImage_CloseMemory(stream);
+ return FALSE;
+ }
+
+ BYTE* thData = NULL;
+ DWORD thSize = 0;
+
+ FreeImage_AcquireMemory(stream, &thData, &thSize);
+
+ BYTE id_length = 5; //< "JFXX"
+ BYTE type = JFXX_TYPE_JPEG;
+
+ DWORD totalsize = id_length + sizeof(type) + thSize;
+ jpeg_write_m_header(cinfo, JPEG_APP0, totalsize);
+
+ jpeg_write_m_byte(cinfo, 'J');
+ jpeg_write_m_byte(cinfo, 'F');
+ jpeg_write_m_byte(cinfo, 'X');
+ jpeg_write_m_byte(cinfo, 'X');
+ jpeg_write_m_byte(cinfo, '\0');
+
+ jpeg_write_m_byte(cinfo, type);
+
+ // write thumbnail to destination.
+ // We "cram it straight into the data destination module", because write_m_byte is slow
+
+ freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
+
+ BYTE* & out = dest->pub.next_output_byte;
+ size_t & bufRemain = dest->pub.free_in_buffer;
+
+ const BYTE *thData_end = thData + thSize;
+
+ while(thData < thData_end) {
+ *(out)++ = *(thData)++;
+ if (--bufRemain == 0) {
+ // buffer full - flush
+ if (!dest->pub.empty_output_buffer(cinfo)) {
+ break;
+ }
+ }
+ }
+
+ FreeImage_CloseMemory(stream);
+
+ return TRUE;
+}
+
+/**
+ Write JPEG special markers
+*/
+static BOOL
+write_markers(j_compress_ptr cinfo, FIBITMAP *dib) {
+ // write thumbnail as a JFXX marker
+ jpeg_write_jfxx(cinfo, dib);
+
+ // write user comment as a JPEG_COM marker
+ jpeg_write_comment(cinfo, dib);
+
+ // write ICC profile
+ jpeg_write_icc_profile(cinfo, dib);
+
+ // write IPTC profile
+ jpeg_write_iptc_profile(cinfo, dib);
+
+ // write Adobe XMP profile
+ jpeg_write_xmp_profile(cinfo, dib);
+
+ // write Exif raw data
+ jpeg_write_exif_profile_raw(cinfo, dib);
+
+ return TRUE;
+}
+
+// ------------------------------------------------------------
+// Keep original size info when using scale option on loading
+// ------------------------------------------------------------
+static void
+store_size_info(FIBITMAP *dib, JDIMENSION width, JDIMENSION height) {
+ char buffer[256];
+ // create a tag
+ FITAG *tag = FreeImage_CreateTag();
+ if(tag) {
+ size_t length = 0;
+ // set the original width
+ sprintf(buffer, "%d", (int)width);
+ length = strlen(buffer) + 1; // include the NULL/0 value
+ FreeImage_SetTagKey(tag, "OriginalJPEGWidth");
+ FreeImage_SetTagLength(tag, (DWORD)length);
+ FreeImage_SetTagCount(tag, (DWORD)length);
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagValue(tag, buffer);
+ FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+ // set the original height
+ sprintf(buffer, "%d", (int)height);
+ length = strlen(buffer) + 1; // include the NULL/0 value
+ FreeImage_SetTagKey(tag, "OriginalJPEGHeight");
+ FreeImage_SetTagLength(tag, (DWORD)length);
+ FreeImage_SetTagCount(tag, (DWORD)length);
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagValue(tag, buffer);
+ FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+ // destroy the tag
+ FreeImage_DeleteTag(tag);
+ }
+}
+
+// ------------------------------------------------------------
+// Rotate a dib according to Exif info
+// ------------------------------------------------------------
+
+static void
+rotate_exif(FIBITMAP **dib) {
+ // check for Exif rotation
+ if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, *dib)) {
+ FIBITMAP *rotated = NULL;
+ // process Exif rotation
+ FITAG *tag = NULL;
+ FreeImage_GetMetadata(FIMD_EXIF_MAIN, *dib, "Orientation", &tag);
+ if(tag != NULL) {
+ if(FreeImage_GetTagID(tag) == TAG_ORIENTATION) {
+ unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag));
+ switch (orientation) {
+ case 1: // "top, left side" => 0°
+ break;
+ case 2: // "top, right side" => flip left-right
+ FreeImage_FlipHorizontal(*dib);
+ break;
+ case 3: // "bottom, right side"; => -180°
+ rotated = FreeImage_Rotate(*dib, 180);
+ FreeImage_Unload(*dib);
+ *dib = rotated;
+ break;
+ case 4: // "bottom, left side" => flip up-down
+ FreeImage_FlipVertical(*dib);
+ break;
+ case 5: // "left side, top" => +90° + flip up-down
+ rotated = FreeImage_Rotate(*dib, 90);
+ FreeImage_Unload(*dib);
+ *dib = rotated;
+ FreeImage_FlipVertical(*dib);
+ break;
+ case 6: // "right side, top" => -90°
+ rotated = FreeImage_Rotate(*dib, -90);
+ FreeImage_Unload(*dib);
+ *dib = rotated;
+ break;
+ case 7: // "right side, bottom" => -90° + flip up-down
+ rotated = FreeImage_Rotate(*dib, -90);
+ FreeImage_Unload(*dib);
+ *dib = rotated;
+ FreeImage_FlipVertical(*dib);
+ break;
+ case 8: // "left side, bottom" => +90°
+ rotated = FreeImage_Rotate(*dib, 90);
+ FreeImage_Unload(*dib);
+ *dib = rotated;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "JPEG";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "JPEG - JFIF Compliant";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "jpg,jif,jpeg,jpe";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return "^\377\330\377";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/jpeg";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE jpeg_signature[] = { 0xFF, 0xD8 };
+ BYTE signature[2] = { 0, 0 };
+
+ io->read_proc(signature, 1, sizeof(jpeg_signature), handle);
+
+ return (memcmp(jpeg_signature, signature, sizeof(jpeg_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 8) ||
+ (depth == 24)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (handle) {
+ FIBITMAP *dib = NULL;
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // set up the jpeglib structures
+
+ struct jpeg_decompress_struct cinfo;
+ ErrorManager fi_error_mgr;
+
+ try {
+
+ // step 1: allocate and initialize JPEG decompression object
+
+ // we set up the normal JPEG error routines, then override error_exit & output_message
+ cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
+ fi_error_mgr.pub.error_exit = jpeg_error_exit;
+ fi_error_mgr.pub.output_message = jpeg_output_message;
+
+ // establish the setjmp return context for jpeg_error_exit to use
+ if (setjmp(fi_error_mgr.setjmp_buffer)) {
+ // If we get here, the JPEG code has signaled an error.
+ // We need to clean up the JPEG object, close the input file, and return.
+ jpeg_destroy_decompress(&cinfo);
+ throw (const char*)NULL;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ // step 2a: specify data source (eg, a handle)
+
+ jpeg_freeimage_src(&cinfo, handle, io);
+
+ // step 2b: save special markers for later reading
+
+ jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
+ for(int m = 0; m < 16; m++) {
+ jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF);
+ }
+
+ // step 3: read handle parameters with jpeg_read_header()
+
+ jpeg_read_header(&cinfo, TRUE);
+
+ // step 4: set parameters for decompression
+
+ unsigned int scale_denom = 1; // fraction by which to scale image
+ int requested_size = flags >> 16; // requested user size in pixels
+ if(requested_size > 0) {
+ // the JPEG codec can perform x2, x4 or x8 scaling on loading
+ // try to find the more appropriate scaling according to user's need
+ double scale = MAX((double)cinfo.image_width, (double)cinfo.image_height) / (double)requested_size;
+ if(scale >= 8) {
+ scale_denom = 8;
+ } else if(scale >= 4) {
+ scale_denom = 4;
+ } else if(scale >= 2) {
+ scale_denom = 2;
+ }
+ }
+ cinfo.scale_num = 1;
+ cinfo.scale_denom = scale_denom;
+
+ if ((flags & JPEG_ACCURATE) != JPEG_ACCURATE) {
+ cinfo.dct_method = JDCT_IFAST;
+ cinfo.do_fancy_upsampling = FALSE;
+ }
+
+ // step 5a: start decompressor and calculate output width and height
+
+ jpeg_start_decompress(&cinfo);
+
+ // step 5b: allocate dib and init header
+
+ if ((cinfo.num_components == 4) && (cinfo.out_color_space == JCS_CMYK)) {
+ // CMYK image
+ if ((flags & JPEG_CMYK) == JPEG_CMYK) {
+ // load as CMYK
+ dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if (!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+ FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
+ } else {
+ // load as CMYK and convert to RGB
+ dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if (!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+ } else {
+ // RGB or greyscale image
+ dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 8 * cinfo.num_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if (!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+
+ if (cinfo.num_components == 1) {
+ // build a greyscale palette
+ RGBQUAD *colors = FreeImage_GetPalette(dib);
+
+ for (int i = 0; i < 256; i++) {
+ colors[i].rgbRed = (BYTE)i;
+ colors[i].rgbGreen = (BYTE)i;
+ colors[i].rgbBlue = (BYTE)i;
+ }
+ }
+ }
+ if(scale_denom != 1) {
+ // store original size info if a scaling was requested
+ store_size_info(dib, cinfo.image_width, cinfo.image_height);
+ }
+
+ // step 5c: handle metrices
+
+ if (cinfo.density_unit == 1) {
+ // dots/inch
+ FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)cinfo.X_density) / 0.0254000 + 0.5));
+ FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)cinfo.Y_density) / 0.0254000 + 0.5));
+ } else if (cinfo.density_unit == 2) {
+ // dots/cm
+ FreeImage_SetDotsPerMeterX(dib, (unsigned) (cinfo.X_density * 100));
+ FreeImage_SetDotsPerMeterY(dib, (unsigned) (cinfo.Y_density * 100));
+ }
+
+ // step 6: read special markers
+
+ read_markers(&cinfo, dib);
+
+ // --- header only mode => clean-up and return
+
+ if (header_only) {
+ // release JPEG decompression object
+ jpeg_destroy_decompress(&cinfo);
+ // return header data
+ return dib;
+ }
+
+ // step 7a: while (scan lines remain to be read) jpeg_read_scanlines(...);
+
+ if ((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) {
+ // convert from CMYK to RGB
+
+ JSAMPARRAY buffer; // output row buffer
+ unsigned row_stride; // physical row width in output buffer
+
+ // JSAMPLEs per row in output buffer
+ row_stride = cinfo.output_width * cinfo.output_components;
+ // make a one-row-high sample array that will go away when done with image
+ buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ while (cinfo.output_scanline < cinfo.output_height) {
+ JSAMPROW src = buffer[0];
+ JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
+
+ jpeg_read_scanlines(&cinfo, buffer, 1);
+
+ for(unsigned x = 0; x < cinfo.output_width; x++) {
+ WORD K = (WORD)src[3];
+ dst[FI_RGBA_RED] = (BYTE)((K * src[0]) / 255); // C -> R
+ dst[FI_RGBA_GREEN] = (BYTE)((K * src[1]) / 255); // M -> G
+ dst[FI_RGBA_BLUE] = (BYTE)((K * src[2]) / 255); // Y -> B
+ src += 4;
+ dst += 3;
+ }
+ }
+ } else if ((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) == JPEG_CMYK)) {
+ // convert from LibJPEG CMYK to standard CMYK
+
+ JSAMPARRAY buffer; // output row buffer
+ unsigned row_stride; // physical row width in output buffer
+
+ // JSAMPLEs per row in output buffer
+ row_stride = cinfo.output_width * cinfo.output_components;
+ // make a one-row-high sample array that will go away when done with image
+ buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ while (cinfo.output_scanline < cinfo.output_height) {
+ JSAMPROW src = buffer[0];
+ JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
+
+ jpeg_read_scanlines(&cinfo, buffer, 1);
+
+ for(unsigned x = 0; x < cinfo.output_width; x++) {
+ // CMYK pixels are inverted
+ dst[0] = ~src[0]; // C
+ dst[1] = ~src[1]; // M
+ dst[2] = ~src[2]; // Y
+ dst[3] = ~src[3]; // K
+ src += 4;
+ dst += 4;
+ }
+ }
+
+ } else {
+ // normal case (RGB or greyscale image)
+
+ while (cinfo.output_scanline < cinfo.output_height) {
+ JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
+
+ jpeg_read_scanlines(&cinfo, &dst, 1);
+ }
+
+ // step 7b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB_RED, ...)
+ // The default behavior of the JPEG library is kept "as is" because LibTIFF uses
+ // LibJPEG "as is".
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ SwapRedBlue32(dib);
+#endif
+ }
+
+ // step 8: finish decompression
+
+ jpeg_finish_decompress(&cinfo);
+
+ // step 9: release JPEG decompression object
+
+ jpeg_destroy_decompress(&cinfo);
+
+ // check for automatic Exif rotation
+ if (!header_only && ((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE)) {
+ rotate_exif(&dib);
+ }
+
+ // everything went well. return the loaded dib
+
+ return dib;
+
+ } catch (const char *text) {
+ jpeg_destroy_decompress(&cinfo);
+ if(NULL != dib) {
+ FreeImage_Unload(dib);
+ }
+ if(NULL != text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if ((dib) && (handle)) {
+ try {
+ // Check dib format
+
+ const char *sError = "only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG";
+
+ FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+ WORD bpp = (WORD)FreeImage_GetBPP(dib);
+
+ if ((bpp != 24) && (bpp != 8)) {
+ throw sError;
+ }
+
+ if(bpp == 8) {
+ // allow grey, reverse grey and palette
+ if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE)) {
+ throw sError;
+ }
+ }
+
+
+ struct jpeg_compress_struct cinfo;
+ ErrorManager fi_error_mgr;
+
+ // Step 1: allocate and initialize JPEG compression object
+
+ // we set up the normal JPEG error routines, then override error_exit & output_message
+ cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
+ fi_error_mgr.pub.error_exit = jpeg_error_exit;
+ fi_error_mgr.pub.output_message = jpeg_output_message;
+
+ // establish the setjmp return context for jpeg_error_exit to use
+ if (setjmp(fi_error_mgr.setjmp_buffer)) {
+ // If we get here, the JPEG code has signaled an error.
+ // We need to clean up the JPEG object, close the input file, and return.
+ jpeg_destroy_compress(&cinfo);
+ throw (const char*)NULL;
+ }
+
+ // Now we can initialize the JPEG compression object
+
+ jpeg_create_compress(&cinfo);
+
+ // Step 2: specify data destination (eg, a file)
+
+ jpeg_freeimage_dst(&cinfo, handle, io);
+
+ // Step 3: set parameters for compression
+
+ cinfo.image_width = FreeImage_GetWidth(dib);
+ cinfo.image_height = FreeImage_GetHeight(dib);
+
+ switch(color_type) {
+ case FIC_MINISBLACK :
+ case FIC_MINISWHITE :
+ cinfo.in_color_space = JCS_GRAYSCALE;
+ cinfo.input_components = 1;
+ break;
+
+ default :
+ cinfo.in_color_space = JCS_RGB;
+ cinfo.input_components = 3;
+ break;
+ }
+
+ jpeg_set_defaults(&cinfo);
+
+ // progressive-JPEG support
+ if ((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) {
+ jpeg_simple_progression(&cinfo);
+ }
+
+ // compute optimal Huffman coding tables for the image
+ if ((flags & JPEG_OPTIMIZE) == JPEG_OPTIMIZE) {
+ cinfo.optimize_coding = TRUE;
+ }
+
+ // Set JFIF density parameters from the DIB data
+
+ cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib));
+ cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib));
+ cinfo.density_unit = 1; // dots / inch
+
+ // thumbnail support (JFIF 1.02 extension markers)
+ if(FreeImage_GetThumbnail(dib) != NULL) {
+ cinfo.write_JFIF_header = 1; //<### force it, though when color is CMYK it will be incorrect
+ cinfo.JFIF_minor_version = 2;
+ }
+
+ // baseline JPEG support
+ if ((flags & JPEG_BASELINE) == JPEG_BASELINE) {
+ cinfo.write_JFIF_header = 0; // No marker for non-JFIF colorspaces
+ cinfo.write_Adobe_marker = 0; // write no Adobe marker by default
+ }
+
+ // set subsampling options if required
+
+ if(cinfo.in_color_space == JCS_RGB) {
+ if ((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) {
+ // 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100%
+ // the horizontal color resolution is quartered
+ cinfo.comp_info[0].h_samp_factor = 4; // Y
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1; // Cb
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1; // Cr
+ cinfo.comp_info[2].v_samp_factor = 1;
+ } else if ((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) {
+ // 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50%
+ // the chrominance resolution in both the horizontal and vertical directions is cut in half
+ cinfo.comp_info[0].h_samp_factor = 2; // Y
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1; // Cb
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1; // Cr
+ cinfo.comp_info[2].v_samp_factor = 1;
+ } else if ((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low)
+ // 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100%
+ // half of the horizontal resolution in the chrominance is dropped (Cb & Cr),
+ // while the full resolution is retained in the vertical direction, with respect to the luminance
+ cinfo.comp_info[0].h_samp_factor = 2; // Y
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1; // Cb
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1; // Cr
+ cinfo.comp_info[2].v_samp_factor = 1;
+ }
+ else if ((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling)
+ // 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100%
+ // the resolution of chrominance information (Cb & Cr) is preserved
+ // at the same rate as the luminance (Y) information
+ cinfo.comp_info[0].h_samp_factor = 1; // Y
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1; // Cb
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1; // Cr
+ cinfo.comp_info[2].v_samp_factor = 1;
+ }
+ }
+
+ // Step 4: set quality
+ // the first 7 bits are reserved for low level quality settings
+ // the other bits are high level (i.e. enum-ish)
+
+ int quality;
+
+ if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) {
+ quality = 10;
+ } else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) {
+ quality = 25;
+ } else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) {
+ quality = 50;
+ } else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) {
+ quality = 75;
+ } else if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) {
+ quality = 100;
+ } else {
+ if ((flags & 0x7F) == 0) {
+ quality = 75;
+ } else {
+ quality = flags & 0x7F;
+ }
+ }
+
+ jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
+
+ // Step 5: Start compressor
+
+ jpeg_start_compress(&cinfo, TRUE);
+
+ // Step 6: Write special markers
+
+ if ((flags & JPEG_BASELINE) != JPEG_BASELINE) {
+ write_markers(&cinfo, dib);
+ }
+
+ // Step 7: while (scan lines remain to be written)
+
+ if(color_type == FIC_RGB) {
+ // 24-bit RGB image : need to swap red and blue channels
+ unsigned pitch = FreeImage_GetPitch(dib);
+ BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE));
+ if (target == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ // get a copy of the scanline
+ memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ // swap R and B channels
+ BYTE *target_p = target;
+ for(unsigned x = 0; x < cinfo.image_width; x++) {
+ INPLACESWAP(target_p[0], target_p[2]);
+ target_p += 3;
+ }
+#endif
+ // write the scanline
+ jpeg_write_scanlines(&cinfo, &target, 1);
+ }
+ free(target);
+ }
+ else if(color_type == FIC_MINISBLACK) {
+ // 8-bit standard greyscale images
+ while (cinfo.next_scanline < cinfo.image_height) {
+ JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
+
+ jpeg_write_scanlines(&cinfo, &b, 1);
+ }
+ }
+ else if(color_type == FIC_PALETTE) {
+ // 8-bit palettized images are converted to 24-bit images
+ RGBQUAD *palette = FreeImage_GetPalette(dib);
+ BYTE *target = (BYTE*)malloc(cinfo.image_width * 3);
+ if (target == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
+ FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ // swap R and B channels
+ BYTE *target_p = target;
+ for(unsigned x = 0; x < cinfo.image_width; x++) {
+ INPLACESWAP(target_p[0], target_p[2]);
+ target_p += 3;
+ }
+#endif
+
+
+ jpeg_write_scanlines(&cinfo, &target, 1);
+ }
+
+ free(target);
+ }
+ else if(color_type == FIC_MINISWHITE) {
+ // reverse 8-bit greyscale image, so reverse grey value on the fly
+ unsigned i;
+ BYTE reverse[256];
+ BYTE *target = (BYTE *)malloc(cinfo.image_width);
+ if (target == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for(i = 0; i < 256; i++) {
+ reverse[i] = (BYTE)(255 - i);
+ }
+
+ while(cinfo.next_scanline < cinfo.image_height) {
+ BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
+ for(i = 0; i < cinfo.image_width; i++) {
+ target[i] = reverse[ source[i] ];
+ }
+ jpeg_write_scanlines(&cinfo, &target, 1);
+ }
+
+ free(target);
+ }
+
+ // Step 8: Finish compression
+
+ jpeg_finish_compress(&cinfo);
+
+ // Step 9: release JPEG compression object
+
+ jpeg_destroy_compress(&cinfo);
+
+ return TRUE;
+
+ } catch (const char *text) {
+ if(text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitJPEG(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginKOALA.cpp b/plugins/AdvaImg/src/FreeImage/PluginKOALA.cpp
new file mode 100644
index 0000000000..6c3ae52254
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginKOALA.cpp
@@ -0,0 +1,243 @@
+// ==========================================================
+// KOALA Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagKOALA {
+ BYTE image[8000]; // pixmap image
+ BYTE colour1[1000]; // first colourmap (colour 1 and 2)
+ BYTE colour2[1000]; // second colourmap (colour 3)
+ BYTE background; // background colour
+} koala_t;
+
+struct colour_t {
+ int r;
+ int g;
+ int b;
+};
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ----------------------------------------------------------
+
+#define CBM_WIDTH 320
+#define CBM_HEIGHT 200
+
+// ----------------------------------------------------------
+
+const colour_t c64colours[16] = {
+ { 0, 0, 0 }, // Black
+ { 255, 255, 255 }, // White
+ { 170, 17, 17 }, // Red
+ { 12, 204, 204 }, // Cyan
+ { 221, 51, 221 }, // Purple
+ { 0, 187, 0 }, // Green
+ { 0, 0, 204 }, // Blue
+ { 255, 255, 140 }, // Yellow
+ { 204, 119, 34 }, // Orange
+ { 136, 68, 0 }, // Brown
+ { 255, 153, 136 }, // Light red
+ { 92, 92, 92 }, // Gray 1
+ { 170, 170, 170 }, // Gray 2
+ { 140, 255, 178 }, // Light green
+ { 39, 148, 255 }, // Light blue
+ { 196, 196, 196 } // Gray 3
+};
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+const char * DLL_CALLCONV
+Format() {
+ return "KOALA";
+}
+
+const char * DLL_CALLCONV
+Description() {
+ return "C64 Koala Graphics";
+}
+
+const char * DLL_CALLCONV
+Extension() {
+ return "koa";
+}
+
+const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-koala";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE koala_signature[] = { 0x00, 0x60 };
+ BYTE signature[2] = { 0, 0 };
+
+ io->read_proc(signature, 1, sizeof(koala_signature), handle);
+
+ return (memcmp(koala_signature, signature, sizeof(koala_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (handle) {
+ koala_t image;
+
+ // read the load address
+
+ unsigned char load_address[2]; // highbit, lowbit
+
+ io->read_proc(&load_address, 1, 2, handle);
+
+ // if the load address is correct, skip it. otherwise ignore the load address
+
+ if ((load_address[0] != 0x00) || (load_address[1] != 0x60)) {
+ ((BYTE *)&image)[0] = load_address[0];
+ ((BYTE *)&image)[1] = load_address[1];
+
+ io->read_proc((BYTE *)&image + 2, 1, 10001 - 2, handle);
+ } else {
+ io->read_proc(&image, 1, 10001, handle);
+ }
+
+ // build DIB in memory
+
+ FIBITMAP *dib = FreeImage_Allocate(CBM_WIDTH, CBM_HEIGHT, 4);
+
+ if (dib) {
+ // write out the commodore 64 color palette
+
+ RGBQUAD *palette = FreeImage_GetPalette(dib);
+
+ for (int i = 0; i < 16; i++) {
+ palette[i].rgbBlue = (BYTE)c64colours[i].b;
+ palette[i].rgbGreen = (BYTE)c64colours[i].g;
+ palette[i].rgbRed = (BYTE)c64colours[i].r;
+ }
+
+ // write out bitmap data
+
+ BYTE pixel_mask[4] = { 0xc0, 0x30, 0x0c, 0x03 };
+ BYTE pixel_displacement[4] = { 6, 4, 2, 0 };
+ int pixel, index, colourindex;
+ unsigned char found_color = 0;
+
+ for (int y = 0; y < 200; y++) {
+ for (int x = 0; x < 160; x++) {
+ // Get value of pixel at (x,y)
+
+ index = (x / 4) * 8 + (y % 8) + (y / 8) * CBM_WIDTH;
+ colourindex = (x / 4) + (y / 8) * 40;
+ pixel = (image.image[index] & pixel_mask[x % 4]) >> pixel_displacement[x % 4];
+
+ // Retrieve RGB values
+
+ switch (pixel) {
+ case 0: // Background
+ found_color = image.background;
+ break;
+
+ case 1: // Colour 1
+ found_color = image.colour1[colourindex] >> 4;
+ break;
+
+ case 2: // Colour 2
+ found_color = image.colour1[colourindex] & 0xf;
+ break;
+
+ case 3: // Colour 3
+ found_color = image.colour2[colourindex] & 0xf;
+ break;
+ };
+
+ *(FreeImage_GetScanLine(dib, CBM_HEIGHT - y - 1) + x) = (found_color << 4) | found_color;
+ }
+ }
+
+ return dib;
+ }
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitKOALA(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginMNG.cpp b/plugins/AdvaImg/src/FreeImage/PluginMNG.cpp
new file mode 100644
index 0000000000..6d2f50ecec
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginMNG.cpp
@@ -0,0 +1,153 @@
+// ==========================================================
+// MNG loader
+//
+// Design and implementation by
+// - Herve Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ----------------------------------------------------------
+
+#define MNG_SIGNATURE_SIZE 8 // size of the signature
+
+// ----------------------------------------------------------
+
+// ----------------------------------------------------------
+// mng interface (see MNGHelper.cpp)
+// ----------------------------------------------------------
+
+FIBITMAP* mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0);
+
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "MNG";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Multiple-image Network Graphics";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "mng";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "video/x-mng";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE mng_signature[8] = { 138, 77, 78, 71, 13, 10, 26, 10 };
+ BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ io->read_proc(&signature, 1, MNG_SIGNATURE_SIZE, handle);
+
+ return (memcmp(mng_signature, signature, MNG_SIGNATURE_SIZE) == 0) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return FALSE;
+}
+
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ return NULL;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ long offset = MNG_SIGNATURE_SIZE; // move to skip first 8 bytes of signature
+
+ // check the signature (8 bytes)
+ if(Validate(io, handle) == FALSE) {
+ return NULL;
+ }
+
+ // parse chunks and decode a jng or mng bitmap
+ return mng_ReadChunks(s_format_id, io, handle, offset, flags);
+}
+
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitMNG(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPCD.cpp b/plugins/AdvaImg/src/FreeImage/PluginPCD.cpp
new file mode 100644
index 0000000000..87cbe967b2
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginPCD.cpp
@@ -0,0 +1,251 @@
+// ==========================================================
+// Kodak PhotoCD Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// Based on pascal code developed by Alex Kwak
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static int
+clamp(double x) {
+ int a = (int)floor(x + 0.5);
+ return (a < 0) ? 0 : (a > 255) ? 255 : a;
+}
+
+static void
+YUV2RGB(int y, int cb, int cr, int &r, int &g, int &b) {
+ double c11 = 0.0054980 * 256.0;
+ double c12 = 0.0000001 * 256.0;
+ double c13 = 0.0051681 * 256.0;
+ double c21 = 0.0054980 * 256.0;
+ double c22 = -0.0015446 * 256.0;
+ double c23 = -0.0026325 * 256.0;
+ double c31 = 0.0054980 * 256.0;
+ double c32 = 0.0079533 * 256.0;
+ double c33 = 0.0000001 * 256.0;
+
+ r = clamp(c11 * y + c12 * (cb - 156) + c13 * (cr - 137));
+ g = clamp(c21 * y + c22 * (cb - 156) + c23 * (cr - 137));
+ b = clamp(c31 * y + c32 * (cb - 156) + c33 * (cr - 137));
+}
+
+static BOOL
+VerticalOrientation(FreeImageIO *io, fi_handle handle) {
+ char buffer[128];
+
+ io->read_proc(buffer, 128, 1, handle);
+
+ return (buffer[72] & 63) == 8;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "PCD";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Kodak PhotoCD";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "pcd";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-photo-cd";
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ FIBITMAP *dib = NULL;
+ unsigned width;
+ unsigned height;
+ const unsigned bpp = 24;
+ int scan_line_add = 1;
+ int start_scan_line = 0;
+
+ BYTE *y1 = NULL, *y2 = NULL, *cbcr = NULL;
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // to make absolute seeks possible we store the current position in the file
+
+ long offset_in_file = io->tell_proc(handle);
+ long seek = 0;
+
+ // decide which bitmap in the cabinet to load
+
+ switch (flags) {
+ case PCD_BASEDIV4 :
+ seek = 0x2000;
+ width = 192;
+ height = 128;
+ break;
+
+ case PCD_BASEDIV16 :
+ seek = 0xB800;
+ width = 384;
+ height = 256;
+ break;
+
+ default :
+ seek = 0x30000;
+ width = 768;
+ height = 512;
+ break;
+ }
+
+ try {
+ // allocate the dib and write out the header
+ dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if (!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+
+ if(header_only) {
+ return dib;
+ }
+
+ // check if the PCD is bottom-up
+
+ if (VerticalOrientation(io, handle)) {
+ scan_line_add = -1;
+ start_scan_line = height - 1;
+ }
+
+ // temporary stuff to load PCD
+
+ BYTE *y1 = (BYTE*)malloc(width * sizeof(BYTE));
+ BYTE *y2 = (BYTE*)malloc(width * sizeof(BYTE));
+ BYTE *cbcr = (BYTE*)malloc(width * sizeof(BYTE));
+ if (!y1 || !y2 || !cbcr) throw FI_MSG_ERROR_MEMORY;
+
+ BYTE *yl[] = { y1, y2 };
+
+ // seek to the part where the bitmap data begins
+
+ io->seek_proc(handle, offset_in_file, SEEK_SET);
+ io->seek_proc(handle, seek, SEEK_CUR);
+
+ // read the data
+
+ for (unsigned y = 0; y < height / 2; y++) {
+ io->read_proc(y1, width, 1, handle);
+ io->read_proc(y2, width, 1, handle);
+ io->read_proc(cbcr, width, 1, handle);
+
+ for (int i = 0; i < 2; i++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, start_scan_line);
+ for (unsigned x = 0; x < width; x++) {
+ int r, g, b;
+
+ YUV2RGB(yl[i][x], cbcr[x / 2], cbcr[(width / 2) + (x / 2)], r, g, b);
+
+ bits[FI_RGBA_BLUE] = (BYTE)b;
+ bits[FI_RGBA_GREEN] = (BYTE)g;
+ bits[FI_RGBA_RED] = (BYTE)r;
+ bits += 3;
+ }
+
+ start_scan_line += scan_line_add;
+ }
+ }
+
+ free(cbcr);
+ free(y2);
+ free(y1);
+
+ return dib;
+
+ } catch(const char *text) {
+ if(dib) FreeImage_Unload(dib);
+ if(cbcr) free(cbcr);
+ if(y2) free(y2);
+ if(y1) free(y1);
+
+ FreeImage_OutputMessageProc(s_format_id, text);
+
+ return NULL;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPCD(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = NULL;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPCX.cpp b/plugins/AdvaImg/src/FreeImage/PluginPCX.cpp
new file mode 100644
index 0000000000..a6feb5fea7
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginPCX.cpp
@@ -0,0 +1,659 @@
+// ==========================================================
+// PCX Loader
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Jani Kajala (janik@remedy.fi)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Juergen Riecker (j.riecker@gmx.de)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#define IO_BUF_SIZE 2048
+
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagPCXHEADER {
+ BYTE manufacturer; // Magic number (0x0A = ZSoft Z)
+ BYTE version; // Version 0 == 2.5
+ // 2 == 2.8 with palette info
+ // 3 == 2.8 without palette info
+ // 5 == 3.0 with palette info
+ BYTE encoding; // Encoding: 0 = uncompressed, 1 = PCX rle compressed
+ BYTE bpp; // Bits per pixel per plane (only 1 or 8)
+ WORD window[4]; // left, upper, right,lower pixel coord.
+ WORD hdpi; // Horizontal resolution
+ WORD vdpi; // Vertical resolution
+ BYTE color_map[48]; // Colormap for 16-color images
+ BYTE reserved;
+ BYTE planes; // Number of planes (1, 3 or 4)
+ WORD bytes_per_line; // Bytes per row (always even)
+ WORD palette_info; // Palette information (1 = color or b&w; 2 = gray scale)
+ WORD h_screen_size;
+ WORD v_screen_size;
+ BYTE filler[54]; // Reserved filler
+} PCXHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static BOOL
+pcx_validate(FreeImageIO *io, fi_handle handle) {
+ BYTE pcx_signature = 0x0A;
+ BYTE signature[4] = { 0, 0, 0, 0 };
+
+ if(io->read_proc(&signature, 1, 4, handle) != 4) {
+ return FALSE;
+ }
+ // magic number (0x0A = ZSoft Z)
+ if(signature[0] == pcx_signature) {
+ // version
+ if(signature[1] <= 5) {
+ // encoding
+ if ((signature[2] == 0) || (signature[2] == 1)) {
+ // bits per pixel per plane
+ if ((signature[3] == 1) || (signature[3] == 8)) {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static unsigned
+readline(FreeImageIO &io, fi_handle handle, BYTE *buffer, unsigned length, BOOL rle, BYTE * ReadBuf, int * ReadPos) {
+ // -----------------------------------------------------------//
+ // Read either run-length encoded or normal image data //
+ // //
+ // THIS IS HOW RUNTIME LENGTH ENCODING WORKS IN PCX: //
+ // //
+ // 1) If the upper 2 bits of a byte are set, //
+ // the lower 6 bits specify the count for the next byte //
+ // //
+ // 2) If the upper 2 bits of the byte are clear, //
+ // the byte is actual data with a count of 1 //
+ // //
+ // Note that a scanline always has an even number of bytes //
+ // -------------------------------------------------------------
+
+ BYTE count = 0, value = 0;
+ unsigned written = 0;
+
+ if (rle) {
+ // run-length encoded read
+
+ while (length--) {
+ if (count == 0) {
+ if (*ReadPos >= IO_BUF_SIZE - 1 ) {
+ if (*ReadPos == IO_BUF_SIZE - 1) {
+ // we still have one BYTE, copy it to the start pos
+
+ *ReadBuf = ReadBuf[IO_BUF_SIZE - 1];
+
+ io.read_proc(ReadBuf + 1, 1, IO_BUF_SIZE - 1, handle);
+ } else {
+ // read the complete buffer
+
+ io.read_proc(ReadBuf, 1, IO_BUF_SIZE, handle);
+ }
+
+ *ReadPos = 0;
+ }
+
+ value = *(ReadBuf + (*ReadPos)++);
+
+ if ((value & 0xC0) == 0xC0) {
+ count = value & 0x3F;
+ value = *(ReadBuf + (*ReadPos)++);
+ } else {
+ count = 1;
+ }
+ }
+
+ count--;
+
+ *(buffer + written++) = value;
+ }
+
+ } else {
+ // normal read
+
+ written = io.read_proc(buffer, length, 1, handle);
+ }
+
+ return written;
+}
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(PCXHEADER *header) {
+ SwapShort(&header->window[0]);
+ SwapShort(&header->window[1]);
+ SwapShort(&header->window[2]);
+ SwapShort(&header->window[3]);
+ SwapShort(&header->hdpi);
+ SwapShort(&header->vdpi);
+ SwapShort(&header->bytes_per_line);
+ SwapShort(&header->palette_info);
+ SwapShort(&header->h_screen_size);
+ SwapShort(&header->v_screen_size);
+}
+#endif
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+/*!
+ Returns the format string for the plugin. Each plugin,
+ both internal in the DLL and external in a .fip file, must have
+ a unique format string to be addressable.
+*/
+
+static const char * DLL_CALLCONV
+Format() {
+ return "PCX";
+}
+
+/*!
+ Returns a description string for the plugin. Though a
+ description is not necessary per-se,
+ it is advised to return an unique string in order to tell the
+ user what type of bitmaps this plugin will read and/or write.
+*/
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Zsoft Paintbrush";
+}
+
+/*!
+ Returns a comma separated list of file
+ extensions indicating what files this plugin can open. The
+ list, being used by FreeImage_GetFIFFromFilename, is usually
+ used as a last resort in finding the type of the bitmap we
+ are dealing with. Best is to check the first few bytes on
+ the low-level bits level first and compare them with a known
+ signature . If this fails, FreeImage_GetFIFFromFilename can be
+ used.
+*/
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "pcx";
+}
+
+/*!
+ Returns an (optional) regular expression to help
+ software identifying a bitmap type. The
+ expression can be applied to the first few bytes (header) of
+ the bitmap. FreeImage is not capable of processing regular expression itself,
+ but FreeImageQt, the FreeImage Trolltech support library, can. If RegExpr
+ returns NULL, FreeImageQt will automatically bypass Trolltech's regular
+ expression support and use its internal functions to find the bitmap type.
+*/
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-pcx";
+}
+
+/*!
+ Validates a bitmap by reading the first few bytes
+ and comparing them with a known bitmap signature.
+ TRUE is returned if the bytes match the signature, FALSE otherwise.
+ The Validate function is used by using FreeImage_GetFileType.
+
+ Note: a plugin can safely read data any data from the bitmap without seeking back
+ to the original entry point; the entry point is stored prior to calling this
+ function and restored after.
+
+ Note: because of FreeImage's io redirection support, the header for the bitmap
+ must be on the start of the bitmap or at least on a known part in the bitmap. It is
+ forbidden to seek to the end of the bitmap or to a point relative to the end of a bitmap,
+ because the end of the bitmap is not always known.
+*/
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ return pcx_validate(io, handle);
+}
+
+/*!
+ This function is used to 'ask' the plugin if it can write
+ a bitmap in a certain bitdepth. Different bitmap types have different
+ capabilities, for example not all formats allow writing in palettized mode.
+ This function is there provide an uniform interface to the plugin's
+ capabilities. SupportsExportDepth returns TRUE if the plugin support writing
+ in the asked bitdepth, or FALSE if it doesn't. The function also
+ returns FALSE if bitmap saving is not supported by the plugin at all.
+*/
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+/*!
+ Loads a bitmap into memory. On entry it is assumed that
+ the bitmap to be loaded is of the correct type. If the bitmap
+ is of an incorrect type, the plugin might not gracefully fail but
+ crash or enter an endless loop. It is also assumed that all
+ the bitmap data is available at one time. If the bitmap is not complete,
+ for example because it is being downloaded while loaded, the plugin
+ might also not gracefully fail.
+
+ The Load function has the following parameters:
+
+ The first parameter (FreeImageIO *io) is a structure providing
+ function pointers in order to make use of FreeImage's IO redirection. Using
+ FreeImage's file i/o functions instead of standard ones it is garantueed
+ that all bitmap types, both current and future ones, can be loaded from
+ memory, file cabinets, the internet and more. The second parameter (fi_handle handle)
+ is a companion of FreeImageIO and can be best compared with the standard FILE* type,
+ in a generalized form.
+
+ The third parameter (int page) indicates wether we will be loading a certain page
+ in the bitmap or if we will load the default one. This parameter is only used if
+ the plugin supports multi-paged bitmaps, e.g. cabinet bitmaps that contain a series
+ of images or pages. If the plugin does support multi-paging, the page parameter
+ can contain either a number higher or equal to 0 to load a certain page, or -1 to
+ load the default page. If the plugin does not support multi-paging,
+ the page parameter is always -1.
+
+ The fourth parameter (int flags) manipulates the load function to load a bitmap
+ in a certain way. Every plugin has a different flag parameter with different meanings.
+
+ The last parameter (void *data) can contain a special data block used when
+ the file is read multi-paged. Because not every plugin supports multi-paging
+ not every plugin will use the data parameter and it will be set to NULL.However,
+ when the plugin does support multi-paging the parameter contains a pointer to a
+ block of data allocated by the Open function.
+*/
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ FIBITMAP *dib = NULL;
+ BYTE *bits; // Pointer to dib data
+ RGBQUAD *pal; // Pointer to dib palette
+ BYTE *line = NULL; // PCX raster line
+ BYTE *ReadBuf = NULL; // buffer;
+ BOOL bIsRLE; // True if the file is run-length encoded
+
+ if (!handle) {
+ return NULL;
+ }
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+ // check PCX identifier
+
+ long start_pos = io->tell_proc(handle);
+ BOOL validated = pcx_validate(io, handle);
+ io->seek_proc(handle, start_pos, SEEK_SET);
+ if (!validated) {
+ throw FI_MSG_ERROR_MAGIC_NUMBER;
+ }
+
+ // process the header
+
+ PCXHEADER header;
+
+ if(io->read_proc(&header, sizeof(PCXHEADER), 1, handle) != 1) {
+ throw FI_MSG_ERROR_PARSING;
+ }
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapHeader(&header);
+#endif
+
+ // allocate a new DIB
+
+ unsigned width = header.window[2] - header.window[0] + 1;
+ unsigned height = header.window[3] - header.window[1] + 1;
+ unsigned bitcount = header.bpp * header.planes;
+
+ if (bitcount == 24) {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bitcount, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, width, height, bitcount);
+ }
+
+ // if the dib couldn't be allocated, throw an error
+
+ if (!dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // metrics handling code
+
+ FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)header.hdpi) / 0.0254000 + 0.5));
+ FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)header.vdpi) / 0.0254000 + 0.5));
+
+ // Set up the palette if needed
+ // ----------------------------
+
+ switch(bitcount) {
+ case 1:
+ {
+ pal = FreeImage_GetPalette(dib);
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+ break;
+ }
+
+ case 4:
+ {
+ pal = FreeImage_GetPalette(dib);
+
+ BYTE *pColormap = &header.color_map[0];
+
+ for (int i = 0; i < 16; i++) {
+ pal[i].rgbRed = pColormap[0];
+ pal[i].rgbGreen = pColormap[1];
+ pal[i].rgbBlue = pColormap[2];
+ pColormap += 3;
+ }
+
+ break;
+ }
+
+ case 8:
+ {
+ BYTE palette_id;
+
+ io->seek_proc(handle, -769L, SEEK_END);
+ io->read_proc(&palette_id, 1, 1, handle);
+
+ if (palette_id == 0x0C) {
+ BYTE *cmap = (BYTE*)malloc(768 * sizeof(BYTE));
+ io->read_proc(cmap, 768, 1, handle);
+
+ pal = FreeImage_GetPalette(dib);
+ BYTE *pColormap = &cmap[0];
+
+ for(int i = 0; i < 256; i++) {
+ pal[i].rgbRed = pColormap[0];
+ pal[i].rgbGreen = pColormap[1];
+ pal[i].rgbBlue = pColormap[2];
+ pColormap += 3;
+ }
+
+ free(cmap);
+ }
+
+ // wrong palette ID, perhaps a gray scale is needed ?
+
+ else if (header.palette_info == 2) {
+ pal = FreeImage_GetPalette(dib);
+
+ for(int i = 0; i < 256; i++) {
+ pal[i].rgbRed = (BYTE)i;
+ pal[i].rgbGreen = (BYTE)i;
+ pal[i].rgbBlue = (BYTE)i;
+ }
+ }
+
+ io->seek_proc(handle, (long)sizeof(PCXHEADER), SEEK_SET);
+ }
+ break;
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // calculate the line length for the PCX and the DIB
+
+ // length of raster line in bytes
+ unsigned linelength = header.bytes_per_line * header.planes;
+ // length of DIB line (rounded to DWORD) in bytes
+ unsigned pitch = FreeImage_GetPitch(dib);
+
+ // run-length encoding ?
+
+ bIsRLE = (header.encoding == 1) ? TRUE : FALSE;
+
+ // load image data
+ // ---------------
+
+ line = (BYTE*)malloc(linelength * sizeof(BYTE));
+ if (!line) throw FI_MSG_ERROR_MEMORY;
+
+ ReadBuf = (BYTE*)malloc(IO_BUF_SIZE * sizeof(BYTE));
+ if (!ReadBuf) throw FI_MSG_ERROR_MEMORY;
+
+ bits = FreeImage_GetScanLine(dib, height - 1);
+
+ int ReadPos = IO_BUF_SIZE;
+
+ if ((header.planes == 1) && ((header.bpp == 1) || (header.bpp == 8))) {
+ BYTE skip;
+ unsigned written;
+
+ for (unsigned y = 0; y < height; y++) {
+ written = readline(*io, handle, bits, linelength, bIsRLE, ReadBuf, &ReadPos);
+
+ // skip trailing garbage at the end of the scanline
+
+ for (unsigned count = written; count < linelength; count++) {
+ if (ReadPos < IO_BUF_SIZE) {
+ ReadPos++;
+ } else {
+ io->read_proc(&skip, sizeof(BYTE), 1, handle);
+ }
+ }
+
+ bits -= pitch;
+ }
+ } else if ((header.planes == 4) && (header.bpp == 1)) {
+ BYTE bit, mask, skip;
+ unsigned index;
+ BYTE *buffer;
+ unsigned x, y, written;
+
+ buffer = (BYTE*)malloc(width * sizeof(BYTE));
+ if (!buffer) throw FI_MSG_ERROR_MEMORY;
+
+ for (y = 0; y < height; y++) {
+ written = readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos);
+
+ // build a nibble using the 4 planes
+
+ memset(buffer, 0, width * sizeof(BYTE));
+
+ for(int plane = 0; plane < 4; plane++) {
+ bit = (BYTE)(1 << plane);
+
+ for (x = 0; x < width; x++) {
+ index = (unsigned)((x / 8) + plane * header.bytes_per_line);
+ mask = (BYTE)(0x80 >> (x & 0x07));
+ buffer[x] |= (line[index] & mask) ? bit : 0;
+ }
+ }
+
+ // then write the DIB row
+
+ for (x = 0; x < width / 2; x++) {
+ bits[x] = (buffer[2*x] << 4) | buffer[2*x+1];
+ }
+
+ // skip trailing garbage at the end of the scanline
+
+ for (unsigned count = written; count < linelength; count++) {
+ if (ReadPos < IO_BUF_SIZE) {
+ ReadPos++;
+ } else {
+ io->read_proc(&skip, sizeof(BYTE), 1, handle);
+ }
+ }
+
+ bits -= pitch;
+ }
+
+ free(buffer);
+
+ } else if ((header.planes == 3) && (header.bpp == 8)) {
+ BYTE *pline;
+
+ for (unsigned y = 0; y < height; y++) {
+ readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos);
+
+ // convert the plane stream to BGR (RRRRGGGGBBBB -> BGRBGRBGRBGR)
+ // well, now with the FI_RGBA_x macros, on BIGENDIAN we convert to RGB
+
+ pline = line;
+ unsigned x;
+
+ for (x = 0; x < width; x++) {
+ bits[x * 3 + FI_RGBA_RED] = pline[x];
+ }
+ pline += header.bytes_per_line;
+
+ for (x = 0; x < width; x++) {
+ bits[x * 3 + FI_RGBA_GREEN] = pline[x];
+ }
+ pline += header.bytes_per_line;
+
+ for (x = 0; x < width; x++) {
+ bits[x * 3 + FI_RGBA_BLUE] = pline[x];
+ }
+ pline += header.bytes_per_line;
+
+ bits -= pitch;
+ }
+ } else {
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+
+ free(line);
+ free(ReadBuf);
+
+ return dib;
+
+ } catch (const char *text) {
+ // free allocated memory
+
+ if (dib != NULL) {
+ FreeImage_Unload(dib);
+ }
+ if (line != NULL) {
+ free(line);
+ }
+ if (ReadBuf != NULL) {
+ free(ReadBuf);
+ }
+
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+/*!
+ Initialises the plugin. The first parameter (Plugin *plugin)
+ contains a pointer to a pre-allocated Plugin structure
+ wherein pointers to the available plugin functions
+ has to be stored. The second parameter (int format_id) is an identification
+ number that the plugin may use to show plugin specific warning messages
+ or other information to the user. The plugin number
+ is generated by FreeImage and can differ everytime the plugin is
+ initialised.
+
+ If you want to create your own plugin you have to take some
+ rules into account. Plugin functions have to be compiled
+ __stdcall using the multithreaded c runtime libraries. Throwing
+ exceptions in plugin functions is allowed, as long as those exceptions
+ are being caught inside the same plugin. It is forbidden for a plugin
+ function to directly call FreeImage functions or to allocate memory
+ and pass it to the main DLL. Exception to this rule is the special file data
+ block that may be allocated the Open function. Allocating a FIBITMAP inside a
+ plugin can be using the function allocate_proc in the FreeImage structure,
+ which will allocate the memory using the DLL's c runtime library.
+*/
+
+void DLL_CALLCONV
+InitPCX(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp b/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp
new file mode 100644
index 0000000000..8ccbd48ba5
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp
@@ -0,0 +1,402 @@
+// ==========================================================
+// PFM Loader and Writer
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/** maximum size of a line in the header */
+#define PFM_MAXLINE 256
+
+/** Big endian / Little endian float conversion */
+#define REVERSEBYTES(source, dest) \
+{ \
+ char *j = (char *) source; \
+ char *dj = (char *) dest; \
+ dj[0] = j[3]; \
+ dj[1] = j[2]; \
+ dj[2] = j[1]; \
+ dj[3] = j[0]; \
+}
+
+/**
+Get a line from a ASCII io stream
+*/
+static BOOL
+pfm_get_line(FreeImageIO *io, fi_handle handle, char *buffer, int length) {
+ int i;
+ memset(buffer, 0, length);
+ for(i = 0; i < length; i++) {
+ if (!io->read_proc(&buffer[i], 1, 1, handle))
+ return FALSE;
+ if(buffer[i] == 0x0A)
+ break;
+ }
+
+ return (i < length) ? TRUE : FALSE;
+}
+
+/**
+Get an integer value from the actual position pointed by handle
+*/
+static int
+pfm_get_int(FreeImageIO *io, fi_handle handle) {
+ char c = 0;
+ BOOL firstchar;
+
+ // skip forward to start of next number
+
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+
+ while (1) {
+ // eat comments
+
+ if (c == '#') {
+ // if we're at a comment, read to end of line
+
+ firstchar = TRUE;
+
+ while (1) {
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+
+ if (firstchar && c == ' ') {
+ // loop off 1 sp after #
+
+ firstchar = FALSE;
+ } else if (c == '\n') {
+ break;
+ }
+ }
+ }
+
+ if (c >= '0' && c <='9') {
+ // we've found what we were looking for
+
+ break;
+ }
+
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+ }
+
+ // we're at the start of a number, continue until we hit a non-number
+
+ int i = 0;
+
+ while (1) {
+ i = (i * 10) + (c - '0');
+
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+
+ if (c < '0' || c > '9')
+ break;
+ }
+
+ return i;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "PFM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Portable floatmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "pfm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-portable-floatmap";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE pfm_id1[] = { 0x50, 0x46 };
+ BYTE pfm_id2[] = { 0x50, 0x66 };
+ BYTE signature[2] = { 0, 0 };
+
+ io->read_proc(signature, 1, sizeof(pfm_id1), handle);
+
+ if (memcmp(pfm_id1, signature, sizeof(pfm_id1)) == 0)
+ return TRUE;
+
+ if (memcmp(pfm_id2, signature, sizeof(pfm_id2)) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (
+ (type == FIT_FLOAT) ||
+ (type == FIT_RGBF)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ char line_buffer[PFM_MAXLINE];
+ char id_one = 0, id_two = 0;
+ FIBITMAP *dib = NULL;
+ float *lineBuffer = NULL;
+
+ if (!handle) {
+ return NULL;
+ }
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+ FREE_IMAGE_TYPE image_type = FIT_UNKNOWN;
+
+ // Read the first two bytes of the file to determine the file format
+ // "PF" = color image
+ // "Pf" = greyscale image
+
+ io->read_proc(&id_one, 1, 1, handle);
+ io->read_proc(&id_two, 1, 1, handle);
+
+ if(id_one == 'P') {
+ if(id_two == 'F') {
+ image_type = FIT_RGBF;
+ } else if(id_two == 'f') {
+ image_type = FIT_FLOAT;
+ }
+ }
+ if(image_type == FIT_UNKNOWN) {
+ // signature error
+ throw FI_MSG_ERROR_MAGIC_NUMBER;
+ }
+
+ // Read the header information: width, height and the scale value
+ unsigned width = (unsigned) pfm_get_int(io, handle);
+ unsigned height = (unsigned) pfm_get_int(io, handle);
+ float scalefactor = 1;
+
+ BOOL bResult = pfm_get_line(io, handle, line_buffer, PFM_MAXLINE);
+ if(bResult) {
+ bResult = (sscanf(line_buffer, "%f", &scalefactor) == 1) ? TRUE : FALSE;
+ }
+ if (!bResult) {
+ throw "Read error: invalid PFM header";
+ }
+
+ // Create a new DIB
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // Read the image...
+
+ if(image_type == FIT_RGBF) {
+ const unsigned lineWidth = 3 * width;
+ lineBuffer = (float*)malloc(lineWidth * sizeof(float));
+ if (!lineBuffer) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (unsigned y = 0; y < height; y++) {
+ FIRGBF *bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) {
+ throw "Read error";
+ }
+ float *channel = lineBuffer;
+ if(scalefactor > 0) {
+ // MSB
+ for (unsigned x = 0; x < width; x++) {
+ REVERSEBYTES(channel++, &bits[x].red);
+ REVERSEBYTES(channel++, &bits[x].green);
+ REVERSEBYTES(channel++, &bits[x].blue);
+ }
+ } else {
+ // LSB
+ for (unsigned x = 0; x < width; x++) {
+ bits[x].red = *channel++;
+ bits[x].green = *channel++;
+ bits[x].blue = *channel++;
+ }
+ }
+ }
+
+ free(lineBuffer);
+ lineBuffer = NULL;
+
+ } else if(image_type == FIT_FLOAT) {
+ const unsigned lineWidth = width;
+ lineBuffer = (float*)malloc(lineWidth * sizeof(float));
+ if (!lineBuffer) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (unsigned y = 0; y < height; y++) {
+ float *bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) {
+ throw "Read error";
+ }
+ float *channel = lineBuffer;
+ if(scalefactor > 0) {
+ // MSB - File is Big endian
+ for (unsigned x = 0; x < width; x++) {
+ REVERSEBYTES(channel++, &bits[x]);
+ }
+ } else {
+ // LSB - File is Little Endian
+ for (unsigned x = 0; x < width; x++) {
+ bits[x] = *channel++;
+ }
+ }
+ }
+
+ free(lineBuffer);
+ lineBuffer = NULL;
+ }
+
+ return dib;
+
+ } catch (const char *text) {
+ if(lineBuffer) free(lineBuffer);
+ if(dib) FreeImage_Unload(dib);
+
+ if(NULL != text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+
+ return NULL;
+ }
+
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if (!dib || !handle) return FALSE;
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ if ((image_type != FIT_RGBF) && (image_type != FIT_FLOAT)) {
+ return FALSE;
+ }
+
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned lineWidth = FreeImage_GetLine(dib);
+
+ // save image as Little Endian
+ const float scalefactor = -1.0F;
+
+ char buffer[PFM_MAXLINE]; // temporary buffer whose size should be enough for what we need
+
+ // Find the appropriate magic number for this file type
+
+ char magic = 0;
+
+ switch(image_type) {
+ case FIT_RGBF:
+ magic = 'F'; // RGBF
+ break;
+ case FIT_FLOAT:
+ magic = 'f'; // float greyscale
+ break;
+ default:
+ return FALSE;
+ }
+
+ // Write the header info
+
+ sprintf(buffer, "P%c\n%d %d\n%f\n", magic, width, height, scalefactor);
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+ // Write the image data
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+ io->write_proc(bits, 1, lineWidth, handle);
+ }
+
+ return TRUE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPFM(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPICT.cpp b/plugins/AdvaImg/src/FreeImage/PluginPICT.cpp
new file mode 100644
index 0000000000..0c44cb2d1f
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginPICT.cpp
@@ -0,0 +1,1342 @@
+// ==========================================================
+// Apple Macintosh QuickDraw/PICT Loader
+//
+// Design and implementation by
+// - Amir Ebrahimi (amir@unity3d.com)
+//
+// Based on PICT loading code from paintlib (http://www.paintlib.de/paintlib/).
+//
+// Paintlib License:
+// The paintlib source code and all documentation are copyright (c) 1996-2002
+// Ulrich von Zadow and other contributors.
+//
+// The paintlib source code is supplied "AS IS". Ulrich von Zadow and other
+// contributors disclaim all warranties, expressed or implied, including, without
+// limitation, the warranties of merchantability and of fitness for any purpose.
+// The authors assume no liability for direct, indirect, incidental, special,
+// exemplary, or consequential damages, which may result from the use of paintlib,
+// even if advised of the possibility of such damage.
+//
+// Permission is hereby granted to use, copy, modify, and distribute this source
+// code, or portions hereof, for any purpose, without fee, subject to the following
+// restrictions:
+//
+// 1. The origin of this source code must not be misrepresented.
+// 2. Altered versions must be plainly marked as such and must not be misrepresented
+// as being the original source.
+// 3. This Copyright notice may not be removed or altered from any source or altered
+// source distribution.
+// 4. Executables containing paintlib or parts of it must state that the software
+// "contains paintlib code. paintlib is copyright (c) 1996-2002 Ulrich von Zadow
+// and other contributors.". This notice must be displayed in at least one place
+// where the copyright for the software itself is displayed. The documentation must
+// also contain this notice.
+//
+// Bug fixes were made to the original code to support version 2 PICT files
+// properly.
+//
+// Additional resources:
+// http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-458.html
+// http://www.fileformat.info/format/macpict/egff.htm
+//
+// Notes (http://lists.apple.com/archives/java-dev/2006/Apr/msg00588.html):
+// There are three main types of PICT files:
+// - Version 1
+// - Version 2
+// - Extended Version 2
+//
+// Some things to look out for:
+// - The bounds and target DPI are stored in a different place in all three.
+// - Some of the values are fixed-point shorts ( short / 65536f )
+// - Values are big endian
+// - All of this may be *preceded* by a 512 byte header--sometimes it is
+// there, and sometimes it isn't. You just have to check for the magic
+// values in both places.
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+static int s_format_id;
+
+static const int outputMessageSize = 256;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static unsigned
+Read8(FreeImageIO *io, fi_handle handle) {
+ unsigned char i = 0;
+ io->read_proc(&i, 1, 1, handle);
+ return i;
+}
+
+static unsigned
+Read16(FreeImageIO *io, fi_handle handle) {
+ // reads a two-byte big-endian integer from the given file and returns its value.
+ // assumes unsigned.
+
+ unsigned hi = Read8(io, handle);
+ unsigned lo = Read8(io, handle);
+ return lo + (hi << 8);
+}
+
+static unsigned
+Read32(FreeImageIO *io, fi_handle handle) {
+ // reads a four-byte big-endian integer from the given file and returns its value.
+ // assumes unsigned.
+
+ unsigned b3 = Read8(io, handle);
+ unsigned b2 = Read8(io, handle);
+ unsigned b1 = Read8(io, handle);
+ unsigned b0 = Read8(io, handle);
+ return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
+}
+
+// ----------------------------------------------------------
+
+struct OpDef
+{
+ const char * name;
+ int len;
+ const char * description;
+};
+
+// for reserved opcodes
+#define res(length) { "reserved", (length), "reserved for Apple use" }
+#define RGB_LEN 6
+#define WORD_LEN -1
+#define NA 0
+
+static OpDef optable[] =
+{
+/* 0x00 */ { "NOP", 0, "nop" },
+/* 0x01 */ { "Clip", NA, "clip" },
+/* 0x02 */ { "BkPat", 8, "background pattern" },
+/* 0x03 */ { "TxFont", 2, "text font (word)" },
+/* 0x04 */ { "TxFace", 1, "text face (byte)" },
+/* 0x05 */ { "TxMode", 2, "text mode (word)" },
+/* 0x06 */ { "SpExtra", 4, "space extra (fixed point)" },
+/* 0x07 */ { "PnSize", 4, "pen size (point)" },
+/* 0x08 */ { "PnMode", 2, "pen mode (word)" },
+/* 0x09 */ { "PnPat", 8, "pen pattern" },
+/* 0x0a */ { "FillPat", 8, "fill pattern" },
+/* 0x0b */ { "OvSize", 4, "oval size (point)" },
+/* 0x0c */ { "Origin", 4, "dh, dv (word)" },
+/* 0x0d */ { "TxSize", 2, "text size (word)" },
+/* 0x0e */ { "FgColor", 4, "foreground color (longword)" },
+/* 0x0f */ { "BkColor", 4, "background color (longword)" },
+/* 0x10 */ { "TxRatio", 8, "numerator (point), denominator (point)" },
+/* 0x11 */ { "Version", 1, "version (byte)" },
+/* 0x12 */ { "BkPixPat", NA, "color background pattern" },
+/* 0x13 */ { "PnPixPat", NA, "color pen pattern" },
+/* 0x14 */ { "FillPixPat", NA, "color fill pattern" },
+/* 0x15 */ { "PnLocHFrac", 2, "fractional pen position" },
+/* 0x16 */ { "ChExtra", 2, "extra for each character" },
+/* 0x17 */ res(0),
+/* 0x18 */ res(0),
+/* 0x19 */ res(0),
+/* 0x1a */ { "RGBFgCol", RGB_LEN, "RGB foreColor" },
+/* 0x1b */ { "RGBBkCol", RGB_LEN, "RGB backColor" },
+/* 0x1c */ { "HiliteMode", 0, "hilite mode flag" },
+/* 0x1d */ { "HiliteColor", RGB_LEN, "RGB hilite color" },
+/* 0x1e */ { "DefHilite", 0, "Use default hilite color" },
+/* 0x1f */ { "OpColor", 6, "RGB OpColor for arithmetic modes" },
+/* 0x20 */ { "Line", 8, "pnLoc (point), newPt (point)" },
+/* 0x21 */ { "LineFrom", 4, "newPt (point)" },
+/* 0x22 */ { "ShortLine", 6, "pnLoc (point, dh, dv (-128 .. 127))" },
+/* 0x23 */ { "ShortLineFrom", 2, "dh, dv (-128 .. 127)" },
+/* 0x24 */ res(WORD_LEN),
+/* 0x25 */ res(WORD_LEN),
+/* 0x26 */ res(WORD_LEN),
+/* 0x27 */ res(WORD_LEN),
+/* 0x28 */ { "LongText", NA, "txLoc (point), count (0..255), text" },
+/* 0x29 */ { "DHText", NA, "dh (0..255), count (0..255), text" },
+/* 0x2a */ { "DVText", NA, "dv (0..255), count (0..255), text" },
+/* 0x2b */ { "DHDVText", NA, "dh, dv (0..255), count (0..255), text" },
+/* 0x2c */ res(WORD_LEN),
+/* 0x2d */ res(WORD_LEN),
+/* 0x2e */ res(WORD_LEN),
+/* 0x2f */ res(WORD_LEN),
+/* 0x30 */ { "frameRect", 8, "rect" },
+/* 0x31 */ { "paintRect", 8, "rect" },
+/* 0x32 */ { "eraseRect", 8, "rect" },
+/* 0x33 */ { "invertRect", 8, "rect" },
+/* 0x34 */ { "fillRect", 8, "rect" },
+/* 0x35 */ res(8),
+/* 0x36 */ res(8),
+/* 0x37 */ res(8),
+/* 0x38 */ { "frameSameRect", 0, "rect" },
+/* 0x39 */ { "paintSameRect", 0, "rect" },
+/* 0x3a */ { "eraseSameRect", 0, "rect" },
+/* 0x3b */ { "invertSameRect", 0, "rect" },
+/* 0x3c */ { "fillSameRect", 0, "rect" },
+/* 0x3d */ res(0),
+/* 0x3e */ res(0),
+/* 0x3f */ res(0),
+/* 0x40 */ { "frameRRect", 8, "rect" },
+/* 0x41 */ { "paintRRect", 8, "rect" },
+/* 0x42 */ { "eraseRRect", 8, "rect" },
+/* 0x43 */ { "invertRRect", 8, "rect" },
+/* 0x44 */ { "fillRRrect", 8, "rect" },
+/* 0x45 */ res(8),
+/* 0x46 */ res(8),
+/* 0x47 */ res(8),
+/* 0x48 */ { "frameSameRRect", 0, "rect" },
+/* 0x49 */ { "paintSameRRect", 0, "rect" },
+/* 0x4a */ { "eraseSameRRect", 0, "rect" },
+/* 0x4b */ { "invertSameRRect", 0, "rect" },
+/* 0x4c */ { "fillSameRRect", 0, "rect" },
+/* 0x4d */ res(0),
+/* 0x4e */ res(0),
+/* 0x4f */ res(0),
+/* 0x50 */ { "frameOval", 8, "rect" },
+/* 0x51 */ { "paintOval", 8, "rect" },
+/* 0x52 */ { "eraseOval", 8, "rect" },
+/* 0x53 */ { "invertOval", 8, "rect" },
+/* 0x54 */ { "fillOval", 8, "rect" },
+/* 0x55 */ res(8),
+/* 0x56 */ res(8),
+/* 0x57 */ res(8),
+/* 0x58 */ { "frameSameOval", 0, "rect" },
+/* 0x59 */ { "paintSameOval", 0, "rect" },
+/* 0x5a */ { "eraseSameOval", 0, "rect" },
+/* 0x5b */ { "invertSameOval", 0, "rect" },
+/* 0x5c */ { "fillSameOval", 0, "rect" },
+/* 0x5d */ res(0),
+/* 0x5e */ res(0),
+/* 0x5f */ res(0),
+/* 0x60 */ { "frameArc", 12, "rect, startAngle, arcAngle" },
+/* 0x61 */ { "paintArc", 12, "rect, startAngle, arcAngle" },
+/* 0x62 */ { "eraseArc", 12, "rect, startAngle, arcAngle" },
+/* 0x63 */ { "invertArc", 12, "rect, startAngle, arcAngle" },
+/* 0x64 */ { "fillArc", 12, "rect, startAngle, arcAngle" },
+/* 0x65 */ res(12),
+/* 0x66 */ res(12),
+/* 0x67 */ res(12),
+/* 0x68 */ { "frameSameArc", 4, "rect, startAngle, arcAngle" },
+/* 0x69 */ { "paintSameArc", 4, "rect, startAngle, arcAngle" },
+/* 0x6a */ { "eraseSameArc", 4, "rect, startAngle, arcAngle" },
+/* 0x6b */ { "invertSameArc", 4, "rect, startAngle, arcAngle" },
+/* 0x6c */ { "fillSameArc", 4, "rect, startAngle, arcAngle" },
+/* 0x6d */ res(4),
+/* 0x6e */ res(4),
+/* 0x6f */ res(4),
+/* 0x70 */ { "framePoly", NA, "poly" },
+/* 0x71 */ { "paintPoly", NA, "poly" },
+/* 0x72 */ { "erasePoly", NA, "poly" },
+/* 0x73 */ { "invertPoly", NA, "poly" },
+/* 0x74 */ { "fillPoly", NA, "poly" },
+/* 0x75 */ res(NA),
+/* 0x76 */ res(NA),
+/* 0x77 */ res(NA),
+/* 0x78 */ { "frameSamePoly", 0, "poly (NYI)" },
+/* 0x79 */ { "paintSamePoly", 0, "poly (NYI)" },
+/* 0x7a */ { "eraseSamePoly", 0, "poly (NYI)" },
+/* 0x7b */ { "invertSamePoly", 0, "poly (NYI)" },
+/* 0x7c */ { "fillSamePoly", 0, "poly (NYI)" },
+/* 0x7d */ res(0),
+/* 0x7e */ res(0),
+/* 0x7f */ res(0),
+/* 0x80 */ { "frameRgn", NA, "region" },
+/* 0x81 */ { "paintRgn", NA, "region" },
+/* 0x82 */ { "eraseRgn", NA, "region" },
+/* 0x83 */ { "invertRgn", NA, "region" },
+/* 0x84 */ { "fillRgn", NA, "region" },
+/* 0x85 */ res(NA),
+/* 0x86 */ res(NA),
+/* 0x87 */ res(NA),
+/* 0x88 */ { "frameSameRgn", 0, "region (NYI)" },
+/* 0x89 */ { "paintSameRgn", 0, "region (NYI)" },
+/* 0x8a */ { "eraseSameRgn", 0, "region (NYI)" },
+/* 0x8b */ { "invertSameRgn", 0, "region (NYI)" },
+/* 0x8c */ { "fillSameRgn", 0, "region (NYI)" },
+/* 0x8d */ res(0),
+/* 0x8e */ res(0),
+/* 0x8f */ res(0),
+/* 0x90 */ { "BitsRect", NA, "copybits, rect clipped" },
+/* 0x91 */ { "BitsRgn", NA, "copybits, rgn clipped" },
+/* 0x92 */ res(WORD_LEN),
+/* 0x93 */ res(WORD_LEN),
+/* 0x94 */ res(WORD_LEN),
+/* 0x95 */ res(WORD_LEN),
+/* 0x96 */ res(WORD_LEN),
+/* 0x97 */ res(WORD_LEN),
+/* 0x98 */ { "PackBitsRect", NA, "packed copybits, rect clipped" },
+/* 0x99 */ { "PackBitsRgn", NA, "packed copybits, rgn clipped" },
+/* 0x9a */ { "Opcode_9A", NA, "the mysterious opcode 9A" },
+/* 0x9b */ res(WORD_LEN),
+/* 0x9c */ res(WORD_LEN),
+/* 0x9d */ res(WORD_LEN),
+/* 0x9e */ res(WORD_LEN),
+/* 0x9f */ res(WORD_LEN),
+/* 0xa0 */ { "ShortComment", 2, "kind (word)" },
+/* 0xa1 */ { "LongComment", NA, "kind (word), size (word), data" }
+};
+
+// ----------------------------------------------------------
+
+struct MacRect
+{
+ WORD top;
+ WORD left;
+ WORD bottom;
+ WORD right;
+};
+
+struct MacpixMap
+{
+ // Ptr baseAddr // Not used in file.
+ // short rowBytes // read in seperatly.
+ struct MacRect Bounds;
+ WORD version;
+ WORD packType;
+ LONG packSize;
+ LONG hRes;
+ LONG vRes;
+ WORD pixelType;
+ WORD pixelSize;
+ WORD cmpCount;
+ WORD cmpSize;
+ LONG planeBytes;
+ LONG pmTable;
+ LONG pmReserved;
+};
+
+struct MacRGBColour
+{
+ WORD red;
+ WORD green;
+ WORD blue;
+};
+
+struct MacPoint
+{
+ WORD x;
+ WORD y;
+};
+
+struct MacPattern // Klaube
+{
+ BYTE pix[64];
+};
+
+// ----------------------------------------------------------
+
+static void
+ReadRect( FreeImageIO *io, fi_handle handle, MacRect* rect ) {
+ rect->top = Read16( io, handle );
+ rect->left = Read16( io, handle );
+ rect->bottom = Read16( io, handle );
+ rect->right = Read16( io, handle );
+}
+
+static void
+ReadPixmap( FreeImageIO *io, fi_handle handle, MacpixMap* pPixMap ) {
+ pPixMap->version = Read16( io, handle );
+ pPixMap->packType = Read16( io, handle );
+ pPixMap->packSize = Read32( io, handle );
+ pPixMap->hRes = Read16( io, handle );
+ Read16( io, handle );
+ pPixMap->vRes = Read16( io, handle );
+ Read16( io, handle );
+ pPixMap->pixelType = Read16( io, handle );
+ pPixMap->pixelSize = Read16( io, handle );
+ pPixMap->cmpCount = Read16( io, handle );
+ pPixMap->cmpSize = Read16( io, handle );
+ pPixMap->planeBytes = Read32( io, handle );
+ pPixMap->pmTable = Read32( io, handle );
+ pPixMap->pmReserved = Read32( io, handle );
+}
+
+/**
+Reads a mac color table into a bitmap palette.
+*/
+static void
+ReadColorTable( FreeImageIO *io, fi_handle handle, WORD* pNumColors, RGBQUAD* pPal ) {
+ LONG ctSeed;
+ WORD ctFlags;
+ WORD val;
+ int i;
+
+ ctSeed = Read32( io, handle );
+ ctFlags = Read16( io, handle );
+ WORD numColors = Read16( io, handle )+1;
+ *pNumColors = numColors;
+
+ for (i = 0; i < numColors; i++) {
+ val = Read16( io, handle );
+ if (ctFlags & 0x8000) {
+ // The indicies in a device colour table are bogus and
+ // usually == 0, so I assume we allocate up the list of
+ // colours in order.
+ val = i;
+ }
+ if (val >= numColors) {
+ throw "pixel value greater than color table size.";
+ }
+ // Mac colour tables contain 16-bit values for R, G, and B...
+ pPal[val].rgbRed = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
+ pPal[val].rgbGreen = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
+ pPal[val].rgbBlue = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
+ }
+}
+
+/**
+skips unneeded packbits.
+pixelSize == Source bits per pixel.
+*/
+static void
+SkipBits( FreeImageIO *io, fi_handle handle, MacRect* bounds, WORD rowBytes, int pixelSize ) {
+ int i;
+ WORD pixwidth; // bytes per row when uncompressed.
+
+ int height = bounds->bottom - bounds->top;
+ int width = bounds->right - bounds->left;
+
+ // High bit of rowBytes is flag.
+ if (pixelSize <= 8) {
+ rowBytes &= 0x7fff;
+ }
+ pixwidth = width;
+
+ if (pixelSize == 16) {
+ pixwidth *= 2;
+ }
+ if (rowBytes == 0) {
+ rowBytes = pixwidth;
+ }
+ if (rowBytes < 8) {
+ io->seek_proc( handle, rowBytes*height, SEEK_CUR );
+ }
+ else {
+ for (i = 0; i < height; i++) {
+ int lineLen; // length of source line in bytes.
+ if (rowBytes > 250) {
+ lineLen = Read16( io, handle );
+ } else {
+ lineLen = Read8( io, handle );
+ }
+ io->seek_proc( handle, lineLen, SEEK_CUR );
+ }
+ }
+}
+
+/**
+Skip polygon or region
+*/
+static void
+SkipPolyOrRegion( FreeImageIO *io, fi_handle handle ) {
+ WORD len = Read16( io, handle ) - 2;
+ io->seek_proc(handle, len, SEEK_CUR);
+}
+
+/**
+Width in bytes for 8 bpp or less.
+Width in pixels for 16 bpp.
+Expands Width units to 32-bit pixel data.
+*/
+static void
+expandBuf( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst ) {
+ switch (bpp) {
+ case 16:
+ for ( int i=0; i<width; i++) {
+ WORD src = Read16( io, handle );
+ dst[ FI_RGBA_BLUE ] = (src & 31)*8; // Blue
+ dst[ FI_RGBA_GREEN ] = ((src >> 5) & 31)*8; // Green
+ dst[ FI_RGBA_RED ] = ((src >> 10) & 31)*8; // Red
+ dst[ FI_RGBA_ALPHA ] = 0xFF; // Alpha
+ dst += 4;
+ }
+ break;
+ default:
+ throw "Bad bits per pixel in expandBuf.";
+ }
+}
+
+/**
+Expands Width units to 8-bit pixel data.
+Max. 8 bpp source format.
+*/
+static void
+expandBuf8( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst )
+{
+ switch (bpp) {
+ case 8:
+ io->read_proc( dst, width, 1, handle );
+ break;
+ case 4:
+ for (int i = 0; i < width; i++) {
+ WORD src = Read8( io, handle );
+ *dst = (src >> 4) & 15;
+ *(dst+1) = (src & 15);
+ dst += 2;
+ }
+ if (width & 1) { // Odd Width?
+ WORD src = Read8( io, handle );
+ *dst = (src >> 4) & 15;
+ dst++;
+ }
+ break;
+ case 2:
+ for (int i = 0; i < width; i++) {
+ WORD src = Read8( io, handle );
+ *dst = (src >> 6) & 3;
+ *(dst+1) = (src >> 4) & 3;
+ *(dst+2) = (src >> 2) & 3;
+ *(dst+3) = (src & 3);
+ dst += 4;
+ }
+ if (width & 3) { // Check for leftover pixels
+ for (int i = 6; i > 8 - (width & 3) * 2; i -= 2) {
+ WORD src = Read8( io, handle );
+ *dst = (src >> i) & 3;
+ dst++;
+ }
+ }
+ break;
+ case 1:
+ for (int i = 0; i < width; i++) {
+ WORD src = Read8( io, handle );
+ *dst = (src >> 7) & 1;
+ *(dst+1) = (src >> 6) & 1;
+ *(dst+2) = (src >> 5) & 1;
+ *(dst+3) = (src >> 4) & 1;
+ *(dst+4) = (src >> 3) & 1;
+ *(dst+5) = (src >> 2) & 1;
+ *(dst+6) = (src >> 1) & 1;
+ *(dst+7) = (src & 1);
+ dst += 8;
+ }
+ if (width & 7) { // Check for leftover pixels
+ for (int i = 7; i > (8-width & 7); i--) {
+ WORD src = Read8( io, handle );
+ *dst = (src >> i) & 1;
+ dst++;
+ }
+ }
+ break;
+ default:
+ throw "Bad bits per pixel in expandBuf8.";
+ }
+}
+
+static BYTE*
+UnpackPictRow( FreeImageIO *io, fi_handle handle, BYTE* pLineBuf, int width, int rowBytes, int srcBytes ) {
+ if (rowBytes < 8) { // Ah-ha! The bits aren't actually packed. This will be easy.
+ io->read_proc( pLineBuf, rowBytes, 1, handle );
+ }
+ else {
+ BYTE* pCurPixel = pLineBuf;
+
+ // Unpack RLE. The data is packed bytewise.
+ for (int j = 0; j < srcBytes; ) {
+ BYTE FlagCounter = Read8( io, handle );
+ if (FlagCounter & 0x80) {
+ if (FlagCounter == 0x80) {
+ // Special case: repeat value of 0.
+ // Apple says ignore.
+ j++;
+ } else {
+ // Packed data.
+ int len = ((FlagCounter ^ 255) & 255) + 2;
+ BYTE p = Read8( io, handle );
+ memset( pCurPixel, p, len);
+ pCurPixel += len;
+ j += 2;
+ }
+ }
+ else {
+ // Unpacked data
+ int len = (FlagCounter & 255) + 1;
+ io->read_proc( pCurPixel, len, 1, handle );
+ pCurPixel += len;
+ j += len + 1;
+ }
+ }
+ }
+
+ return pLineBuf;
+}
+
+/**
+This routine decompresses BitsRects with a packType of 4 (and 32 bits per pixel).
+In this format, each line is separated into 8-bit-bitplanes and then compressed via RLE.
+To decode, the routine decompresses each line & then juggles the bytes around to get pixel-oriented data.
+NumBitPlanes == 3 if RGB, 4 if RGBA
+*/
+static void
+Unpack32Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int numPlanes ) {
+ int height = bounds->bottom - bounds->top;
+ int width = bounds->right - bounds->left;
+
+ if (rowBytes == 0) {
+ rowBytes = width*4;
+ }
+
+ BYTE* pLineBuf = (BYTE*)malloc( rowBytes ); // Let's allocate enough for 4 bit planes
+ if ( pLineBuf ) {
+ try {
+ for ( int i = 0; i < height; i++ ) {
+ // for each line do...
+ int linelen; // length of source line in bytes.
+ if (rowBytes > 250) {
+ linelen = Read16( io, handle );
+ } else {
+ linelen = Read8( io, handle);
+ }
+
+ BYTE* pBuf = UnpackPictRow( io, handle, pLineBuf, width, rowBytes, linelen );
+
+ // Convert plane-oriented data into pixel-oriented data &
+ // copy into destination bitmap.
+ BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
+
+ if ( numPlanes == 3 ) {
+ for ( int j = 0; j < width; j++ ) {
+ // For each pixel in line...
+ dst[ FI_RGBA_BLUE ] = (*(pBuf+width*2)); // Blue
+ dst[ FI_RGBA_GREEN ] = (*(pBuf+width)); // Green
+ dst[ FI_RGBA_RED ] = (*pBuf); // Red
+ dst[ FI_RGBA_ALPHA ] = (0xFF);
+ dst += 4;
+ pBuf++;
+ }
+ } else {
+ for ( int j = 0; j < width; j++ ) {
+ // For each pixel in line...
+ dst[ FI_RGBA_BLUE ] = (*(pBuf+width*3)); // Blue
+ dst[ FI_RGBA_GREEN ] = (*(pBuf+width*2)); // Green
+ dst[ FI_RGBA_RED ] = (*(pBuf+width)); // Red
+ dst[ FI_RGBA_ALPHA ] = (*pBuf);
+ dst += 4;
+ pBuf++;
+ }
+ }
+ }
+ }
+ catch( ... ) {
+ free( pLineBuf );
+ throw;
+ }
+ }
+ free( pLineBuf );
+}
+
+/**
+Decompression routine for 8 bpp.
+rowBytes is the number of bytes each source row would take if it were uncompressed.
+This _isn't_ equal to the number of pixels in the row - it seems apple pads the data to a word boundary and then compresses it.
+Of course, we have to decompress the excess data and then throw it away.
+*/
+static void
+Unpack8Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes ) {
+ int height = bounds->bottom - bounds->top;
+ int width = bounds->right - bounds->left;
+
+ // High bit of rowBytes is flag.
+ rowBytes &= 0x7fff;
+
+ if (rowBytes == 0) {
+ rowBytes = width;
+ }
+
+ for ( int i = 0; i < height; i++ ) {
+ int linelen; // length of source line in bytes.
+ if (rowBytes > 250) {
+ linelen = Read16( io, handle );
+ } else {
+ linelen = Read8( io, handle );
+ }
+ BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
+ dst = UnpackPictRow( io, handle, dst, width, rowBytes, linelen );
+ }
+}
+
+/**
+Decompression routine for everything but 8 & 32 bpp.
+This routine is slower than the two routines above since it has to deal with a lot of special cases :-(.
+It's also a bit chaotic because of these special cases...
+unpack8bits is basically a dumber version of unpackbits.
+pixelSize == Source bits per pixel.
+*/
+static void
+UnpackBits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int pixelSize ) {
+ WORD pixwidth; // bytes per row when uncompressed.
+ int pkpixsize;
+ int PixelPerRLEUnit;
+
+ char outputMessage[ outputMessageSize ] = "";
+
+ int height = bounds->bottom - bounds->top;
+ int width = bounds->right - bounds->left;
+
+ // High bit of rowBytes is flag.
+ if (pixelSize <= 8) {
+ rowBytes &= 0x7fff;
+ }
+
+ pixwidth = width;
+ pkpixsize = 1; // RLE unit: one byte for everything...
+ if (pixelSize == 16) { // ...except 16 bpp.
+ pkpixsize = 2;
+ pixwidth *= 2;
+ }
+
+ if (rowBytes == 0) {
+ rowBytes = pixwidth;
+ }
+
+ {
+ // I allocate the temporary line buffer here. I allocate too
+ // much memory to compensate for sloppy (& hence fast) decompression.
+ switch (pixelSize) {
+ case 1:
+ PixelPerRLEUnit = 8;
+ break;
+ case 2:
+ PixelPerRLEUnit = 4;
+ break;
+ case 4:
+ PixelPerRLEUnit = 2;
+ break;
+ case 8:
+ PixelPerRLEUnit = 1;
+ break;
+ case 16:
+ PixelPerRLEUnit = 1;
+ break;
+ default:
+ sprintf( outputMessage, "Illegal bpp value in unpackbits: %d\n", pixelSize );
+ throw outputMessage;
+ }
+
+ if (rowBytes < 8) {
+ // ah-ha! The bits aren't actually packed. This will be easy.
+ for ( int i = 0; i < height; i++ ) {
+ BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
+ if (pixelSize == 16) {
+ expandBuf( io, handle, width, pixelSize, dst );
+ } else {
+ expandBuf8( io, handle, width, pixelSize, dst );
+ }
+ }
+ }
+ else {
+ for ( int i = 0; i < height; i++ ) {
+ // For each line do...
+ int linelen; // length of source line in bytes.
+ if (rowBytes > 250) {
+ linelen = Read16( io, handle );
+ } else {
+ linelen = Read8( io, handle );
+ }
+
+ BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
+ BYTE FlagCounter;
+
+ // Unpack RLE. The data is packed bytewise - except for
+ // 16 bpp data, which is packed per pixel :-(.
+ for ( int j = 0; j < linelen; ) {
+ FlagCounter = Read8( io, handle );
+ if (FlagCounter & 0x80) {
+ if (FlagCounter == 0x80) {
+ // Special case: repeat value of 0.
+ // Apple says ignore.
+ j++;
+ }
+ else {
+ // Packed data.
+ int len = ((FlagCounter ^ 255) & 255) + 2;
+
+ // This is slow for some formats...
+ if (pixelSize == 16) {
+ expandBuf( io, handle, 1, pixelSize, dst );
+ for ( int k = 1; k < len; k++ ) {
+ // Repeat the pixel len times.
+ memcpy( dst+(k*4*PixelPerRLEUnit), dst, 4*PixelPerRLEUnit);
+ }
+ dst += len*4*PixelPerRLEUnit;
+ }
+ else {
+ expandBuf8( io, handle, 1, pixelSize, dst );
+ for ( int k = 1; k < len; k++ ) {
+ // Repeat the expanded byte len times.
+ memcpy( dst+(k*PixelPerRLEUnit), dst, PixelPerRLEUnit);
+ }
+ dst += len*PixelPerRLEUnit;
+ }
+ j += pkpixsize + 1;
+ }
+ }
+ else {
+ // Unpacked data
+ int len = (FlagCounter & 255) + 1;
+ if (pixelSize == 16) {
+ expandBuf( io, handle, len, pixelSize, dst );
+ dst += len*4*PixelPerRLEUnit;
+ }
+ else {
+ expandBuf8( io, handle, len, pixelSize, dst );
+ dst += len*PixelPerRLEUnit;
+ }
+ j += ( len * pkpixsize ) + 1;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+DecodeOp9a( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacpixMap* pixMap ) {
+ // Do the actual unpacking.
+ switch ( pixMap->pixelSize ) {
+ case 32:
+ Unpack32Bits( io, handle, dib, &pixMap->Bounds, 0, pixMap->cmpCount );
+ break;
+ case 8:
+ Unpack8Bits( io, handle, dib, &pixMap->Bounds, 0 );
+ break;
+ default:
+ UnpackBits( io, handle, dib, &pixMap->Bounds, 0, pixMap->pixelSize );
+ }
+}
+
+static void
+DecodeBitmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacRect* bounds, WORD rowBytes ) {
+ WORD mode = Read16( io, handle );
+
+ if ( isRegion ) {
+ SkipPolyOrRegion( io, handle );
+ }
+
+ RGBQUAD* pal = FreeImage_GetPalette( dib );
+ if ( !pal ) {
+ throw "No palette for bitmap!";
+ }
+
+ for (int i = 0; i < 2; i++) {
+ unsigned char val = i ? 0xFF : 0x0;
+ pal[i].rgbRed = val;
+ pal[i].rgbGreen = val;
+ pal[i].rgbBlue = val;
+ }
+
+ UnpackBits( io, handle, dib, bounds, rowBytes, 1 );
+}
+
+static void
+DecodePixmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacpixMap* pixMap, WORD rowBytes ) {
+ // Read mac colour table into windows palette.
+ WORD numColors; // Palette size.
+ RGBQUAD ct[256];
+
+ ReadColorTable( io, handle, &numColors, ct );
+ if ( FreeImage_GetBPP( dib ) == 8 ) {
+ RGBQUAD* pal = FreeImage_GetPalette( dib );
+ if ( !pal ) {
+ throw "No palette for bitmap!";
+ }
+
+ for (int i = 0; i < numColors; i++) {
+ pal[i].rgbRed = ct[ i ].rgbRed;
+ pal[i].rgbGreen = ct[ i ].rgbGreen;
+ pal[i].rgbBlue = ct[ i ].rgbBlue;
+ }
+ }
+
+ // Ignore source & destination rectangle as well as transfer mode.
+ MacRect tempRect;
+ ReadRect( io, handle, &tempRect );
+ ReadRect( io, handle, &tempRect );
+ WORD mode = Read16( io, handle );
+
+ if ( isRegion) {
+ SkipPolyOrRegion( io, handle );
+ }
+
+ switch ( pixMap->pixelSize ) {
+ case 32:
+ Unpack32Bits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->cmpCount );
+ break;
+ case 8:
+ Unpack8Bits( io, handle, dib, &pixMap->Bounds, rowBytes );
+ break;
+ default:
+ UnpackBits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->pixelSize );
+ }
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "PICT";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Macintosh PICT";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "pct,pict,pic";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-pict";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ if(io->seek_proc(handle, 522, SEEK_SET) == 0) {
+ BYTE pict_signature[] = { 0x00, 0x11, 0x02, 0xFF, 0x0C, 0X00 };
+ BYTE signature[6];
+
+ if(io->read_proc(signature, 1, sizeof(pict_signature), handle)) {
+ // v1.0 files have 0x11 (version operator) followed by 0x01 (version number)
+ // v2.0 files have 0x0011 (version operator) followed by 0x02ff (version number)
+ // and additionally 0x0c00 as a header opcode
+ // Currently, we are only supporting v2.0
+ return (memcmp(pict_signature, signature, sizeof(pict_signature)) == 0);
+ } else {
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+ return FALSE;
+}
+
+/**
+This plugin decodes macintosh PICT files with 1,2,4,8,16 and 32 bits per pixel as well as PICT/JPEG.
+If an alpha channel is present in a 32-bit-PICT, it is decoded as well.
+The PICT format is a general picture file format and can contain a lot of other elements besides bitmaps.
+These elements are ignored.
+*/
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ char outputMessage[ outputMessageSize ] = "";
+ FIBITMAP* dib = NULL;
+ try {
+ // Skip empty 512 byte header.
+ if ( !io->seek_proc(handle, 512, SEEK_CUR) == 0 )
+ return NULL;
+
+ // Read PICT header
+ Read16( io, handle ); // Skip version 1 picture size
+
+ MacRect frame;
+ ReadRect( io, handle, &frame );
+
+ BYTE b = 0;
+ while ((b = Read8(io, handle)) == 0);
+ if ( b != 0x11 ) {
+ throw "invalid header: version number missing.";
+ }
+
+ int version = Read8( io, handle );
+ if ( version == 2 && Read8( io, handle ) != 0xff ) {
+ throw "invalid header: illegal version number.";
+ }
+
+ enum PICTType {none, op9a, jpeg, pixmap, bitmap};
+ PICTType pictType = none;
+
+ MacRect bounds;
+ MacpixMap pixMap;
+ int hRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point)
+ int vRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point)
+ WORD rowBytes = 0;
+ BOOL isRegion = FALSE;
+ BOOL done = FALSE;
+ long currentPos = 0;
+
+ while ( !done ) {
+ WORD opcode = 0;
+
+ // get the current stream position (used to avoid infinite loops)
+ currentPos = io->tell_proc(handle);
+
+ if ((version == 1) || ((io->tell_proc( handle ) % 2) != 0)) {
+ // align to word for version 2
+ opcode = Read8( io, handle );
+ }
+ if (version == 2) {
+ opcode = Read16( io, handle );
+ }
+
+ if (opcode == 0xFF || opcode == 0xFFFF) {
+ done = TRUE;
+ throw "PICT contained only vector data!";
+ }
+ else if (opcode < 0xa2) {
+ switch (opcode) {
+ case 0x01:
+ {
+ // skip clipping rectangle
+ MacRect clipRect;
+ WORD len = Read16( io, handle );
+
+ if (len == 0x000a) {
+ /* null rgn */
+ ReadRect( io, handle, &clipRect );
+ } else {
+ io->seek_proc(handle, len - 2, SEEK_CUR);
+ }
+ break;
+ }
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ {
+ // skip pattern definition
+ WORD patType;
+ WORD rowBytes;
+ MacpixMap p;
+ WORD numColors;
+
+ patType = Read16( io, handle );
+
+ switch( patType ) {
+ case 2:
+ io->seek_proc(handle, 8, SEEK_CUR);
+ io->seek_proc(handle, 5, SEEK_CUR);
+ break;
+ case 1:
+ {
+ io->seek_proc(handle, 8, SEEK_CUR);
+ rowBytes = Read16( io, handle );
+ ReadRect( io, handle, &p.Bounds );
+ ReadPixmap( io, handle, &p);
+
+ RGBQUAD ct[256];
+ ReadColorTable(io, handle, &numColors, ct );
+ SkipBits( io, handle, &p.Bounds, rowBytes, p.pixelSize );
+ break;
+ }
+ default:
+ throw "Unknown pattern type.";
+ }
+
+ break;
+ }
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ {
+ SkipPolyOrRegion( io, handle );
+ break;
+ }
+ case 0x90:
+ case 0x98:
+ {
+ // Bitmap/pixmap data clipped by a rectangle.
+ rowBytes = Read16( io, handle ); // Bytes per row in source when uncompressed.
+ isRegion = FALSE;
+
+ if ( rowBytes & 0x8000) {
+ pictType = pixmap;
+ } else {
+ pictType = bitmap;
+ }
+ done = TRUE;
+ break;
+ }
+ case 0x91:
+ case 0x99:
+ {
+ // Bitmap/pixmap data clipped by a region.
+ rowBytes = Read16( io, handle ); // Bytes per row in source when uncompressed.
+ isRegion = TRUE;
+
+ if ( rowBytes & 0x8000) {
+ pictType = pixmap;
+ } else {
+ pictType = bitmap;
+ }
+ done = TRUE;
+ break;
+ }
+ case 0x9a:
+ {
+ // DirectBitsRect.
+ Read32( io, handle ); // Skip fake len and fake EOF.
+ Read16( io, handle ); // bogus row bytes.
+
+ // Read in the PixMap fields.
+ ReadRect( io, handle, &pixMap.Bounds );
+ ReadPixmap( io, handle, &pixMap );
+
+ // Ignore source & destination rectangle as well as transfer mode.
+ MacRect dummy;
+ ReadRect( io, handle, &dummy );
+ ReadRect( io, handle, &dummy );
+ WORD mode = Read16( io, handle );
+
+ pictType=op9a;
+ done = TRUE;
+ break;
+ }
+ case 0xa1:
+ {
+ // long comment
+ WORD type;
+ WORD len;
+
+ type = Read16( io, handle );
+ len = Read16( io, handle);
+ if (len > 0) {
+ io->seek_proc(handle, len, SEEK_CUR);
+ }
+ break;
+ }
+ default:
+ // No function => skip to next opcode
+ if (optable[opcode].len == WORD_LEN) {
+ WORD len = Read16( io, handle );
+ io->seek_proc(handle, len, SEEK_CUR);
+ } else {
+ io->seek_proc(handle, optable[opcode].len, SEEK_CUR);
+ }
+ break;
+ }
+ }
+ else if (opcode == 0xc00) {
+ // version 2 header (26 bytes)
+ WORD minorVersion = Read16( io, handle ); // always FFFE (-2) for extended version 2
+ Read16( io, handle ); // reserved
+ hRes = Read32( io, handle ); // original horizontal resolution in pixels/inch
+ vRes = Read32( io, handle ); // original horizontal resolution in pixels/inch
+ MacRect dummy;
+ ReadRect( io, handle, &dummy ); // frame bounds at original resolution
+ Read32( io, handle ); // reserved
+ }
+ else if (opcode == 0x8200) {
+ // jpeg
+ long opLen = Read32( io, handle );
+ BOOL found = FALSE;
+ int i = 0;
+
+ // skip to JPEG header.
+ while ( !found && i < opLen ) {
+// io->seek_proc( handle, 24, SEEK_CUR );
+// MacRect dummy;
+// ReadRect( io, handle, &dummy );
+// io->seek_proc( handle, 122, SEEK_CUR );
+// found = TRUE;
+ BYTE data[ 2 ];
+ if ( io->read_proc( data, 2, 1, handle ) ) {
+ io->seek_proc( handle, -2, SEEK_CUR );
+
+ if ( data[0] == 0xFF && data[1] == 0xD8 ) {
+ found = TRUE;
+ } else {
+ Read8( io, handle );
+ i++;
+ }
+ }
+ }
+
+ if ( found ) {
+ // Pass the data to the JPEG decoder.
+ pictType = jpeg;
+ } else {
+ throw "PICT file contains unrecognized quicktime data.";
+ }
+ done = TRUE;
+ }
+ else if (opcode >= 0xa2 && opcode <= 0xaf) {
+ // reserved
+ WORD len = Read16( io, handle );
+ io->seek_proc(handle, len, SEEK_CUR);
+ }
+ else if ((opcode >= 0xb0 && opcode <= 0xcf) || (opcode >= 0x8000 && opcode <= 0x80ff)) {
+ // just a reserved opcode, no data
+ }
+ else if ((opcode >= 0xd0 && opcode <= 0xfe) || opcode >= 8100) {
+ // reserved
+ LONG len = Read32( io, handle );
+ io->seek_proc(handle, len, SEEK_CUR);
+ }
+ else if (opcode >= 0x100 && opcode <= 0x7fff) {
+ // reserved
+ io->seek_proc(handle, ((opcode >> 7) & 255), SEEK_CUR);
+ }
+ else {
+ sprintf( outputMessage, "Can't handle opcode %x.\n", opcode );
+ throw outputMessage;
+ }
+
+ if(currentPos == io->tell_proc(handle)) {
+ // we probaly reached the end of file as we can no longer move forward ...
+ throw "Invalid PICT file";
+ }
+ }
+
+ switch ( pictType ) {
+ case op9a:
+ {
+ bounds = pixMap.Bounds;
+ int width = bounds.right - bounds.left;
+ int height = bounds.bottom - bounds.top;
+
+ if ( pixMap.pixelSize > 8 ) {
+ dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_Allocate( width, height, 8);
+ }
+ hRes = pixMap.hRes << 16;
+ vRes = pixMap.vRes << 16;
+ break;
+ }
+
+ case jpeg:
+ {
+ dib = FreeImage_LoadFromHandle( FIF_JPEG, io, handle );
+ break;
+ }
+
+ case pixmap:
+ {
+ // Decode version 2 pixmap
+ ReadRect( io, handle, &pixMap.Bounds );
+ ReadPixmap( io, handle, &pixMap );
+
+ bounds = pixMap.Bounds;
+ int width = bounds.right - bounds.left;
+ int height = bounds.bottom - bounds.top;
+
+ if ( pixMap.pixelSize > 8 ) {
+ dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_Allocate( width, height, 8);
+ }
+ hRes = pixMap.hRes << 16;
+ vRes = pixMap.vRes << 16;
+ break;
+ }
+
+ case bitmap:
+ {
+ // Decode version 1 bitmap: 1 bpp.
+ MacRect srcRect;
+ MacRect dstRect;
+ WORD width; // Width in pixels
+ WORD height; // Height in pixels
+
+ ReadRect( io, handle, &bounds );
+ ReadRect( io, handle, &srcRect );
+ ReadRect( io, handle, &dstRect );
+
+ width = bounds.right - bounds.left;
+ height = bounds.bottom - bounds.top;
+
+ dib = FreeImage_Allocate(width, height, 8);
+ break;
+ }
+ }
+
+ if ( dib ) {
+ // need to convert resolution figures from fixed point, pixels/inch
+ // to floating point, pixels/meter.
+ float hres_ppm = hRes * ((float)39.4 / (float)65536.0);
+ float vres_ppm = vRes * ((float)39.4 / (float)65536.0);
+
+ FreeImage_SetDotsPerMeterX( dib, (LONG)hres_ppm );
+ FreeImage_SetDotsPerMeterY( dib, (LONG)vres_ppm );
+
+ switch( pictType ) {
+ case op9a:
+ DecodeOp9a( io, handle, dib, &pixMap );
+ break;
+ case jpeg:
+ // Already decoded if the embedded format was valid.
+ break;
+ case pixmap:
+ DecodePixmap( io, handle, dib, isRegion, &pixMap, rowBytes );
+ break;
+ case bitmap:
+ DecodeBitmap( io, handle, dib, isRegion, &bounds, rowBytes );
+ break;
+ default:
+ throw "invalid pict type";
+ }
+ }
+
+ return dib;
+ }
+ catch(const char *message) {
+ FreeImage_Unload( dib );
+ FreeImage_OutputMessageProc(s_format_id, message);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPICT(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = NULL;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp b/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp
new file mode 100644
index 0000000000..ad398a87e8
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp
@@ -0,0 +1,953 @@
+// ==========================================================
+// PNG Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Herve Drolon (drolon@infonie.fr)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Aaron Shumate (trek@startreker.com)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+// ----------------------------------------------------------
+
+#define PNG_BYTES_TO_CHECK 8
+
+// ----------------------------------------------------------
+
+#include "../ZLib/zlib.h"
+#include "../LibPNG/png.h"
+
+// ----------------------------------------------------------
+
+typedef struct {
+ FreeImageIO *s_io;
+ fi_handle s_handle;
+} fi_ioStructure, *pfi_ioStructure;
+
+/////////////////////////////////////////////////////////////////////////////
+// libpng interface
+//
+
+static void
+_ReadProc(png_structp png_ptr, unsigned char *data, png_size_t size) {
+ pfi_ioStructure pfio = (pfi_ioStructure)png_get_io_ptr(png_ptr);
+ unsigned n = pfio->s_io->read_proc(data, (unsigned int)size, 1, pfio->s_handle);
+ if(size && (n == 0)) {
+ throw "Read error: invalid or corrupted PNG file";
+ }
+}
+
+static void
+_WriteProc(png_structp png_ptr, unsigned char *data, png_size_t size) {
+ pfi_ioStructure pfio = (pfi_ioStructure)png_get_io_ptr(png_ptr);
+ pfio->s_io->write_proc(data, (unsigned int)size, 1, pfio->s_handle);
+}
+
+static void
+_FlushProc(png_structp png_ptr) {
+ (png_structp)png_ptr;
+ // empty flush implementation
+}
+
+static void
+error_handler(png_structp png_ptr, const char *error) {
+ (png_structp)png_ptr;
+ throw error;
+}
+
+// in FreeImage warnings disabled
+
+static void
+warning_handler(png_structp png_ptr, const char *warning) {
+ (png_structp)png_ptr;
+ (char*)warning;
+}
+
+// ==========================================================
+// Metadata routines
+// ==========================================================
+
+static BOOL
+ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) {
+ // XMP keyword
+ const char *g_png_xmp_keyword = "XML:com.adobe.xmp";
+
+ FITAG *tag = NULL;
+ png_textp text_ptr = NULL;
+ int num_text = 0;
+
+ // iTXt/tEXt/zTXt chuncks
+ if(png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) {
+ for(int i = 0; i < num_text; i++) {
+ // create a tag
+ tag = FreeImage_CreateTag();
+ if (!tag) return FALSE;
+
+ DWORD tag_length = (DWORD) MAX(text_ptr[i].text_length, text_ptr[i].itxt_length);
+
+ FreeImage_SetTagLength(tag, tag_length);
+ FreeImage_SetTagCount(tag, tag_length);
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagValue(tag, text_ptr[i].text);
+
+ if(strcmp(text_ptr[i].key, g_png_xmp_keyword) == 0) {
+ // store the tag as XMP
+ FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+ FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+ } else {
+ // store the tag as a comment
+ FreeImage_SetTagKey(tag, text_ptr[i].key);
+ FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
+ }
+
+ // destroy the tag
+ FreeImage_DeleteTag(tag);
+ }
+ }
+
+ return TRUE;
+}
+
+static BOOL
+WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) {
+ // XMP keyword
+ const char *g_png_xmp_keyword = "XML:com.adobe.xmp";
+
+ FITAG *tag = NULL;
+ FIMETADATA *mdhandle = NULL;
+ BOOL bResult = TRUE;
+
+ png_text text_metadata;
+
+ // set the 'Comments' metadata as iTXt chuncks
+
+ mdhandle = FreeImage_FindFirstMetadata(FIMD_COMMENTS, dib, &tag);
+
+ if(mdhandle) {
+ do {
+ memset(&text_metadata, 0, sizeof(png_text));
+ text_metadata.compression = 1; // iTXt, none
+ text_metadata.key = (char*)FreeImage_GetTagKey(tag); // keyword, 1-79 character description of "text"
+ text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "")
+ text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string
+ text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string
+ text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer
+ text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer
+
+ // set the tag
+ png_set_text(png_ptr, info_ptr, &text_metadata, 1);
+
+ } while(FreeImage_FindNextMetadata(mdhandle, &tag));
+
+ FreeImage_FindCloseMetadata(mdhandle);
+ bResult &= TRUE;
+ }
+
+ // set the 'XMP' metadata as iTXt chuncks
+ tag = NULL;
+ FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag);
+ if(tag && FreeImage_GetTagLength(tag)) {
+ memset(&text_metadata, 0, sizeof(png_text));
+ text_metadata.compression = 1; // iTXt, none
+ text_metadata.key = (char*)g_png_xmp_keyword; // keyword, 1-79 character description of "text"
+ text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "")
+ text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string
+ text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string
+ text_metadata.lang = 0; // language code, 0-79 characters or a NULL pointer
+ text_metadata.lang_key = 0; // keyword translated UTF-8 string, 0 or more chars or a NULL pointer
+
+ // set the tag
+ png_set_text(png_ptr, info_ptr, &text_metadata, 1);
+ bResult &= TRUE;
+ }
+
+ return bResult;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "PNG";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Portable Network Graphics";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "png";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return "^.PNG\r";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/png";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+ BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ io->read_proc(&signature, 1, 8, handle);
+
+ return (memcmp(png_signature, signature, 8) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 1) ||
+ (depth == 4) ||
+ (depth == 8) ||
+ (depth == 24) ||
+ (depth == 32)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (
+ (type == FIT_BITMAP) ||
+ (type == FIT_UINT16) ||
+ (type == FIT_RGB16) ||
+ (type == FIT_RGBA16)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ png_structp png_ptr = NULL;
+ png_infop info_ptr;
+ png_uint_32 width, height;
+ png_colorp png_palette = NULL;
+ int color_type, palette_entries = 0;
+ int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels
+
+ FIBITMAP *dib = NULL;
+ RGBQUAD *palette = NULL; // pointer to dib palette
+ png_bytepp row_pointers = NULL;
+ int i;
+
+ fi_ioStructure fio;
+ fio.s_handle = handle;
+ fio.s_io = io;
+
+ if (handle) {
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+ // check to see if the file is in fact a PNG file
+
+ BYTE png_check[PNG_BYTES_TO_CHECK];
+
+ io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle);
+
+ if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) {
+ return NULL; // Bad signature
+ }
+
+ // create the chunk manage structure
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler);
+
+ if (!png_ptr) {
+ return NULL;
+ }
+
+ // create the info structure
+
+ info_ptr = png_create_info_struct(png_ptr);
+
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+ return NULL;
+ }
+
+ // init the IO
+
+ png_set_read_fn(png_ptr, &fio, _ReadProc);
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return NULL;
+ }
+
+ // because we have already read the signature...
+
+ png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
+
+ // read the IHDR chunk
+
+ png_read_info(png_ptr, info_ptr);
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
+
+ pixel_depth = png_get_bit_depth(png_ptr, info_ptr) * png_get_channels(png_ptr, info_ptr);
+
+ // get image data type (assume standard image type)
+
+ FREE_IMAGE_TYPE image_type = FIT_BITMAP;
+ if (bit_depth == 16) {
+ if ((pixel_depth == 16) && (color_type == PNG_COLOR_TYPE_GRAY)) {
+ image_type = FIT_UINT16;
+ }
+ else if ((pixel_depth == 48) && (color_type == PNG_COLOR_TYPE_RGB)) {
+ image_type = FIT_RGB16;
+ }
+ else if ((pixel_depth == 64) && (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) {
+ image_type = FIT_RGBA16;
+ } else {
+ // tell libpng to strip 16 bit/color files down to 8 bits/color
+ png_set_strip_16(png_ptr);
+ bit_depth = 8;
+ }
+ }
+
+#ifndef FREEIMAGE_BIGENDIAN
+ if ((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
+ // turn on 16 bit byte swapping
+ png_set_swap(png_ptr);
+ }
+#endif
+
+ // set some additional flags
+
+ switch(color_type) {
+ case PNG_COLOR_TYPE_RGB:
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ // flip the RGB pixels to BGR (or RGBA to BGRA)
+
+ if(image_type == FIT_BITMAP) {
+ png_set_bgr(png_ptr);
+ }
+#endif
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE:
+ // expand palette images to the full 8 bits from 2 bits/pixel
+
+ if (pixel_depth == 2) {
+ png_set_packing(png_ptr);
+ pixel_depth = 8;
+ }
+
+ break;
+
+ case PNG_COLOR_TYPE_GRAY:
+ // expand grayscale images to the full 8 bits from 2 bits/pixel
+ // but *do not* expand fully transparent palette entries to a full alpha channel
+
+ if (pixel_depth == 2) {
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ pixel_depth = 8;
+ }
+
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ // expand 8-bit greyscale + 8-bit alpha to 32-bit
+
+ png_set_gray_to_rgb(png_ptr);
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ // flip the RGBA pixels to BGRA
+
+ png_set_bgr(png_ptr);
+#endif
+ pixel_depth = 32;
+
+ break;
+
+ default:
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+
+ // unlike the example in the libpng documentation, we have *no* idea where
+ // this file may have come from--so if it doesn't have a file gamma, don't
+ // do any correction ("do no harm")
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
+ double gamma = 0;
+ double screen_gamma = 2.2;
+
+ if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) {
+ png_set_gamma(png_ptr, screen_gamma, gamma);
+ }
+ }
+
+ // all transformations have been registered; now update info_ptr data
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ // color type may have changed, due to our transformations
+
+ color_type = png_get_color_type(png_ptr,info_ptr);
+
+ // create a DIB and write the bitmap header
+ // set up the DIB palette, if needed
+
+ switch (color_type) {
+ case PNG_COLOR_TYPE_RGB:
+ png_set_invert_alpha(png_ptr);
+
+ if(image_type == FIT_BITMAP) {
+ dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth);
+ }
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ if(image_type == FIT_BITMAP) {
+ dib = FreeImage_AllocateHeader(header_only, width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth);
+ }
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE:
+ dib = FreeImage_AllocateHeader(header_only, width, height, pixel_depth);
+
+ png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries);
+
+ palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib));
+ palette = FreeImage_GetPalette(dib);
+
+ // store the palette
+
+ for (i = 0; i < palette_entries; i++) {
+ palette[i].rgbRed = png_palette[i].red;
+ palette[i].rgbGreen = png_palette[i].green;
+ palette[i].rgbBlue = png_palette[i].blue;
+ }
+ break;
+
+ case PNG_COLOR_TYPE_GRAY:
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth);
+
+ if(pixel_depth <= 8) {
+ palette = FreeImage_GetPalette(dib);
+ palette_entries = 1 << pixel_depth;
+
+ for (i = 0; i < palette_entries; i++) {
+ palette[i].rgbRed =
+ palette[i].rgbGreen =
+ palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1));
+ }
+ }
+ break;
+
+ default:
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+
+ // store the transparency table
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ // array of alpha (transparency) entries for palette
+ png_bytep trans_alpha = NULL;
+ // number of transparent entries
+ int num_trans = 0;
+ // graylevel or color sample values of the single transparent color for non-paletted images
+ png_color_16p trans_color = NULL;
+
+ png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color);
+
+ if ((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) {
+ // single transparent color
+ if (trans_color->gray < palette_entries) {
+ BYTE table[256];
+ memset(table, 0xFF, palette_entries);
+ table[trans_color->gray] = 0;
+ FreeImage_SetTransparencyTable(dib, table, palette_entries);
+ }
+ } else if ((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) {
+ // transparency table
+ FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans);
+ }
+ }
+
+ // store the background color
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) {
+ // Get the background color to draw transparent and alpha images over.
+ // Note that even if the PNG file supplies a background, you are not required to
+ // use it - you should use the (solid) application background if it has one.
+
+ png_color_16p image_background = NULL;
+ RGBQUAD rgbBkColor;
+
+ if (png_get_bKGD(png_ptr, info_ptr, &image_background)) {
+ rgbBkColor.rgbRed = (BYTE)image_background->red;
+ rgbBkColor.rgbGreen = (BYTE)image_background->green;
+ rgbBkColor.rgbBlue = (BYTE)image_background->blue;
+ rgbBkColor.rgbReserved = 0;
+
+ FreeImage_SetBackgroundColor(dib, &rgbBkColor);
+ }
+ }
+
+ // get physical resolution
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) {
+ png_uint_32 res_x, res_y;
+
+ // we'll overload this var and use 0 to mean no phys data,
+ // since if it's not in meters we can't use it anyway
+
+ int res_unit_type = PNG_RESOLUTION_UNKNOWN;
+
+ png_get_pHYs(png_ptr,info_ptr, &res_x, &res_y, &res_unit_type);
+
+ if (res_unit_type == PNG_RESOLUTION_METER) {
+ FreeImage_SetDotsPerMeterX(dib, res_x);
+ FreeImage_SetDotsPerMeterY(dib, res_y);
+ }
+ }
+
+ // get possible ICC profile
+
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
+ png_charp profile_name = NULL;
+ png_bytep profile_data = NULL;
+ png_uint_32 profile_length = 0;
+ int compression_type;
+
+ png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length);
+
+ // copy ICC profile data (must be done after FreeImage_AllocateHeader)
+
+ FreeImage_CreateICCProfile(dib, profile_data, profile_length);
+ }
+
+ // --- header only mode => clean-up and return
+
+ if (header_only) {
+ // get possible metadata (it can be located both before and after the image data)
+ ReadMetadata(png_ptr, info_ptr, dib);
+ if (png_ptr) {
+ // clean up after the read, and free any memory allocated - REQUIRED
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ }
+ return dib;
+ }
+
+ // set the individual row_pointers to point at the correct offsets
+
+ row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep));
+
+ if (!row_pointers) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ FreeImage_Unload(dib);
+ return NULL;
+ }
+
+ // read in the bitmap bits via the pointer table
+ // allow loading of PNG with minor errors (such as images with several IDAT chunks)
+
+ for (png_uint_32 k = 0; k < height; k++) {
+ row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k);
+ }
+
+ png_set_benign_errors(png_ptr, 1);
+ png_read_image(png_ptr, row_pointers);
+
+ // check if the bitmap contains transparency, if so enable it in the header
+
+ if (FreeImage_GetBPP(dib) == 32) {
+ if (FreeImage_GetColorType(dib) == FIC_RGBALPHA) {
+ FreeImage_SetTransparent(dib, TRUE);
+ } else {
+ FreeImage_SetTransparent(dib, FALSE);
+ }
+ }
+
+ // cleanup
+
+ if (row_pointers) {
+ free(row_pointers);
+ row_pointers = NULL;
+ }
+
+ // read the rest of the file, getting any additional chunks in info_ptr
+
+ png_read_end(png_ptr, info_ptr);
+
+ // get possible metadata (it can be located both before and after the image data)
+
+ ReadMetadata(png_ptr, info_ptr, dib);
+
+ if (png_ptr) {
+ // clean up after the read, and free any memory allocated - REQUIRED
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ }
+
+ return dib;
+
+ } catch (const char *text) {
+ if (png_ptr) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ }
+ if (row_pointers) {
+ free(row_pointers);
+ }
+ if (dib) {
+ FreeImage_Unload(dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, text);
+
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_colorp palette = NULL;
+ png_uint_32 width, height;
+ BOOL has_alpha_channel = FALSE;
+
+ RGBQUAD *pal; // pointer to dib palette
+ int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels
+ int palette_entries;
+ int interlace_type;
+
+ fi_ioStructure fio;
+ fio.s_handle = handle;
+ fio.s_io = io;
+
+ if ((dib) && (handle)) {
+ try {
+ // create the chunk manage structure
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler);
+
+ if (!png_ptr) {
+ return FALSE;
+ }
+
+ // allocate/initialize the image information data.
+
+ info_ptr = png_create_info_struct(png_ptr);
+
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ return FALSE;
+ }
+
+ // Set error handling. REQUIRED if you aren't supplying your own
+ // error handling functions in the png_create_write_struct() call.
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ // if we get here, we had a problem reading the file
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return FALSE;
+ }
+
+ // init the IO
+
+ png_set_write_fn(png_ptr, &fio, _WriteProc, _FlushProc);
+
+ // set physical resolution
+
+ png_uint_32 res_x = (png_uint_32)FreeImage_GetDotsPerMeterX(dib);
+ png_uint_32 res_y = (png_uint_32)FreeImage_GetDotsPerMeterY(dib);
+
+ if ((res_x > 0) && (res_y > 0)) {
+ png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER);
+ }
+
+ // Set the image information here. Width and height are up to 2^31,
+ // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
+ // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
+ // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
+ // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
+ // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
+ // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
+
+ width = FreeImage_GetWidth(dib);
+ height = FreeImage_GetHeight(dib);
+ pixel_depth = FreeImage_GetBPP(dib);
+
+ BOOL bInterlaced = FALSE;
+ if ( (flags & PNG_INTERLACED) == PNG_INTERLACED) {
+ interlace_type = PNG_INTERLACE_ADAM7;
+ bInterlaced = TRUE;
+ } else {
+ interlace_type = PNG_INTERLACE_NONE;
+ }
+
+ // set the ZLIB compression level or default to PNG default compression level (ZLIB level = 6)
+ int zlib_level = flags & 0x0F;
+ if ((zlib_level >= 1) && (zlib_level <= 9)) {
+ png_set_compression_level(png_ptr, zlib_level);
+ } else if ((flags & PNG_Z_NO_COMPRESSION) == PNG_Z_NO_COMPRESSION) {
+ png_set_compression_level(png_ptr, Z_NO_COMPRESSION);
+ }
+
+ // filtered strategy works better for high color images
+ if(pixel_depth >= 16){
+ png_set_compression_strategy(png_ptr, Z_FILTERED);
+ png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH);
+ } else {
+ png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
+ }
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ if(image_type == FIT_BITMAP) {
+ // standard image type
+ bit_depth = (pixel_depth > 8) ? 8 : pixel_depth;
+ } else {
+ // 16-bit greyscale or 16-bit RGB(A)
+ bit_depth = 16;
+ }
+
+ switch (FreeImage_GetColorType(dib)) {
+ case FIC_MINISWHITE:
+ // Invert monochrome files to have 0 as black and 1 as white (no break here)
+ png_set_invert_mono(png_ptr);
+
+ case FIC_MINISBLACK:
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+ PNG_COLOR_TYPE_GRAY, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ break;
+
+ case FIC_PALETTE:
+ {
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+ PNG_COLOR_TYPE_PALETTE, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ // set the palette
+
+ palette_entries = 1 << bit_depth;
+ palette = (png_colorp)png_malloc(png_ptr, palette_entries * sizeof (png_color));
+ pal = FreeImage_GetPalette(dib);
+
+ for (int i = 0; i < palette_entries; i++) {
+ palette[i].red = pal[i].rgbRed;
+ palette[i].green = pal[i].rgbGreen;
+ palette[i].blue = pal[i].rgbBlue;
+ }
+
+ png_set_PLTE(png_ptr, info_ptr, palette, palette_entries);
+
+ // You must not free palette here, because png_set_PLTE only makes a link to
+ // the palette that you malloced. Wait until you are about to destroy
+ // the png structure.
+
+ break;
+ }
+
+ case FIC_RGBALPHA :
+ has_alpha_channel = TRUE;
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+ PNG_COLOR_TYPE_RGBA, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ // flip BGR pixels to RGB
+ if(image_type == FIT_BITMAP) {
+ png_set_bgr(png_ptr);
+ }
+#endif
+ break;
+
+ case FIC_RGB:
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+ PNG_COLOR_TYPE_RGB, interlace_type,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ // flip BGR pixels to RGB
+ if(image_type == FIT_BITMAP) {
+ png_set_bgr(png_ptr);
+ }
+#endif
+ break;
+
+ case FIC_CMYK:
+ break;
+ }
+
+ // write possible ICC profile
+
+ FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
+ if (iccProfile->size && iccProfile->data) {
+ png_set_iCCP(png_ptr, info_ptr, "Embedded Profile", 0, (png_const_bytep)iccProfile->data, iccProfile->size);
+ }
+
+ // write metadata
+
+ WriteMetadata(png_ptr, info_ptr, dib);
+
+ // Optional gamma chunk is strongly suggested if you have any guess
+ // as to the correct gamma of the image.
+ // png_set_gAMA(png_ptr, info_ptr, gamma);
+
+ // set the transparency table
+
+ if (FreeImage_IsTransparent(dib) && (FreeImage_GetTransparencyCount(dib) > 0)) {
+ png_set_tRNS(png_ptr, info_ptr, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib), NULL);
+ }
+
+ // set the background color
+
+ if(FreeImage_HasBackgroundColor(dib)) {
+ png_color_16 image_background;
+ RGBQUAD rgbBkColor;
+
+ FreeImage_GetBackgroundColor(dib, &rgbBkColor);
+ memset(&image_background, 0, sizeof(png_color_16));
+ image_background.blue = rgbBkColor.rgbBlue;
+ image_background.green = rgbBkColor.rgbGreen;
+ image_background.red = rgbBkColor.rgbRed;
+ image_background.index = rgbBkColor.rgbReserved;
+
+ png_set_bKGD(png_ptr, info_ptr, &image_background);
+ }
+
+ // Write the file header information.
+
+ png_write_info(png_ptr, info_ptr);
+
+ // write out the image data
+
+#ifndef FREEIMAGE_BIGENDIAN
+ if (bit_depth == 16) {
+ // turn on 16 bit byte swapping
+ png_set_swap(png_ptr);
+ }
+#endif
+
+ int number_passes = 1;
+ if (bInterlaced) {
+ number_passes = png_set_interlace_handling(png_ptr);
+ }
+
+ if ((pixel_depth == 32) && (!has_alpha_channel)) {
+ BYTE *buffer = (BYTE *)malloc(width * 3);
+
+ // transparent conversion to 24-bit
+ // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images
+ for (int pass = 0; pass < number_passes; pass++) {
+ for (png_uint_32 k = 0; k < height; k++) {
+ FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width);
+ png_write_row(png_ptr, buffer);
+ }
+ }
+ free(buffer);
+ } else {
+ // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images
+ for (int pass = 0; pass < number_passes; pass++) {
+ for (png_uint_32 k = 0; k < height; k++) {
+ png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1));
+ }
+ }
+ }
+
+ // It is REQUIRED to call this to finish writing the rest of the file
+ // Bug with png_flush
+
+ png_write_end(png_ptr, info_ptr);
+
+ // clean up after the write, and free any memory allocated
+ if (palette) {
+ png_free(png_ptr, palette);
+ }
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return TRUE;
+ } catch (const char *text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+ }
+
+ return FALSE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPNG(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPNM.cpp b/plugins/AdvaImg/src/FreeImage/PluginPNM.cpp
new file mode 100644
index 0000000000..a30f858851
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginPNM.cpp
@@ -0,0 +1,831 @@
+// ==========================================================
+// PNM (PPM, PGM, PBM) Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/**
+Get an integer value from the actual position pointed by handle
+*/
+static int
+GetInt(FreeImageIO *io, fi_handle handle) {
+ char c = 0;
+ BOOL firstchar;
+
+ // skip forward to start of next number
+
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+
+ while (1) {
+ // eat comments
+
+ if (c == '#') {
+ // if we're at a comment, read to end of line
+
+ firstchar = TRUE;
+
+ while (1) {
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+
+ if (firstchar && c == ' ') {
+ // loop off 1 sp after #
+
+ firstchar = FALSE;
+ } else if (c == '\n') {
+ break;
+ }
+ }
+ }
+
+ if (c >= '0' && c <='9') {
+ // we've found what we were looking for
+
+ break;
+ }
+
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+ }
+
+ // we're at the start of a number, continue until we hit a non-number
+
+ int i = 0;
+
+ while (1) {
+ i = (i * 10) + (c - '0');
+
+ if (!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING;
+
+ if (c < '0' || c > '9')
+ break;
+ }
+
+ return i;
+}
+
+/**
+Read a WORD value taking into account the endianess issue
+*/
+static inline WORD
+ReadWord(FreeImageIO *io, fi_handle handle) {
+ WORD level = 0;
+ io->read_proc(&level, 2, 1, handle);
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapShort(&level); // PNM uses the big endian convention
+#endif
+ return level;
+}
+
+/**
+Write a WORD value taking into account the endianess issue
+*/
+static inline void
+WriteWord(FreeImageIO *io, fi_handle handle, const WORD value) {
+ WORD level = value;
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapShort(&level); // PNM uses the big endian convention
+#endif
+ io->write_proc(&level, 2, 1, handle);
+}
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "PNM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Portable Network Media";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "pbm,pgm,ppm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/freeimage-pnm";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE pbm_id1[] = { 0x50, 0x31 };
+ BYTE pbm_id2[] = { 0x50, 0x34 };
+ BYTE pgm_id1[] = { 0x50, 0x32 };
+ BYTE pgm_id2[] = { 0x50, 0x35 };
+ BYTE ppm_id1[] = { 0x50, 0x33 };
+ BYTE ppm_id2[] = { 0x50, 0x36 };
+ BYTE signature[2] = { 0, 0 };
+
+ io->read_proc(signature, 1, sizeof(pbm_id1), handle);
+
+ if (memcmp(pbm_id1, signature, sizeof(pbm_id1)) == 0)
+ return TRUE;
+
+ if (memcmp(pbm_id2, signature, sizeof(pbm_id2)) == 0)
+ return TRUE;
+
+ if (memcmp(pgm_id1, signature, sizeof(pgm_id1)) == 0)
+ return TRUE;
+
+ if (memcmp(pgm_id2, signature, sizeof(pgm_id2)) == 0)
+ return TRUE;
+
+ if (memcmp(ppm_id1, signature, sizeof(ppm_id1)) == 0)
+ return TRUE;
+
+ if (memcmp(ppm_id2, signature, sizeof(ppm_id2)) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 1) ||
+ (depth == 8) ||
+ (depth == 24)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (
+ (type == FIT_BITMAP) ||
+ (type == FIT_UINT16) ||
+ (type == FIT_RGB16)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ char id_one = 0, id_two = 0;
+ int x, y;
+ FIBITMAP *dib = NULL;
+ RGBQUAD *pal; // pointer to dib palette
+ int i;
+
+ if (!handle) {
+ return NULL;
+ }
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+ FREE_IMAGE_TYPE image_type = FIT_BITMAP; // standard image: 1-, 8-, 24-bit
+
+ // Read the first two bytes of the file to determine the file format
+ // "P1" = ascii bitmap, "P2" = ascii greymap, "P3" = ascii pixmap,
+ // "P4" = raw bitmap, "P5" = raw greymap, "P6" = raw pixmap
+
+ io->read_proc(&id_one, 1, 1, handle);
+ io->read_proc(&id_two, 1, 1, handle);
+
+ if ((id_one != 'P') || (id_two < '1') || (id_two > '6')) {
+ // signature error
+ throw FI_MSG_ERROR_MAGIC_NUMBER;
+ }
+
+ // Read the header information: width, height and the 'max' value if any
+
+ int width = GetInt(io, handle);
+ int height = GetInt(io, handle);
+ int maxval = 1;
+
+ if ((id_two == '2') || (id_two == '5') || (id_two == '3') || (id_two == '6')) {
+ maxval = GetInt(io, handle);
+ if ((maxval <= 0) || (maxval > 65535)) {
+ FreeImage_OutputMessageProc(s_format_id, "Invalid max value : %d", maxval);
+ throw (const char*)NULL;
+ }
+ }
+
+ // Create a new DIB
+
+ switch (id_two) {
+ case '1':
+ case '4':
+ // 1-bit
+ dib = FreeImage_AllocateHeader(header_only, width, height, 1);
+ break;
+
+ case '2':
+ case '5':
+ if(maxval > 255) {
+ // 16-bit greyscale
+ image_type = FIT_UINT16;
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
+ } else {
+ // 8-bit greyscale
+ dib = FreeImage_AllocateHeader(header_only, width, height, 8);
+ }
+ break;
+
+ case '3':
+ case '6':
+ if(maxval > 255) {
+ // 48-bit RGB
+ image_type = FIT_RGB16;
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height);
+ } else {
+ // 24-bit RGB
+ dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ }
+ break;
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // Build a greyscale palette if needed
+
+ if(image_type == FIT_BITMAP) {
+ switch(id_two) {
+ case '1':
+ case '4':
+ pal = FreeImage_GetPalette(dib);
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+ break;
+
+ case '2':
+ case '5':
+ pal = FreeImage_GetPalette(dib);
+ for (i = 0; i < 256; i++) {
+ pal[i].rgbRed =
+ pal[i].rgbGreen =
+ pal[i].rgbBlue = (BYTE)i;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // Read the image...
+
+ switch(id_two) {
+ case '1':
+ case '4':
+ // write the bitmap data
+
+ if (id_two == '1') { // ASCII bitmap
+ for (y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ if (GetInt(io, handle) == 0)
+ bits[x >> 3] |= (0x80 >> (x & 0x7));
+ else
+ bits[x >> 3] &= (0xFF7F >> (x & 0x7));
+ }
+ }
+ } else { // Raw bitmap
+ int line = CalculateLine(width, 1);
+
+ for (y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < line; x++) {
+ io->read_proc(&bits[x], 1, 1, handle);
+
+ bits[x] = ~bits[x];
+ }
+ }
+ }
+
+ return dib;
+
+ case '2':
+ case '5':
+ if(image_type == FIT_BITMAP) {
+ // write the bitmap data
+
+ if(id_two == '2') { // ASCII greymap
+ int level = 0;
+
+ for (y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ level = GetInt(io, handle);
+ bits[x] = (BYTE)((255 * level) / maxval);
+ }
+ }
+ } else { // Raw greymap
+ BYTE level = 0;
+
+ for (y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ io->read_proc(&level, 1, 1, handle);
+ bits[x] = (BYTE)((255 * (int)level) / maxval);
+ }
+ }
+ }
+ }
+ else if(image_type == FIT_UINT16) {
+ // write the bitmap data
+
+ if(id_two == '2') { // ASCII greymap
+ int level = 0;
+
+ for (y = 0; y < height; y++) {
+ WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ level = GetInt(io, handle);
+ bits[x] = (WORD)((65535 * (double)level) / maxval);
+ }
+ }
+ } else { // Raw greymap
+ WORD level = 0;
+
+ for (y = 0; y < height; y++) {
+ WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ level = ReadWord(io, handle);
+ bits[x] = (WORD)((65535 * (double)level) / maxval);
+ }
+ }
+ }
+ }
+
+ return dib;
+
+ case '3':
+ case '6':
+ if(image_type == FIT_BITMAP) {
+ // write the bitmap data
+
+ if (id_two == '3') { // ASCII pixmap
+ int level = 0;
+
+ for (y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ level = GetInt(io, handle);
+ bits[FI_RGBA_RED] = (BYTE)((255 * level) / maxval); // R
+ level = GetInt(io, handle);
+ bits[FI_RGBA_GREEN] = (BYTE)((255 * level) / maxval); // G
+ level = GetInt(io, handle);
+ bits[FI_RGBA_BLUE] = (BYTE)((255 * level) / maxval); // B
+
+ bits += 3;
+ }
+ }
+ } else { // Raw pixmap
+ BYTE level = 0;
+
+ for (y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ io->read_proc(&level, 1, 1, handle);
+ bits[FI_RGBA_RED] = (BYTE)((255 * (int)level) / maxval); // R
+
+ io->read_proc(&level, 1, 1, handle);
+ bits[FI_RGBA_GREEN] = (BYTE)((255 * (int)level) / maxval); // G
+
+ io->read_proc(&level, 1, 1, handle);
+ bits[FI_RGBA_BLUE] = (BYTE)((255 * (int)level) / maxval); // B
+
+ bits += 3;
+ }
+ }
+ }
+ }
+ else if(image_type == FIT_RGB16) {
+ // write the bitmap data
+
+ if (id_two == '3') { // ASCII pixmap
+ int level = 0;
+
+ for (y = 0; y < height; y++) {
+ FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ level = GetInt(io, handle);
+ bits[x].red = (WORD)((65535 * (double)level) / maxval); // R
+ level = GetInt(io, handle);
+ bits[x].green = (WORD)((65535 * (double)level) / maxval); // G
+ level = GetInt(io, handle);
+ bits[x].blue = (WORD)((65535 * (double)level) / maxval); // B
+ }
+ }
+ } else { // Raw pixmap
+ WORD level = 0;
+
+ for (y = 0; y < height; y++) {
+ FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ level = ReadWord(io, handle);
+ bits[x].red = (WORD)((65535 * (double)level) / maxval); // R
+ level = ReadWord(io, handle);
+ bits[x].green = (WORD)((65535 * (double)level) / maxval); // G
+ level = ReadWord(io, handle);
+ bits[x].blue = (WORD)((65535 * (double)level) / maxval); // B
+ }
+ }
+ }
+ }
+
+ return dib;
+ }
+
+ } catch (const char *text) {
+ if(dib) FreeImage_Unload(dib);
+
+ if(NULL != text) {
+ switch(id_two) {
+ case '1':
+ case '4':
+ FreeImage_OutputMessageProc(s_format_id, text);
+ break;
+
+ case '2':
+ case '5':
+ FreeImage_OutputMessageProc(s_format_id, text);
+ break;
+
+ case '3':
+ case '6':
+ FreeImage_OutputMessageProc(s_format_id, text);
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ // ----------------------------------------------------------
+ // PNM Saving
+ // ----------------------------------------------------------
+ //
+ // Output format :
+ //
+ // Bit depth flags file format
+ // ------------- -------------- -----------
+ // 1-bit / pixel PNM_SAVE_ASCII PBM (P1)
+ // 1-bit / pixel PNM_SAVE_RAW PBM (P4)
+ // 8-bit / pixel PNM_SAVE_ASCII PGM (P2)
+ // 8-bit / pixel PNM_SAVE_RAW PGM (P5)
+ // 24-bit / pixel PNM_SAVE_ASCII PPM (P3)
+ // 24-bit / pixel PNM_SAVE_RAW PPM (P6)
+ // ----------------------------------------------------------
+
+ int x, y;
+
+ char buffer[256]; // temporary buffer whose size should be enough for what we need
+
+ if (!dib || !handle) return FALSE;
+
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ int bpp = FreeImage_GetBPP(dib);
+ int width = FreeImage_GetWidth(dib);
+ int height = FreeImage_GetHeight(dib);
+
+ // Find the appropriate magic number for this file type
+
+ int magic = 0;
+ int maxval = 255;
+
+ switch(image_type) {
+ case FIT_BITMAP:
+ switch (bpp) {
+ case 1 :
+ magic = 1; // PBM file (B & W)
+ break;
+ case 8 :
+ magic = 2; // PGM file (Greyscale)
+ break;
+
+ case 24 :
+ magic = 3; // PPM file (RGB)
+ break;
+
+ default:
+ return FALSE; // Invalid bit depth
+ }
+ break;
+
+ case FIT_UINT16:
+ magic = 2; // PGM file (Greyscale)
+ maxval = 65535;
+ break;
+
+ case FIT_RGB16:
+ magic = 3; // PPM file (RGB)
+ maxval = 65535;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+
+ if (flags == PNM_SAVE_RAW)
+ magic += 3;
+
+ // Write the header info
+
+ sprintf(buffer, "P%d\n%d %d\n", magic, width, height);
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+ if (bpp != 1) {
+ sprintf(buffer, "%d\n", maxval);
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+ }
+
+ // Write the image data
+ ///////////////////////
+
+ if(image_type == FIT_BITMAP) {
+ switch(bpp) {
+ case 24 : // 24-bit RGB, 3 bytes per pixel
+ {
+ if (flags == PNM_SAVE_RAW) {
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ io->write_proc(&bits[FI_RGBA_RED], 1, 1, handle); // R
+ io->write_proc(&bits[FI_RGBA_GREEN], 1, 1, handle); // G
+ io->write_proc(&bits[FI_RGBA_BLUE], 1, 1, handle); // B
+
+ bits += 3;
+ }
+ }
+ } else {
+ int length = 0;
+
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ sprintf(buffer, "%3d %3d %3d ", bits[FI_RGBA_RED], bits[FI_RGBA_GREEN], bits[FI_RGBA_BLUE]);
+
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+ length += 12;
+
+ if(length > 58) {
+ // No line should be longer than 70 characters
+ sprintf(buffer, "\n");
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+ length = 0;
+ }
+
+ bits += 3;
+ }
+ }
+
+ }
+ }
+ break;
+
+ case 8: // 8-bit greyscale
+ {
+ if (flags == PNM_SAVE_RAW) {
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ io->write_proc(&bits[x], 1, 1, handle);
+ }
+ }
+ } else {
+ int length = 0;
+
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ sprintf(buffer, "%3d ", bits[x]);
+
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+ length += 4;
+
+ if (length > 66) {
+ // No line should be longer than 70 characters
+ sprintf(buffer, "\n");
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+ length = 0;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 1: // 1-bit B & W
+ {
+ int color;
+
+ if (flags == PNM_SAVE_RAW) {
+ for(y = 0; y < height; y++) {
+ // write the scanline to disc
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for(x = 0; x < (int)FreeImage_GetLine(dib); x++)
+ io->write_proc(&bits[x], 1, 1, handle);
+ }
+ } else {
+ int length = 0;
+
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < (int)FreeImage_GetLine(dib) * 8; x++) {
+ color = (bits[x>>3] & (0x80 >> (x & 0x07))) != 0;
+
+ sprintf(buffer, "%c ", color ? '1':'0');
+
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+ length += 2;
+
+ if (length > 68) {
+ // No line should be longer than 70 characters
+ sprintf(buffer, "\n");
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+ length = 0;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ } // if(FIT_BITMAP)
+
+ else if(image_type == FIT_UINT16) { // 16-bit greyscale
+ if (flags == PNM_SAVE_RAW) {
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ WriteWord(io, handle, bits[x]);
+ }
+ }
+ } else {
+ int length = 0;
+
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ WORD *bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ sprintf(buffer, "%5d ", bits[x]);
+
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+ length += 6;
+
+ if (length > 64) {
+ // No line should be longer than 70 characters
+ sprintf(buffer, "\n");
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+ length = 0;
+ }
+ }
+ }
+ }
+ }
+
+ else if(image_type == FIT_RGB16) { // 48-bit RGB
+ if (flags == PNM_SAVE_RAW) {
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ WriteWord(io, handle, bits[x].red); // R
+ WriteWord(io, handle, bits[x].green); // G
+ WriteWord(io, handle, bits[x].blue); // B
+ }
+ }
+ } else {
+ int length = 0;
+
+ for (y = 0; y < height; y++) {
+ // write the scanline to disc
+ FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < width; x++) {
+ sprintf(buffer, "%5d %5d %5d ", bits[x].red, bits[x].green, bits[x].blue);
+
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+
+ length += 18;
+
+ if(length > 52) {
+ // No line should be longer than 70 characters
+ sprintf(buffer, "\n");
+ io->write_proc(&buffer, (unsigned int)strlen(buffer), 1, handle);
+ length = 0;
+ }
+ }
+ }
+
+ }
+ }
+
+ return TRUE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPNM(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginPSD.cpp b/plugins/AdvaImg/src/FreeImage/PluginPSD.cpp
new file mode 100644
index 0000000000..e453a31789
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginPSD.cpp
@@ -0,0 +1,131 @@
+// ==========================================================
+// Photoshop Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// Based on LGPL code created and published by http://sourceforge.net/projects/elynx/
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "PSDParser.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "PSD";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Adobe Photoshop";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "psd";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/vnd.adobe.photoshop";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE psd_id[] = { 0x38, 0x42, 0x50, 0x53 };
+ BYTE signature[4] = { 0, 0, 0, 0 };
+
+ io->read_proc(signature, 1, 4, handle);
+
+ if(memcmp(psd_id, signature, 4) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if(handle) {
+ psdParser parser;
+
+ FIBITMAP *dib = parser.Load(io, handle, s_format_id, flags);
+
+ return dib;
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitPSD(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = NULL;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginRAS.cpp b/plugins/AdvaImg/src/FreeImage/PluginRAS.cpp
new file mode 100644
index 0000000000..08fc0c9e17
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginRAS.cpp
@@ -0,0 +1,512 @@
+// ==========================================================
+// Sun rasterfile Loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagSUNHEADER {
+ DWORD magic; // Magic number
+ DWORD width; // Image width in pixels
+ DWORD height; // Image height in pixels
+ DWORD depth; // Depth (1, 8, 24 or 32 bits) of each pixel
+ DWORD length; // Image length (in bytes)
+ DWORD type; // Format of file (see RT_* below)
+ DWORD maptype; // Type of colormap (see RMT_* below)
+ DWORD maplength; // Length of colormap (in bytes)
+} SUNHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// ----------------------------------------------------------
+
+// Following the header is the colormap, for maplength bytes (unless maplength is zero),
+// then the image. Each row of the image is rounded to 2 bytes.
+
+#define RAS_MAGIC 0x59A66A95 // Magic number for Sun rasterfiles
+
+// Sun supported type's
+
+#define RT_OLD 0 // Old format (raw image in 68000 byte order)
+#define RT_STANDARD 1 // Raw image in 68000 byte order
+#define RT_BYTE_ENCODED 2 // Run-length encoding of bytes
+#define RT_FORMAT_RGB 3 // XRGB or RGB instead of XBGR or BGR
+#define RT_FORMAT_TIFF 4 // TIFF <-> standard rasterfile
+#define RT_FORMAT_IFF 5 // IFF (TAAC format) <-> standard rasterfile
+
+#define RT_EXPERIMENTAL 0xffff // Reserved for testing
+
+// These are the possible colormap types.
+// if it's in RGB format, the map is made up of three byte arrays
+// (red, green, then blue) that are each 1/3 of the colormap length.
+
+#define RMT_NONE 0 // maplength is expected to be 0
+#define RMT_EQUAL_RGB 1 // red[maplength/3], green[maplength/3], blue[maplength/3]
+#define RMT_RAW 2 // Raw colormap
+#define RESC 128 // Run-length encoding escape character
+
+// ----- NOTES -----
+// Each line of the image is rounded out to a multiple of 16 bits.
+// This corresponds to the rounding convention used by the memory pixrect
+// package (/usr/include/pixrect/memvar.h) of the SunWindows system.
+// The ras_encoding field (always set to 0 by Sun's supported software)
+// was renamed to ras_length in release 2.0. As a result, rasterfiles
+// of type 0 generated by the old software claim to have 0 length; for
+// compatibility, code reading rasterfiles must be prepared to compute the
+// TRUE length from the width, height, and depth fields.
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static void
+ReadData(FreeImageIO *io, fi_handle handle, BYTE *buf, DWORD length, BOOL rle) {
+ // Read either Run-Length Encoded or normal image data
+
+ static BYTE repchar, remaining= 0;
+
+ if (rle) {
+ // Run-length encoded read
+
+ while(length--) {
+ if (remaining) {
+ remaining--;
+ *(buf++)= repchar;
+ } else {
+ io->read_proc(&repchar, 1, 1, handle);
+
+ if (repchar == RESC) {
+ io->read_proc(&remaining, 1, 1, handle);
+
+ if (remaining == 0) {
+ *(buf++)= RESC;
+ } else {
+ io->read_proc(&repchar, 1, 1, handle);
+
+ *(buf++)= repchar;
+ }
+ } else {
+ *(buf++)= repchar;
+ }
+ }
+ }
+ } else {
+ // Normal read
+
+ io->read_proc(buf, length, 1, handle);
+ }
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "RAS";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Sun Raster Image";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "ras";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-cmu-raster";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE ras_signature[] = { 0x59, 0xA6, 0x6A, 0x95 };
+ BYTE signature[4] = { 0, 0, 0, 0 };
+
+ io->read_proc(signature, 1, sizeof(ras_signature), handle);
+
+ return (memcmp(ras_signature, signature, sizeof(ras_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ SUNHEADER header; // Sun file header
+ WORD linelength; // Length of raster line in bytes
+ WORD fill; // Number of fill bytes per raster line
+ BOOL rle; // TRUE if RLE file
+ BOOL isRGB; // TRUE if file type is RT_FORMAT_RGB
+ BYTE fillchar;
+
+ FIBITMAP *dib = NULL;
+ BYTE *bits; // Pointer to dib data
+ WORD x, y;
+
+ if (!handle) {
+ return NULL;
+ }
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+ // Read SUN raster header
+
+ io->read_proc(&header, sizeof(SUNHEADER), 1, handle);
+
+#ifndef FREEIMAGE_BIGENDIAN
+ // SUN rasterfiles are big endian only
+
+ SwapLong(&header.magic);
+ SwapLong(&header.width);
+ SwapLong(&header.height);
+ SwapLong(&header.depth);
+ SwapLong(&header.length);
+ SwapLong(&header.type);
+ SwapLong(&header.maptype);
+ SwapLong(&header.maplength);
+#endif
+
+ // Verify SUN identifier
+
+ if (header.magic != RAS_MAGIC) {
+ throw FI_MSG_ERROR_MAGIC_NUMBER;
+ }
+
+ // Allocate a new DIB
+
+ switch(header.depth) {
+ case 1:
+ case 8:
+ dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth);
+ break;
+
+ case 24:
+ dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ break;
+
+ case 32:
+ dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ break;
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // Check the file format
+
+ rle = FALSE;
+ isRGB = FALSE;
+
+ switch(header.type) {
+ case RT_OLD:
+ case RT_STANDARD:
+ case RT_FORMAT_TIFF: // I don't even know what these format are...
+ case RT_FORMAT_IFF: //The TIFF and IFF format types indicate that the raster
+ //file was originally converted from either of these file formats.
+ //so lets at least try to process them as RT_STANDARD
+ break;
+
+ case RT_BYTE_ENCODED:
+ rle = TRUE;
+ break;
+
+ case RT_FORMAT_RGB:
+ isRGB = TRUE;
+ break;
+
+ default:
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+
+ // set up the colormap if needed
+
+ switch(header.maptype) {
+ case RMT_NONE :
+ {
+ if (header.depth < 24) {
+ // Create linear color ramp
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ int numcolors = 1 << header.depth;
+
+ for (int i = 0; i < numcolors; i++) {
+ pal[i].rgbRed = (BYTE)((255 * i) / (numcolors - 1));
+ pal[i].rgbGreen = (BYTE)((255 * i) / (numcolors - 1));
+ pal[i].rgbBlue = (BYTE)((255 * i) / (numcolors - 1));
+ }
+ }
+
+ break;
+ }
+
+ case RMT_EQUAL_RGB:
+ {
+ BYTE *r, *g, *b;
+
+ // Read SUN raster colormap
+
+ int numcolors = 1 << header.depth;
+ if ((DWORD)(3 * numcolors) > header.maplength) {
+ // some RAS may have less colors than the full palette
+ numcolors = header.maplength / 3;
+ } else {
+ throw "Invalid palette";
+ }
+
+ r = (BYTE*)malloc(3 * numcolors * sizeof(BYTE));
+ g = r + numcolors;
+ b = g + numcolors;
+
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ io->read_proc(r, 3 * numcolors, 1, handle);
+
+ for (int i = 0; i < numcolors; i++) {
+ pal[i].rgbRed = r[i];
+ pal[i].rgbGreen = g[i];
+ pal[i].rgbBlue = b[i];
+ }
+
+ free(r);
+ break;
+ }
+
+ case RMT_RAW:
+ {
+ BYTE *colormap;
+
+ // Read (skip) SUN raster colormap.
+
+ colormap = (BYTE *)malloc(header.maplength * sizeof(BYTE));
+
+ io->read_proc(colormap, header.maplength, 1, handle);
+
+ free(colormap);
+ break;
+ }
+ }
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ // Calculate the line + pitch
+ // Each row is multiple of 16 bits (2 bytes).
+
+ if (header.depth == 1) {
+ linelength = (WORD)((header.width / 8) + (header.width % 8 ? 1 : 0));
+ } else {
+ linelength = (WORD)header.width;
+ }
+
+ fill = (linelength % 2) ? 1 : 0;
+
+ unsigned pitch = FreeImage_GetPitch(dib);
+
+ // Read the image data
+
+ switch(header.depth) {
+ case 1:
+ case 8:
+ {
+ bits = FreeImage_GetBits(dib) + (header.height - 1) * pitch;
+
+ for (y = 0; y < header.height; y++) {
+ ReadData(io, handle, bits, linelength, rle);
+
+ bits -= pitch;
+
+ if (fill) {
+ ReadData(io, handle, &fillchar, fill, rle);
+ }
+ }
+
+ break;
+ }
+
+ case 24:
+ {
+ BYTE *buf, *bp;
+
+ buf = (BYTE*)malloc(header.width * 3);
+
+ for (y = 0; y < header.height; y++) {
+ bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
+
+ ReadData(io, handle, buf, header.width * 3, rle);
+
+ bp = buf;
+
+ if (isRGB) {
+ for (x = 0; x < header.width; x++) {
+ bits[FI_RGBA_RED] = *(bp++); // red
+ bits[FI_RGBA_GREEN] = *(bp++); // green
+ bits[FI_RGBA_BLUE] = *(bp++); // blue
+
+ bits += 3;
+ }
+ } else {
+ for (x = 0; x < header.width; x++) {
+ bits[FI_RGBA_RED] = *(bp + 2); // red
+ bits[FI_RGBA_GREEN] = *(bp + 1);// green
+ bits[FI_RGBA_BLUE] = *bp; // blue
+
+ bits += 3; bp += 3;
+ }
+ }
+
+ if (fill) {
+ ReadData(io, handle, &fillchar, fill, rle);
+ }
+ }
+
+ free(buf);
+ break;
+ }
+
+ case 32:
+ {
+ BYTE *buf, *bp;
+
+ buf = (BYTE*)malloc(header.width * 4);
+
+ for (y = 0; y < header.height; y++) {
+ bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
+
+ ReadData(io, handle, buf, header.width * 4, rle);
+
+ bp = buf;
+
+ if (isRGB) {
+ for (x = 0; x < header.width; x++) {
+ bits[FI_RGBA_ALPHA] = *(bp++); // alpha
+ bits[FI_RGBA_RED] = *(bp++); // red
+ bits[FI_RGBA_GREEN] = *(bp++); // green
+ bits[FI_RGBA_BLUE] = *(bp++); // blue
+
+ bits += 4;
+ }
+ }
+ else {
+ for (x = 0; x < header.width; x++) {
+ bits[FI_RGBA_RED] = *(bp + 3); // red
+ bits[FI_RGBA_GREEN] = *(bp + 2); // green
+ bits[FI_RGBA_BLUE] = *(bp + 1); // blue
+ bits[FI_RGBA_ALPHA] = *bp; // alpha
+
+ bits += 4;
+ bp += 4;
+ }
+ }
+
+ if (fill) {
+ ReadData(io, handle, &fillchar, fill, rle);
+ }
+ }
+
+ free(buf);
+ break;
+ }
+ }
+
+ return dib;
+
+ } catch (const char *text) {
+ if(dib) {
+ FreeImage_Unload(dib);
+ }
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitRAS(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginRAW.cpp b/plugins/AdvaImg/src/FreeImage/PluginRAW.cpp
new file mode 100644
index 0000000000..4611824f4d
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginRAW.cpp
@@ -0,0 +1,542 @@
+// ==========================================================
+// RAW camera image loader
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "../LibRawLite/libraw/libraw.h"
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../Metadata/FreeImageTag.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+// ----------------------------------------------------------
+// FreeImage datastream wrapper
+// ----------------------------------------------------------
+
+class LibRaw_freeimage_datastream : public LibRaw_abstract_datastream {
+private:
+ FreeImageIO *_io;
+ fi_handle _handle;
+ long _eof;
+
+public:
+ LibRaw_freeimage_datastream(FreeImageIO *io, fi_handle handle) : _io(io), _handle(handle) {
+ long start_pos = io->tell_proc(handle);
+ io->seek_proc(handle, 0, SEEK_END);
+ _eof = io->tell_proc(handle);
+ io->seek_proc(handle, start_pos, SEEK_SET);
+ }
+ ~LibRaw_freeimage_datastream() {
+ }
+ virtual void * make_jas_stream() {
+ return NULL;
+ }
+ virtual int valid() {
+ return (_io && _handle);
+ }
+ virtual int read(void *buffer, size_t size, size_t count) {
+ if(substream) return substream->read(buffer, size, count);
+ return _io->read_proc(buffer, (unsigned)size, (unsigned)count, _handle);
+ }
+ virtual int eof() {
+ if(substream) return substream->eof();
+ return (_io->tell_proc(_handle) >= _eof);
+ }
+ virtual int seek(INT64 offset, int origin) {
+ if(substream) return substream->seek(offset, origin);
+ return _io->seek_proc(_handle, (long)offset, origin);
+ }
+ virtual INT64 tell() {
+ if(substream) return substream->tell();
+ return _io->tell_proc(_handle);
+ }
+ virtual int get_char() {
+ int c = 0;
+ if(substream) return substream->get_char();
+ if (!_io->read_proc(&c, 1, 1, _handle)) return -1;
+ return c;
+ }
+ virtual char* gets(char *buffer, int length) {
+ if (substream) return substream->gets(buffer, length);
+ memset(buffer, 0, length);
+ for(int i = 0; i < length; i++) {
+ if (!_io->read_proc(&buffer[i], 1, 1, _handle))
+ return NULL;
+ if(buffer[i] == 0x0A)
+ break;
+ }
+ return buffer;
+ }
+ virtual int scanf_one(const char *fmt, void* val) {
+ std::string buffer;
+ char element = 0;
+ bool bDone = false;
+ if(substream) return substream->scanf_one(fmt,val);
+ do {
+ if(_io->read_proc(&element, 1, 1, _handle) == 1) {
+ switch(element) {
+ case '0':
+ case '\n':
+ case ' ':
+ case '\t':
+ bDone = true;
+ break;
+ default:
+ break;
+ }
+ buffer.append(&element, 1);
+ } else {
+ return 0;
+ }
+ } while(!bDone);
+
+ return sscanf(buffer.c_str(), fmt, val);
+ }
+};
+
+// ----------------------------------------------------------
+
+/**
+Convert a processed raw data array to a FIBITMAP
+@param image Processed raw image
+@return Returns the converted dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP *
+libraw_ConvertToDib(libraw_processed_image_t *image) {
+ FIBITMAP *dib = NULL;
+ try {
+ unsigned width = image->width;
+ unsigned height = image->height;
+ unsigned bpp = image->bits;
+ if(bpp == 16) {
+ // allocate output dib
+ dib = FreeImage_AllocateT(FIT_RGB16, width, height);
+ if (!dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+ // write data
+ WORD *raw_data = (WORD*)image->data;
+ for(unsigned y = 0; y < height; y++) {
+ FIRGB16 *output = (FIRGB16*)FreeImage_GetScanLine(dib, height - 1 - y);
+ for(unsigned x = 0; x < width; x++) {
+ output[x].red = raw_data[0];
+ output[x].green = raw_data[1];
+ output[x].blue = raw_data[2];
+ raw_data += 3;
+ }
+ }
+ } else if(bpp == 8) {
+ // allocate output dib
+ dib = FreeImage_AllocateT(FIT_BITMAP, width, height, 24);
+ if (!dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+ // write data
+ BYTE *raw_data = (BYTE*)image->data;
+ for(unsigned y = 0; y < height; y++) {
+ RGBTRIPLE *output = (RGBTRIPLE*)FreeImage_GetScanLine(dib, height - 1 - y);
+ for(unsigned x = 0; x < width; x++) {
+ output[x].rgbtRed = raw_data[0];
+ output[x].rgbtGreen = raw_data[1];
+ output[x].rgbtBlue = raw_data[2];
+ raw_data += 3;
+ }
+ }
+ }
+
+ } catch(const char *text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+
+ return dib;
+}
+
+/**
+Get the embedded JPEG preview image from RAW picture with included Exif Data.
+@param RawProcessor Libraw handle
+@param flags JPEG load flags
+@return Returns the loaded dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP *
+libraw_LoadEmbeddedPreview(LibRaw& RawProcessor, int flags) {
+ FIBITMAP *dib = NULL;
+ libraw_processed_image_t *thumb_image = NULL;
+
+ try {
+ // unpack data
+ if(RawProcessor.unpack_thumb() != LIBRAW_SUCCESS) {
+ // run silently "LibRaw : failed to run unpack_thumb"
+ return NULL;
+ }
+
+ // retrieve thumb image
+ int error_code = 0;
+ thumb_image = RawProcessor.dcraw_make_mem_thumb(&error_code);
+ if(thumb_image) {
+ if(thumb_image->type != LIBRAW_IMAGE_BITMAP) {
+ // attach the binary data to a memory stream
+ FIMEMORY *hmem = FreeImage_OpenMemory((BYTE*)thumb_image->data, (DWORD)thumb_image->data_size);
+ // get the file type
+ FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
+ if(fif == FIF_JPEG) {
+ // rotate according to Exif orientation
+ flags |= JPEG_EXIFROTATE;
+ }
+ // load an image from the memory stream
+ dib = FreeImage_LoadFromMemory(fif, hmem, flags);
+ // close the stream
+ FreeImage_CloseMemory(hmem);
+ } else {
+ // convert processed data to output dib
+ dib = libraw_ConvertToDib(thumb_image);
+ }
+ } else {
+ throw "LibRaw : failed to run dcraw_make_mem_thumb";
+ }
+
+ // clean-up and return
+ RawProcessor.dcraw_clear_mem(thumb_image);
+
+ return dib;
+
+ } catch(const char *text) {
+ // clean-up and return
+ if(thumb_image) {
+ RawProcessor.dcraw_clear_mem(thumb_image);
+ }
+ if(text != NULL) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+ }
+
+ return NULL;
+}
+/**
+Load raw data and convert to FIBITMAP
+@param RawProcessor Libraw handle
+@param bitspersample Output bitdepth (8- or 16-bit)
+@return Returns the loaded dib if successfull, returns NULL otherwise
+*/
+static FIBITMAP *
+libraw_LoadRawData(LibRaw& RawProcessor, int bitspersample) {
+ FIBITMAP *dib = NULL;
+ libraw_processed_image_t *processed_image = NULL;
+
+ try {
+ // set decoding parameters
+ // -----------------------
+
+ // (-6) 16-bit or 8-bit
+ RawProcessor.imgdata.params.output_bps = bitspersample;
+ // (-g power toe_slope)
+ if(bitspersample == 16) {
+ // set -g 1 1 for linear curve
+ RawProcessor.imgdata.params.gamm[0] = 1;
+ RawProcessor.imgdata.params.gamm[1] = 1;
+ } else if(bitspersample == 8) {
+ // by default settings for rec. BT.709 are used: power 2.222 (i.e. gamm[0]=1/2.222) and slope 4.5
+ RawProcessor.imgdata.params.gamm[0] = 1/2.222;
+ RawProcessor.imgdata.params.gamm[1] = 4.5;
+ }
+ // (-W) Don't use automatic increase of brightness by histogram
+ RawProcessor.imgdata.params.no_auto_bright = 1;
+ // (-a) Use automatic white balance obtained after averaging over the entire image
+ RawProcessor.imgdata.params.use_auto_wb = 1;
+ // (-q 3) Adaptive homogeneity-directed demosaicing algorithm (AHD)
+ RawProcessor.imgdata.params.user_qual = 3;
+
+ // -----------------------
+
+ // unpack data
+ if(RawProcessor.unpack() != LIBRAW_SUCCESS) {
+ throw "LibRaw : failed to unpack data";
+ }
+
+ // process data (... most consuming task ...)
+ if(RawProcessor.dcraw_process() != LIBRAW_SUCCESS) {
+ throw "LibRaw : failed to process data";
+ }
+
+ // retrieve processed image
+ int error_code = 0;
+ processed_image = RawProcessor.dcraw_make_mem_image(&error_code);
+ if(processed_image) {
+ // type SHOULD be LIBRAW_IMAGE_BITMAP, but we'll check
+ if(processed_image->type != LIBRAW_IMAGE_BITMAP) {
+ throw "invalid image type";
+ }
+ // only 3-color images supported...
+ if(processed_image->colors != 3) {
+ throw "only 3-color images supported";
+ }
+ } else {
+ throw "LibRaw : failed to run dcraw_make_mem_image";
+ }
+
+ // convert processed data to output dib
+ dib = libraw_ConvertToDib(processed_image);
+
+ // clean-up and return
+ RawProcessor.dcraw_clear_mem(processed_image);
+
+ return dib;
+
+ } catch(const char *text) {
+ // clean-up and return
+ if(processed_image) {
+ RawProcessor.dcraw_clear_mem(processed_image);
+ }
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "RAW";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "RAW camera image";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ /**
+ Below are known RAW file extensions that you can check using FreeImage_GetFIFFromFormat.
+ If a file extension is not listed, that doesn't mean that you cannot load it.
+ Using FreeImage_GetFileType is the best way to know if a RAW file format is supported.
+ */
+ static const char *raw_extensions =
+ "3fr," // Hasselblad Digital Camera Raw Image Format.
+ "arw," // Sony Digital Camera Raw Image Format for Alpha devices.
+ "bay," // Casio Digital Camera Raw File Format.
+ "bmq," // NuCore Raw Image File.
+ "cap," // Phase One Digital Camera Raw Image Format.
+ "cine," // Phantom Software Raw Image File.
+ "cr2," // Canon Digital Camera RAW Image Format version 2.0. These images are based on the TIFF image standard.
+ "crw," // Canon Digital Camera RAW Image Format version 1.0.
+ "cs1," // Sinar Capture Shop Raw Image File.
+ "dc2," // Kodak DC25 Digital Camera File.
+ "dcr," // Kodak Digital Camera Raw Image Format for these models: Kodak DSC Pro SLR/c, Kodak DSC Pro SLR/n, Kodak DSC Pro 14N, Kodak DSC PRO 14nx.
+ "drf," // Kodak Digital Camera Raw Image Format.
+ "dsc," // Kodak Digital Camera Raw Image Format.
+ "dng," // Adobe Digital Negative: DNG is publicly available archival format for the raw files generated by digital cameras. By addressing the lack of an open standard for the raw files created by individual camera models, DNG helps ensure that photographers will be able to access their files in the future.
+ "erf," // Epson Digital Camera Raw Image Format.
+ "fff," // Imacon Digital Camera Raw Image Format.
+ "ia," // Sinar Raw Image File.
+ "iiq," // Phase One Digital Camera Raw Image Format.
+ "k25," // Kodak DC25 Digital Camera Raw Image Format.
+ "kc2," // Kodak DCS200 Digital Camera Raw Image Format.
+ "kdc," // Kodak Digital Camera Raw Image Format.
+ "mdc," // Minolta RD175 Digital Camera Raw Image Format.
+ "mef," // Mamiya Digital Camera Raw Image Format.
+ "mos," // Leaf Raw Image File.
+ "mrw," // Minolta Dimage Digital Camera Raw Image Format.
+ "nef," // Nikon Digital Camera Raw Image Format.
+ "nrw," // Nikon Digital Camera Raw Image Format.
+ "orf," // Olympus Digital Camera Raw Image Format.
+ "pef," // Pentax Digital Camera Raw Image Format.
+ "ptx," // Pentax Digital Camera Raw Image Format.
+ "pxn," // Logitech Digital Camera Raw Image Format.
+ "qtk," // Apple Quicktake 100/150 Digital Camera Raw Image Format.
+ "raf," // Fuji Digital Camera Raw Image Format.
+ "raw," // Panasonic Digital Camera Image Format.
+ "rdc," // Digital Foto Maker Raw Image File.
+ "rw2," // Panasonic LX3 Digital Camera Raw Image Format.
+ "rwl," // Leica Camera Raw Image Format.
+ "rwz," // Rawzor Digital Camera Raw Image Format.
+ "sr2," // Sony Digital Camera Raw Image Format.
+ "srf," // Sony Digital Camera Raw Image Format for DSC-F828 8 megapixel digital camera or Sony DSC-R1.
+ "srw," // Samsung Raw Image Format.
+ "sti"; // Sinar Capture Shop Raw Image File.
+// "x3f" // Sigma Digital Camera Raw Image Format for devices based on Foveon X3 direct image sensor.
+ return raw_extensions;
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-dcraw";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ LibRaw RawProcessor;
+ BOOL bSuccess = TRUE;
+
+ // wrap the input datastream
+ LibRaw_freeimage_datastream datastream(io, handle);
+
+ // open the datastream
+ if(RawProcessor.open_datastream(&datastream) != LIBRAW_SUCCESS) {
+ bSuccess = FALSE; // LibRaw : failed to open input stream (unknown format)
+ }
+
+ // clean-up internal memory allocations
+ RawProcessor.recycle();
+
+ return bSuccess;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ FIBITMAP *dib = NULL;
+ LibRaw RawProcessor;
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+ // wrap the input datastream
+ LibRaw_freeimage_datastream datastream(io, handle);
+
+ // set decoding parameters
+ // the following parameters affect data reading
+ // --------------------------------------------
+
+ // (-s [0..N-1]) Select one raw image from input file
+ RawProcessor.imgdata.params.shot_select = 0;
+ // (-w) Use camera white balance, if possible (otherwise, fallback to auto_wb)
+ RawProcessor.imgdata.params.use_camera_wb = 1;
+ // (-h) outputs the image in 50% size
+ RawProcessor.imgdata.params.half_size = ((flags & RAW_HALFSIZE) == RAW_HALFSIZE) ? 1 : 0;
+
+ // open the datastream
+ if(RawProcessor.open_datastream(&datastream) != LIBRAW_SUCCESS) {
+ throw "LibRaw : failed to open input stream (unknown format)";
+ }
+
+ if(header_only) {
+ // header only mode
+ dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, RawProcessor.imgdata.sizes.width, RawProcessor.imgdata.sizes.height);
+ }
+ else if ((flags & RAW_PREVIEW) == RAW_PREVIEW) {
+ // try to get the embedded JPEG
+ dib = libraw_LoadEmbeddedPreview(RawProcessor, 0);
+ if (!dib) {
+ // no JPEG preview: try to load as 8-bit/sample (i.e. RGB 24-bit)
+ dib = libraw_LoadRawData(RawProcessor, 8);
+ }
+ }
+ else if ((flags & RAW_DISPLAY) == RAW_DISPLAY) {
+ // load raw data as 8-bit/sample (i.e. RGB 24-bit)
+ dib = libraw_LoadRawData(RawProcessor, 8);
+ }
+ else {
+ // default: load raw data as linear 16-bit/sample (i.e. RGB 48-bit)
+ dib = libraw_LoadRawData(RawProcessor, 16);
+ }
+
+ // save ICC profile if present
+ if(dib && (NULL != RawProcessor.imgdata.color.profile)) {
+ FreeImage_CreateICCProfile(dib, RawProcessor.imgdata.color.profile, RawProcessor.imgdata.color.profile_length);
+ }
+
+ // try to get JPEG embedded Exif metadata
+ if(dib && !((flags & RAW_PREVIEW) == RAW_PREVIEW)) {
+ FIBITMAP *metadata_dib = libraw_LoadEmbeddedPreview(RawProcessor, FIF_LOAD_NOPIXELS);
+ if(metadata_dib) {
+ FreeImage_CloneMetadata(dib, metadata_dib);
+ FreeImage_Unload(metadata_dib);
+ }
+ }
+
+ // clean-up internal memory allocations
+ RawProcessor.recycle();
+
+ return dib;
+
+ } catch(const char *text) {
+ if(dib) {
+ FreeImage_Unload(dib);
+ }
+ RawProcessor.recycle();
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitRAW(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginSGI.cpp b/plugins/AdvaImg/src/FreeImage/PluginSGI.cpp
new file mode 100644
index 0000000000..3ac189e0ef
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginSGI.cpp
@@ -0,0 +1,425 @@
+// ==========================================================
+// SGI Loader
+//
+// Design and implementation by
+// - Sherman Wilcox
+// - Noam Gat
+//
+// References :
+// ------------
+// - The SGI Image File Format, Version 1.0
+// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html
+// - SGI RGB Image Format
+// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/
+//
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagSGIHeader {
+ /** IRIS image file magic number. This should be decimal 474. */
+ WORD magic;
+ /** Storage format: 0 for uncompressed, 1 for RLE compression. */
+ BYTE storage;
+ /** Number of bytes per pixel channel. Legally 1 or 2. */
+ BYTE bpc;
+ /**
+ Number of dimensions. Legally 1, 2, or 3.
+ 1 means a single row, XSIZE long
+ 2 means a single 2D image
+ 3 means multiple 2D images
+ */
+ WORD dimension;
+ /** X size in pixels */
+ WORD xsize;
+ /** Y size in pixels */
+ WORD ysize;
+ /**
+ Number of channels.
+ 1 indicates greyscale
+ 3 indicates RGB
+ 4 indicates RGB and Alpha
+ */
+ WORD zsize;
+ /** Minimum pixel value. This is the lowest pixel value in the image.*/
+ LONG pixmin;
+ /** Maximum pixel value. This is the highest pixel value in the image.*/
+ LONG pixmax;
+ /** Ignored. Normally set to 0. */
+ char dummy[4];
+ /** Image name. Must be null terminated, therefore at most 79 bytes. */
+ char imagename[80];
+ /**
+ Colormap ID.
+ 0 - normal mode
+ 1 - dithered, 3 mits for red and green, 2 for blue, obsolete
+ 2 - index colour, obsolete
+ 3 - not an image but a colourmap
+ */
+ LONG colormap;
+ /** Ignored. Should be set to 0, makes the header 512 bytes. */
+ char reserved[404];
+} SGIHeader;
+
+typedef struct tagRLEStatus {
+ int cnt;
+ int val;
+} RLEStatus;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+static const char *SGI_LESS_THAN_HEADER_LENGTH = "Incorrect header size";
+static const char *SGI_16_BIT_COMPONENTS_NOT_SUPPORTED = "No 16 bit support";
+static const char *SGI_COLORMAPS_NOT_SUPPORTED = "No colormap support";
+static const char *SGI_EOF_IN_RLE_INDEX = "EOF in run length encoding";
+static const char *SGI_EOF_IN_IMAGE_DATA = "EOF in image data";
+static const char *SGI_INVALID_CHANNEL_COUNT = "Invalid channel count";
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+#ifndef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(SGIHeader *header) {
+ SwapShort(&header->magic);
+ SwapShort(&header->dimension);
+ SwapShort(&header->xsize);
+ SwapShort(&header->ysize);
+ SwapShort(&header->zsize);
+ SwapLong((DWORD*)&header->pixmin);
+ SwapLong((DWORD*)&header->pixmax);
+ SwapLong((DWORD*)&header->colormap);
+}
+#endif
+
+static int
+get_rlechar(FreeImageIO *io, fi_handle handle, RLEStatus *pstatus) {
+ if (!pstatus->cnt) {
+ int cnt = 0;
+ while (0 == cnt) {
+ BYTE packed = 0;
+ if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
+ return EOF;
+ }
+ cnt = packed;
+ }
+ if (cnt == EOF) {
+ return EOF;
+ }
+ pstatus->cnt = cnt & 0x7F;
+ if (cnt & 0x80) {
+ pstatus->val = -1;
+ } else {
+ BYTE packed = 0;
+ if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
+ return EOF;
+ }
+ pstatus->val = packed;
+ }
+ }
+ pstatus->cnt--;
+ if (pstatus->val == -1) {
+ BYTE packed = 0;
+ if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
+ return EOF;
+ }
+ return packed;
+ }
+ else {
+ return pstatus->val;
+ }
+}
+
+static const char * DLL_CALLCONV
+Format() {
+ return "SGI";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "SGI Image Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "sgi";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-sgi";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE sgi_signature[2] = { 0x01, 0xDA };
+ BYTE signature[2] = { 0, 0 };
+
+ io->read_proc(signature, 1, sizeof(sgi_signature), handle);
+
+ return (memcmp(sgi_signature, signature, sizeof(sgi_signature)) == 0);
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ int width = 0, height = 0, zsize = 0;
+ int i, dim;
+ int bitcount;
+ SGIHeader sgiHeader;
+ RLEStatus my_rle_status;
+ FIBITMAP *dib = NULL;
+ LONG *pRowIndex = NULL;
+
+ try {
+ // read the header
+ memset(&sgiHeader, 0, sizeof(SGIHeader));
+ if(io->read_proc(&sgiHeader, 1, sizeof(SGIHeader), handle) < sizeof(SGIHeader)) {
+ throw SGI_LESS_THAN_HEADER_LENGTH;
+ }
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapHeader(&sgiHeader);
+#endif
+ if(sgiHeader.magic != 474) {
+ throw FI_MSG_ERROR_MAGIC_NUMBER;
+ }
+
+ BOOL bIsRLE = (sgiHeader.storage == 1) ? TRUE : FALSE;
+
+ // check for unsupported image types
+ if (sgiHeader.bpc != 1) {
+ // Expected one byte per color component
+ throw SGI_16_BIT_COMPONENTS_NOT_SUPPORTED;
+ }
+ if (sgiHeader.colormap != 0) {
+ // Indexed or dithered images not supported
+ throw SGI_COLORMAPS_NOT_SUPPORTED;
+ }
+
+ // get the width & height
+ dim = sgiHeader.dimension;
+ width = sgiHeader.xsize;
+ if (dim < 3) {
+ zsize = 1;
+ } else {
+ zsize = sgiHeader.zsize;
+ }
+
+ if (dim < 2) {
+ height = 1;
+ } else {
+ height = sgiHeader.ysize;
+ }
+
+ if(bIsRLE) {
+ // read the Offset Tables
+ int index_len = height * zsize;
+ pRowIndex = (LONG*)malloc(index_len * sizeof(LONG));
+ if (!pRowIndex) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ if ((unsigned)index_len != io->read_proc(pRowIndex, sizeof(LONG), index_len, handle)) {
+ throw SGI_EOF_IN_RLE_INDEX;
+ }
+
+#ifndef FREEIMAGE_BIGENDIAN
+ // Fix byte order in index
+ for (i = 0; i < index_len; i++) {
+ SwapLong((DWORD*)&pRowIndex[i]);
+ }
+#endif
+ // Discard row size index
+ for (i = 0; i < (int)(index_len * sizeof(LONG)); i++) {
+ BYTE packed = 0;
+ if ( io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1 ) {
+ throw SGI_EOF_IN_RLE_INDEX;
+ }
+ }
+ }
+
+ switch(zsize) {
+ case 1:
+ bitcount = 8;
+ break;
+ case 2:
+ //Grayscale+Alpha. Need to fake RGBA
+ bitcount = 32;
+ break;
+ case 3:
+ bitcount = 24;
+ break;
+ case 4:
+ bitcount = 32;
+ break;
+ default:
+ throw SGI_INVALID_CHANNEL_COUNT;
+ }
+
+ dib = FreeImage_Allocate(width, height, bitcount);
+ if (!dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ if (bitcount == 8) {
+ // 8-bit SGI files are grayscale images, so we'll generate
+ // a grayscale palette.
+ RGBQUAD *pclrs = FreeImage_GetPalette(dib);
+ for (i = 0; i < 256; i++) {
+ pclrs[i].rgbRed = (BYTE)i;
+ pclrs[i].rgbGreen = (BYTE)i;
+ pclrs[i].rgbBlue = (BYTE)i;
+ pclrs[i].rgbReserved = 0;
+ }
+ }
+
+ // decode the image
+
+ memset(&my_rle_status, 0, sizeof(RLEStatus));
+
+ int ns = FreeImage_GetPitch(dib);
+ BYTE *pStartRow = FreeImage_GetScanLine(dib, 0);
+ int offset_table[] = { 2, 1, 0, 3 };
+ int numChannels = zsize;
+ if (zsize < 3) {
+ offset_table[0] = 0;
+ }
+ if (zsize == 2)
+ {
+ //This is how faked grayscale+alpha works.
+ //First channel goes into first
+ //second channel goes into alpha (4th channel)
+ //Two channels are left empty and will be copied later
+ offset_table[1] = 3;
+ numChannels = 4;
+ }
+
+ LONG *pri = pRowIndex;
+ for (i = 0; i < zsize; i++) {
+ BYTE *pRow = pStartRow + offset_table[i];
+ for (int j = 0; j < height; j++, pRow += ns, pri++) {
+ BYTE *p = pRow;
+ if (bIsRLE) {
+ my_rle_status.cnt = 0;
+ io->seek_proc(handle, *pri, SEEK_SET);
+ }
+ for (int k = 0; k < width; k++, p += numChannels) {
+ int ch;
+ BYTE packed = 0;
+ if (bIsRLE) {
+ ch = get_rlechar(io, handle, &my_rle_status);
+ packed = (BYTE)ch;
+ }
+ else {
+ ch = io->read_proc(&packed, sizeof(BYTE), 1, handle);
+ }
+ if (ch == EOF) {
+ throw SGI_EOF_IN_IMAGE_DATA;
+ }
+ *p = packed;
+ }
+ }
+ }
+
+ if (zsize == 2)
+ {
+ BYTE *pRow = pStartRow;
+ //If faking RGBA from grayscale + alpha, copy first channel to second and third
+ for (int i=0; i<height; i++, pRow += ns)
+ {
+ BYTE *pPixel = pRow;
+ for (int j=0; j<width; j++)
+ {
+ pPixel[2] = pPixel[1] = pPixel[0];
+ pPixel += 4;
+ }
+ }
+ }
+ if(pRowIndex)
+ free(pRowIndex);
+
+ return dib;
+
+ } catch(const char *text) {
+ if(pRowIndex) free(pRowIndex);
+ if(dib) FreeImage_Unload(dib);
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return NULL;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitSGI(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp b/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp
new file mode 100644
index 0000000000..4e3f1a720a
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp
@@ -0,0 +1,1565 @@
+// ==========================================================
+// TARGA Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Jani Kajala (janik@remedy.fi)
+// - Martin Weber (martweb@gmx.net)
+// - Machiel ten Brinke (brinkem@uni-one.nl)
+// - Peter Lemmens (peter.lemmens@planetinternet.be)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Constants + headers
+// ----------------------------------------------------------
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagTGAHEADER {
+ BYTE id_length; // ID length
+ BYTE color_map_type; // color map type
+ BYTE image_type; // image type
+
+ WORD cm_first_entry; // first entry index
+ WORD cm_length; // color map length
+ BYTE cm_size; // color map entry size, in bits
+
+ WORD is_xorigin; // X-origin of image
+ WORD is_yorigin; // Y-origin of image
+ WORD is_width; // image width
+ WORD is_height; // image height
+ BYTE is_pixel_depth; // pixel depth
+ BYTE is_image_descriptor; // image descriptor
+} TGAHEADER;
+
+typedef struct tagTGAEXTENSIONAREA {
+ WORD extension_size; // Size in bytes of the extension area, always 495
+ char author_name[41]; // Name of the author. If not used, bytes should be set to NULL (\0) or spaces
+ char author_comments[324]; // A comment, organized as four lines, each consisting of 80 characters plus a NULL
+ WORD datetime_stamp[6]; // Date and time at which the image was created
+ char job_name[41]; // Job ID
+ WORD job_time[3]; // Hours, minutes and seconds spent creating the file (for billing, etc.)
+ char software_id[41]; // The application that created the file
+ BYTE software_version[3];
+ DWORD key_color;
+ WORD pixel_aspect_ratio[2];
+ WORD gamma_value[2];
+ DWORD color_correction_offset; // Number of bytes from the beginning of the file to the color correction table if present
+ DWORD postage_stamp_offset; // Number of bytes from the beginning of the file to the postage stamp image if present
+ DWORD scan_line_offset; // Number of bytes from the beginning of the file to the scan lines table if present
+ BYTE attributes_type; // Specifies the alpha channel
+} TGAEXTENSIONAREA;
+
+typedef struct tagTGAFOOTER {
+ DWORD extension_offset; // extension area offset : offset in bytes from the beginning of the file
+ DWORD developer_offset; // developer directory offset : offset in bytes from the beginning of the file
+ char signature[18]; // signature string : contains "TRUEVISION-XFILE.\0"
+} TGAFOOTER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+static const char *FI_MSG_ERROR_CORRUPTED = "Image data corrupted";
+
+// ----------------------------------------------------------
+// Image type
+//
+#define TGA_NULL 0 // no image data included
+#define TGA_CMAP 1 // uncompressed, color-mapped image
+#define TGA_RGB 2 // uncompressed, true-color image
+#define TGA_MONO 3 // uncompressed, black-and-white image
+#define TGA_RLECMAP 9 // run-length encoded, color-mapped image
+#define TGA_RLERGB 10 // run-length encoded, true-color image
+#define TGA_RLEMONO 11 // run-length encoded, black-and-white image
+#define TGA_CMPCMAP 32 // compressed (Huffman/Delta/RLE) color-mapped image (e.g., VDA/D) - Obsolete
+#define TGA_CMPCMAP4 33 // compressed (Huffman/Delta/RLE) color-mapped four pass image (e.g., VDA/D) - Obsolete
+
+// ==========================================================
+// Thumbnail functions
+// ==========================================================
+
+class TargaThumbnail
+{
+public:
+ TargaThumbnail() : _w(0), _h(0), _depth(0), _data(NULL) {
+ }
+ ~TargaThumbnail() {
+ if(_data) {
+ free(_data);
+ }
+ }
+
+ BOOL isNull() const {
+ return (_data == NULL);
+ }
+
+ BOOL read(FreeImageIO *io, fi_handle handle, size_t size) {
+ io->read_proc(&_w, 1, 1, handle);
+ io->read_proc(&_h, 1, 1, handle);
+
+ const size_t sizeofData = size - 2;
+ _data = (BYTE*)malloc(sizeofData);
+ if(_data) {
+ return (io->read_proc(_data, 1, (unsigned)sizeofData, handle) == sizeofData);
+ }
+ return FALSE;
+ }
+
+ void setDepth(BYTE dp) {
+ _depth = dp;
+ }
+
+ FIBITMAP* toFIBITMAP();
+
+private:
+ BYTE _w;
+ BYTE _h;
+ BYTE _depth;
+ BYTE* _data;
+};
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+swapShortPixels(FIBITMAP* dib) {
+ if(FreeImage_GetImageType(dib) != FIT_BITMAP) {
+ return;
+ }
+
+ const unsigned Bpp = FreeImage_GetBPP(dib)/8;
+ if(Bpp != 2) {
+ return;
+ }
+
+ BYTE* bits = FreeImage_GetBits(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+
+ BYTE* line = bits;
+ for(unsigned y = 0; y < height; y++, line += pitch) {
+ for(BYTE* pixel = line; pixel < line + pitch ; pixel += Bpp) {
+ SwapShort((WORD*)pixel);
+ }
+ }
+}
+#endif // FREEIMAGE_BIGENDIAN
+
+FIBITMAP* TargaThumbnail::toFIBITMAP() {
+ if(isNull() || _depth == 0) {
+ return NULL;
+ }
+
+ const unsigned line_size = _depth * _w / 8;
+ FIBITMAP* dib = FreeImage_Allocate(_w, _h, _depth);
+ if (!dib) {
+ return NULL;
+ }
+
+ const BYTE* line = _data;
+ const BYTE height = _h;
+ for (BYTE h = 0; h < height; ++h, line += line_size) {
+ BYTE* dst_line = FreeImage_GetScanLine(dib, height - 1 - h);
+ memcpy(dst_line, line, line_size);
+ }
+
+#ifdef FREEIMAGE_BIGENDIAN
+ swapShortPixels(dib);
+#endif
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ SwapRedBlue32(dib);
+#endif
+
+ return dib;
+}
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+/** This class is used when loading RLE compressed images, it implements io cache of fixed size.
+ In general RLE compressed images *should* be compressed line by line with line sizes stored in Scan Line Table section.
+ In reality, however there are images not obeying the specification, compressing image data continuously across lines,
+ making it impossible to load the file cached at every line.
+*/
+class IOCache
+{
+public:
+ IOCache(FreeImageIO *io, fi_handle handle, size_t size) :
+ _ptr(NULL), _begin(NULL), _end(NULL), _size(size), _io(io), _handle(handle) {
+ _begin = (BYTE*)malloc(size);
+ if (_begin) {
+ _end = _begin + _size;
+ _ptr = _end; // will force refill on first access
+ }
+ }
+
+ ~IOCache() {
+ if (_begin != NULL) {
+ free(_begin);
+ }
+ }
+
+ BOOL isNull() { return _begin == NULL;}
+
+ inline
+ BYTE getByte() {
+ if (_ptr >= _end) {
+ // need refill
+ _ptr = _begin;
+ _io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle); //### EOF - no problem?
+ }
+
+ BYTE result = *_ptr;
+
+ _ptr++;
+
+ return result;
+ }
+
+ inline
+ BYTE* getBytes(size_t count /*must be < _size!*/) {
+ if (_ptr + count >= _end) {
+
+ // need refill
+
+ // 'count' bytes might span two cache bounds,
+ // SEEK back to add the remains of the current cache again into the new one
+
+ long read = long(_ptr - _begin);
+ long remaining = long(_size - read);
+
+ _io->seek_proc(_handle, -remaining, SEEK_CUR);
+
+ _ptr = _begin;
+ _io->read_proc(_ptr, sizeof(BYTE), (unsigned)_size, _handle); //### EOF - no problem?
+ }
+
+ BYTE *result = _ptr;
+
+ _ptr += count;
+
+ return result;
+ }
+
+private:
+ IOCache& operator=(const IOCache& src); // deleted
+ IOCache(const IOCache& other); // deleted
+
+private:
+ BYTE *_ptr;
+ BYTE *_begin;
+ BYTE *_end;
+ const size_t _size;
+ const FreeImageIO *_io;
+ const fi_handle _handle;
+};
+
+#ifdef FREEIMAGE_BIGENDIAN
+static void
+SwapHeader(TGAHEADER *header) {
+ SwapShort(&header->cm_first_entry);
+ SwapShort(&header->cm_length);
+ SwapShort(&header->is_xorigin);
+ SwapShort(&header->is_yorigin);
+ SwapShort(&header->is_width);
+ SwapShort(&header->is_height);
+}
+
+static void
+SwapExtensionArea(TGAEXTENSIONAREA *ex) {
+ SwapShort(&ex->extension_size);
+ SwapShort(&ex->datetime_stamp[0]);
+ SwapShort(&ex->datetime_stamp[1]);
+ SwapShort(&ex->datetime_stamp[2]);
+ SwapShort(&ex->datetime_stamp[3]);
+ SwapShort(&ex->datetime_stamp[4]);
+ SwapShort(&ex->datetime_stamp[5]);
+ SwapShort(&ex->job_time[0]);
+ SwapShort(&ex->job_time[1]);
+ SwapShort(&ex->job_time[2]);
+ SwapLong (&ex->key_color);
+ SwapShort(&ex->pixel_aspect_ratio[0]);
+ SwapShort(&ex->pixel_aspect_ratio[1]);
+ SwapShort(&ex->gamma_value[0]);
+ SwapShort(&ex->gamma_value[1]);
+ SwapLong (&ex->color_correction_offset);
+ SwapLong (&ex->postage_stamp_offset);
+ SwapLong (&ex->scan_line_offset);
+}
+
+static void
+SwapFooter(TGAFOOTER *footer) {
+ SwapLong(&footer->extension_offset);
+ SwapLong(&footer->developer_offset);
+}
+
+#endif // FREEIMAGE_BIGENDIAN
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "TARGA";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Truevision Targa";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "tga,targa";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-tga";
+}
+
+static BOOL
+isTARGA20(FreeImageIO *io, fi_handle handle) {
+ const unsigned sizeofSig = 18;
+ // tga_signature = "TRUEVISION-XFILE." (TGA 2.0 only)
+ BYTE tga_signature[sizeofSig] = { 84, 82, 85, 69, 86, 73, 83, 73, 79, 78, 45, 88, 70, 73, 76, 69, 46, 0 };
+ // get the start offset
+ const long start_offset = io->tell_proc(handle);
+ // get the end-of-file
+ io->seek_proc(handle, 0, SEEK_END);
+ const long eof = io->tell_proc(handle);
+ // read the signature
+
+ io->seek_proc(handle, start_offset + eof - sizeofSig, SEEK_SET);
+ BYTE signature[sizeofSig];
+ io->read_proc(&signature, 1, sizeofSig, handle);
+
+ // rewind
+ io->seek_proc(handle, start_offset, SEEK_SET);
+
+ return (memcmp(tga_signature, signature, sizeofSig) == 0);
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ if(isTARGA20(io, handle)) {
+ return TRUE;
+ }
+
+ // not a 2.0 image, try testing if it's a valid TGA anyway (not robust)
+ BOOL bResult = FALSE;
+
+ const long start_offset = io->tell_proc(handle);
+
+ TGAHEADER header;
+ io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle);
+
+ // rewind
+ io->seek_proc(handle, start_offset, SEEK_SET);
+
+ switch(header.image_type) {
+ case TGA_CMAP :
+ case TGA_RGB:
+ case TGA_MONO :
+ case TGA_RLECMAP:
+ case TGA_RLERGB:
+ case TGA_RLEMONO:
+ switch(header.is_pixel_depth) {
+ case 8 :
+ case 16:
+ case 24:
+ case 32:
+ bResult = TRUE;
+ break;
+ default:
+ bResult = FALSE;
+ break;
+ }
+ break;
+ default:
+ bResult = FALSE;
+ break;
+ }
+
+ return bResult;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 8) ||
+ (depth == 16) ||
+ (depth == 24) ||
+ (depth == 32)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+/**
+Used for all 32 and 24 bit loading of uncompressed images
+*/
+static void
+loadTrueColor(FIBITMAP* dib, int width, int height, int file_pixel_size, FreeImageIO* io, fi_handle handle, BOOL as24bit) {
+ const int pixel_size = as24bit ? 3 : file_pixel_size;
+
+ // input line cache
+ BYTE* file_line = (BYTE*)malloc( width * file_pixel_size);
+
+ if (!file_line) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (int y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ io->read_proc(file_line, file_pixel_size, width, handle);
+ BYTE *bgra = file_line;
+
+ for (int x = 0; x < width; x++) {
+
+ bits[FI_RGBA_BLUE] = bgra[0];
+ bits[FI_RGBA_GREEN] = bgra[1];
+ bits[FI_RGBA_RED] = bgra[2];
+
+ if (!as24bit) {
+ bits[FI_RGBA_ALPHA] = bgra[3];
+ }
+
+ bgra += file_pixel_size;
+
+ bits += pixel_size;
+ }
+ }
+
+ free(file_line);
+}
+
+/**
+For the generic RLE loader we need to abstract away the pixel format.
+We use a specific overload based on bits-per-pixel for each type of pixel
+*/
+
+template <int nBITS>
+inline static void
+_assignPixel(BYTE* bits, BYTE* val, BOOL as24bit = FALSE) {
+ // static assert should go here
+ assert(FALSE);
+}
+
+template <>
+inline void
+_assignPixel<8>(BYTE* bits, BYTE* val, BOOL as24bit) {
+ *bits = *val;
+}
+
+template <>
+inline void
+_assignPixel<16>(BYTE* bits, BYTE* val, BOOL as24bit) {
+ WORD value(*reinterpret_cast<WORD*>(val));
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&value);
+#endif
+
+ if (as24bit) {
+ bits[FI_RGBA_BLUE] = (BYTE)((((value & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+ bits[FI_RGBA_GREEN] = (BYTE)((((value & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+ bits[FI_RGBA_RED] = (BYTE)((((value & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+
+ } else {
+ *reinterpret_cast<WORD *>(bits) = 0x7FFF & value;
+ }
+}
+
+template <>
+inline void
+_assignPixel<24>(BYTE* bits, BYTE* val, BOOL as24bit) {
+ bits[FI_RGBA_BLUE] = val[0];
+ bits[FI_RGBA_GREEN] = val[1];
+ bits[FI_RGBA_RED] = val[2];
+}
+
+template <>
+inline void
+_assignPixel<32>(BYTE* bits, BYTE* val, BOOL as24bit) {
+ if (as24bit) {
+ _assignPixel<24>(bits, val, TRUE);
+
+ } else {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ *(reinterpret_cast<unsigned*>(bits)) = *(reinterpret_cast<unsigned*> (val));
+#else // NOTE This is faster then doing reinterpret_cast to int + INPLACESWAP !
+ bits[FI_RGBA_BLUE] = val[0];
+ bits[FI_RGBA_GREEN] = val[1];
+ bits[FI_RGBA_RED] = val[2];
+ bits[FI_RGBA_ALPHA] = val[3];
+#endif
+ }
+}
+
+/**
+Generic RLE loader
+*/
+template<int bPP>
+static void
+loadRLE(FIBITMAP* dib, int width, int height, FreeImageIO* io, fi_handle handle, long eof, BOOL as24bit) {
+ const int file_pixel_size = bPP/8;
+ const int pixel_size = as24bit ? 3 : file_pixel_size;
+
+ const BYTE bpp = as24bit ? 24 : bPP;
+ const int line_size = CalculateLine(width, bpp);
+
+ // Note, many of the params can be computed inside the function.
+ // However, because this is a template function, it will lead to redundant code duplication.
+
+ BYTE rle;
+ BYTE *line_bits;
+
+ // this is used to guard against writing beyond the end of the image (on corrupted rle block)
+ const BYTE* dib_end = FreeImage_GetScanLine(dib, height);//< one-past-end row
+
+ // Compute the rough size of a line...
+ long pixels_offset = io->tell_proc(handle);
+ long sz = ((eof - pixels_offset) / height);
+
+ // ...and allocate cache of this size (yields good results)
+ IOCache cache(io, handle, sz);
+ if(cache.isNull()) {
+ FreeImage_Unload(dib);
+ dib = NULL;
+ return;
+ }
+
+ int x = 0, y = 0;
+
+ line_bits = FreeImage_GetScanLine(dib, y);
+
+ while (y < height) {
+
+ rle = cache.getByte();
+
+ BOOL has_rle = rle & 0x80;
+ rle &= ~0x80; // remove type-bit
+
+ BYTE packet_count = rle + 1;
+
+ //packet_count might be corrupt, test if we are not about to write beyond the last image bit
+
+ if ((line_bits+x) + packet_count*pixel_size > dib_end) {
+ FreeImage_OutputMessageProc(s_format_id, FI_MSG_ERROR_CORRUPTED);
+ // return what is left from the bitmap
+ return;
+ }
+
+ if (has_rle) {
+
+ // read a pixel value from file...
+ BYTE *val = cache.getBytes(file_pixel_size);
+
+ //...and fill packet_count pixels with it
+
+ for (int ix = 0; ix < packet_count; ix++) {
+ _assignPixel<bPP>((line_bits+x), val, as24bit);
+ x += pixel_size;
+
+ if (x >= line_size) {
+ x = 0;
+ y++;
+ line_bits = FreeImage_GetScanLine(dib, y);
+ }
+ }
+
+ } else {
+ // no rle commpresion
+
+ // copy packet_count pixels from file to dib
+ for (int ix = 0; ix < packet_count; ix++) {
+ BYTE *val = cache.getBytes(file_pixel_size);
+ _assignPixel<bPP>((line_bits+x), val, as24bit);
+ x += pixel_size;
+
+ if (x >= line_size) {
+ x = 0;
+ y++;
+ line_bits = FreeImage_GetScanLine(dib, y);
+ }
+ } //< packet_count
+ } //< has_rle
+
+ } //< while height
+
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ FIBITMAP *dib = NULL;
+
+ if (!handle) {
+ return NULL;
+ }
+
+ try {
+
+ const BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // remember the start offset
+ long start_offset = io->tell_proc(handle);
+
+ // remember end-of-file (used for RLE cache)
+ io->seek_proc(handle, 0, SEEK_END);
+ long eof = io->tell_proc(handle);
+ io->seek_proc(handle, start_offset, SEEK_SET);
+
+ // read and process the bitmap's footer
+
+ TargaThumbnail thumbnail;
+ if(isTARGA20(io, handle)) {
+ TGAFOOTER footer;
+ const long footer_offset = start_offset + eof - sizeof(footer);
+
+ io->seek_proc(handle, footer_offset, SEEK_SET);
+ io->read_proc(&footer, sizeof(tagTGAFOOTER), 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapFooter(&footer);
+#endif
+ BOOL hasExtensionArea = footer.extension_offset > 0;
+ if(hasExtensionArea) {
+ TGAEXTENSIONAREA extensionarea;
+ io->seek_proc(handle, footer.extension_offset, SEEK_SET);
+ io->read_proc(&extensionarea, sizeof(extensionarea), 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapExtensionArea(&extensionarea);
+#endif
+
+ DWORD postage_stamp_offset = extensionarea.postage_stamp_offset;
+ BOOL hasThumbnail = (postage_stamp_offset > 0) && (postage_stamp_offset < (DWORD)footer_offset);
+ if(hasThumbnail) {
+ io->seek_proc(handle, postage_stamp_offset, SEEK_SET);
+ thumbnail.read(io, handle, footer_offset - postage_stamp_offset);
+ }
+ }
+ }
+
+ // read and process the bitmap's header
+
+ TGAHEADER header;
+
+ io->seek_proc(handle, start_offset, SEEK_SET);
+ io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapHeader(&header);
+#endif
+
+ thumbnail.setDepth(header.is_pixel_depth);
+
+ const int line = CalculateLine(header.is_width, header.is_pixel_depth);
+ const int pixel_bits = header.is_pixel_depth;
+ const int pixel_size = pixel_bits/8;
+
+ int fliphoriz = (header.is_image_descriptor & 0x10) ? 1 : 0;
+ int flipvert = (header.is_image_descriptor & 0x20) ? 1 : 0;
+
+ // skip comment
+ io->seek_proc(handle, header.id_length, SEEK_CUR);
+
+ switch (header.is_pixel_depth) {
+ case 8 : {
+ dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, 8);
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // read the palette (even if header only)
+
+ RGBQUAD *palette = FreeImage_GetPalette(dib);
+
+ if (header.color_map_type > 0) {
+ unsigned count, csize;
+
+ // calculate the color map size
+ csize = header.cm_length * header.cm_size / 8;
+ BYTE *cmap = (BYTE*)malloc(csize * sizeof(BYTE));
+
+ io->read_proc(cmap, sizeof(BYTE), csize, handle);
+
+ // build the palette
+
+ switch (header.cm_size) {
+ case 16: {
+ WORD *rgb555 = (WORD*)&cmap[0];
+
+ for (count = header.cm_first_entry; count < header.cm_length; count++) {
+ palette[count].rgbRed = (BYTE)((((*rgb555 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
+ palette[count].rgbGreen = (BYTE)((((*rgb555 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
+ palette[count].rgbBlue = (BYTE)((((*rgb555 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
+ rgb555++;
+ }
+ }
+ break;
+
+ case 24: {
+ FILE_BGR *bgr = (FILE_BGR*)&cmap[0];
+
+ for (count = header.cm_first_entry; count < header.cm_length; count++) {
+ palette[count].rgbBlue = bgr->b;
+ palette[count].rgbGreen = bgr->g;
+ palette[count].rgbRed = bgr->r;
+ bgr++;
+ }
+ }
+ break;
+
+ case 32: {
+ BYTE trns[256];
+
+ // clear the transparency table
+ memset(trns, 0xFF, 256);
+
+ FILE_BGRA *bgra = (FILE_BGRA*)&cmap[0];
+
+ for (count = header.cm_first_entry; count < header.cm_length; count++) {
+ palette[count].rgbBlue = bgra->b;
+ palette[count].rgbGreen = bgra->g;
+ palette[count].rgbRed = bgra->r;
+ // alpha
+ trns[count] = bgra->a;
+ bgra++;
+ }
+
+ // set the tranparency table
+ FreeImage_SetTransparencyTable(dib, trns, 256);
+ }
+ break;
+
+ } // switch(header.cm_size)
+
+ free(cmap);
+ }
+
+ // handle thumbnail
+
+ FIBITMAP* th = thumbnail.toFIBITMAP();
+ if(th) {
+ RGBQUAD* pal = FreeImage_GetPalette(dib);
+ RGBQUAD* dst_pal = FreeImage_GetPalette(th);
+ if(dst_pal && pal) {
+ for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) {
+ dst_pal[i] = pal[i];
+ }
+ }
+
+ FreeImage_SetTransparencyTable(th, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
+
+ FreeImage_SetThumbnail(dib, th);
+ FreeImage_Unload(th);
+ }
+
+ if(header_only) {
+ return dib;
+ }
+
+ // read in the bitmap bits
+
+ switch (header.image_type) {
+ case TGA_CMAP:
+ case TGA_MONO: {
+ BYTE *bits = NULL;
+
+ for (unsigned count = 0; count < header.is_height; count++) {
+ bits = FreeImage_GetScanLine(dib, count);
+ io->read_proc(bits, sizeof(BYTE), line, handle);
+ }
+ }
+ break;
+
+ case TGA_RLECMAP:
+ case TGA_RLEMONO: { //(8 bit)
+ loadRLE<8>(dib, header.is_width, header.is_height, io, handle, eof, FALSE);
+ }
+ break;
+
+ default :
+ FreeImage_Unload(dib);
+ return NULL;
+ }
+ }
+ break; // header.is_pixel_depth == 8
+
+ case 15 :
+
+ case 16 : {
+ int pixel_bits = 16;
+
+ // allocate the dib
+
+ if (TARGA_LOAD_RGB888 & flags) {
+ pixel_bits = 24;
+ dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK);
+ }
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // handle thumbnail
+
+ FIBITMAP* th = thumbnail.toFIBITMAP();
+ if(th) {
+ if(TARGA_LOAD_RGB888 & flags) {
+ FIBITMAP* t = FreeImage_ConvertTo24Bits(th);
+ FreeImage_Unload(th);
+ th = t;
+ }
+
+ FreeImage_SetThumbnail(dib, th);
+ FreeImage_Unload(th);
+ }
+
+ if(header_only) {
+ return dib;
+ }
+
+ int line = CalculateLine(header.is_width, pixel_bits);
+
+ const unsigned pixel_size = unsigned(pixel_bits) / 8;
+ const unsigned src_pixel_size = sizeof(WORD);
+
+ // note header.cm_size is a misleading name, it should be seen as header.cm_bits
+ // ignore current position in file and set filepointer explicitly from the beginning of the file
+
+ int garblen = 0;
+
+ if (header.color_map_type != 0) {
+ garblen = (int)((header.cm_size + 7) / 8) * header.cm_length; /* should byte align */
+
+ } else {
+ garblen = 0;
+ }
+
+ io->seek_proc(handle, start_offset, SEEK_SET);
+ io->seek_proc(handle, sizeof(tagTGAHEADER) + header.id_length + garblen, SEEK_SET);
+
+ // read in the bitmap bits
+
+ switch (header.image_type) {
+ case TGA_RGB: { //(16 bit)
+ // input line cache
+ BYTE *in_line = (BYTE*)malloc(header.is_width * sizeof(WORD));
+
+ if (!in_line)
+ throw FI_MSG_ERROR_MEMORY;
+
+ const int h = header.is_height;
+
+ for (int y = 0; y < h; y++) {
+
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ io->read_proc(in_line, src_pixel_size, header.is_width, handle);
+
+ BYTE *val = in_line;
+ for (int x = 0; x < line; x += pixel_size) {
+
+ _assignPixel<16>(bits+x, val, TARGA_LOAD_RGB888 & flags);
+
+ val += src_pixel_size;
+ }
+ }
+
+ free(in_line);
+ }
+ break;
+
+ case TGA_RLERGB: { //(16 bit)
+ loadRLE<16>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags);
+ }
+ break;
+
+ default :
+ FreeImage_Unload(dib);
+ return NULL;
+ }
+ }
+ break; // header.is_pixel_depth == 15 or 16
+
+ case 24 : {
+
+ dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ FIBITMAP* th = thumbnail.toFIBITMAP();
+ if(th) {
+ FreeImage_SetThumbnail(dib, th);
+ FreeImage_Unload(th);
+ }
+
+ if(header_only) {
+ return dib;
+ }
+
+ // read in the bitmap bits
+
+ switch (header.image_type) {
+ case TGA_RGB: { //(24 bit)
+ //uncompressed
+ loadTrueColor(dib, header.is_width, header.is_height, pixel_size,io, handle, TRUE);
+ }
+ break;
+
+ case TGA_RLERGB: { //(24 bit)
+ loadRLE<24>(dib, header.is_width, header.is_height, io, handle, eof, TRUE);
+ }
+ break;
+
+ default :
+ FreeImage_Unload(dib);
+ return NULL;
+ }
+ }
+ break; // header.is_pixel_depth == 24
+
+ case 32 : {
+ int pixel_bits = 32;
+
+ if (TARGA_LOAD_RGB888 & flags) {
+ pixel_bits = 24;
+ }
+
+ dib = FreeImage_AllocateHeader(header_only, header.is_width, header.is_height, pixel_bits, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // handle thumbnail
+
+ FIBITMAP* th = thumbnail.toFIBITMAP();
+ if(th) {
+ if(TARGA_LOAD_RGB888 & flags) {
+ FIBITMAP* t = FreeImage_ConvertTo24Bits(th);
+ FreeImage_Unload(th);
+ th = t;
+ }
+ FreeImage_SetThumbnail(dib, th);
+ FreeImage_Unload(th);
+ }
+
+ if(header_only) {
+ return dib;
+ }
+
+ // read in the bitmap bits
+
+ switch (header.image_type) {
+ case TGA_RGB: { //(32 bit)
+ // uncompressed
+ loadTrueColor(dib, header.is_width, header.is_height, 4 /*file_pixel_size*/, io, handle, TARGA_LOAD_RGB888 & flags);
+ }
+ break;
+
+ case TGA_RLERGB: { //(32 bit)
+ loadRLE<32>(dib, header.is_width, header.is_height, io, handle, eof, TARGA_LOAD_RGB888 & flags);
+ }
+ break;
+
+ default :
+ FreeImage_Unload(dib);
+ return NULL;
+ }
+ }
+ break; // header.is_pixel_depth == 32
+
+ } // switch(header.is_pixel_depth)
+
+ if (flipvert) {
+ FreeImage_FlipVertical(dib);
+ }
+
+ if (fliphoriz) {
+ FreeImage_FlipHorizontal(dib);
+ }
+
+ return dib;
+
+ } catch (const char *message) {
+ if (dib) {
+ FreeImage_Unload(dib);
+ }
+
+ FreeImage_OutputMessageProc(s_format_id, message);
+
+ return NULL;
+ }
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL
+hasValidThumbnail(FIBITMAP* dib) {
+ FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+
+ return thumbnail
+ && SupportsExportType(FreeImage_GetImageType(thumbnail))
+ && SupportsExportDepth(FreeImage_GetBPP(thumbnail))
+ // Requirements according to the specification:
+ && FreeImage_GetBPP(thumbnail) == FreeImage_GetBPP(dib)
+ && FreeImage_GetImageType(thumbnail) == FreeImage_GetImageType(dib)
+ && FreeImage_GetWidth(thumbnail) <= 255
+ && FreeImage_GetHeight(thumbnail) <= 255;
+}
+
+/**
+Writes the ready RLE packet to buffer
+*/
+static inline void
+flushPacket(BYTE*& dest, unsigned pixel_size, BYTE* packet_begin, BYTE*& packet, BYTE& packet_count, BOOL& has_rle) {
+ if (packet_count) {
+ const BYTE type_bit = has_rle ? 0x80 : 0x0;
+ const BYTE write_count = has_rle ? 1 : packet_count;
+
+ // build packet header: zero-based count + type bit
+ assert(packet_count >= 1);
+ BYTE rle = packet_count - 1;
+ rle |= type_bit;
+
+ // write packet header
+ *dest = rle;
+ ++dest;
+
+ // write packet data
+ memcpy(dest, packet_begin, write_count * pixel_size);
+ dest += write_count * pixel_size;
+
+ // reset state
+ packet_count = 0;
+ packet = packet_begin;
+ has_rle = FALSE;
+ }
+}
+
+
+static inline void
+writeToPacket(BYTE* packet, BYTE* pixel, unsigned pixel_size) {
+ // Take care of channel and byte order here, because packet will be flushed straight to the file
+ switch (pixel_size) {
+ case 1:
+ *packet = *pixel;
+ break;
+
+ case 2: {
+ WORD val(*(WORD*)pixel);
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&val);
+#endif
+ *(WORD*)packet = val;
+ }
+ break;
+
+ case 3: {
+ packet[0] = pixel[FI_RGBA_BLUE];
+ packet[1] = pixel[FI_RGBA_GREEN];
+ packet[2] = pixel[FI_RGBA_RED];
+ }
+ break;
+
+ case 4: {
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ *(reinterpret_cast<unsigned*>(packet)) = *(reinterpret_cast<unsigned*> (pixel));
+#else
+ packet[0] = pixel[FI_RGBA_BLUE];
+ packet[1] = pixel[FI_RGBA_GREEN];
+ packet[2] = pixel[FI_RGBA_RED];
+ packet[3] = pixel[FI_RGBA_ALPHA];
+#endif
+ }
+ break;
+
+ default:
+ assert(FALSE);
+ }
+}
+
+static inline BOOL
+isEqualPixel(BYTE* lhs, BYTE* rhs, unsigned pixel_size) {
+ switch (pixel_size) {
+ case 1:
+ return *lhs == *rhs;
+
+ case 2:
+ return *(WORD*)lhs == *(WORD*)rhs;
+
+ case 3:
+ return *(WORD*)lhs == *(WORD*)rhs && lhs[2] == rhs[2];
+
+ case 4:
+ return *(unsigned*)lhs == *(unsigned*)rhs;
+
+ default:
+ assert(FALSE);
+ return FALSE;
+ }
+}
+
+static void
+saveRLE(FIBITMAP* dib, FreeImageIO* io, fi_handle handle) {
+ // Image is compressed line by line, packets don't span multiple lines (TGA2.0 recommendation)
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ const unsigned pixel_size = FreeImage_GetBPP(dib)/8;
+ const unsigned line_size = FreeImage_GetLine(dib);
+
+ const BYTE max_packet_size = 128;
+ BYTE packet_count = 0;
+ BOOL has_rle = FALSE;
+
+ // packet (compressed or not) to be written to line
+
+ BYTE* const packet_begin = (BYTE*)malloc(max_packet_size * pixel_size);
+ BYTE* packet = packet_begin;
+
+ // line to be written to disk
+ // Note: we need some extra bytes for anti-commpressed lines. The worst case is:
+ // 8 bit images were every 3th pixel is different.
+ // Rle packet becomes two pixels, but nothing is compressed: two byte pixels are transformed into byte header and byte pixel value
+ // After every rle packet there is a non-rle packet of one pixel: an extra byte for the header will be added for it
+ // In the end we gain no bytes from compression, but also must insert a byte at every 3th pixel
+
+ // add extra space for anti-commpressed lines
+ size_t extra_space = (size_t)ceil(width / 3.0);
+ BYTE* const line_begin = (BYTE*)malloc(width * pixel_size + extra_space);
+ BYTE* line = line_begin;
+
+ BYTE *current = (BYTE*)malloc(pixel_size);
+ BYTE *next = (BYTE*)malloc(pixel_size);
+
+ for(unsigned y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+ // rewind line pointer
+ line = line_begin;
+
+ for(unsigned x = 0; x < line_size; x += pixel_size) {
+
+ AssignPixel(current, (bits + x), pixel_size);
+
+ // read next pixel from dib
+
+ if ( x + 1*pixel_size < line_size) {
+ AssignPixel(next, (bits + x + 1*pixel_size), pixel_size);
+
+ } else {
+ // last pixel in line
+
+ // include current pixel and flush
+ if (!has_rle) {
+
+ writeToPacket(packet, current, pixel_size);
+ packet += pixel_size;
+
+ }
+
+ assert(packet_count < max_packet_size);
+
+ ++packet_count;
+
+ flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+
+ // start anew on next line
+ break;
+ }
+
+ if(isEqualPixel(current, next, pixel_size)) {
+
+ // has rle
+
+ if (!has_rle) {
+ // flush non rle packet
+
+ flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+
+ // start a rle packet
+
+ has_rle = TRUE;
+
+ writeToPacket(packet, current, pixel_size);
+ packet += pixel_size;
+ }
+
+ // otherwise do nothing. We will just increase the count at the end
+
+ } else {
+
+ // no rle
+
+ if(has_rle) {
+ // flush rle packet
+
+ // include current pixel first
+ assert(packet_count < max_packet_size);
+ ++packet_count;
+
+ flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+
+ // start anew on the next pixel
+ continue;
+
+ } else {
+
+ writeToPacket(packet, current, pixel_size);
+ packet += pixel_size;
+ }
+
+ }
+
+ // increase counter on every pixel
+
+ ++packet_count;
+
+ if(packet_count == max_packet_size) {
+ flushPacket(line, pixel_size, packet_begin, packet, packet_count, has_rle);
+ }
+
+ }//for width
+
+ // write line to disk
+ io->write_proc(line_begin, 1, (unsigned)(line - line_begin), handle);
+
+ }//for height
+
+ free(line_begin);
+ free(packet_begin);
+ free(current);
+ free(next);
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if ((dib == NULL) || (handle == NULL)) {
+ return FALSE;
+ }
+
+ RGBQUAD *palette = FreeImage_GetPalette(dib);
+ const unsigned bpp = FreeImage_GetBPP(dib);
+
+ // write the file header
+
+ TGAHEADER header;
+
+ header.id_length = 0;
+ header.cm_first_entry = 0;
+ header.is_xorigin = 0;
+ header.is_yorigin = 0;
+ header.is_width = (WORD)FreeImage_GetWidth(dib);
+ header.is_height = (WORD)FreeImage_GetHeight(dib);
+ header.is_pixel_depth = (BYTE)bpp;
+ header.is_image_descriptor = 0;
+
+ if (palette) {
+ header.color_map_type = 1;
+ header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLECMAP : TGA_CMAP;
+ header.cm_length = (WORD)(1 << bpp);
+
+ if (FreeImage_IsTransparent(dib)) {
+ header.cm_size = 32;
+ } else {
+ header.cm_size = 24;
+ }
+
+ } else {
+ header.color_map_type = 0;
+ header.image_type = (TARGA_SAVE_RLE & flags) ? TGA_RLERGB : TGA_RGB;
+ header.cm_length = 0;
+ header.cm_size = 0;
+ }
+
+ // write the header
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapHeader(&header);
+#endif
+
+ io->write_proc(&header, sizeof(header), 1, handle);
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapHeader(&header);
+#endif
+
+ // write the palette
+
+ if (palette) {
+ if (FreeImage_IsTransparent(dib)) {
+ FILE_BGRA *bgra_pal = (FILE_BGRA*)malloc(header.cm_length * sizeof(FILE_BGRA));
+
+ // get the transparency table
+ BYTE *trns = FreeImage_GetTransparencyTable(dib);
+
+ for (unsigned i = 0; i < header.cm_length; i++) {
+ bgra_pal[i].b = palette[i].rgbBlue;
+ bgra_pal[i].g = palette[i].rgbGreen;
+ bgra_pal[i].r = palette[i].rgbRed;
+ bgra_pal[i].a = trns[i];
+ }
+
+ io->write_proc(bgra_pal, sizeof(FILE_BGRA), header.cm_length, handle);
+
+ free(bgra_pal);
+
+ } else {
+ FILE_BGR *bgr_pal = (FILE_BGR*)malloc(header.cm_length * sizeof(FILE_BGR));
+
+ for (unsigned i = 0; i < header.cm_length; i++) {
+ bgr_pal[i].b = palette[i].rgbBlue;
+ bgr_pal[i].g = palette[i].rgbGreen;
+ bgr_pal[i].r = palette[i].rgbRed;
+ }
+
+ io->write_proc(bgr_pal, sizeof(FILE_BGR), header.cm_length, handle);
+
+ free(bgr_pal);
+ }
+ }
+
+ // write the data bits
+
+
+ if (TARGA_SAVE_RLE & flags) {
+
+ saveRLE(dib, io, handle);
+
+ } else {
+
+ // -- no rle compression --
+
+ const unsigned width = header.is_width;
+ const unsigned height = header.is_height;
+ const unsigned pixel_size = bpp/8;
+
+ BYTE *line, *const line_begin = (BYTE*)malloc(width * pixel_size);
+ BYTE *line_source = line_begin;
+
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *scanline = FreeImage_GetScanLine(dib, y);
+
+ // rewind the line pointer
+ line = line_begin;
+
+ switch (bpp) {
+ case 8: {
+ // don't copy line, read straight from dib
+ line_source = scanline;
+ }
+ break;
+
+ case 16: {
+ for (unsigned x = 0; x < width; x++) {
+ WORD pixel = *(((WORD *)scanline) + x);
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapShort(&pixel);
+#endif
+ *(WORD*)line = pixel;
+
+ line += pixel_size;
+ }
+ }
+ break;
+
+ case 24: {
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ line_source = scanline;
+#else
+ for (unsigned x = 0; x < width; ++x) {
+ RGBTRIPLE* trip = ((RGBTRIPLE *)scanline) + x;
+ line[0] = trip->rgbtBlue;
+ line[1] = trip->rgbtGreen;
+ line[2] = trip->rgbtRed;
+
+ line += pixel_size;
+ }
+#endif
+ }
+ break;
+
+ case 32: {
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ line_source = scanline;
+#else
+ for (unsigned x = 0; x < width; ++x) {
+ RGBQUAD* quad = ((RGBQUAD *)scanline) + x;
+ line[0] = quad->rgbBlue;
+ line[1] = quad->rgbGreen;
+ line[2] = quad->rgbRed;
+ line[3] = quad->rgbReserved;
+
+ line += pixel_size;
+ }
+#endif
+ }
+ break;
+
+ }//switch(bpp)
+
+ // write line to disk
+
+ io->write_proc(line_source, pixel_size, width, handle);
+
+ }//for height
+
+ free(line_begin);
+ }
+
+
+ long extension_offset = 0 ;
+ if(hasValidThumbnail(dib)) {
+ // write extension area
+
+ extension_offset = io->tell_proc(handle);
+
+ TGAEXTENSIONAREA ex;
+ memset(&ex, 0, sizeof(ex));
+
+ assert(sizeof(ex) == 495);
+ ex.extension_size = sizeof(ex);
+ ex.postage_stamp_offset = extension_offset + ex.extension_size + 0 /*< no Scan Line Table*/;
+ ex.attributes_type = FreeImage_GetBPP(dib) == 32 ? 3 /*< useful Alpha channel data*/ : 0 /*< no Alpha data*/;
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapExtensionArea(&ex);
+#endif
+
+ io->write_proc(&ex, sizeof(ex), 1, handle);
+
+ // (no Scan Line Table)
+
+ // write thumbnail
+
+ io->seek_proc(handle, ex.postage_stamp_offset, SEEK_SET);
+
+ FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
+ BYTE width = (BYTE)FreeImage_GetWidth(thumbnail);
+ BYTE height = (BYTE)FreeImage_GetHeight(thumbnail);
+
+ io->write_proc(&width, 1, 1, handle);
+ io->write_proc(&height, 1, 1, handle);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
+ SwapRedBlue32(dib);
+#endif
+
+#ifdef FREEIMAGE_BIGENDIAN
+ swapShortPixels(dib);
+#endif
+
+ const unsigned line_size = FreeImage_GetLine(thumbnail);
+
+ for (BYTE h = 0; h < height; ++h) {
+ BYTE* src_line = FreeImage_GetScanLine(thumbnail, height - 1 - h);
+ io->write_proc(src_line, 1, line_size, handle);
+ }
+ }
+
+ // (no Color Correction Table)
+
+ // write the footer
+
+ TGAFOOTER footer;
+ footer.extension_offset = extension_offset;
+ footer.developer_offset = 0;
+ strcpy(footer.signature, "TRUEVISION-XFILE.");
+
+
+#ifdef FREEIMAGE_BIGENDIAN
+ SwapFooter(&footer);
+#endif
+
+ io->write_proc(&footer, sizeof(footer), 1, handle);
+
+ return TRUE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitTARGA(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp b/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp
new file mode 100644
index 0000000000..54f81e0938
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp
@@ -0,0 +1,2629 @@
+// ==========================================================
+// TIFF Loader and Writer
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+// - Hervé Drolon (drolon@infonie.fr)
+// - Markus Loibl (markus.loibl@epost.de)
+// - Luca Piergentili (l.pierge@terra.es)
+// - Detlev Vendt (detlev.vendt@brillit.de)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+#ifdef unix
+#undef unix
+#endif
+#ifdef __unix
+#undef __unix
+#endif
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../LibTIFF4/tiffiop.h"
+#include "../Metadata/FreeImageTag.h"
+#include "../OpenEXR/Half/half.h"
+
+#include "FreeImageIO.h"
+#include "PSDParser.h"
+
+// ----------------------------------------------------------
+// geotiff interface (see XTIFF.cpp)
+// ----------------------------------------------------------
+
+// Extended TIFF Directory GEO Tag Support
+void XTIFFInitialize();
+
+// GeoTIFF profile
+void tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib);
+void tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib);
+
+// ----------------------------------------------------------
+// exif interface (see XTIFF.cpp)
+// ----------------------------------------------------------
+
+// TIFF Exif profile
+BOOL tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib);
+BOOL tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib);
+
+// ----------------------------------------------------------
+// LogLuv conversion functions interface (see TIFFLogLuv.cpp)
+// ----------------------------------------------------------
+
+void tiff_ConvertLineXYZToRGB(BYTE *target, BYTE *source, double stonits, int width_in_pixels);
+void tiff_ConvertLineRGBToXYZ(BYTE *target, BYTE *source, int width_in_pixels);
+
+// ----------------------------------------------------------
+
+/** Supported loading methods */
+typedef enum {
+ LoadAsRBGA = 0,
+ LoadAsCMYK = 1,
+ LoadAs8BitTrns = 2,
+ LoadAsGenericStrip = 3,
+ LoadAsTiled = 4,
+ LoadAsLogLuv = 5,
+ LoadAsHalfFloat = 6
+} TIFFLoadMethod;
+
+// ----------------------------------------------------------
+// local prototypes
+// ----------------------------------------------------------
+
+static tmsize_t _tiffReadProc(thandle_t handle, void* buf, tmsize_t size);
+static tmsize_t _tiffWriteProc(thandle_t handle, void* buf, tmsize_t size);
+static toff_t _tiffSeekProc(thandle_t handle, toff_t off, int whence);
+static int _tiffCloseProc(thandle_t fd);
+static int _tiffMapProc(thandle_t fd, void** pbase, toff_t* psize);
+static void _tiffUnmapProc(thandle_t fd, void* base, toff_t size);
+
+static uint16 CheckColormap(int n, uint16* r, uint16* g, uint16* b);
+static uint16 GetPhotometric(FIBITMAP *dib);
+
+static void ReadResolution(TIFF *tiff, FIBITMAP *dib);
+static void WriteResolution(TIFF *tiff, FIBITMAP *dib);
+
+static void ReadPalette(TIFF *tiff, uint16 photometric, uint16 bitspersample, FIBITMAP *dib);
+
+static FIBITMAP* CreateImageType(BOOL header_only, FREE_IMAGE_TYPE fit, int width, int height, uint16 bitspersample, uint16 samplesperpixel);
+static FREE_IMAGE_TYPE ReadImageType(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel);
+static void WriteImageType(TIFF *tiff, FREE_IMAGE_TYPE fit);
+
+static void WriteCompression(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel, uint16 photometric, int flags);
+
+static BOOL tiff_read_iptc_profile(TIFF *tiff, FIBITMAP *dib);
+static BOOL tiff_read_xmp_profile(TIFF *tiff, FIBITMAP *dib);
+static BOOL tiff_read_exif_profile(TIFF *tiff, FIBITMAP *dib);
+static void ReadMetadata(TIFF *tiff, FIBITMAP *dib);
+
+static BOOL tiff_write_iptc_profile(TIFF *tiff, FIBITMAP *dib);
+static BOOL tiff_write_xmp_profile(TIFF *tiff, FIBITMAP *dib);
+static void WriteMetadata(TIFF *tiff, FIBITMAP *dib);
+
+static TIFFLoadMethod FindLoadMethod(TIFF *tif, uint16 photometric, uint16 bitspersample, uint16 samplesperpixel, FREE_IMAGE_TYPE image_type, int flags);
+
+static void ReadThumbnail(FreeImageIO *io, fi_handle handle, void *data, TIFF *tiff, FIBITMAP *dib);
+
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+typedef struct {
+ FreeImageIO *io;
+ fi_handle handle;
+ TIFF *tif;
+} fi_TIFFIO;
+
+// ----------------------------------------------------------
+// libtiff interface
+// ----------------------------------------------------------
+
+static tmsize_t
+_tiffReadProc(thandle_t handle, void *buf, tmsize_t size) {
+ fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+ return fio->io->read_proc(buf, size, 1, fio->handle) * size;
+}
+
+static tmsize_t
+_tiffWriteProc(thandle_t handle, void *buf, tmsize_t size) {
+ fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+ return fio->io->write_proc(buf, size, 1, fio->handle) * size;
+}
+
+static toff_t
+_tiffSeekProc(thandle_t handle, toff_t off, int whence) {
+ fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+ fio->io->seek_proc(fio->handle, off, whence);
+ return fio->io->tell_proc(fio->handle);
+}
+
+static int
+_tiffCloseProc(thandle_t fd) {
+ return 0;
+}
+
+#include <sys/stat.h>
+
+static toff_t
+_tiffSizeProc(thandle_t handle) {
+ fi_TIFFIO *fio = (fi_TIFFIO*)handle;
+ long currPos = fio->io->tell_proc(fio->handle);
+ fio->io->seek_proc(fio->handle, 0, SEEK_END);
+ long fileSize = fio->io->tell_proc(fio->handle);
+ fio->io->seek_proc(fio->handle, currPos, SEEK_SET);
+ return fileSize;
+}
+
+static int
+_tiffMapProc(thandle_t, void** base, toff_t* size) {
+ return 0;
+}
+
+static void
+_tiffUnmapProc(thandle_t, void* base, toff_t size) {
+}
+
+/**
+Open a TIFF file descriptor for reading or writing
+@param handle File handle
+@param name Name of the file handle
+@param mode Specifies if the file is to be opened for reading ("r") or writing ("w")
+*/
+TIFF *
+TIFFFdOpen(thandle_t handle, const char *name, const char *mode) {
+ TIFF *tif;
+
+ // Set up the callback for extended TIFF directory tag support
+ // (see XTIFF.cpp)
+ XTIFFInitialize();
+
+ // Open the file; the callback will set everything up
+ tif = TIFFClientOpen(name, mode, handle,
+ _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
+ _tiffSizeProc, _tiffMapProc, _tiffUnmapProc);
+
+ // Warning: tif_fd is declared as 'int' currently (see libTIFF),
+ // may result in incorrect file pointers inside libTIFF on
+ // 64bit machines (sizeof(int) != sizeof(long)).
+ // Needs to be fixed within libTIFF.
+ if (tif) {
+ tif->tif_fd = (long)handle;
+ }
+
+ return tif;
+}
+
+/**
+Open a TIFF file for reading or writing
+@param name
+@param mode
+*/
+TIFF*
+TIFFOpen(const char* name, const char* mode) {
+ return 0;
+}
+
+// ----------------------------------------------------------
+// TIFF library FreeImage-specific routines.
+// ----------------------------------------------------------
+
+void*
+_TIFFmalloc(tmsize_t s) {
+ return malloc(s);
+}
+
+void
+_TIFFfree(void *p) {
+ free(p);
+}
+
+void*
+_TIFFrealloc(void* p, tmsize_t s) {
+ return realloc(p, s);
+}
+
+void
+_TIFFmemset(void* p, int v, tmsize_t c) {
+ memset(p, v, (size_t) c);
+}
+
+void
+_TIFFmemcpy(void* d, const void* s, tmsize_t c) {
+ memcpy(d, s, (size_t) c);
+}
+
+int
+_TIFFmemcmp(const void* p1, const void* p2, tmsize_t c) {
+ return (memcmp(p1, p2, (size_t) c));
+}
+
+// ----------------------------------------------------------
+// in FreeImage warnings and errors are disabled
+// ----------------------------------------------------------
+
+static void
+msdosWarningHandler(const char* module, const char* fmt, va_list ap) {
+}
+
+TIFFErrorHandler _TIFFwarningHandler = msdosWarningHandler;
+
+static void
+msdosErrorHandler(const char* module, const char* fmt, va_list ap) {
+
+ // use this for diagnostic only (do not use otherwise, even in DEBUG mode)
+ /*
+ if (module != NULL) {
+ char msg[1024];
+ vsprintf(msg, fmt, ap);
+ FreeImage_OutputMessageProc(s_format_id, "%s: %s", module, msg);
+ }
+ */
+}
+
+TIFFErrorHandler _TIFFerrorHandler = msdosErrorHandler;
+
+// ----------------------------------------------------------
+
+#define CVT(x) (((x) * 255L) / ((1L<<16)-1))
+#define SCALE(x) (((x)*((1L<<16)-1))/255)
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static uint16
+CheckColormap(int n, uint16* r, uint16* g, uint16* b) {
+ while (n-- > 0) {
+ if (*r++ >= 256 || *g++ >= 256 || *b++ >= 256) {
+ return 16;
+ }
+ }
+
+ return 8;
+}
+
+/**
+Get the TIFFTAG_PHOTOMETRIC value from the dib
+*/
+static uint16
+GetPhotometric(FIBITMAP *dib) {
+ FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+ switch(color_type) {
+ case FIC_MINISWHITE: // min value is white
+ return PHOTOMETRIC_MINISWHITE;
+ case FIC_MINISBLACK: // min value is black
+ return PHOTOMETRIC_MINISBLACK;
+ case FIC_PALETTE: // color map indexed
+ return PHOTOMETRIC_PALETTE;
+ case FIC_RGB: // RGB color model
+ case FIC_RGBALPHA: // RGB color model with alpha channel
+ return PHOTOMETRIC_RGB;
+ case FIC_CMYK: // CMYK color model
+ return PHOTOMETRIC_RGB; // default to RGB unless the save flag is set to TIFF_CMYK
+ default:
+ return PHOTOMETRIC_MINISBLACK;
+ }
+}
+
+/**
+Get the resolution from the TIFF and fill the dib with universal units
+*/
+static void
+ReadResolution(TIFF *tiff, FIBITMAP *dib) {
+ float fResX = 300.0;
+ float fResY = 300.0;
+ uint16 resUnit = RESUNIT_INCH;
+
+ TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit);
+ TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &fResX);
+ TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &fResY);
+
+ // If we don't have a valid resolution unit and valid resolution is specified then assume inch
+ if (resUnit == RESUNIT_NONE && fResX > 0.0 && fResY > 0.0) {
+ resUnit = RESUNIT_INCH;
+ }
+ if (resUnit == RESUNIT_INCH) {
+ FreeImage_SetDotsPerMeterX(dib, (unsigned) (fResX/0.0254000 + 0.5));
+ FreeImage_SetDotsPerMeterY(dib, (unsigned) (fResY/0.0254000 + 0.5));
+ } else if(resUnit == RESUNIT_CENTIMETER) {
+ FreeImage_SetDotsPerMeterX(dib, (unsigned) (fResX*100.0 + 0.5));
+ FreeImage_SetDotsPerMeterY(dib, (unsigned) (fResY*100.0 + 0.5));
+ }
+}
+
+/**
+Set the resolution to the TIFF using english units
+*/
+static void
+WriteResolution(TIFF *tiff, FIBITMAP *dib) {
+ double res;
+
+ TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
+
+ res = (unsigned long) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib));
+ TIFFSetField(tiff, TIFFTAG_XRESOLUTION, res);
+
+ res = (unsigned long) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib));
+ TIFFSetField(tiff, TIFFTAG_YRESOLUTION, res);
+}
+
+/**
+Fill the dib palette according to the TIFF photometric
+*/
+static void
+ReadPalette(TIFF *tiff, uint16 photometric, uint16 bitspersample, FIBITMAP *dib) {
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ switch(photometric) {
+ case PHOTOMETRIC_MINISBLACK: // bitmap and greyscale image types
+ case PHOTOMETRIC_MINISWHITE:
+ // Monochrome image
+
+ if (bitspersample == 1) {
+ if (photometric == PHOTOMETRIC_MINISWHITE) {
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0;
+ } else {
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+ }
+
+ } else if ((bitspersample == 4) ||(bitspersample == 8)) {
+ // need to build the scale for greyscale images
+ int ncolors = FreeImage_GetColorsUsed(dib);
+
+ if (photometric == PHOTOMETRIC_MINISBLACK) {
+ for (int i = 0; i < ncolors; i++) {
+ pal[i].rgbRed =
+ pal[i].rgbGreen =
+ pal[i].rgbBlue = (BYTE)(i*(255/(ncolors-1)));
+ }
+ } else {
+ for (int i = 0; i < ncolors; i++) {
+ pal[i].rgbRed =
+ pal[i].rgbGreen =
+ pal[i].rgbBlue = (BYTE)(255-i*(255/(ncolors-1)));
+ }
+ }
+ }
+
+ break;
+
+ case PHOTOMETRIC_PALETTE: // color map indexed
+ uint16 *red;
+ uint16 *green;
+ uint16 *blue;
+
+ TIFFGetField(tiff, TIFFTAG_COLORMAP, &red, &green, &blue);
+
+ // load the palette in the DIB
+
+ if (CheckColormap(1<<bitspersample, red, green, blue) == 16) {
+ for (int i = (1 << bitspersample) - 1; i >= 0; i--) {
+ pal[i].rgbRed =(BYTE) CVT(red[i]);
+ pal[i].rgbGreen = (BYTE) CVT(green[i]);
+ pal[i].rgbBlue = (BYTE) CVT(blue[i]);
+ }
+ } else {
+ for (int i = (1 << bitspersample) - 1; i >= 0; i--) {
+ pal[i].rgbRed = (BYTE) red[i];
+ pal[i].rgbGreen = (BYTE) green[i];
+ pal[i].rgbBlue = (BYTE) blue[i];
+ }
+ }
+
+ break;
+ }
+}
+
+/**
+Allocate a FIBITMAP
+@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP
+@param fit Image type
+@param width Image width in pixels
+@param height Image height in pixels
+@param bitspersample # bits per sample
+@param samplesperpixel # samples per pixel
+@return Returns the allocated image if successful, returns NULL otherwise
+*/
+static FIBITMAP*
+CreateImageType(BOOL header_only, FREE_IMAGE_TYPE fit, int width, int height, uint16 bitspersample, uint16 samplesperpixel) {
+ FIBITMAP *dib = NULL;
+
+ if ((width < 0) || (height < 0)) {
+ // check for malicious images
+ return NULL;
+ }
+
+ int bpp = bitspersample * samplesperpixel;
+
+ if(fit == FIT_BITMAP) {
+ // standard bitmap type
+
+ if(bpp == 16) {
+
+ if ((samplesperpixel == 2) && (bitspersample == 8)) {
+ // 8-bit indexed + 8-bit alpha channel -> convert to 8-bit transparent
+ dib = FreeImage_AllocateHeader(header_only, width, height, 8);
+ } else {
+ // 16-bit RGB -> expect it to be 565
+ dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK);
+ }
+
+ }
+ else {
+
+ dib = FreeImage_AllocateHeader(header_only, width, height, MIN(bpp, 32), FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ }
+
+
+ } else {
+ // other bitmap types
+
+ dib = FreeImage_AllocateHeaderT(header_only, fit, width, height, bpp);
+ }
+
+ return dib;
+}
+
+/**
+Read the TIFFTAG_SAMPLEFORMAT tag and convert to FREE_IMAGE_TYPE
+@param tiff LibTIFF TIFF Handle
+@param bitspersample # bit per sample
+@param samplesperpixel # samples per pixel
+@return Returns the image type as a FREE_IMAGE_TYPE value
+*/
+static FREE_IMAGE_TYPE
+ReadImageType(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel) {
+ uint16 sampleformat = 0;
+ FREE_IMAGE_TYPE fit = FIT_BITMAP ;
+
+ uint16 bpp = bitspersample * samplesperpixel;
+
+ // try the sampleformat tag
+ if(TIFFGetField(tiff, TIFFTAG_SAMPLEFORMAT, &sampleformat)) {
+
+ switch (sampleformat) {
+ case SAMPLEFORMAT_UINT:
+ switch (bpp) {
+ case 1:
+ case 4:
+ case 8:
+ case 24:
+ fit = FIT_BITMAP;
+ break;
+ case 16:
+ // 8-bit + alpha or 16-bit greyscale
+ if(samplesperpixel == 2) {
+ fit = FIT_BITMAP;
+ } else {
+ fit = FIT_UINT16;
+ }
+ break;
+ case 32:
+ if(samplesperpixel == 4) {
+ fit = FIT_BITMAP;
+ } else {
+ fit = FIT_UINT32;
+ }
+ break;
+ case 48:
+ if(samplesperpixel == 3) {
+ fit = FIT_RGB16;
+ }
+ break;
+ case 64:
+ if(samplesperpixel == 4) {
+ fit = FIT_RGBA16;
+ }
+ break;
+ }
+ break;
+
+ case SAMPLEFORMAT_INT:
+ switch (bpp) {
+ case 16:
+ if(samplesperpixel == 3) {
+ fit = FIT_BITMAP;
+ } else {
+ fit = FIT_INT16;
+ }
+ break;
+ case 32:
+ fit = FIT_INT32;
+ break;
+ }
+ break;
+
+ case SAMPLEFORMAT_IEEEFP:
+ switch (bpp) {
+ case 32:
+ fit = FIT_FLOAT;
+ break;
+ case 48:
+ // 3 x half float => convert to RGBF
+ if ((samplesperpixel == 3) && (bitspersample == 16)) {
+ fit = FIT_RGBF;
+ }
+ break;
+ case 64:
+ if(samplesperpixel == 2) {
+ fit = FIT_FLOAT;
+ } else {
+ fit = FIT_DOUBLE;
+ }
+ break;
+ case 96:
+ fit = FIT_RGBF;
+ break;
+ default:
+ if(bpp >= 128) {
+ fit = FIT_RGBAF;
+ }
+ break;
+ }
+ break;
+ case SAMPLEFORMAT_COMPLEXIEEEFP:
+ switch (bpp) {
+ case 64:
+ break;
+ case 128:
+ fit = FIT_COMPLEX;
+ break;
+ }
+ break;
+
+ }
+ }
+ // no sampleformat tag : assume SAMPLEFORMAT_UINT
+ else {
+ if(samplesperpixel == 1) {
+ switch (bpp) {
+ case 16:
+ fit = FIT_UINT16;
+ break;
+
+ case 32:
+ fit = FIT_UINT32;
+ break;
+ }
+ }
+ else if(samplesperpixel == 3) {
+ if(bpp == 48) fit = FIT_RGB16;
+ }
+ else if(samplesperpixel >= 4) {
+ if(bitspersample == 16) {
+ fit = FIT_RGBA16;
+ }
+ }
+
+ }
+
+ return fit;
+}
+
+/**
+Convert FREE_IMAGE_TYPE and write TIFFTAG_SAMPLEFORMAT
+@param tiff LibTIFF TIFF Handle
+@param fit Image type as a FREE_IMAGE_TYPE value
+*/
+static void
+WriteImageType(TIFF *tiff, FREE_IMAGE_TYPE fit) {
+ switch(fit) {
+ case FIT_BITMAP: // standard image: 1-, 4-, 8-, 16-, 24-, 32-bit
+ case FIT_UINT16: // array of unsigned short : unsigned 16-bit
+ case FIT_UINT32: // array of unsigned long : unsigned 32-bit
+ case FIT_RGB16: // 48-bit RGB image : 3 x 16-bit
+ case FIT_RGBA16: // 64-bit RGBA image : 4 x 16-bit
+ TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
+ break;
+
+ case FIT_INT16: // array of short : signed 16-bit
+ case FIT_INT32: // array of long : signed 32-bit
+ TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
+ break;
+
+ case FIT_FLOAT: // array of float : 32-bit
+ case FIT_DOUBLE: // array of double : 64-bit
+ case FIT_RGBF: // 96-bit RGB float image : 3 x 32-bit IEEE floating point
+ case FIT_RGBAF: // 128-bit RGBA float image : 4 x 32-bit IEEE floating point
+ TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
+ break;
+
+ case FIT_COMPLEX: // array of COMPLEX : 2 x 64-bit
+ TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_COMPLEXIEEEFP);
+ break;
+ }
+}
+
+/**
+Select the compression algorithm
+@param tiff LibTIFF TIFF Handle
+@param
+*/
+static void
+WriteCompression(TIFF *tiff, uint16 bitspersample, uint16 samplesperpixel, uint16 photometric, int flags) {
+ uint16 compression;
+ uint16 bitsperpixel = bitspersample * samplesperpixel;
+
+ if(photometric == PHOTOMETRIC_LOGLUV) {
+ compression = COMPRESSION_SGILOG;
+ } else if ((flags & TIFF_PACKBITS) == TIFF_PACKBITS) {
+ compression = COMPRESSION_PACKBITS;
+ } else if ((flags & TIFF_DEFLATE) == TIFF_DEFLATE) {
+ compression = COMPRESSION_DEFLATE;
+ } else if ((flags & TIFF_ADOBE_DEFLATE) == TIFF_ADOBE_DEFLATE) {
+ compression = COMPRESSION_ADOBE_DEFLATE;
+ } else if ((flags & TIFF_NONE) == TIFF_NONE) {
+ compression = COMPRESSION_NONE;
+ } else if ((bitsperpixel == 1) && ((flags & TIFF_CCITTFAX3) == TIFF_CCITTFAX3)) {
+ compression = COMPRESSION_CCITTFAX3;
+ } else if ((bitsperpixel == 1) && ((flags & TIFF_CCITTFAX4) == TIFF_CCITTFAX4)) {
+ compression = COMPRESSION_CCITTFAX4;
+ } else if ((flags & TIFF_LZW) == TIFF_LZW) {
+ compression = COMPRESSION_LZW;
+ } else if ((flags & TIFF_JPEG) == TIFF_JPEG) {
+ if (((bitsperpixel == 8) && (photometric != PHOTOMETRIC_PALETTE)) || (bitsperpixel == 24)) {
+ compression = COMPRESSION_JPEG;
+ // RowsPerStrip must be multiple of 8 for JPEG
+ uint32 rowsperstrip = (uint32) -1;
+ rowsperstrip = TIFFDefaultStripSize(tiff, rowsperstrip);
+ rowsperstrip = rowsperstrip + (8 - (rowsperstrip % 8));
+ // overwrite previous RowsPerStrip
+ TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
+ } else {
+ // default to LZW
+ compression = COMPRESSION_LZW;
+ }
+ }
+ else {
+ // default compression scheme
+
+ switch(bitsperpixel) {
+ case 1:
+ compression = COMPRESSION_CCITTFAX4;
+ break;
+
+ case 4:
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ compression = COMPRESSION_LZW;
+ break;
+ case 48:
+ case 64:
+ case 96:
+ case 128:
+ compression = COMPRESSION_LZW;
+ break;
+
+ default :
+ compression = COMPRESSION_NONE;
+ break;
+ }
+ }
+
+ TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression);
+
+ if(compression == COMPRESSION_LZW) {
+ // This option is only meaningful with LZW compression: a predictor value of 2
+ // causes each scanline of the output image to undergo horizontal differencing
+ // before it is encoded; a value of 1 forces each scanline to be encoded without differencing.
+
+ // Found on LibTIFF mailing list :
+ // LZW without differencing works well for 1-bit images, 4-bit grayscale images,
+ // and many palette-color images. But natural 24-bit color images and some 8-bit
+ // grayscale images do much better with differencing.
+
+ if ((bitspersample == 8) || (bitspersample == 16)) {
+ if ((bitsperpixel >= 8) && (photometric != PHOTOMETRIC_PALETTE)) {
+ TIFFSetField(tiff, TIFFTAG_PREDICTOR, 2);
+ } else {
+ TIFFSetField(tiff, TIFFTAG_PREDICTOR, 1);
+ }
+ } else {
+ TIFFSetField(tiff, TIFFTAG_PREDICTOR, 1);
+ }
+ }
+ else if(compression == COMPRESSION_CCITTFAX3) {
+ // try to be compliant with the TIFF Class F specification
+ // that documents the TIFF tags specific to FAX applications
+ // see http://palimpsest.stanford.edu/bytopic/imaging/std/tiff-f.html
+ uint32 group3options = GROUP3OPT_2DENCODING | GROUP3OPT_FILLBITS;
+ TIFFSetField(tiff, TIFFTAG_GROUP3OPTIONS, group3options); // 2d-encoded, has aligned EOL
+ TIFFSetField(tiff, TIFFTAG_FILLORDER, FILLORDER_LSB2MSB); // lsb-to-msb fillorder
+ }
+}
+
+// ==========================================================
+// TIFF metadata routines
+// ==========================================================
+
+/**
+ Read the TIFFTAG_RICHTIFFIPTC tag (IPTC/NAA or Adobe Photoshop profile)
+*/
+static BOOL
+tiff_read_iptc_profile(TIFF *tiff, FIBITMAP *dib) {
+ BYTE *profile = NULL;
+ uint32 profile_size = 0;
+
+ if(TIFFGetField(tiff,TIFFTAG_RICHTIFFIPTC, &profile_size, &profile) == 1) {
+ if (TIFFIsByteSwapped(tiff) != 0) {
+ TIFFSwabArrayOfLong((uint32 *) profile, (unsigned long)profile_size);
+ }
+
+ return read_iptc_profile(dib, profile, 4 * profile_size);
+ }
+
+ return FALSE;
+}
+
+/**
+ Read the TIFFTAG_XMLPACKET tag (XMP profile)
+ @param dib Input FIBITMAP
+ @param tiff LibTIFF TIFF handle
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+tiff_read_xmp_profile(TIFF *tiff, FIBITMAP *dib) {
+ BYTE *profile = NULL;
+ uint32 profile_size = 0;
+
+ if (TIFFGetField(tiff, TIFFTAG_XMLPACKET, &profile_size, &profile) == 1) {
+ // create a tag
+ FITAG *tag = FreeImage_CreateTag();
+ if (!tag) return FALSE;
+
+ FreeImage_SetTagID(tag, TIFFTAG_XMLPACKET); // 700
+ FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
+ FreeImage_SetTagLength(tag, profile_size);
+ FreeImage_SetTagCount(tag, profile_size);
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagValue(tag, profile);
+
+ // store the tag
+ FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
+
+ // destroy the tag
+ FreeImage_DeleteTag(tag);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Read the Exif profile embedded in a TIFF
+ @param dib Input FIBITMAP
+ @param tiff LibTIFF TIFF handle
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+tiff_read_exif_profile(TIFF *tiff, FIBITMAP *dib) {
+ BOOL bResult = FALSE;
+ toff_t exif_offset = 0;
+
+ // read EXIF-TIFF tags
+ bResult = tiff_read_exif_tags(tiff, TagLib::EXIF_MAIN, dib);
+
+ // get the IFD offset
+ if(TIFFGetField(tiff, TIFFTAG_EXIFIFD, &exif_offset)) {
+
+ // read EXIF tags
+ if (!TIFFReadEXIFDirectory(tiff, exif_offset)) {
+ return FALSE;
+ }
+
+ // read all known exif tags
+ bResult = tiff_read_exif_tags(tiff, TagLib::EXIF_EXIF, dib);
+ }
+
+ return bResult;
+}
+
+/**
+Read TIFF special profiles
+*/
+static void
+ReadMetadata(TIFF *tiff, FIBITMAP *dib) {
+
+ // IPTC/NAA
+ tiff_read_iptc_profile(tiff, dib);
+
+ // Adobe XMP
+ tiff_read_xmp_profile(tiff, dib);
+
+ // GeoTIFF
+ tiff_read_geotiff_profile(tiff, dib);
+
+ // Exif-TIFF
+ tiff_read_exif_profile(tiff, dib);
+}
+
+// ----------------------------------------------------------
+
+/**
+ Write the TIFFTAG_RICHTIFFIPTC tag (IPTC/NAA or Adobe Photoshop profile)
+*/
+static BOOL
+tiff_write_iptc_profile(TIFF *tiff, FIBITMAP *dib) {
+ if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) {
+ BYTE *profile = NULL;
+ uint32 profile_size = 0;
+ // create a binary profile
+ if(write_iptc_profile(dib, &profile, &profile_size)) {
+ uint32 iptc_size = profile_size;
+ iptc_size += (4-(iptc_size & 0x03)); // Round up for long word alignment
+ BYTE *iptc_profile = (BYTE*)malloc(iptc_size);
+ if (!iptc_profile) {
+ free(profile);
+ return FALSE;
+ }
+ memset(iptc_profile, 0, iptc_size);
+ memcpy(iptc_profile, profile, profile_size);
+ if (TIFFIsByteSwapped(tiff)) {
+ TIFFSwabArrayOfLong((uint32 *) iptc_profile, (unsigned long)iptc_size/4);
+ }
+ // Tag is type TIFF_LONG so byte length is divided by four
+ TIFFSetField(tiff, TIFFTAG_RICHTIFFIPTC, iptc_size/4, iptc_profile);
+ // release the profile data
+ free(iptc_profile);
+ free(profile);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Write the TIFFTAG_XMLPACKET tag (XMP profile)
+ @param dib Input FIBITMAP
+ @param tiff LibTIFF TIFF handle
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+tiff_write_xmp_profile(TIFF *tiff, FIBITMAP *dib) {
+ FITAG *tag_xmp = NULL;
+ FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp);
+
+ if(tag_xmp && (NULL != FreeImage_GetTagValue(tag_xmp))) {
+
+ TIFFSetField(tiff, TIFFTAG_XMLPACKET, (uint32)FreeImage_GetTagLength(tag_xmp), (BYTE*)FreeImage_GetTagValue(tag_xmp));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Write the Exif profile to TIFF
+ @param dib Input FIBITMAP
+ @param tiff LibTIFF TIFF handle
+ @return Returns TRUE if successful, FALSE otherwise
+*/
+static BOOL
+tiff_write_exif_profile(TIFF *tiff, FIBITMAP *dib) {
+ BOOL bResult = FALSE;
+ uint32 exif_offset = 0;
+
+ // write EXIF_MAIN tags, EXIF_EXIF not supported yet
+ bResult = tiff_write_exif_tags(tiff, TagLib::EXIF_MAIN, dib);
+
+ return bResult;
+}
+
+/**
+Write TIFF special profiles
+*/
+static void
+WriteMetadata(TIFF *tiff, FIBITMAP *dib) {
+ // IPTC
+ tiff_write_iptc_profile(tiff, dib);
+
+ // Adobe XMP
+ tiff_write_xmp_profile(tiff, dib);
+
+ // EXIF_MAIN tags
+ tiff_write_exif_profile(tiff, dib);
+
+ // GeoTIFF tags
+ tiff_write_geotiff_profile(tiff, dib);
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "TIFF";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Tagged Image File Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "tif,tiff";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return "^[MI][MI][\\x01*][\\x01*]";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/tiff";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ BYTE tiff_id1[] = { 0x49, 0x49, 0x2A, 0x00 };
+ BYTE tiff_id2[] = { 0x4D, 0x4D, 0x00, 0x2A };
+ BYTE signature[4] = { 0, 0, 0, 0 };
+
+ io->read_proc(signature, 1, 4, handle);
+
+ if(memcmp(tiff_id1, signature, 4) == 0)
+ return TRUE;
+
+ if(memcmp(tiff_id2, signature, 4) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 1) ||
+ (depth == 4) ||
+ (depth == 8) ||
+ (depth == 24) ||
+ (depth == 32)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (
+ (type == FIT_BITMAP) ||
+ (type == FIT_UINT16) ||
+ (type == FIT_INT16) ||
+ (type == FIT_UINT32) ||
+ (type == FIT_INT32) ||
+ (type == FIT_FLOAT) ||
+ (type == FIT_DOUBLE) ||
+ (type == FIT_COMPLEX) ||
+ (type == FIT_RGB16) ||
+ (type == FIT_RGBA16) ||
+ (type == FIT_RGBF) ||
+ (type == FIT_RGBAF)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsICCProfiles() {
+ return TRUE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static void * DLL_CALLCONV
+Open(FreeImageIO *io, fi_handle handle, BOOL read) {
+ // wrapper for TIFF I/O
+ fi_TIFFIO *fio = (fi_TIFFIO*)malloc(sizeof(fi_TIFFIO));
+ if (!fio) return NULL;
+ fio->io = io;
+ fio->handle = handle;
+
+ if (read) {
+ fio->tif = TIFFFdOpen((thandle_t)fio, "", "r");
+ } else {
+ fio->tif = TIFFFdOpen((thandle_t)fio, "", "w");
+ }
+ if(fio->tif == NULL) {
+ free(fio);
+ FreeImage_OutputMessageProc(s_format_id, "Error while opening TIFF: data is invalid");
+ return NULL;
+ }
+ return fio;
+}
+
+static void DLL_CALLCONV
+Close(FreeImageIO *io, fi_handle handle, void *data) {
+ if(data) {
+ fi_TIFFIO *fio = (fi_TIFFIO*)data;
+ TIFFClose(fio->tif);
+ free(fio);
+ }
+}
+
+// ----------------------------------------------------------
+
+static int DLL_CALLCONV
+PageCount(FreeImageIO *io, fi_handle handle, void *data) {
+ if(data) {
+ fi_TIFFIO *fio = (fi_TIFFIO*)data;
+ TIFF *tif = (TIFF *)fio->tif;
+ int nr_ifd = 0;
+
+ do {
+ nr_ifd++;
+ } while (TIFFReadDirectory(tif));
+
+ return nr_ifd;
+ }
+
+ return 0;
+}
+
+// ----------------------------------------------------------
+
+/**
+check for uncommon bitspersample values (e.g. 10, 12, ...)
+@param photometric TIFFTAG_PHOTOMETRIC tiff tag
+@param bitspersample TIFFTAG_BITSPERSAMPLE tiff tag
+@param samplesperpixel TIFFTAG_SAMPLESPERPIXEL tiff tag
+@return Returns FALSE if a uncommon bit-depth is encountered, returns TRUE otherwise
+*/
+static BOOL
+IsValidBitsPerSample(uint16 photometric, uint16 bitspersample, uint16 samplesperpixel) {
+
+ switch(bitspersample) {
+ case 1:
+ case 4:
+ if ((photometric == PHOTOMETRIC_MINISWHITE) || (photometric == PHOTOMETRIC_MINISBLACK) || (photometric == PHOTOMETRIC_PALETTE)) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ break;
+ case 8:
+ return TRUE;
+ case 16:
+ if(photometric != PHOTOMETRIC_PALETTE) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ break;
+ case 32:
+ return TRUE;
+ case 64:
+ case 128:
+ if(photometric == PHOTOMETRIC_MINISBLACK) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+}
+
+static TIFFLoadMethod
+FindLoadMethod(TIFF *tif, FREE_IMAGE_TYPE image_type, int flags) {
+ uint16 bitspersample = (uint16)-1;
+ uint16 samplesperpixel = (uint16)-1;
+ uint16 photometric = (uint16)-1;
+ uint16 planar_config = (uint16)-1;
+
+ TIFFLoadMethod loadMethod = LoadAsGenericStrip;
+
+ TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
+ TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
+ TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
+
+ BOOL bIsTiled = (TIFFIsTiled(tif) == 0) ? FALSE:TRUE;
+
+ switch(photometric) {
+ // convert to 24 or 32 bits RGB if the image is full color
+ case PHOTOMETRIC_RGB:
+ if ((image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) {
+ // load 48-bit RGB and 64-bit RGBA without conversion
+ loadMethod = LoadAsGenericStrip;
+ }
+ else if(image_type == FIT_RGBF) {
+ if ((samplesperpixel == 3) && (bitspersample == 16)) {
+ // load 3 x 16-bit half as RGBF
+ loadMethod = LoadAsHalfFloat;
+ }
+ }
+ break;
+ case PHOTOMETRIC_YCBCR:
+ case PHOTOMETRIC_CIELAB:
+ case PHOTOMETRIC_ICCLAB:
+ case PHOTOMETRIC_ITULAB:
+ loadMethod = LoadAsRBGA;
+ break;
+ case PHOTOMETRIC_LOGLUV:
+ loadMethod = LoadAsLogLuv;
+ break;
+ case PHOTOMETRIC_SEPARATED:
+ // if image is PHOTOMETRIC_SEPARATED _and_ comes with an ICC profile,
+ // then the image should preserve its original (CMYK) colour model and
+ // should be read as CMYK (to keep the match of pixel and profile and
+ // to avoid multiple conversions. Conversion can be done by changing
+ // the profile from it's original CMYK to an RGB profile with an
+ // apropriate color management system. Works with non-tiled TIFFs.
+ if (!bIsTiled) {
+ loadMethod = LoadAsCMYK;
+ }
+ break;
+ case PHOTOMETRIC_MINISWHITE:
+ case PHOTOMETRIC_MINISBLACK:
+ case PHOTOMETRIC_PALETTE:
+ // When samplesperpixel = 2 and bitspersample = 8, set the image as a
+ // 8-bit indexed image + 8-bit alpha layer image
+ // and convert to a 8-bit image with a transparency table
+ if ((samplesperpixel > 1) && (bitspersample == 8)) {
+ loadMethod = LoadAs8BitTrns;
+ } else {
+ loadMethod = LoadAsGenericStrip;
+ }
+ break;
+ default:
+ loadMethod = LoadAsGenericStrip;
+ break;
+ }
+
+ if ((loadMethod == LoadAsGenericStrip) && bIsTiled) {
+ loadMethod = LoadAsTiled;
+ }
+
+ return loadMethod;
+}
+
+// ==========================================================
+// TIFF thumbnail routines
+// ==========================================================
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data);
+
+/**
+Read embedded thumbnail
+*/
+static void
+ReadThumbnail(FreeImageIO *io, fi_handle handle, void *data, TIFF *tiff, FIBITMAP *dib) {
+ FIBITMAP* thumbnail = NULL;
+
+ // read exif thumbnail (IFD 1) ...
+
+ uint32 exif_offset = 0;
+ if(TIFFGetField(tiff, TIFFTAG_EXIFIFD, &exif_offset)) {
+
+ if(TIFFLastDirectory(tiff) != 0) {
+ // save current position
+ long tell_pos = io->tell_proc(handle);
+ uint16 cur_dir = TIFFCurrentDirectory(tiff);
+
+ // load the thumbnail
+ int page = 1;
+ int flags = TIFF_DEFAULT;
+ thumbnail = Load(io, handle, page, flags, data);
+ // store the thumbnail (remember to release it later ...)
+ FreeImage_SetThumbnail(dib, thumbnail);
+
+ // restore current position
+ io->seek_proc(handle, tell_pos, SEEK_SET);
+ TIFFSetDirectory(tiff, cur_dir);
+ }
+ }
+
+ // ... or read the first subIFD
+
+ if (!thumbnail) {
+ uint16 subIFD_count = 0;
+ uint64* subIFD_offsets = NULL;
+ // ### Theoretically this should also read the first subIFD from a Photoshop-created file with "pyramid".
+ // It does not however - the tag is there (using Tag Viewer app) but libtiff refuses to read it
+ if(TIFFGetField(tiff, TIFFTAG_SUBIFD, &subIFD_count, &subIFD_offsets)) {
+ if(subIFD_count > 0) {
+ // save current position
+ long tell_pos = io->tell_proc(handle);
+ uint16 cur_dir = TIFFCurrentDirectory(tiff);
+ if(TIFFSetSubDirectory(tiff, subIFD_offsets[0])) {
+ // load the thumbnail
+ int page = -1;
+ int flags = TIFF_DEFAULT;
+ thumbnail = Load(io, handle, page, flags, data);
+ // store the thumbnail (remember to release it later ...)
+ FreeImage_SetThumbnail(dib, thumbnail);
+ }
+ // restore current position
+ io->seek_proc(handle, tell_pos, SEEK_SET);
+ TIFFSetDirectory(tiff, cur_dir);
+ }
+ }
+ }
+
+ // ... or read Photoshop thumbnail
+
+ if (!thumbnail) {
+ uint32 ps_size = 0;
+ void *ps_data = NULL;
+
+ if(TIFFGetField(tiff, TIFFTAG_PHOTOSHOP, &ps_size, &ps_data)) {
+ FIMEMORY *handle = FreeImage_OpenMemory((BYTE*)ps_data, ps_size);
+
+ FreeImageIO io;
+ SetMemoryIO(&io);
+
+ psdParser parser;
+ parser.ReadImageResources(&io, handle, ps_size);
+
+ FreeImage_SetThumbnail(dib, parser.GetThumbnail());
+
+ FreeImage_CloseMemory(handle);
+ }
+
+ }
+
+ // release thumbnail
+ FreeImage_Unload(thumbnail);
+}
+
+// --------------------------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ if (!handle || !data ) {
+ return NULL;
+ }
+
+ TIFF *tif = NULL;
+ uint32 height = 0;
+ uint32 width = 0;
+ uint16 bitspersample = 1;
+ uint16 samplesperpixel = 1;
+ uint32 rowsperstrip = (uint32)-1;
+ uint16 photometric = PHOTOMETRIC_MINISWHITE;
+ uint16 compression = (uint16)-1;
+ uint16 planar_config;
+
+ FIBITMAP *dib = NULL;
+ uint32 iccSize = 0; // ICC profile length
+ void *iccBuf = NULL; // ICC profile data
+
+ const BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ try {
+ fi_TIFFIO *fio = (fi_TIFFIO*)data;
+ tif = fio->tif;
+
+ if (page != -1) {
+ if (!tif || !TIFFSetDirectory(tif, (uint16)page)) {
+ throw "Error encountered while opening TIFF file";
+ }
+ }
+
+ const BOOL asCMYK = (flags & TIFF_CMYK) == TIFF_CMYK;
+
+ // first, get the photometric, the compression and basic metadata
+ // ---------------------------------------------------------------------------------
+
+ TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
+ TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
+
+ // check for HDR formats
+ // ---------------------------------------------------------------------------------
+
+ if(photometric == PHOTOMETRIC_LOGLUV) {
+ // check the compression
+ if(compression != COMPRESSION_SGILOG && compression != COMPRESSION_SGILOG24) {
+ throw "Only support SGILOG compressed LogLuv data";
+ }
+ // set decoder to output in IEEE 32-bit float XYZ values
+ TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT);
+ }
+
+ // ---------------------------------------------------------------------------------
+
+ TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
+ TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
+ TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
+ TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
+ TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+ TIFFGetField(tif, TIFFTAG_ICCPROFILE, &iccSize, &iccBuf);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
+
+ // check for unsupported formats
+ // ---------------------------------------------------------------------------------
+
+ if(IsValidBitsPerSample(photometric, bitspersample, samplesperpixel) == FALSE) {
+ FreeImage_OutputMessageProc(s_format_id,
+ "Unable to handle this format: bitspersample = %d, samplesperpixel = %d, photometric = %d",
+ (int)bitspersample, (int)samplesperpixel, (int)photometric);
+ throw (char*)NULL;
+ }
+
+ // ---------------------------------------------------------------------------------
+
+ // get image data type
+
+ FREE_IMAGE_TYPE image_type = ReadImageType(tif, bitspersample, samplesperpixel);
+
+ // get the most appropriate loading method
+
+ TIFFLoadMethod loadMethod = FindLoadMethod(tif, image_type, flags);
+
+ // ---------------------------------------------------------------------------------
+
+ if(loadMethod == LoadAsRBGA) {
+ // ---------------------------------------------------------------------------------
+ // RGB[A] loading using the TIFFReadRGBAImage() API
+ // ---------------------------------------------------------------------------------
+
+ BOOL has_alpha = FALSE;
+
+ // Read the whole image into one big RGBA buffer and then
+ // convert it to a DIB. This is using the traditional
+ // TIFFReadRGBAImage() API that we trust.
+
+ uint32 *raster = NULL;
+
+ if (!header_only) {
+
+ raster = (uint32*)_TIFFmalloc(width * height * sizeof(uint32));
+ if (raster == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // read the image in one chunk into an RGBA array
+
+ if (!TIFFReadRGBAImage(tif, width, height, raster, 1)) {
+ _TIFFfree(raster);
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+ }
+ // TIFFReadRGBAImage always deliveres 3 or 4 samples per pixel images
+ // (RGB or RGBA, see below). Cut-off possibly present channels (additional
+ // alpha channels) from e.g. Photoshop. Any CMYK(A..) is now treated as RGB,
+ // any additional alpha channel on RGB(AA..) is lost on conversion to RGB(A)
+
+ if(samplesperpixel > 4) { // TODO Write to Extra Channels
+ FreeImage_OutputMessageProc(s_format_id, "Warning: %d additional alpha channel(s) ignored", samplesperpixel-4);
+ samplesperpixel = 4;
+ }
+
+ // create a new DIB (take care of different samples-per-pixel in case
+ // of converted CMYK image (RGB conversion is on sample per pixel less)
+
+ if (photometric == PHOTOMETRIC_SEPARATED && samplesperpixel == 4) {
+ samplesperpixel = 3;
+ }
+
+ dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
+ if (dib == NULL) {
+ // free the raster pointer and output an error if allocation failed
+ if(raster) {
+ _TIFFfree(raster);
+ }
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // fill in the resolution (english or universal)
+
+ ReadResolution(tif, dib);
+
+ if (!header_only) {
+
+ // read the raster lines and save them in the DIB
+ // with RGB mode, we have to change the order of the 3 samples RGB
+ // We use macros for extracting components from the packed ABGR
+ // form returned by TIFFReadRGBAImage.
+
+ uint32 *row = &raster[0];
+
+ if (samplesperpixel == 4) {
+ // 32-bit RGBA
+ for (uint32 y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ for (uint32 x = 0; x < width; x++) {
+ bits[FI_RGBA_BLUE] = (BYTE)TIFFGetB(row[x]);
+ bits[FI_RGBA_GREEN] = (BYTE)TIFFGetG(row[x]);
+ bits[FI_RGBA_RED] = (BYTE)TIFFGetR(row[x]);
+ bits[FI_RGBA_ALPHA] = (BYTE)TIFFGetA(row[x]);
+
+ if (bits[FI_RGBA_ALPHA] != 0) {
+ has_alpha = TRUE;
+ }
+
+ bits += 4;
+ }
+ row += width;
+ }
+ } else {
+ // 24-bit RGB
+ for (uint32 y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+ for (uint32 x = 0; x < width; x++) {
+ bits[FI_RGBA_BLUE] = (BYTE)TIFFGetB(row[x]);
+ bits[FI_RGBA_GREEN] = (BYTE)TIFFGetG(row[x]);
+ bits[FI_RGBA_RED] = (BYTE)TIFFGetR(row[x]);
+
+ bits += 3;
+ }
+ row += width;
+ }
+ }
+
+ _TIFFfree(raster);
+ }
+
+ // ### Not correct when header only
+ FreeImage_SetTransparent(dib, has_alpha);
+
+ } else if(loadMethod == LoadAs8BitTrns) {
+ // ---------------------------------------------------------------------------------
+ // 8-bit + 8-bit alpha layer loading
+ // ---------------------------------------------------------------------------------
+
+ // create a new 8-bit DIB
+ dib = CreateImageType(header_only, image_type, width, height, bitspersample, MIN<uint16>(2, samplesperpixel));
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // fill in the resolution (english or universal)
+
+ ReadResolution(tif, dib);
+
+ // set up the colormap based on photometric
+
+ ReadPalette(tif, photometric, bitspersample, dib);
+
+ // calculate the line + pitch (separate for scr & dest)
+
+ const tmsize_t src_line = TIFFScanlineSize(tif);
+ // here, the pitch is 2x less than the original as we only keep the first layer
+ int dst_pitch = FreeImage_GetPitch(dib);
+
+ // transparency table for 8-bit + 8-bit alpha images
+
+ BYTE trns[256];
+ // clear the transparency table
+ memset(trns, 0xFF, 256 * sizeof(BYTE));
+
+ // In the tiff file the lines are saved from up to down
+ // In a DIB the lines must be saved from down to up
+
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+ // read the tiff lines and save them in the DIB
+
+ if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
+
+ BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+ if(buf == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
+ free(buf);
+ throw FI_MSG_ERROR_PARSING;
+ }
+ for (int l = 0; l < nrow; l++) {
+ BYTE *p = bits;
+ BYTE *b = buf + l * src_line;
+
+ for(uint32 x = 0; x < (uint32)(src_line / samplesperpixel); x++) {
+ // copy the 8-bit layer
+ *p = b[0];
+ // convert the 8-bit alpha layer to a trns table
+ trns[ b[0] ] = b[1];
+
+ p++;
+ b += samplesperpixel;
+ }
+ bits -= dst_pitch;
+ }
+ }
+
+ free(buf);
+ }
+ else if(planar_config == PLANARCONFIG_SEPARATE && !header_only) {
+ tmsize_t stripsize = TIFFStripSize(tif) * sizeof(BYTE);
+ BYTE *buf = (BYTE*)malloc(2 * stripsize);
+ if(buf == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ BYTE *grey = buf;
+ BYTE *alpha = buf + stripsize;
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), grey, nrow * src_line) == -1) {
+ free(buf);
+ throw FI_MSG_ERROR_PARSING;
+ }
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 1), alpha, nrow * src_line) == -1) {
+ free(buf);
+ throw FI_MSG_ERROR_PARSING;
+ }
+
+ for (int l = 0; l < nrow; l++) {
+ BYTE *p = bits;
+ BYTE *g = grey + l * src_line;
+ BYTE *a = alpha + l * src_line;
+
+ for(uint32 x = 0; x < (uint32)(src_line); x++) {
+ // copy the 8-bit layer
+ *p = g[0];
+ // convert the 8-bit alpha layer to a trns table
+ trns[ g[0] ] = a[0];
+
+ p++;
+ g++;
+ a++;
+ }
+ bits -= dst_pitch;
+ }
+ }
+
+ free(buf);
+
+ }
+
+ FreeImage_SetTransparencyTable(dib, &trns[0], 256);
+ FreeImage_SetTransparent(dib, TRUE);
+
+ } else if(loadMethod == LoadAsCMYK) {
+ // ---------------------------------------------------------------------------------
+ // CMYK loading
+ // ---------------------------------------------------------------------------------
+
+ // At this place, samplesperpixel could be > 4, esp. when a CMYK(A) format
+ // is recognized. Where all other formats are handled straight-forward, this
+ // format has to be handled special
+
+ BOOL isCMYKA = (photometric == PHOTOMETRIC_SEPARATED) && (samplesperpixel > 4);
+
+ // We use a temp dib to store the alpha for the CMYKA to RGBA conversion
+ // NOTE this is until we have Extra channels implementation.
+ // Also then it will be possible to merge LoadAsCMYK with LoadAsGenericStrip
+
+ FIBITMAP *alpha = NULL;
+ unsigned alpha_pitch = 0;
+ BYTE *alpha_bits = NULL;
+ unsigned alpha_Bpp = 0;
+
+ if(isCMYKA && !asCMYK && !header_only) {
+ if(bitspersample == 16) {
+ alpha = FreeImage_AllocateT(FIT_UINT16, width, height);
+ } else if (bitspersample == 8) {
+ alpha = FreeImage_Allocate(width, height, 8);
+ }
+
+ if (!alpha) {
+ FreeImage_OutputMessageProc(s_format_id, "Failed to allocate temporary alpha channel");
+ } else {
+ alpha_bits = FreeImage_GetScanLine(alpha, height - 1);
+ alpha_pitch = FreeImage_GetPitch(alpha);
+ alpha_Bpp = FreeImage_GetBPP(alpha) / 8;
+ }
+
+ }
+
+ // create a new DIB
+ const uint16 chCount = MIN<uint16>(samplesperpixel, 4);
+ dib = CreateImageType(header_only, image_type, width, height, bitspersample, chCount);
+ if (dib == NULL) {
+ FreeImage_Unload(alpha);
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // fill in the resolution (english or universal)
+
+ ReadResolution(tif, dib);
+
+ if (!header_only) {
+
+ // calculate the line + pitch (separate for scr & dest)
+
+ const tmsize_t src_line = TIFFScanlineSize(tif);
+ const tmsize_t dst_line = FreeImage_GetLine(dib);
+ const unsigned dib_pitch = FreeImage_GetPitch(dib);
+ const unsigned dibBpp = FreeImage_GetBPP(dib) / 8;
+ const unsigned Bpc = dibBpp / chCount;
+ const unsigned srcBpp = bitspersample * samplesperpixel / 8;
+
+ assert(Bpc <= 2); //< CMYK is only BYTE or SHORT
+
+ // In the tiff file the lines are save from up to down
+ // In a DIB the lines must be saved from down to up
+
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+ // read the tiff lines and save them in the DIB
+
+ BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+ if(buf == NULL) {
+ FreeImage_Unload(alpha);
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ if(planar_config == PLANARCONFIG_CONTIG) {
+
+ // - loop for strip blocks -
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, strips * src_line) == -1) {
+ free(buf);
+ FreeImage_Unload(alpha);
+ throw FI_MSG_ERROR_PARSING;
+ }
+
+ // - loop for strips -
+
+ if(src_line != dst_line) {
+ // CMYKA+
+ if(alpha) {
+ for (int l = 0; l < strips; l++) {
+ for(BYTE *pixel = bits, *al_pixel = alpha_bits, *src_pixel = buf + l * src_line; pixel < bits + dib_pitch; pixel += dibBpp, al_pixel += alpha_Bpp, src_pixel += srcBpp) {
+ // copy pixel byte by byte
+ BYTE b = 0;
+ for ( ; b < dibBpp; ++b) {
+ pixel[b] = src_pixel[b];
+ }
+ // TODO write the remaining bytes to extra channel(s)
+
+ // HACK write the first alpha to a separate dib (assume BYTE or WORD)
+ al_pixel[0] = src_pixel[b];
+ if(Bpc > 1) {
+ al_pixel[1] = src_pixel[b + 1];
+ }
+
+ }
+ bits -= dib_pitch;
+ alpha_bits -= alpha_pitch;
+ }
+ }
+ else {
+ // alpha/extra channels alloc failed
+ for (int l = 0; l < strips; l++) {
+ for(BYTE* pixel = bits, * src_pixel = buf + l * src_line; pixel < bits + dst_line; pixel += dibBpp, src_pixel += srcBpp) {
+ AssignPixel(pixel, src_pixel, dibBpp);
+ }
+ bits -= dib_pitch;
+ }
+ }
+ }
+ else {
+ // CMYK to CMYK
+ for (int l = 0; l < strips; l++) {
+ BYTE *b = buf + l * src_line;
+ memcpy(bits, b, src_line);
+ bits -= dib_pitch;
+ }
+ }
+
+ } // height
+
+ }
+ else if(planar_config == PLANARCONFIG_SEPARATE) {
+
+ BYTE *dib_strip = bits;
+ BYTE *al_strip = alpha_bits;
+
+ // - loop for strip blocks -
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ // - loop for channels (planes) -
+
+ for(uint16 sample = 0; sample < samplesperpixel; sample++) {
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, sample), buf, strips * src_line) == -1) {
+ free(buf);
+ FreeImage_Unload(alpha);
+ throw FI_MSG_ERROR_PARSING;
+ }
+
+ BYTE *dst_strip = dib_strip;
+ unsigned dst_pitch = dib_pitch;
+ uint16 ch = sample;
+ unsigned Bpp = dibBpp;
+
+ if(sample >= chCount) {
+ // TODO Write to Extra Channel
+
+ // HACK redirect write to temp alpha
+ if(alpha && sample == chCount) {
+
+ dst_strip = al_strip;
+ dst_pitch = alpha_pitch;
+
+ ch = 0;
+ Bpp = alpha_Bpp;
+ }
+ else {
+ break;
+ }
+ }
+
+ const unsigned channelOffset = ch * Bpc;
+
+ // - loop for strips in block -
+
+ BYTE *src_line_begin = buf;
+ BYTE *dst_line_begin = dst_strip;
+ for (int l = 0; l < strips; l++, src_line_begin += src_line, dst_line_begin -= dst_pitch ) {
+ // - loop for pixels in strip -
+
+ const BYTE* const src_line_end = src_line_begin + src_line;
+ for (BYTE *src_bits = src_line_begin, * dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) {
+ AssignPixel(dst_bits + channelOffset, src_bits, Bpc);
+ } // line
+
+ } // strips
+
+ } // channels
+
+ // done with a strip block, incr to the next
+ dib_strip -= strips * dib_pitch;
+ al_strip -= strips * alpha_pitch;
+
+ } //< height
+
+ }
+
+ free(buf);
+
+ if (!asCMYK) {
+ ConvertCMYKtoRGBA(dib);
+
+ // The ICC Profile is invalid, clear it
+ iccSize = 0;
+ iccBuf = NULL;
+
+ if(isCMYKA) {
+ // HACK until we have Extra channels. (ConvertCMYKtoRGBA will then do the work)
+
+ FreeImage_SetChannel(dib, alpha, FICC_ALPHA);
+ FreeImage_Unload(alpha);
+ alpha = NULL;
+ }
+ else {
+ FIBITMAP *t = RemoveAlphaChannel(dib);
+ if(t) {
+ FreeImage_Unload(dib);
+ dib = t;
+ }
+ else {
+ FreeImage_OutputMessageProc(s_format_id, "Cannot allocate memory for buffer. CMYK image converted to RGB + pending Alpha");
+ }
+ }
+ }
+
+ } // !header_only
+
+ } else if(loadMethod == LoadAsGenericStrip) {
+ // ---------------------------------------------------------------------------------
+ // Generic loading
+ // ---------------------------------------------------------------------------------
+
+ // create a new DIB
+ const uint16 chCount = MIN<uint16>(samplesperpixel, 4);
+ dib = CreateImageType(header_only, image_type, width, height, bitspersample, chCount);
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // fill in the resolution (english or universal)
+
+ ReadResolution(tif, dib);
+
+ // set up the colormap based on photometric
+
+ ReadPalette(tif, photometric, bitspersample, dib);
+
+ if (!header_only) {
+ // calculate the line + pitch (separate for scr & dest)
+
+ const tmsize_t src_line = TIFFScanlineSize(tif);
+ const tmsize_t dst_line = FreeImage_GetLine(dib);
+ const unsigned dst_pitch = FreeImage_GetPitch(dib);
+ const unsigned Bpp = FreeImage_GetBPP(dib) / 8;
+ const unsigned srcBpp = bitspersample * samplesperpixel / 8;
+
+ // In the tiff file the lines are save from up to down
+ // In a DIB the lines must be saved from down to up
+
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+ // read the tiff lines and save them in the DIB
+
+ BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+ if(buf == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ BOOL bThrowMessage = FALSE;
+
+ if(planar_config == PLANARCONFIG_CONTIG) {
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, strips * src_line) == -1) {
+ // ignore errors as they can be frequent and not really valid errors, especially with fax images
+ bThrowMessage = TRUE;
+ /*
+ free(buf);
+ throw FI_MSG_ERROR_PARSING;
+ */
+ }
+ if(src_line == dst_line) {
+ // channel count match
+ for (int l = 0; l < strips; l++) {
+ memcpy(bits, buf + l * src_line, src_line);
+ bits -= dst_pitch;
+ }
+ }
+ else {
+ for (int l = 0; l < strips; l++) {
+ for(BYTE *pixel = bits, *src_pixel = buf + l * src_line; pixel < bits + dst_pitch; pixel += Bpp, src_pixel += srcBpp) {
+ AssignPixel(pixel, src_pixel, Bpp);
+ }
+ bits -= dst_pitch;
+ }
+ }
+ }
+ }
+ else if(planar_config == PLANARCONFIG_SEPARATE) {
+
+ const unsigned Bpc = bitspersample / 8;
+ BYTE* dib_strip = bits;
+ // - loop for strip blocks -
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ // - loop for channels (planes) -
+
+ for(uint16 sample = 0; sample < samplesperpixel; sample++) {
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, sample), buf, strips * src_line) == -1) {
+ // ignore errors as they can be frequent and not really valid errors, especially with fax images
+ bThrowMessage = TRUE;
+ }
+
+ if(sample >= chCount) {
+ // TODO Write to Extra Channel
+ break;
+ }
+
+ const unsigned channelOffset = sample * Bpc;
+
+ // - loop for strips in block -
+
+ BYTE* src_line_begin = buf;
+ BYTE* dst_line_begin = dib_strip;
+ for (int l = 0; l < strips; l++, src_line_begin += src_line, dst_line_begin -= dst_pitch ) {
+
+ // - loop for pixels in strip -
+
+ const BYTE* const src_line_end = src_line_begin + src_line;
+
+ for (BYTE* src_bits = src_line_begin, * dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) {
+ // actually assigns channel
+ AssignPixel(dst_bits + channelOffset, src_bits, Bpc);
+ } // line
+
+ } // strips
+
+ } // channels
+
+ // done with a strip block, incr to the next
+ dib_strip -= strips * dst_pitch;
+
+ } // height
+
+ }
+ free(buf);
+
+ if(bThrowMessage) {
+ FreeImage_OutputMessageProc(s_format_id, "Warning: parsing error. Image may be incomplete or contain invalid data !");
+ }
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ SwapRedBlue32(dib);
+#endif
+
+ } // !header only
+
+ } else if(loadMethod == LoadAsTiled) {
+ // ---------------------------------------------------------------------------------
+ // Tiled image loading
+ // ---------------------------------------------------------------------------------
+
+ uint32 tileWidth, tileHeight;
+ uint32 src_line = 0;
+
+ // create a new DIB
+ dib = CreateImageType( header_only, image_type, width, height, bitspersample, samplesperpixel);
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // fill in the resolution (english or universal)
+
+ ReadResolution(tif, dib);
+
+ // set up the colormap based on photometric
+
+ ReadPalette(tif, photometric, bitspersample, dib);
+
+ // get the tile geometry
+ if (!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth) || !TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileHeight)) {
+ throw "Invalid tiled TIFF image";
+ }
+
+ // read the tiff lines and save them in the DIB
+
+ if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
+
+ // get the maximum number of bytes required to contain a tile
+ tmsize_t tileSize = TIFFTileSize(tif);
+
+ // allocate tile buffer
+ BYTE *tileBuffer = (BYTE*)malloc(tileSize * sizeof(BYTE));
+ if(tileBuffer == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // calculate src line and dst pitch
+ int dst_pitch = FreeImage_GetPitch(dib);
+ int tileRowSize = TIFFTileRowSize(tif);
+ int imageRowSize = TIFFScanlineSize(tif);
+
+
+ // In the tiff file the lines are saved from up to down
+ // In a DIB the lines must be saved from down to up
+
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+ uint32 x, y, rowSize;
+ for (y = 0; y < height; y += tileHeight) {
+ int32 nrows = (y + tileHeight > height ? height - y : tileHeight);
+
+ for (x = 0, rowSize = 0; x < width; x += tileWidth, rowSize += tileRowSize) {
+ memset(tileBuffer, 0, tileSize);
+
+ // read one tile
+ if (TIFFReadTile(tif, tileBuffer, x, y, 0, 0) < 0) {
+ free(tileBuffer);
+ throw "Corrupted tiled TIFF file";
+ }
+ // convert to strip
+ if(x + tileWidth > width) {
+ src_line = imageRowSize - rowSize;
+ } else {
+ src_line = tileRowSize;
+ }
+ BYTE *src_bits = tileBuffer;
+ BYTE *dst_bits = bits + rowSize;
+ for(int k = 0; k < nrows; k++) {
+ memcpy(dst_bits, src_bits, src_line);
+ src_bits += tileRowSize;
+ dst_bits -= dst_pitch;
+ }
+ }
+
+ bits -= nrows * dst_pitch;
+ }
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ SwapRedBlue32(dib);
+#endif
+ free(tileBuffer);
+ }
+ else if(planar_config == PLANARCONFIG_SEPARATE) {
+ throw "Separated tiled TIFF images are not supported";
+ }
+
+
+ } else if(loadMethod == LoadAsLogLuv) {
+ // ---------------------------------------------------------------------------------
+ // RGBF LogLuv compressed loading
+ // ---------------------------------------------------------------------------------
+
+ double stonits; // input conversion to nits
+ if (!TIFFGetField(tif, TIFFTAG_STONITS, &stonits)) {
+ stonits = 1;
+ }
+
+ // create a new DIB
+ dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // fill in the resolution (english or universal)
+
+ ReadResolution(tif, dib);
+
+ if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
+ // calculate the line + pitch (separate for scr & dest)
+
+ tmsize_t src_line = TIFFScanlineSize(tif);
+ int dst_pitch = FreeImage_GetPitch(dib);
+
+ // In the tiff file the lines are save from up to down
+ // In a DIB the lines must be saved from down to up
+
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+ // read the tiff lines and save them in the DIB
+
+ BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+ if(buf == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
+ free(buf);
+ throw FI_MSG_ERROR_PARSING;
+ }
+ // convert from XYZ to RGB
+ for (int l = 0; l < nrow; l++) {
+ tiff_ConvertLineXYZToRGB(bits, buf + l * src_line, stonits, width);
+ bits -= dst_pitch;
+ }
+ }
+
+ free(buf);
+ }
+ else if(planar_config == PLANARCONFIG_SEPARATE) {
+ // this cannot happen according to the LogLuv specification
+ throw "Unable to handle PLANARCONFIG_SEPARATE LogLuv images";
+ }
+
+ } else if(loadMethod == LoadAsHalfFloat) {
+ // ---------------------------------------------------------------------------------
+ // RGBF loading from a half format
+ // ---------------------------------------------------------------------------------
+
+ // create a new DIB
+ dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
+ if (dib == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // fill in the resolution (english or universal)
+
+ ReadResolution(tif, dib);
+
+ if (!header_only) {
+
+ // calculate the line + pitch (separate for scr & dest)
+
+ tmsize_t src_line = TIFFScanlineSize(tif);
+ unsigned dst_pitch = FreeImage_GetPitch(dib);
+
+ // In the tiff file the lines are save from up to down
+ // In a DIB the lines must be saved from down to up
+
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
+
+ // read the tiff lines and save them in the DIB
+
+ if(planar_config == PLANARCONFIG_CONTIG) {
+
+ BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
+ if(buf == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (uint32 y = 0; y < height; y += rowsperstrip) {
+ uint32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
+
+ if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
+ free(buf);
+ throw FI_MSG_ERROR_PARSING;
+ }
+
+ // convert from half (16-bit) to float (32-bit)
+ // !!! use OpenEXR half helper class
+
+ half half_value;
+
+ for (uint32 l = 0; l < nrow; l++) {
+ WORD *src_pixel = (WORD*)(buf + l * src_line);
+ float *dst_pixel = (float*)bits;
+
+ for(tmsize_t x = 0; x < (tmsize_t)(src_line / sizeof(WORD)); x++) {
+ half_value.setBits(src_pixel[x]);
+ dst_pixel[x] = half_value;
+ }
+
+ bits -= dst_pitch;
+ }
+ }
+
+ free(buf);
+ }
+ else if(planar_config == PLANARCONFIG_SEPARATE) {
+ // this use case was never encountered yet
+ throw "Unable to handle PLANARCONFIG_SEPARATE RGB half float images";
+ }
+
+ } // !header only
+
+ } else {
+ // ---------------------------------------------------------------------------------
+ // Unknown or unsupported format
+ // ---------------------------------------------------------------------------------
+
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+
+ // copy ICC profile data (must be done after FreeImage_Allocate)
+
+ FreeImage_CreateICCProfile(dib, iccBuf, iccSize);
+ if (photometric == PHOTOMETRIC_SEPARATED && asCMYK) {
+ FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
+ }
+
+ // copy TIFF metadata (must be done after FreeImage_Allocate)
+
+ ReadMetadata(tif, dib);
+
+ // copy TIFF thumbnail (must be done after FreeImage_Allocate)
+
+ ReadThumbnail(io, handle, data, tif, dib);
+
+ return (FIBITMAP *)dib;
+
+ } catch (const char *message) {
+ if(dib) {
+ FreeImage_Unload(dib);
+ }
+ if(message) {
+ FreeImage_OutputMessageProc(s_format_id, message);
+ }
+ return NULL;
+ }
+
+}
+
+// --------------------------------------------------------------------------
+
+static BOOL
+SaveOneTIFF(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data, unsigned ifd, unsigned ifdCount) {
+ if (!dib || !handle || !data) {
+ return FALSE;
+ }
+
+ try {
+ fi_TIFFIO *fio = (fi_TIFFIO*)data;
+ TIFF *out = fio->tif;
+
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+
+ const uint32 width = FreeImage_GetWidth(dib);
+ const uint32 height = FreeImage_GetHeight(dib);
+ const uint16 bitsperpixel = (uint16)FreeImage_GetBPP(dib);
+
+ const FIICCPROFILE* iccProfile = FreeImage_GetICCProfile(dib);
+
+ // setup out-variables based on dib and flag options
+
+ uint16 bitspersample;
+ uint16 samplesperpixel;
+ uint16 photometric;
+
+ if(image_type == FIT_BITMAP) {
+ // standard image: 1-, 4-, 8-, 16-, 24-, 32-bit
+
+ samplesperpixel = ((bitsperpixel == 24) ? 3 : ((bitsperpixel == 32) ? 4 : 1));
+ bitspersample = bitsperpixel / samplesperpixel;
+ photometric = GetPhotometric(dib);
+
+ if ((bitsperpixel == 8) && FreeImage_IsTransparent(dib)) {
+ // 8-bit transparent picture : convert later to 8-bit + 8-bit alpha
+ samplesperpixel = 2;
+ bitspersample = 8;
+ }
+ else if(bitsperpixel == 32) {
+ // 32-bit images : check for CMYK or alpha transparency
+
+ if ((((iccProfile->flags & FIICC_COLOR_IS_CMYK) == FIICC_COLOR_IS_CMYK) || ((flags & TIFF_CMYK) == TIFF_CMYK))) {
+ // CMYK support
+ photometric = PHOTOMETRIC_SEPARATED;
+ TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
+ TIFFSetField(out, TIFFTAG_NUMBEROFINKS, 4);
+ }
+ else if(photometric == PHOTOMETRIC_RGB) {
+ // transparency mask support
+ uint16 sampleinfo[1];
+ // unassociated alpha data is transparency information
+ sampleinfo[0] = EXTRASAMPLE_UNASSALPHA;
+ TIFFSetField(out, TIFFTAG_EXTRASAMPLES, 1, sampleinfo);
+ }
+ }
+ } else if(image_type == FIT_RGB16) {
+ // 48-bit RGB
+
+ samplesperpixel = 3;
+ bitspersample = bitsperpixel / samplesperpixel;
+ photometric = PHOTOMETRIC_RGB;
+ } else if(image_type == FIT_RGBA16) {
+ // 64-bit RGBA
+
+ samplesperpixel = 4;
+ bitspersample = bitsperpixel / samplesperpixel;
+ if ((((iccProfile->flags & FIICC_COLOR_IS_CMYK) == FIICC_COLOR_IS_CMYK) || ((flags & TIFF_CMYK) == TIFF_CMYK))) {
+ // CMYK support
+ photometric = PHOTOMETRIC_SEPARATED;
+ TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
+ TIFFSetField(out, TIFFTAG_NUMBEROFINKS, 4);
+ }
+ else {
+ photometric = PHOTOMETRIC_RGB;
+ // transparency mask support
+ uint16 sampleinfo[1];
+ // unassociated alpha data is transparency information
+ sampleinfo[0] = EXTRASAMPLE_UNASSALPHA;
+ TIFFSetField(out, TIFFTAG_EXTRASAMPLES, 1, sampleinfo);
+ }
+ } else if(image_type == FIT_RGBF) {
+ // 96-bit RGBF => store with a LogLuv encoding ?
+
+ samplesperpixel = 3;
+ bitspersample = bitsperpixel / samplesperpixel;
+ // the library converts to and from floating-point XYZ CIE values
+ if ((flags & TIFF_LOGLUV) == TIFF_LOGLUV) {
+ photometric = PHOTOMETRIC_LOGLUV;
+ TIFFSetField(out, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT);
+ // TIFFSetField(out, TIFFTAG_STONITS, 1.0); // assume unknown
+ }
+ else {
+ // store with default compression (LZW) or with input compression flag
+ photometric = PHOTOMETRIC_RGB;
+ }
+
+ } else if (image_type == FIT_RGBAF) {
+ // 128-bit RGBAF => store with default compression (LZW) or with input compression flag
+
+ samplesperpixel = 4;
+ bitspersample = bitsperpixel / samplesperpixel;
+ photometric = PHOTOMETRIC_RGB;
+ } else {
+ // special image type (int, long, double, ...)
+
+ samplesperpixel = 1;
+ bitspersample = bitsperpixel;
+ photometric = PHOTOMETRIC_MINISBLACK;
+ }
+
+ // set image data type
+
+ WriteImageType(out, image_type);
+
+ // write possible ICC profile
+
+ if (iccProfile->size && iccProfile->data) {
+ TIFFSetField(out, TIFFTAG_ICCPROFILE, iccProfile->size, iccProfile->data);
+ }
+
+ // handle standard width/height/bpp stuff
+
+ TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
+ TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
+ TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
+ TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitspersample);
+ TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
+ TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); // single image plane
+ TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(out, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
+ TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(out, (uint32) -1));
+
+ // handle metrics
+
+ WriteResolution(out, dib);
+
+ // multi-paging
+
+ if (page >= 0) {
+ char page_number[20];
+ sprintf(page_number, "Page %d", page);
+
+ TIFFSetField(out, TIFFTAG_SUBFILETYPE, (uint32)FILETYPE_PAGE);
+ TIFFSetField(out, TIFFTAG_PAGENUMBER, (uint16)page, (uint16)0);
+ TIFFSetField(out, TIFFTAG_PAGENAME, page_number);
+
+ } else {
+ // is it a thumbnail ?
+ TIFFSetField(out, TIFFTAG_SUBFILETYPE, (ifd == 0) ? (uint32)0 : (uint32)FILETYPE_REDUCEDIMAGE);
+ }
+
+ // palettes (image colormaps are automatically scaled to 16-bits)
+
+ if (photometric == PHOTOMETRIC_PALETTE) {
+ uint16 *r, *g, *b;
+ uint16 nColors = (uint16)FreeImage_GetColorsUsed(dib);
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+
+ r = (uint16 *) _TIFFmalloc(sizeof(uint16) * 3 * nColors);
+ if(r == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ g = r + nColors;
+ b = g + nColors;
+
+ for (int i = nColors - 1; i >= 0; i--) {
+ r[i] = SCALE((uint16)pal[i].rgbRed);
+ g[i] = SCALE((uint16)pal[i].rgbGreen);
+ b[i] = SCALE((uint16)pal[i].rgbBlue);
+ }
+
+ TIFFSetField(out, TIFFTAG_COLORMAP, r, g, b);
+
+ _TIFFfree(r);
+ }
+
+ // compression tag
+
+ WriteCompression(out, bitspersample, samplesperpixel, photometric, flags);
+
+ // metadata
+
+ WriteMetadata(out, dib);
+
+ // thumbnail tag
+
+ if ((ifd == 0) && (ifdCount > 1)) {
+ uint16 nsubifd = 1;
+ uint64 subifd[1];
+ subifd[0] = 0;
+ TIFFSetField(out, TIFFTAG_SUBIFD, nsubifd, subifd);
+ }
+
+ // read the DIB lines from bottom to top
+ // and save them in the TIF
+ // -------------------------------------
+
+ const uint32 pitch = FreeImage_GetPitch(dib);
+
+ if(image_type == FIT_BITMAP) {
+ // standard bitmap type
+
+ switch(bitsperpixel) {
+ case 1 :
+ case 4 :
+ case 8 :
+ {
+ if ((bitsperpixel == 8) && FreeImage_IsTransparent(dib)) {
+ // 8-bit transparent picture : convert to 8-bit + 8-bit alpha
+
+ // get the transparency table
+ BYTE *trns = FreeImage_GetTransparencyTable(dib);
+
+ BYTE *buffer = (BYTE *)malloc(2 * width * sizeof(BYTE));
+ if(buffer == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (int y = height - 1; y >= 0; y--) {
+ BYTE *bits = FreeImage_GetScanLine(dib, y);
+
+ BYTE *p = bits, *b = buffer;
+
+ for(uint32 x = 0; x < width; x++) {
+ // copy the 8-bit layer
+ b[0] = *p;
+ // convert the trns table to a 8-bit alpha layer
+ b[1] = trns[ b[0] ];
+
+ p++;
+ b += samplesperpixel;
+ }
+
+ // write the scanline to disc
+
+ TIFFWriteScanline(out, buffer, height - y - 1, 0);
+ }
+
+ free(buffer);
+ }
+ else {
+ // other cases
+ BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+ if(buffer == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (uint32 y = 0; y < height; y++) {
+ // get a copy of the scanline
+ memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
+ // write the scanline to disc
+ TIFFWriteScanline(out, buffer, y, 0);
+ }
+ free(buffer);
+ }
+
+ break;
+ }
+
+ case 24:
+ case 32:
+ {
+ BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+ if(buffer == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (uint32 y = 0; y < height; y++) {
+ // get a copy of the scanline
+
+ memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ if (photometric != PHOTOMETRIC_SEPARATED) {
+ // TIFFs store color data RGB(A) instead of BGR(A)
+
+ BYTE *pBuf = buffer;
+
+ for (uint32 x = 0; x < width; x++) {
+ INPLACESWAP(pBuf[0], pBuf[2]);
+ pBuf += samplesperpixel;
+ }
+ }
+#endif
+ // write the scanline to disc
+
+ TIFFWriteScanline(out, buffer, y, 0);
+ }
+
+ free(buffer);
+
+ break;
+ }
+ }//< switch (bitsperpixel)
+
+ } else if(image_type == FIT_RGBF && (flags & TIFF_LOGLUV) == TIFF_LOGLUV) {
+ // RGBF image => store as XYZ using a LogLuv encoding
+
+ BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+ if(buffer == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (uint32 y = 0; y < height; y++) {
+ // get a copy of the scanline and convert from RGB to XYZ
+ tiff_ConvertLineRGBToXYZ(buffer, FreeImage_GetScanLine(dib, height - y - 1), width);
+ // write the scanline to disc
+ TIFFWriteScanline(out, buffer, y, 0);
+ }
+ free(buffer);
+ } else {
+ // just dump the dib (tiff supports all dib types)
+
+ BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
+ if(buffer == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ for (uint32 y = 0; y < height; y++) {
+ // get a copy of the scanline
+ memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
+ // write the scanline to disc
+ TIFFWriteScanline(out, buffer, y, 0);
+ }
+ free(buffer);
+ }
+
+ // write out the directory tag if we wrote a page other than -1 or if we have a thumbnail to write later
+
+ if ( (page >= 0) || ((ifd == 0) && (ifdCount > 1)) ) {
+ TIFFWriteDirectory(out);
+ // else: TIFFClose will WriteDirectory
+ }
+
+ return TRUE;
+
+ } catch(const char *text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return FALSE;
+ }
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ BOOL bResult = FALSE;
+
+ // handle thumbnail as SubIFD
+ const BOOL bHasThumbnail = (FreeImage_GetThumbnail(dib) != NULL);
+ const unsigned ifdCount = bHasThumbnail ? 2 : 1;
+
+ FIBITMAP *bitmap = dib;
+
+ for(unsigned ifd = 0; ifd < ifdCount; ifd++) {
+ // redirect dib to thumbnail for the second pass
+ if(ifd == 1) {
+ bitmap = FreeImage_GetThumbnail(dib);
+ }
+
+ bResult = SaveOneTIFF(io, bitmap, handle, page, flags, data, ifd, ifdCount);
+ if (!bResult) {
+ return FALSE;
+ }
+ }
+
+ return bResult;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitTIFF(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = Open;
+ plugin->close_proc = Close;
+ plugin->pagecount_proc = PageCount;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = SupportsICCProfiles;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginWBMP.cpp b/plugins/AdvaImg/src/FreeImage/PluginWBMP.cpp
new file mode 100644
index 0000000000..2ff8f023c3
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginWBMP.cpp
@@ -0,0 +1,372 @@
+// ==========================================================
+// Wireless Bitmap Format Loader and Writer
+//
+// Design and implementation by
+// - Hervé Drolon <drolon@infonie.fr>
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ----------------------------------------------------------
+// Wireless Bitmap Format
+// ----------------------
+// The WBMP format enables graphical information to be sent to a variety of handsets.
+// The WBMP format is terminal independent and describes only graphical information.
+
+// IMPLEMENTATION NOTES:
+// ------------------------
+// The WBMP format is configured according to a type field value (TypeField below),
+// which maps to all relevant image encoding information, such as:
+// · Pixel organisation and encoding
+// · Palette organisation and encoding
+// · Compression characteristics
+// · Animation encoding
+// For each TypeField value, all relevant image characteristics are
+// fully specified as part of the WAP documentation.
+// Currently, a simple compact, monochrome image format is defined
+// within the WBMP type space :
+//
+// Image Type Identifier, multi-byte integer 0
+// Image Format description 0 B/W, no compression
+// -------------------------------------------------------------------------------
+
+// WBMP Header
+
+#ifdef _WIN32
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+typedef struct tagWBMPHEADER {
+ WORD TypeField; // Image type identifier of multi-byte length
+ BYTE FixHeaderField; // Octet of general header information
+ BYTE ExtHeaderFields; // Zero or more extension header fields
+ WORD Width; // Multi-byte width field
+ WORD Height; // Multi-byte height field
+} WBMPHEADER;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+// The extension headers may be of type binary 00 through binary 11, defined as follows.
+
+// - Type 00 indicates a multi-byte bitfield used to specify additional header information.
+// The first bit is set if a type 00, extension header is set if more data follows.
+// The other bits are reserved for future use.
+// - Type 01 - reserved for future use.
+// - Type 10 - reserved for future use.
+// - Type 11 indicates a sequence of parameter/value pairs. These can be used for
+// optimisations and special purpose extensions, eg, animation image formats.
+// The parameter size tells the length (1-8 bytes) of the following parameter name.
+// The value size gives the length (1-16 bytes) of the following parameter value.
+// The concatenation flag indicates whether another parameter/value pair will follow
+// after reading the specified bytes of data.
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+static DWORD
+multiByteRead(FreeImageIO *io, fi_handle handle) {
+ // Multi-byte encoding / decoding
+ // -------------------------------
+ // A multi-byte integer consists of a series of octets, where the most significant bit
+ // is the continuation flag, and the remaining seven bits are a scalar value.
+ // The continuation flag is used to indicate that an octet is not the end of the multi-byte
+ // sequence.
+
+ DWORD Out = 0;
+ BYTE In = 0;
+
+ while (io->read_proc(&In, 1, 1, handle)) {
+ Out += (In & 0x7F);
+
+ if ((In & 0x80) == 0x00)
+ break;
+
+ Out <<= 7;
+ }
+
+ return Out;
+}
+
+static void
+multiByteWrite(FreeImageIO *io, fi_handle handle, DWORD In) {
+ BYTE Out, k = 1;
+
+ while (In & (0x7F << 7*k))
+ k++;
+
+ while (k > 1) {
+ k--;
+
+ Out = (BYTE)(0x80 | (In >> 7*k) & 0xFF);
+
+ io->write_proc(&Out, 1, 1, handle);
+ }
+
+ Out = (BYTE)(In & 0x7F);
+
+ io->write_proc(&Out, 1, 1, handle);
+}
+
+static void
+readExtHeader(FreeImageIO *io, fi_handle handle, BYTE b) {
+ // Extension header fields
+ // ------------------------
+ // Read the extension header fields
+ // (since we don't use them for the moment, we skip them).
+
+ switch (b & 0x60) {
+ // Type 00: read multi-byte bitfield
+
+ case 0x00:
+ {
+ DWORD info = multiByteRead(io, handle);
+ break;
+ }
+
+ // Type 11: read a sequence of parameter/value pairs.
+
+ case 0x60:
+ {
+ BYTE sizeParamIdent = (b & 0x70) >> 4; // Size of Parameter Identifier (in bytes)
+ BYTE sizeParamValue = (b & 0x0F); // Size of Parameter Value (in bytes)
+
+ BYTE *Ident = (BYTE*)malloc(sizeParamIdent * sizeof(BYTE));
+ BYTE *Value = (BYTE*)malloc(sizeParamValue * sizeof(BYTE));
+
+ io->read_proc(Ident, sizeParamIdent, 1, handle);
+ io->read_proc(Value, sizeParamValue, 1, handle);
+
+ free(Ident);
+ free(Value);
+ break;
+ }
+
+ // reserved for future use
+
+ case 0x20: // Type 01
+ case 0x40: // Type 10
+ break;
+ }
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "WBMP";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "Wireless Bitmap";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "wap,wbmp,wbm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/vnd.wap.wbmp";
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 1)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ WORD x, y, width, height;
+ FIBITMAP *dib;
+ BYTE *bits; // pointer to dib data
+ RGBQUAD *pal; // pointer to dib palette
+
+ WBMPHEADER header;
+
+ if (handle) {
+ try {
+ // Read header information
+ // -----------------------
+
+ // Type
+
+ header.TypeField = (WORD)multiByteRead(io, handle);
+
+ if (header.TypeField != 0) {
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+
+ // FixHeaderField
+
+ io->read_proc(&header.FixHeaderField, 1, 1, handle);
+
+ // ExtHeaderFields
+ // 1 = more will follow, 0 = last octet
+
+ if (header.FixHeaderField & 0x80) {
+ header.ExtHeaderFields = 0x80;
+
+ while(header.ExtHeaderFields & 0x80) {
+ io->read_proc(&header.ExtHeaderFields, 1, 1, handle);
+
+ readExtHeader(io, handle, header.ExtHeaderFields);
+ }
+ }
+
+ // width & height
+
+ width = (WORD)multiByteRead(io, handle);
+ height = (WORD)multiByteRead(io, handle);
+
+ // Allocate a new dib
+
+ dib = FreeImage_Allocate(width, height, 1);
+ if (!dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // write the palette data
+
+ pal = FreeImage_GetPalette(dib);
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+
+ // read the bitmap data
+
+ int line = FreeImage_GetLine(dib);
+
+ for (y = 0; y < height; y++) {
+ bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for (x = 0; x < line; x++) {
+ io->read_proc(&bits[x], 1, 1, handle);
+ }
+ }
+
+ return dib;
+
+ } catch(const char *text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+
+ return NULL;
+ }
+
+ }
+
+ return NULL;
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ BYTE *bits; // pointer to dib data
+
+ if ((dib) && (handle)) {
+ try {
+ if (FreeImage_GetBPP(dib) != 1)
+ throw "Only 1-bit depth bitmaps can be saved as WBMP";
+
+ // write the header
+
+ WBMPHEADER header;
+ header.TypeField = 0; // Type 0: B/W, no compression
+ header.FixHeaderField = 0; // No ExtHeaderField
+ header.Width = (WORD)FreeImage_GetWidth(dib); // Image width
+ header.Height = (WORD)FreeImage_GetHeight(dib); // Image height
+
+ multiByteWrite(io, handle, header.TypeField);
+
+ io->write_proc(&header.FixHeaderField, 1, 1, handle);
+
+ multiByteWrite(io, handle, header.Width);
+ multiByteWrite(io, handle, header.Height);
+
+ // write the bitmap data
+
+ WORD linelength = (WORD)FreeImage_GetLine(dib);
+
+ for (WORD y = 0; y < header.Height; y++) {
+ bits = FreeImage_GetScanLine(dib, header.Height - 1 - y);
+
+ io->write_proc(&bits[0], linelength, 1, handle);
+ }
+
+ return TRUE;
+
+ } catch (const char* text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+ }
+ }
+
+ return FALSE;
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitWBMP(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = NULL;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/PluginXBM.cpp b/plugins/AdvaImg/src/FreeImage/PluginXBM.cpp
new file mode 100644
index 0000000000..140029e862
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginXBM.cpp
@@ -0,0 +1,399 @@
+// ==========================================================
+// XBM Loader
+//
+// Design and implementation by
+// - Hervé Drolon <drolon@infonie.fr>
+// part of the code adapted from the netPBM package (xbmtopbm.c)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+//
+//
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Internal functions
+// ==========================================================
+
+#define MAX_LINE 512
+
+static const char *ERR_XBM_SYNTAX = "Syntax error";
+static const char *ERR_XBM_LINE = "Line too long";
+static const char *ERR_XBM_DECL = "Unable to find a line in the file containing the start of C array declaration (\"static char\" or whatever)";
+static const char *ERR_XBM_EOFREAD = "EOF / read error";
+static const char *ERR_XBM_WIDTH = "Invalid width";
+static const char *ERR_XBM_HEIGHT = "Invalid height";
+static const char *ERR_XBM_MEMORY = "Out of memory";
+
+/**
+Get a string from a stream.
+Read the string from the current stream to the first newline character.
+The result stored in str is appended with a null character.
+@param str Storage location for data
+@param n Maximum number of characters to read
+@param io Pointer to the FreeImageIO structure
+@param handle Handle to the stream
+@return Returns str. NULL is returned to indicate an error or an end-of-file condition.
+*/
+static char*
+readLine(char *str, int n, FreeImageIO *io, fi_handle handle) {
+ char c;
+ int count, i = 0;
+ do {
+ count = io->read_proc(&c, 1, 1, handle);
+ str[i++] = c;
+ } while((c != '\n') && (i < n));
+ if(count <= 0)
+ return NULL;
+ str[i] = '\0';
+ return str;
+}
+
+/**
+Get a char from the stream
+@param io Pointer to the FreeImageIO structure
+@param handle Handle to the stream
+@return Returns the next character in the stream
+*/
+static int
+readChar(FreeImageIO *io, fi_handle handle) {
+ BYTE c;
+ io->read_proc(&c, 1, 1, handle);
+ return c;
+}
+
+/**
+Read an XBM file into a buffer
+@param io Pointer to the FreeImageIO structure
+@param handle Handle to the stream
+@param widthP (return value) Pointer to the bitmap width
+@param heightP (return value) Pointer to the bitmap height
+@param dataP (return value) Pointer to the bitmap buffer
+@return Returns NULL if OK, returns an error message otherwise
+*/
+static const char*
+readXBMFile(FreeImageIO *io, fi_handle handle, int *widthP, int *heightP, char **dataP) {
+ char line[MAX_LINE], name_and_type[MAX_LINE];
+ char* ptr;
+ char* t;
+ int version = 0;
+ int raster_length, v;
+ int bytes, bytes_per_line, padding;
+ int c1, c2, value1, value2;
+ int hex_table[256];
+ BOOL found_declaration;
+ /* in scanning through the bitmap file, we have found the first
+ line of the C declaration of the array (the "static char ..."
+ or whatever line)
+ */
+ BOOL eof; // we've encountered end of file while searching file
+
+ *widthP = *heightP = -1;
+
+ found_declaration = FALSE; // haven't found it yet; haven't even looked
+ eof = FALSE; // haven't encountered end of file yet
+
+ while(!found_declaration && !eof) {
+
+ if ( readLine(line, MAX_LINE, io, handle) == NULL) {
+ eof = TRUE;
+ }
+ else {
+ if ( strlen( line ) == MAX_LINE - 1 )
+ return( ERR_XBM_LINE );
+ if ( sscanf(line, "#define %s %d", name_and_type, &v) == 2 ) {
+ if ( ( t = strrchr( name_and_type, '_' ) ) == NULL )
+ t = name_and_type;
+ else
+ t++;
+ if ( ! strcmp( "width", t ) )
+ *widthP = v;
+ else if ( ! strcmp( "height", t ) )
+ *heightP = v;
+ continue;
+ }
+
+ if ( sscanf( line, "static short %s = {", name_and_type ) == 1 ) {
+ version = 10;
+ found_declaration = TRUE;
+ }
+ else if ( sscanf( line, "static char %s = {", name_and_type ) == 1 ) {
+ version = 11;
+ found_declaration = TRUE;
+ }
+ else if(sscanf(line, "static unsigned char %s = {", name_and_type ) == 1 ) {
+ version = 11;
+ found_declaration = TRUE;
+ }
+ }
+ }
+
+ if (!found_declaration)
+ return( ERR_XBM_DECL );
+
+ if (*widthP == -1 )
+ return( ERR_XBM_WIDTH );
+ if ( *heightP == -1 )
+ return( ERR_XBM_HEIGHT );
+
+ padding = 0;
+ if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && (version == 10) )
+ padding = 1;
+
+ bytes_per_line = (*widthP + 7) / 8 + padding;
+
+ raster_length = bytes_per_line * *heightP;
+ *dataP = (char*) malloc( raster_length );
+ if ( *dataP == (char*) 0 )
+ return( ERR_XBM_MEMORY );
+
+ // initialize hex_table
+ for ( c1 = 0; c1 < 256; c1++ ) {
+ hex_table[c1] = 256;
+ }
+ hex_table['0'] = 0;
+ hex_table['1'] = 1;
+ hex_table['2'] = 2;
+ hex_table['3'] = 3;
+ hex_table['4'] = 4;
+ hex_table['5'] = 5;
+ hex_table['6'] = 6;
+ hex_table['7'] = 7;
+ hex_table['8'] = 8;
+ hex_table['9'] = 9;
+ hex_table['A'] = 10;
+ hex_table['B'] = 11;
+ hex_table['C'] = 12;
+ hex_table['D'] = 13;
+ hex_table['E'] = 14;
+ hex_table['F'] = 15;
+ hex_table['a'] = 10;
+ hex_table['b'] = 11;
+ hex_table['c'] = 12;
+ hex_table['d'] = 13;
+ hex_table['e'] = 14;
+ hex_table['f'] = 15;
+
+ if(version == 10) {
+ for ( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) {
+ while( ( c1 = readChar(io, handle) ) != 'x' ) {
+ if ( c1 == EOF )
+ return( ERR_XBM_EOFREAD );
+ }
+
+ c1 = readChar(io, handle);
+ c2 = readChar(io, handle);
+ if ( c1 == EOF || c2 == EOF )
+ return( ERR_XBM_EOFREAD );
+ value1 = ( hex_table[c1] << 4 ) + hex_table[c2];
+ if ( value1 >= 256 )
+ return( ERR_XBM_SYNTAX );
+ c1 = readChar(io, handle);
+ c2 = readChar(io, handle);
+ if ( c1 == EOF || c2 == EOF )
+ return( ERR_XBM_EOFREAD );
+ value2 = ( hex_table[c1] << 4 ) + hex_table[c2];
+ if ( value2 >= 256 )
+ return( ERR_XBM_SYNTAX );
+ *ptr++ = (char)value2;
+ if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) )
+ *ptr++ = (char)value1;
+ }
+ }
+ else {
+ for(bytes = 0, ptr = *dataP; bytes < raster_length; bytes++ ) {
+ /*
+ ** skip until digit is found
+ */
+ for ( ; ; ) {
+ c1 = readChar(io, handle);
+ if ( c1 == EOF )
+ return( ERR_XBM_EOFREAD );
+ value1 = hex_table[c1];
+ if ( value1 != 256 )
+ break;
+ }
+ /*
+ ** loop on digits
+ */
+ for ( ; ; ) {
+ c2 = readChar(io, handle);
+ if ( c2 == EOF )
+ return( ERR_XBM_EOFREAD );
+ value2 = hex_table[c2];
+ if ( value2 != 256 ) {
+ value1 = (value1 << 4) | value2;
+ if ( value1 >= 256 )
+ return( ERR_XBM_SYNTAX );
+ }
+ else if ( c2 == 'x' || c2 == 'X' ) {
+ if ( value1 == 0 )
+ continue;
+ else return( ERR_XBM_SYNTAX );
+ }
+ else break;
+ }
+ *ptr++ = (char)value1;
+ }
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+
+static int s_format_id;
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "XBM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "X11 Bitmap Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "xbm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return NULL;
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-xbitmap";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ char magic[8];
+ if(readLine(magic, 7, io, handle)) {
+ if(strcmp(magic, "#define") == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return FALSE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ char *buffer = NULL;
+ int width, height;
+ FIBITMAP *dib = NULL;
+
+ try {
+
+ // load the bitmap data
+ const char* error = readXBMFile(io, handle, &width, &height, &buffer);
+ // Microsoft doesn't implement throw between functions :(
+ if(error) throw (char*)error;
+
+
+ // allocate a new dib
+ dib = FreeImage_Allocate(width, height, 1);
+ if (!dib) throw (char*)ERR_XBM_MEMORY;
+
+ // write the palette data
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
+ pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
+
+ // copy the bitmap
+ BYTE *bP = (BYTE*)buffer;
+ for(int y = 0; y < height; y++) {
+ BYTE count = 0;
+ BYTE mask = 1;
+ BYTE *bits = FreeImage_GetScanLine(dib, height - 1 - y);
+
+ for(int x = 0; x < width; x++) {
+ if(count >= 8) {
+ bP++;
+ count = 0;
+ mask = 1;
+ }
+ if (*bP & mask) {
+ // Set bit(x, y) to 0
+ bits[x >> 3] &= (0xFF7F >> (x & 0x7));
+ } else {
+ // Set bit(x, y) to 1
+ bits[x >> 3] |= (0x80 >> (x & 0x7));
+ }
+ count++;
+ mask <<= 1;
+ }
+ bP++;
+ }
+
+ free(buffer);
+ return dib;
+
+ } catch(const char *text) {
+ if(buffer) free(buffer);
+ if(dib) FreeImage_Unload(dib);
+ FreeImage_OutputMessageProc(s_format_id, text);
+ return NULL;
+ }
+}
+
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitXBM(Plugin *plugin, int format_id) {
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = NULL;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/PluginXPM.cpp b/plugins/AdvaImg/src/FreeImage/PluginXPM.cpp
new file mode 100644
index 0000000000..16db443c47
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/PluginXPM.cpp
@@ -0,0 +1,487 @@
+// ==========================================================
+// XPM Loader and Writer
+//
+// Design and implementation by
+// - Ryan Rubley (ryan@lostreality.org)
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#ifdef _MSC_VER
+#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
+#endif
+
+// IMPLEMENTATION NOTES:
+// ------------------------
+// Initial design and implementation by
+// - Karl-Heinz Bussian (khbussian@moss.de)
+// - Hervé Drolon (drolon@infonie.fr)
+// Completely rewritten from scratch by Ryan Rubley (ryan@lostreality.org)
+// in order to address the following major fixes:
+// * Supports any number of chars per pixel (not just 1 or 2)
+// * Files with 2 chars per pixel but <= 256colors are loaded as 256 color (not 24bit)
+// * Loads much faster, uses much less memory
+// * supports #rgb #rrrgggbbb and #rrrrggggbbbb colors (not just #rrggbb)
+// * supports symbolic color names
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+// ==========================================================
+// Plugin Interface
+// ==========================================================
+static int s_format_id;
+
+// ==========================================================
+// Internal Functions
+// ==========================================================
+
+// read in and skip all junk until we find a certain char
+static BOOL
+FindChar(FreeImageIO *io, fi_handle handle, BYTE look_for) {
+ BYTE c;
+ io->read_proc(&c, sizeof(BYTE), 1, handle);
+ while(c != look_for) {
+ if ( io->read_proc(&c, sizeof(BYTE), 1, handle) != 1 )
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// find start of string, read data until ending quote found, allocate memory and return a string
+static char *
+ReadString(FreeImageIO *io, fi_handle handle) {
+ if ( !FindChar(io, handle,'"') )
+ return NULL;
+ BYTE c;
+ std::string s;
+ io->read_proc(&c, sizeof(BYTE), 1, handle);
+ while(c != '"') {
+ s += c;
+ if ( io->read_proc(&c, sizeof(BYTE), 1, handle) != 1 )
+ return NULL;
+ }
+ char *cstr = (char *)malloc(s.length()+1);
+ strcpy(cstr,s.c_str());
+ return cstr;
+}
+
+static char *
+Base92(unsigned int num) {
+ static char b92[16]; //enough for more then 64 bits
+ static char digit[] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
+ b92[15] = '\0';
+ int i = 14;
+ do {
+ b92[i--] = digit[num % 92];
+ num /= 92;
+ } while( num && i >= 0 );
+ return b92+i+1;
+}
+
+// ==========================================================
+// Plugin Implementation
+// ==========================================================
+
+static const char * DLL_CALLCONV
+Format() {
+ return "XPM";
+}
+
+static const char * DLL_CALLCONV
+Description() {
+ return "X11 Pixmap Format";
+}
+
+static const char * DLL_CALLCONV
+Extension() {
+ return "xpm";
+}
+
+static const char * DLL_CALLCONV
+RegExpr() {
+ return "^[ \\t]*/\\* XPM \\*/[ \\t]$";
+}
+
+static const char * DLL_CALLCONV
+MimeType() {
+ return "image/x-xpixmap";
+}
+
+static BOOL DLL_CALLCONV
+Validate(FreeImageIO *io, fi_handle handle) {
+ char buffer[256];
+
+ // checks the first 256 characters for the magic string
+ int count = io->read_proc(buffer, 1, 256, handle);
+ if(count <= 9) return FALSE;
+ for(int i = 0; i < (count - 9); i++) {
+ if(strncmp(&buffer[i], "/* XPM */", 9) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportDepth(int depth) {
+ return (
+ (depth == 8) ||
+ (depth == 24)
+ );
+}
+
+static BOOL DLL_CALLCONV
+SupportsExportType(FREE_IMAGE_TYPE type) {
+ return (type == FIT_BITMAP) ? TRUE : FALSE;
+}
+
+static BOOL DLL_CALLCONV
+SupportsNoPixels() {
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+
+static FIBITMAP * DLL_CALLCONV
+Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
+ char msg[256];
+ FIBITMAP *dib = NULL;
+
+ if (!handle) return NULL;
+
+ try {
+ char *str;
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ //find the starting brace
+ if ( !FindChar(io, handle,'{') )
+ throw "Could not find starting brace";
+
+ //read info string
+ str = ReadString(io, handle);
+ if (!str)
+ throw "Error reading info string";
+
+ int width, height, colors, cpp;
+ if ( sscanf(str, "%d %d %d %d", &width, &height, &colors, &cpp) != 4 ) {
+ free(str);
+ throw "Improperly formed info string";
+ }
+ free(str);
+
+ if (colors > 256) {
+ dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ } else {
+ dib = FreeImage_AllocateHeader(header_only, width, height, 8);
+ }
+
+ //build a map of color chars to rgb values
+ std::map<std::string,FILE_RGBA> rawpal; //will store index in Alpha if 8bpp
+ for(int i = 0; i < colors; i++ ) {
+ FILE_RGBA rgba;
+
+ str = ReadString(io, handle);
+ if (!str)
+ throw "Error reading color strings";
+
+ std::string chrs(str,cpp); //create a string for the color chars using the first cpp chars
+ char *keys = str + cpp; //the color keys for these chars start after the first cpp chars
+
+ //translate all the tabs to spaces
+ char *tmp = keys;
+ while( strchr(tmp,'\t') ) {
+ tmp = strchr(tmp,'\t');
+ *tmp++ = ' ';
+ }
+
+ //prefer the color visual
+ if ( strstr(keys," c ") ) {
+ char *clr = strstr(keys," c ") + 3;
+ while( *clr == ' ' ) clr++; //find the start of the hex rgb value
+ if ( *clr == '#' ) {
+ int red = 0, green = 0, blue = 0, n;
+ clr++;
+ //end string at first space, if any found
+ if ( strchr(clr,' ') )
+ *(strchr(clr,' ')) = '\0';
+ //parse hex color, it can be #rgb #rrggbb #rrrgggbbb or #rrrrggggbbbb
+ switch( strlen(clr) ) {
+ case 3: n = sscanf(clr,"%01x%01x%01x",&red,&green,&blue);
+ red |= (red << 4);
+ green |= (green << 4);
+ blue |= (blue << 4);
+ break;
+ case 6: n = sscanf(clr,"%02x%02x%02x",&red,&green,&blue);
+ break;
+ case 9: n = sscanf(clr,"%03x%03x%03x",&red,&green,&blue);
+ red >>= 4;
+ green >>= 4;
+ blue >>= 4;
+ break;
+ case 12: n = sscanf(clr,"%04x%04x%04x",&red,&green,&blue);
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ break;
+ default:
+ n = 0;
+ break;
+ }
+ if ( n != 3 ) {
+ free(str);
+ throw "Improperly formed hex color value";
+ }
+ rgba.r = (BYTE)red;
+ rgba.g = (BYTE)green;
+ rgba.b = (BYTE)blue;
+ } else if ( !strncmp(clr,"None",4) || !strncmp(clr,"none",4) ) {
+ rgba.r = rgba.g = rgba.b = 0xFF;
+ } else {
+ char *tmp = clr;
+
+ //scan forward for each space, if its " x " or " xx " end the string there
+ //this means its probably some other visual data beyond that point and not
+ //part of the color name. How many named color end with a 1 or 2 character
+ //word? Probably none in our list at least.
+ while( (tmp = strchr(tmp,' ')) != NULL ) {
+ if ( tmp[1] != ' ' ) {
+ if ( (tmp[2] == ' ') || (tmp[2] != ' ' && tmp[3] == ' ') ) {
+ tmp[0] = '\0';
+ break;
+ }
+ }
+ tmp++;
+ }
+
+ //remove any trailing spaces
+ tmp = clr+strlen(clr)-1;
+ while( *tmp == ' ' ) {
+ *tmp = '\0';
+ tmp--;
+ }
+
+ if (!FreeImage_LookupX11Color(clr, &rgba.r, &rgba.g, &rgba.b)) {
+ sprintf(msg, "Unknown color name '%s'", str);
+ free(str);
+ throw msg;
+ }
+ }
+ } else {
+ free(str);
+ throw "Only color visuals are supported";
+ }
+
+ //add color to map
+ rgba.a = (BYTE)((colors > 256) ? 0 : i);
+ rawpal[chrs] = rgba;
+
+ //build palette if needed
+ if ( colors <= 256 ) {
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ pal[i].rgbBlue = rgba.b;
+ pal[i].rgbGreen = rgba.g;
+ pal[i].rgbRed = rgba.r;
+ }
+
+ free(str);
+ }
+ //done parsing color map
+
+ if(header_only) {
+ // header only mode
+ return dib;
+ }
+
+ //read in pixel data
+ for(int y = 0; y < height; y++ ) {
+ BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
+ str = ReadString(io, handle);
+ if (!str)
+ throw "Error reading pixel strings";
+ char *pixel_ptr = str;
+
+ for(int x = 0; x < width; x++ ) {
+ //locate the chars in the color map
+ std::string chrs(pixel_ptr,cpp);
+ FILE_RGBA rgba = rawpal[chrs];
+
+ if ( colors > 256 ) {
+ line[FI_RGBA_BLUE] = rgba.b;
+ line[FI_RGBA_GREEN] = rgba.g;
+ line[FI_RGBA_RED] = rgba.r;
+ line += 3;
+ } else {
+ *line = rgba.a;
+ line++;
+ }
+
+ pixel_ptr += cpp;
+ }
+
+ free(str);
+ }
+ //done reading pixel data
+
+ return dib;
+ } catch(const char *text) {
+ FreeImage_OutputMessageProc(s_format_id, text);
+
+ if ( dib != NULL )
+ FreeImage_Unload(dib);
+
+ return NULL;
+ }
+}
+
+static BOOL DLL_CALLCONV
+Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
+ if ((dib != NULL) && (handle != NULL)) {
+ char header[] = "/* XPM */\nstatic char *freeimage[] = {\n/* width height num_colors chars_per_pixel */\n\"",
+ start_colors[] = "\",\n/* colors */\n\"",
+ start_pixels[] = "\",\n/* pixels */\n\"",
+ new_line[] = "\",\n\"",
+ footer[] = "\"\n};\n",
+ buf[256]; //256 is more then enough to sprintf 4 ints into, or the base-92 chars and #rrggbb line
+
+ if ( io->write_proc(header, (unsigned int)strlen(header), 1, handle) != 1 )
+ return FALSE;
+
+ int width = FreeImage_GetWidth(dib), height = FreeImage_GetHeight(dib), bpp = FreeImage_GetBPP(dib);
+ RGBQUAD *pal = FreeImage_GetPalette(dib);
+ int x,y;
+
+ //map base92 chrs to the rgb value to create the palette
+ std::map<DWORD,FILE_RGB> chrs2color;
+ //map 8bpp index or 24bpp rgb value to the base92 chrs to create pixel data
+ typedef union {
+ DWORD index;
+ FILE_RGBA rgba;
+ } DWORDRGBA;
+ std::map<DWORD,std::string> color2chrs;
+
+ //loop thru entire dib, if new color, inc num_colors and add to both maps
+ int num_colors = 0;
+ for(y = 0; y < height; y++ ) {
+ BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
+ for(x = 0; x < width; x++ ) {
+ FILE_RGB rgb;
+ DWORDRGBA u;
+ if ( bpp > 8 ) {
+ u.rgba.b = rgb.b = line[FI_RGBA_BLUE];
+ u.rgba.g = rgb.g = line[FI_RGBA_GREEN];
+ u.rgba.r = rgb.r = line[FI_RGBA_RED];
+ u.rgba.a = 0;
+ line += 3;
+ } else {
+ u.index = *line;
+ rgb.b = pal[u.index].rgbBlue;
+ rgb.g = pal[u.index].rgbGreen;
+ rgb.r = pal[u.index].rgbRed;
+ line++;
+ }
+ if ( color2chrs.find(u.index) == color2chrs.end() ) { //new color
+ std::string chrs(Base92(num_colors));
+ color2chrs[u.index] = chrs;
+ chrs2color[num_colors] = rgb;
+ num_colors++;
+ }
+ }
+ }
+
+ int cpp = (int)(log((double)num_colors)/log(92.0)) + 1;
+
+ sprintf(buf, "%d %d %d %d", FreeImage_GetWidth(dib), FreeImage_GetHeight(dib), num_colors, cpp );
+ if ( io->write_proc(buf, (unsigned int)strlen(buf), 1, handle) != 1 )
+ return FALSE;
+
+ if ( io->write_proc(start_colors, (unsigned int)strlen(start_colors), 1, handle) != 1 )
+ return FALSE;
+
+ //write colors, using map of chrs->rgb
+ for(x = 0; x < num_colors; x++ ) {
+ sprintf(buf, "%*s c #%02x%02x%02x", cpp, Base92(x), chrs2color[x].r, chrs2color[x].g, chrs2color[x].b );
+ if ( io->write_proc(buf, (unsigned int)strlen(buf), 1, handle) != 1 )
+ return FALSE;
+ if ( x == num_colors - 1 ) {
+ if ( io->write_proc(start_pixels, (unsigned int)strlen(start_pixels), 1, handle) != 1 )
+ return FALSE;
+ } else {
+ if ( io->write_proc(new_line, (unsigned int)strlen(new_line), 1, handle) != 1 )
+ return FALSE;
+ }
+ }
+
+
+ //write pixels, using map of rgb(if 24bpp) or index(if 8bpp)->chrs
+ for(y = 0; y < height; y++ ) {
+ BYTE *line = FreeImage_GetScanLine(dib, height - y - 1);
+ for(x = 0; x < width; x++ ) {
+ DWORDRGBA u;
+ if ( bpp > 8 ) {
+ u.rgba.b = line[FI_RGBA_BLUE];
+ u.rgba.g = line[FI_RGBA_GREEN];
+ u.rgba.r = line[FI_RGBA_RED];
+ u.rgba.a = 0;
+ line += 3;
+ } else {
+ u.index = *line;
+ line++;
+ }
+ sprintf(buf, "%*s", cpp, (char *)color2chrs[u.index].c_str());
+ if ( io->write_proc(buf, cpp, 1, handle) != 1 )
+ return FALSE;
+ }
+ if ( y == height - 1 ) {
+ if ( io->write_proc(footer, (unsigned int)strlen(footer), 1, handle) != 1 )
+ return FALSE;
+ } else {
+ if ( io->write_proc(new_line, (unsigned int)strlen(new_line), 1, handle) != 1 )
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+// ==========================================================
+// Init
+// ==========================================================
+
+void DLL_CALLCONV
+InitXPM(Plugin *plugin, int format_id)
+{
+ s_format_id = format_id;
+
+ plugin->format_proc = Format;
+ plugin->description_proc = Description;
+ plugin->extension_proc = Extension;
+ plugin->regexpr_proc = RegExpr;
+ plugin->open_proc = NULL;
+ plugin->close_proc = NULL;
+ plugin->pagecount_proc = NULL;
+ plugin->pagecapability_proc = NULL;
+ plugin->load_proc = Load;
+ plugin->save_proc = Save;
+ plugin->validate_proc = Validate;
+ plugin->mime_proc = MimeType;
+ plugin->supports_export_bpp_proc = SupportsExportDepth;
+ plugin->supports_export_type_proc = SupportsExportType;
+ plugin->supports_icc_profiles_proc = NULL;
+ plugin->supports_no_pixels_proc = SupportsNoPixels;
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/TIFFLogLuv.cpp b/plugins/AdvaImg/src/FreeImage/TIFFLogLuv.cpp
new file mode 100644
index 0000000000..124b25f64f
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/TIFFLogLuv.cpp
@@ -0,0 +1,65 @@
+// ==========================================================
+// XYZ to RGB TIFF conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+void tiff_ConvertLineXYZToRGB(BYTE *target, BYTE *source, double stonits, int width_in_pixels) {
+ FIRGBF *rgbf = (FIRGBF*)target;
+ float *xyz = (float*)source;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ // assume CCIR-709 primaries (matrix from tif_luv.c)
+ // LOG Luv XYZ (D65) -> sRGB (CIE Illuminant E)
+ rgbf->red = (float)( 2.690*xyz[0] + -1.276*xyz[1] + -0.414*xyz[2]);
+ rgbf->green = (float)(-1.022*xyz[0] + 1.978*xyz[1] + 0.044*xyz[2]);
+ rgbf->blue = (float)( 0.061*xyz[0] + -0.224*xyz[1] + 1.163*xyz[2]);
+
+ /*
+ if (stonits != 0.0) {
+ rgbf->red = (float)(rgbf->red * stonits);
+ rgbf->green = (float)(rgbf->green * stonits);
+ rgbf->blue = (float)(rgbf->blue * stonits);
+ }
+ */
+
+ rgbf++;
+ xyz += 3;
+ }
+}
+
+void tiff_ConvertLineRGBToXYZ(BYTE *target, BYTE *source, int width_in_pixels) {
+ FIRGBF *rgbf = (FIRGBF*)source;
+ float *xyz = (float*)target;
+
+ for (int cols = 0; cols < width_in_pixels; cols++) {
+ // assume CCIR-709 primaries, whitepoint x = 1/3 y = 1/3 (D_E)
+ // "The LogLuv Encoding for Full Gamut, High Dynamic Range Images" <G.Ward>
+ // sRGB ( CIE Illuminant E ) -> LOG Luv XYZ (D65)
+ xyz[0] = (float)(0.497*rgbf->red + 0.339*rgbf->green + 0.164*rgbf->blue);
+ xyz[1] = (float)(0.256*rgbf->red + 0.678*rgbf->green + 0.066*rgbf->blue);
+ xyz[2] = (float)(0.023*rgbf->red + 0.113*rgbf->green + 0.864*rgbf->blue);
+
+ rgbf++;
+ xyz += 3;
+ }
+}
+
diff --git a/plugins/AdvaImg/src/FreeImage/ToneMapping.cpp b/plugins/AdvaImg/src/FreeImage/ToneMapping.cpp
new file mode 100644
index 0000000000..33ae08ea59
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ToneMapping.cpp
@@ -0,0 +1,75 @@
+// ==========================================================
+// Tone mapping operators
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+
+/**
+Performs a tone mapping on a 48-bit RGB or a 96-bit RGBF image and returns a 24-bit image.
+The meaning of the parameters depends on the choosen algorithm.
+When both parameters are set to zero, a default set of parameters is used.
+@param dib Input RGB/RGBF image
+@param tmo Tone mapping operator
+@param first_param First parameter of the algorithm
+@param second_param Second parameter of the algorithm
+return Returns a 24-bit tone mapped image if successful, returns NULL otherwise
+*/
+FIBITMAP * DLL_CALLCONV
+FreeImage_ToneMapping(FIBITMAP *dib, FREE_IMAGE_TMO tmo, double first_param, double second_param) {
+ if(FreeImage_HasPixels(dib)) {
+ switch(tmo) {
+ // Adaptive logarithmic mapping (F. Drago, 2003)
+ case FITMO_DRAGO03:
+ if ((first_param == 0) && (second_param == 0)) {
+ // use default values (gamma = 2.2, exposure = 0)
+ return FreeImage_TmoDrago03(dib, 2.2, 0);
+ } else {
+ // use user's value
+ return FreeImage_TmoDrago03(dib, first_param, second_param);
+ }
+ break;
+ // Dynamic range reduction inspired by photoreceptor phhysiology (E. Reinhard, 2005)
+ case FITMO_REINHARD05:
+ if ((first_param == 0) && (second_param == 0)) {
+ // use default values by setting intensity to 0 and contrast to 0
+ return FreeImage_TmoReinhard05(dib, 0, 0);
+ } else {
+ // use user's value
+ return FreeImage_TmoReinhard05(dib, first_param, second_param);
+ }
+ break;
+ // Gradient Domain HDR Compression (R. Fattal, 2002)
+ case FITMO_FATTAL02:
+ if ((first_param == 0) && (second_param == 0)) {
+ // use default values by setting color saturation to 0.5 and attenuation to 0.85
+ return FreeImage_TmoFattal02(dib, 0.5, 0.85);
+ } else {
+ // use user's value
+ return FreeImage_TmoFattal02(dib, first_param, second_param);
+ }
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
diff --git a/plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp b/plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp
new file mode 100644
index 0000000000..532fbfc3e6
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp
@@ -0,0 +1,538 @@
+///////////////////////////////////////////////////////////////////////
+// C Implementation of Wu's Color Quantizer (v. 2)
+// (see Graphics Gems vol. II, pp. 126-133)
+//
+// Author: Xiaolin Wu
+// Dept. of Computer Science
+// Univ. of Western Ontario
+// London, Ontario N6A 5B7
+// wu@csd.uwo.ca
+//
+// Algorithm: Greedy orthogonal bipartition of RGB space for variance
+// minimization aided by inclusion-exclusion tricks.
+// For speed no nearest neighbor search is done. Slightly
+// better performance can be expected by more sophisticated
+// but more expensive versions.
+//
+// The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
+// additional documentation and a cure to a previous bug.
+//
+// Free to distribute, comments and suggestions are appreciated.
+///////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////
+// History
+// -------
+// July 2000: C++ Implementation of Wu's Color Quantizer
+// and adaptation for the FreeImage 2 Library
+// Author: Hervé Drolon (drolon@infonie.fr)
+// March 2004: Adaptation for the FreeImage 3 library (port to big endian processors)
+// Author: Hervé Drolon (drolon@infonie.fr)
+///////////////////////////////////////////////////////////////////////
+
+#include "Quantizers.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+
+///////////////////////////////////////////////////////////////////////
+
+// Size of a 3D array : 33 x 33 x 33
+#define SIZE_3D 35937
+
+// 3D array indexation
+#define INDEX(r, g, b) ((r << 10) + (r << 6) + r + (g << 5) + g + b)
+
+#define MAXCOLOR 256
+
+// Constructor / Destructor
+
+WuQuantizer::WuQuantizer(FIBITMAP *dib) {
+ width = FreeImage_GetWidth(dib);
+ height = FreeImage_GetHeight(dib);
+ pitch = FreeImage_GetPitch(dib);
+ m_dib = dib;
+
+ gm2 = NULL;
+ wt = mr = mg = mb = NULL;
+ Qadd = NULL;
+
+ // Allocate 3D arrays
+ gm2 = (float*)malloc(SIZE_3D * sizeof(float));
+ wt = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+ mr = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+ mg = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+ mb = (LONG*)malloc(SIZE_3D * sizeof(LONG));
+
+ // Allocate Qadd
+ Qadd = (WORD *)malloc(sizeof(WORD) * width * height);
+
+ if (!gm2 || !wt || !mr || !mg || !mb || !Qadd) {
+ if(gm2) free(gm2);
+ if(wt) free(wt);
+ if(mr) free(mr);
+ if(mg) free(mg);
+ if(mb) free(mb);
+ if(Qadd) free(Qadd);
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ memset(gm2, 0, SIZE_3D * sizeof(float));
+ memset(wt, 0, SIZE_3D * sizeof(LONG));
+ memset(mr, 0, SIZE_3D * sizeof(LONG));
+ memset(mg, 0, SIZE_3D * sizeof(LONG));
+ memset(mb, 0, SIZE_3D * sizeof(LONG));
+ memset(Qadd, 0, sizeof(WORD) * width * height);
+}
+
+WuQuantizer::~WuQuantizer() {
+ if(gm2) free(gm2);
+ if(wt) free(wt);
+ if(mr) free(mr);
+ if(mg) free(mg);
+ if(mb) free(mb);
+ if(Qadd) free(Qadd);
+}
+
+
+// Histogram is in elements 1..HISTSIZE along each axis,
+// element 0 is for base or marginal value
+// NB: these must start out 0!
+
+// Build 3-D color histogram of counts, r/g/b, c^2
+void
+WuQuantizer::Hist3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2, int ReserveSize, RGBQUAD *ReservePalette) {
+ int ind = 0;
+ int inr, ing, inb, table[256];
+ int i;
+ unsigned y, x;
+
+ for(i = 0; i < 256; i++)
+ table[i] = i * i;
+
+ for(y = 0; y < height; y++) {
+ BYTE *bits = FreeImage_GetScanLine(m_dib, y);
+
+ for(x = 0; x < width; x++) {
+ inr = (bits[FI_RGBA_RED] >> 3) + 1;
+ ing = (bits[FI_RGBA_GREEN] >> 3) + 1;
+ inb = (bits[FI_RGBA_BLUE] >> 3) + 1;
+ ind = INDEX(inr, ing, inb);
+ Qadd[y*width + x] = (WORD)ind;
+ // [inr][ing][inb]
+ vwt[ind]++;
+ vmr[ind] += bits[FI_RGBA_RED];
+ vmg[ind] += bits[FI_RGBA_GREEN];
+ vmb[ind] += bits[FI_RGBA_BLUE];
+ m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]);
+ bits += 3;
+ }
+ }
+
+ if ( ReserveSize > 0 ) {
+ int max = 0;
+ for(i = 0; i < SIZE_3D; i++) {
+ if ( vwt[i] > max ) max = vwt[i];
+ }
+ max++;
+ for(i = 0; i < ReserveSize; i++) {
+ inr = (ReservePalette[i].rgbRed >> 3) + 1;
+ ing = (ReservePalette[i].rgbGreen >> 3) + 1;
+ inb = (ReservePalette[i].rgbBlue >> 3) + 1;
+ ind = INDEX(inr, ing, inb);
+ wt[ind] = max;
+ mr[ind] = max * ReservePalette[i].rgbRed;
+ mg[ind] = max * ReservePalette[i].rgbGreen;
+ mb[ind] = max * ReservePalette[i].rgbBlue;
+ gm2[ind] = (float)max * (float)(table[ReservePalette[i].rgbRed] + table[ReservePalette[i].rgbGreen] + table[ReservePalette[i].rgbBlue]);
+ }
+ }
+}
+
+
+// At conclusion of the histogram step, we can interpret
+// wt[r][g][b] = sum over voxel of P(c)
+// mr[r][g][b] = sum over voxel of r*P(c) , similarly for mg, mb
+// m2[r][g][b] = sum over voxel of c^2*P(c)
+// Actually each of these should be divided by 'ImageSize' to give the usual
+// interpretation of P() as ranging from 0 to 1, but we needn't do that here.
+
+
+// We now convert histogram into moments so that we can rapidly calculate
+// the sums of the above quantities over any desired box.
+
+// Compute cumulative moments
+void
+WuQuantizer::M3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2) {
+ unsigned ind1, ind2;
+ BYTE i, r, g, b;
+ LONG line, line_r, line_g, line_b;
+ LONG area[33], area_r[33], area_g[33], area_b[33];
+ float line2, area2[33];
+
+ for(r = 1; r <= 32; r++) {
+ for(i = 0; i <= 32; i++) {
+ area2[i] = 0;
+ area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
+ }
+ for(g = 1; g <= 32; g++) {
+ line2 = 0;
+ line = line_r = line_g = line_b = 0;
+ for(b = 1; b <= 32; b++) {
+ ind1 = INDEX(r, g, b); // [r][g][b]
+ line += vwt[ind1];
+ line_r += vmr[ind1];
+ line_g += vmg[ind1];
+ line_b += vmb[ind1];
+ line2 += m2[ind1];
+ area[b] += line;
+ area_r[b] += line_r;
+ area_g[b] += line_g;
+ area_b[b] += line_b;
+ area2[b] += line2;
+ ind2 = ind1 - 1089; // [r-1][g][b]
+ vwt[ind1] = vwt[ind2] + area[b];
+ vmr[ind1] = vmr[ind2] + area_r[b];
+ vmg[ind1] = vmg[ind2] + area_g[b];
+ vmb[ind1] = vmb[ind2] + area_b[b];
+ m2[ind1] = m2[ind2] + area2[b];
+ }
+ }
+ }
+}
+
+// Compute sum over a box of any given statistic
+LONG
+WuQuantizer::Vol( Box *cube, LONG *mmt ) {
+ return( mmt[INDEX(cube->r1, cube->g1, cube->b1)]
+ - mmt[INDEX(cube->r1, cube->g1, cube->b0)]
+ - mmt[INDEX(cube->r1, cube->g0, cube->b1)]
+ + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
+ - mmt[INDEX(cube->r0, cube->g1, cube->b1)]
+ + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
+ + mmt[INDEX(cube->r0, cube->g0, cube->b1)]
+ - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+}
+
+// The next two routines allow a slightly more efficient calculation
+// of Vol() for a proposed subbox of a given box. The sum of Top()
+// and Bottom() is the Vol() of a subbox split in the given direction
+// and with the specified new upper bound.
+
+
+// Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1
+// (depending on dir)
+
+LONG
+WuQuantizer::Bottom(Box *cube, BYTE dir, LONG *mmt) {
+ switch(dir)
+ {
+ case FI_RGBA_RED:
+ return( - mmt[INDEX(cube->r0, cube->g1, cube->b1)]
+ + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
+ + mmt[INDEX(cube->r0, cube->g0, cube->b1)]
+ - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+ break;
+ case FI_RGBA_GREEN:
+ return( - mmt[INDEX(cube->r1, cube->g0, cube->b1)]
+ + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
+ + mmt[INDEX(cube->r0, cube->g0, cube->b1)]
+ - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+ break;
+ case FI_RGBA_BLUE:
+ return( - mmt[INDEX(cube->r1, cube->g1, cube->b0)]
+ + mmt[INDEX(cube->r1, cube->g0, cube->b0)]
+ + mmt[INDEX(cube->r0, cube->g1, cube->b0)]
+ - mmt[INDEX(cube->r0, cube->g0, cube->b0)] );
+ break;
+ }
+
+ return 0;
+}
+
+
+// Compute remainder of Vol(cube, mmt), substituting pos for
+// r1, g1, or b1 (depending on dir)
+
+LONG
+WuQuantizer::Top(Box *cube, BYTE dir, int pos, LONG *mmt) {
+ switch(dir)
+ {
+ case FI_RGBA_RED:
+ return( mmt[INDEX(pos, cube->g1, cube->b1)]
+ -mmt[INDEX(pos, cube->g1, cube->b0)]
+ -mmt[INDEX(pos, cube->g0, cube->b1)]
+ +mmt[INDEX(pos, cube->g0, cube->b0)] );
+ break;
+ case FI_RGBA_GREEN:
+ return( mmt[INDEX(cube->r1, pos, cube->b1)]
+ -mmt[INDEX(cube->r1, pos, cube->b0)]
+ -mmt[INDEX(cube->r0, pos, cube->b1)]
+ +mmt[INDEX(cube->r0, pos, cube->b0)] );
+ break;
+ case FI_RGBA_BLUE:
+ return( mmt[INDEX(cube->r1, cube->g1, pos)]
+ -mmt[INDEX(cube->r1, cube->g0, pos)]
+ -mmt[INDEX(cube->r0, cube->g1, pos)]
+ +mmt[INDEX(cube->r0, cube->g0, pos)] );
+ break;
+ }
+
+ return 0;
+}
+
+// Compute the weighted variance of a box
+// NB: as with the raw statistics, this is really the variance * ImageSize
+
+float
+WuQuantizer::Var(Box *cube) {
+ float dr = (float) Vol(cube, mr);
+ float dg = (float) Vol(cube, mg);
+ float db = (float) Vol(cube, mb);
+ float xx = gm2[INDEX(cube->r1, cube->g1, cube->b1)]
+ -gm2[INDEX(cube->r1, cube->g1, cube->b0)]
+ -gm2[INDEX(cube->r1, cube->g0, cube->b1)]
+ +gm2[INDEX(cube->r1, cube->g0, cube->b0)]
+ -gm2[INDEX(cube->r0, cube->g1, cube->b1)]
+ +gm2[INDEX(cube->r0, cube->g1, cube->b0)]
+ +gm2[INDEX(cube->r0, cube->g0, cube->b1)]
+ -gm2[INDEX(cube->r0, cube->g0, cube->b0)];
+
+ return (xx - (dr*dr+dg*dg+db*db)/(float)Vol(cube,wt));
+}
+
+// We want to minimize the sum of the variances of two subboxes.
+// The sum(c^2) terms can be ignored since their sum over both subboxes
+// is the same (the sum for the whole box) no matter where we split.
+// The remaining terms have a minus sign in the variance formula,
+// so we drop the minus sign and MAXIMIZE the sum of the two terms.
+
+float
+WuQuantizer::Maximize(Box *cube, BYTE dir, int first, int last , int *cut, LONG whole_r, LONG whole_g, LONG whole_b, LONG whole_w) {
+ LONG half_r, half_g, half_b, half_w;
+ int i;
+ float temp;
+
+ LONG base_r = Bottom(cube, dir, mr);
+ LONG base_g = Bottom(cube, dir, mg);
+ LONG base_b = Bottom(cube, dir, mb);
+ LONG base_w = Bottom(cube, dir, wt);
+
+ float max = 0.0;
+
+ *cut = -1;
+
+ for (i = first; i < last; i++) {
+ half_r = base_r + Top(cube, dir, i, mr);
+ half_g = base_g + Top(cube, dir, i, mg);
+ half_b = base_b + Top(cube, dir, i, mb);
+ half_w = base_w + Top(cube, dir, i, wt);
+
+ // now half_x is sum over lower half of box, if split at i
+
+ if (half_w == 0) { // subbox could be empty of pixels!
+ continue; // never split into an empty box
+ } else {
+ temp = ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w;
+ }
+
+ half_r = whole_r - half_r;
+ half_g = whole_g - half_g;
+ half_b = whole_b - half_b;
+ half_w = whole_w - half_w;
+
+ if (half_w == 0) { // subbox could be empty of pixels!
+ continue; // never split into an empty box
+ } else {
+ temp += ((float)half_r*half_r + (float)half_g*half_g + (float)half_b*half_b)/half_w;
+ }
+
+ if (temp > max) {
+ max=temp;
+ *cut=i;
+ }
+ }
+
+ return max;
+}
+
+bool
+WuQuantizer::Cut(Box *set1, Box *set2) {
+ BYTE dir;
+ int cutr, cutg, cutb;
+
+ LONG whole_r = Vol(set1, mr);
+ LONG whole_g = Vol(set1, mg);
+ LONG whole_b = Vol(set1, mb);
+ LONG whole_w = Vol(set1, wt);
+
+ float maxr = Maximize(set1, FI_RGBA_RED, set1->r0+1, set1->r1, &cutr, whole_r, whole_g, whole_b, whole_w);
+ float maxg = Maximize(set1, FI_RGBA_GREEN, set1->g0+1, set1->g1, &cutg, whole_r, whole_g, whole_b, whole_w);
+ float maxb = Maximize(set1, FI_RGBA_BLUE, set1->b0+1, set1->b1, &cutb, whole_r, whole_g, whole_b, whole_w);
+
+ if ((maxr >= maxg) && (maxr >= maxb)) {
+ dir = FI_RGBA_RED;
+
+ if (cutr < 0) {
+ return false; // can't split the box
+ }
+ } else if ((maxg >= maxr) && (maxg>=maxb)) {
+ dir = FI_RGBA_GREEN;
+ } else {
+ dir = FI_RGBA_BLUE;
+ }
+
+ set2->r1 = set1->r1;
+ set2->g1 = set1->g1;
+ set2->b1 = set1->b1;
+
+ switch (dir) {
+ case FI_RGBA_RED:
+ set2->r0 = set1->r1 = cutr;
+ set2->g0 = set1->g0;
+ set2->b0 = set1->b0;
+ break;
+
+ case FI_RGBA_GREEN:
+ set2->g0 = set1->g1 = cutg;
+ set2->r0 = set1->r0;
+ set2->b0 = set1->b0;
+ break;
+
+ case FI_RGBA_BLUE:
+ set2->b0 = set1->b1 = cutb;
+ set2->r0 = set1->r0;
+ set2->g0 = set1->g0;
+ break;
+ }
+
+ set1->vol = (set1->r1-set1->r0)*(set1->g1-set1->g0)*(set1->b1-set1->b0);
+ set2->vol = (set2->r1-set2->r0)*(set2->g1-set2->g0)*(set2->b1-set2->b0);
+
+ return true;
+}
+
+
+void
+WuQuantizer::Mark(Box *cube, int label, BYTE *tag) {
+ for (int r = cube->r0 + 1; r <= cube->r1; r++) {
+ for (int g = cube->g0 + 1; g <= cube->g1; g++) {
+ for (int b = cube->b0 + 1; b <= cube->b1; b++) {
+ tag[INDEX(r, g, b)] = (BYTE)label;
+ }
+ }
+ }
+}
+
+// Wu Quantization algorithm
+FIBITMAP *
+WuQuantizer::Quantize(int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) {
+ BYTE *tag = NULL;
+
+ try {
+ Box cube[MAXCOLOR];
+ int next;
+ LONG i, weight;
+ int k;
+ float vv[MAXCOLOR], temp;
+
+ // Compute 3D histogram
+
+ Hist3D(wt, mr, mg, mb, gm2, ReserveSize, ReservePalette);
+
+ // Compute moments
+
+ M3D(wt, mr, mg, mb, gm2);
+
+ cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
+ cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
+ next = 0;
+
+ for (i = 1; i < PaletteSize; i++) {
+ if(Cut(&cube[next], &cube[i])) {
+ // volume test ensures we won't try to cut one-cell box
+ vv[next] = (cube[next].vol > 1) ? Var(&cube[next]) : 0;
+ vv[i] = (cube[i].vol > 1) ? Var(&cube[i]) : 0;
+ } else {
+ vv[next] = 0.0; // don't try to split this box again
+ i--; // didn't create box i
+ }
+
+ next = 0; temp = vv[0];
+
+ for (k = 1; k <= i; k++) {
+ if (vv[k] > temp) {
+ temp = vv[k]; next = k;
+ }
+ }
+
+ if (temp <= 0.0) {
+ PaletteSize = i + 1;
+
+ // Error: "Only got 'PaletteSize' boxes"
+
+ break;
+ }
+ }
+
+ // Partition done
+
+ // the space for array gm2 can be freed now
+
+ free(gm2);
+
+ gm2 = NULL;
+
+ // Allocate a new dib
+
+ FIBITMAP *new_dib = FreeImage_Allocate(width, height, 8);
+
+ if (new_dib == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+
+ // create an optimized palette
+
+ RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+ tag = (BYTE*) malloc(SIZE_3D * sizeof(BYTE));
+ if (tag == NULL) {
+ throw FI_MSG_ERROR_MEMORY;
+ }
+ memset(tag, 0, SIZE_3D * sizeof(BYTE));
+
+ for (k = 0; k < PaletteSize ; k++) {
+ Mark(&cube[k], k, tag);
+ weight = Vol(&cube[k], wt);
+
+ if (weight) {
+ new_pal[k].rgbRed = (BYTE)(((float)Vol(&cube[k], mr) / (float)weight) + 0.5f);
+ new_pal[k].rgbGreen = (BYTE)(((float)Vol(&cube[k], mg) / (float)weight) + 0.5f);
+ new_pal[k].rgbBlue = (BYTE)(((float)Vol(&cube[k], mb) / (float)weight) + 0.5f);
+ } else {
+ // Error: bogus box 'k'
+
+ new_pal[k].rgbRed = new_pal[k].rgbGreen = new_pal[k].rgbBlue = 0;
+ }
+ }
+
+ int npitch = FreeImage_GetPitch(new_dib);
+
+ for (unsigned y = 0; y < height; y++) {
+ BYTE *new_bits = FreeImage_GetBits(new_dib) + (y * npitch);
+
+ for (unsigned x = 0; x < width; x++) {
+ new_bits[x] = tag[Qadd[y*width + x]];
+ }
+ }
+
+ // output 'new_pal' as color look-up table contents,
+ // 'new_bits' as the quantized image (array of table addresses).
+
+ free(tag);
+
+ return (FIBITMAP*) new_dib;
+ } catch(...) {
+ free(tag);
+ }
+
+ return NULL;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/ZLibInterface.cpp b/plugins/AdvaImg/src/FreeImage/ZLibInterface.cpp
new file mode 100644
index 0000000000..3ab6d321f2
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/ZLibInterface.cpp
@@ -0,0 +1,223 @@
+// ==========================================================
+// ZLib library interface
+//
+// Design and implementation by
+// - Floris van den Berg (flvdberg@wxs.nl)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "../ZLib/zlib.h"
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "../ZLib/zutil.h" /* must be the last header because of error C3163 in VS2008 (_vsnprintf defined in stdio.h) */
+
+/**
+Compresses a source buffer into a target buffer, using the ZLib library.
+Upon entry, target_size is the total size of the destination buffer,
+which must be at least 0.1% larger than source_size plus 12 bytes.
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the compressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibUncompress
+*/
+DWORD DLL_CALLCONV
+FreeImage_ZLibCompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+ uLongf dest_len = (uLongf)target_size;
+
+ int zerr = compress(target, &dest_len, source, source_size);
+ switch(zerr) {
+ case Z_MEM_ERROR: // not enough memory
+ case Z_BUF_ERROR: // not enough room in the output buffer
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+ return 0;
+ case Z_OK:
+ return dest_len;
+ }
+
+ return 0;
+}
+
+/**
+Decompresses a source buffer into a target buffer, using the ZLib library.
+Upon entry, target_size is the total size of the destination buffer,
+which must be large enough to hold the entire uncompressed data.
+The size of the uncompressed data must have been saved previously by the compressor
+and transmitted to the decompressor by some mechanism outside the scope of this
+compression library.
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the uncompressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibCompress
+*/
+DWORD DLL_CALLCONV
+FreeImage_ZLibUncompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+ uLongf dest_len = (uLongf)target_size;
+
+ int zerr = uncompress(target, &dest_len, source, source_size);
+ switch(zerr) {
+ case Z_MEM_ERROR: // not enough memory
+ case Z_BUF_ERROR: // not enough room in the output buffer
+ case Z_DATA_ERROR: // input data was corrupted
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+ return 0;
+ case Z_OK:
+ return dest_len;
+ }
+
+ return 0;
+}
+
+/**
+Compresses a source buffer into a target buffer, using the ZLib library.
+On success, the target buffer contains a GZIP compatible layout.
+Upon entry, target_size is the total size of the destination buffer,
+which must be at least 0.1% larger than source_size plus 24 bytes.
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the compressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibCompress
+*/
+DWORD DLL_CALLCONV
+FreeImage_ZLibGZip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+ uLongf dest_len = (uLongf)target_size - 12;
+ DWORD crc = crc32(0L, NULL, 0);
+
+ // set up header (stolen from zlib/gzio.c)
+ sprintf((char *)target, "%c%c%c%c%c%c%c%c", 0x1f, 0x8b,
+ Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/);
+ int zerr = compress2(target + 8, &dest_len, source, source_size, Z_BEST_COMPRESSION);
+ switch(zerr) {
+ case Z_MEM_ERROR: // not enough memory
+ case Z_BUF_ERROR: // not enough room in the output buffer
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+ return 0;
+ case Z_OK: {
+ // patch header, setup crc and length (stolen from mod_trace_output)
+ BYTE *p = target + 8; *p++ = 2; *p = OS_CODE; // xflags, os_code
+ crc = crc32(crc, source, source_size);
+ memcpy(target + 4 + dest_len, &crc, 4);
+ memcpy(target + 8 + dest_len, &source_size, 4);
+ return dest_len + 12;
+ }
+ }
+ return 0;
+}
+
+/**
+Decompresses a gzipped source buffer into a target buffer, using the ZLib library.
+Upon entry, target_size is the total size of the destination buffer,
+which must be large enough to hold the entire uncompressed data.
+The size of the uncompressed data must have been saved previously by the compressor
+and transmitted to the decompressor by some mechanism outside the scope of this
+compression library.
+
+@param target Destination buffer
+@param target_size Size of the destination buffer, in bytes
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the actual size of the uncompressed buffer, returns 0 if an error occured
+@see FreeImage_ZLibGZip
+*/
+
+static int get_byte(z_stream *stream) {
+ if (stream->avail_in <= 0) return EOF;
+ stream->avail_in--;
+ return *(stream->next_in)++;
+}
+
+static int checkheader(z_stream *stream) {
+ int flags, c;
+ DWORD len;
+
+ if (get_byte(stream) != 0x1f || get_byte(stream) != 0x8b)
+ return Z_DATA_ERROR;
+ if (get_byte(stream) != Z_DEFLATED || ((flags = get_byte(stream)) & 0xE0) != 0)
+ return Z_DATA_ERROR;
+ for (len = 0; len < 6; len++) (void)get_byte(stream);
+
+ if ((flags & 0x04) != 0) { /* skip the extra field */
+ len = (DWORD)get_byte(stream);
+ len += ((DWORD)get_byte(stream)) << 8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(stream) != EOF) ;
+ }
+ if ((flags & 0x08) != 0) { /* skip the original file name */
+ while ((c = get_byte(stream)) != 0 && c != EOF) ;
+ }
+ if ((flags & 0x10) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(stream)) != 0 && c != EOF) ;
+ }
+ if ((flags & 0x02) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) (void)get_byte(stream);
+ }
+ return Z_OK;
+}
+
+DWORD DLL_CALLCONV
+FreeImage_ZLibGUnzip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
+ DWORD src_len = source_size;
+ DWORD dest_len = target_size;
+ int zerr = Z_DATA_ERROR;
+
+ if (src_len > 0) {
+ z_stream stream;
+ memset(&stream, 0, sizeof (stream));
+ if ((zerr = inflateInit2(&stream, -MAX_WBITS)) == Z_OK) {
+ stream.next_in = source;
+ stream.avail_in = source_size;
+
+ stream.next_out = target;
+ stream.avail_out = target_size;
+
+ if ((zerr = checkheader(&stream)) == Z_OK) {
+ zerr = inflate (&stream, Z_NO_FLUSH);
+ dest_len = target_size - stream.avail_out;
+
+ if (zerr == Z_OK || zerr == Z_STREAM_END)
+ inflateEnd(&stream);
+ }
+ }
+ }
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
+ return 0;
+ }
+ return dest_len;
+}
+
+/**
+Update a running crc from source and return the updated crc, using the ZLib library.
+If source is NULL, this function returns the required initial value for the crc.
+
+@param crc Running crc value
+@param source Source buffer
+@param source_size Size of the source buffer, in bytes
+@return Returns the new crc value
+*/
+DWORD DLL_CALLCONV
+FreeImage_ZLibCRC32(DWORD crc, BYTE *source, DWORD source_size) {
+
+ return crc32(crc, source, source_size);
+}
diff --git a/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp b/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp
new file mode 100644
index 0000000000..367cb13c96
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp
@@ -0,0 +1,479 @@
+// ==========================================================
+// High Dynamic Range bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Convert RGB to and from Yxy, same as in Reinhard et al. SIGGRAPH 2002
+// References :
+// [1] Radiance Home Page [Online] http://radsite.lbl.gov/radiance/HOME.html
+// [2] E. Reinhard, M. Stark, P. Shirley, and J. Ferwerda,
+// Photographic Tone Reproduction for Digital Images, ACM Transactions on Graphics,
+// 21(3):267-276, 2002 (Proceedings of SIGGRAPH 2002).
+// [3] J. Tumblin and H.E. Rushmeier,
+// Tone Reproduction for Realistic Images. IEEE Computer Graphics and Applications,
+// 13(6):42-48, 1993.
+// ----------------------------------------------------------
+
+/**
+nominal CRT primaries
+*/
+/*
+static const float CIE_x_r = 0.640F;
+static const float CIE_y_r = 0.330F;
+static const float CIE_x_g = 0.290F;
+static const float CIE_y_g = 0.600F;
+static const float CIE_x_b = 0.150F;
+static const float CIE_y_b = 0.060F;
+static const float CIE_x_w = 0.3333F; // use true white
+static const float CIE_y_w = 0.3333F;
+*/
+/**
+sRGB primaries
+*/
+static const float CIE_x_r = 0.640F;
+static const float CIE_y_r = 0.330F;
+static const float CIE_x_g = 0.300F;
+static const float CIE_y_g = 0.600F;
+static const float CIE_x_b = 0.150F;
+static const float CIE_y_b = 0.060F;
+static const float CIE_x_w = 0.3127F; // Illuminant D65
+static const float CIE_y_w = 0.3290F;
+
+static const float CIE_D = ( CIE_x_r*(CIE_y_g - CIE_y_b) + CIE_x_g*(CIE_y_b - CIE_y_r) + CIE_x_b*(CIE_y_r - CIE_y_g));
+static const float CIE_C_rD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_g - CIE_y_b) - CIE_y_w*(CIE_x_g - CIE_x_b) + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g));
+static const float CIE_C_gD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_b - CIE_y_r) - CIE_y_w*(CIE_x_b - CIE_x_r) - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r));
+static const float CIE_C_bD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_r - CIE_y_g) - CIE_y_w*(CIE_x_r - CIE_x_g) + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r));
+
+/**
+RGB to XYZ (no white balance)
+*/
+static const float RGB2XYZ[3][3] = {
+ { CIE_x_r*CIE_C_rD / CIE_D,
+ CIE_x_g*CIE_C_gD / CIE_D,
+ CIE_x_b*CIE_C_bD / CIE_D
+ },
+ { CIE_y_r*CIE_C_rD / CIE_D,
+ CIE_y_g*CIE_C_gD / CIE_D,
+ CIE_y_b*CIE_C_bD / CIE_D
+ },
+ { (1 - CIE_x_r-CIE_y_r)*CIE_C_rD / CIE_D,
+ (1 - CIE_x_g-CIE_y_g)*CIE_C_gD / CIE_D,
+ (1 - CIE_x_b-CIE_y_b)*CIE_C_bD / CIE_D
+ }
+};
+
+/**
+XYZ to RGB (no white balance)
+*/
+static const float XYZ2RGB[3][3] = {
+ {(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g) / CIE_C_rD,
+ (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b) / CIE_C_rD,
+ (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) / CIE_C_rD
+ },
+ {(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b) / CIE_C_gD,
+ (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) / CIE_C_gD,
+ (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b) / CIE_C_gD
+ },
+ {(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r) / CIE_C_bD,
+ (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g) / CIE_C_bD,
+ (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) / CIE_C_bD
+ }
+};
+
+/**
+This gives approximately the following matrices :
+
+static const float RGB2XYZ[3][3] = {
+ { 0.41239083F, 0.35758433F, 0.18048081F },
+ { 0.21263903F, 0.71516865F, 0.072192319F },
+ { 0.019330820F, 0.11919473F, 0.95053220F }
+};
+static const float XYZ2RGB[3][3] = {
+ { 3.2409699F, -1.5373832F, -0.49861079F },
+ { -0.96924376F, 1.8759676F, 0.041555084F },
+ { 0.055630036F, -0.20397687F, 1.0569715F }
+};
+*/
+
+// ----------------------------------------------------------
+
+static const float EPSILON = 1e-06F;
+static const float INF = 1e+10F;
+
+/**
+Convert in-place floating point RGB data to Yxy.<br>
+On output, pixel->red == Y, pixel->green == x, pixel->blue == y
+@param dib Input RGBF / Output Yxy image
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL
+ConvertInPlaceRGBFToYxy(FIBITMAP *dib) {
+ float result[3];
+
+ if(FreeImage_GetImageType(dib) != FIT_RGBF)
+ return FALSE;
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+ for(unsigned y = 0; y < height; y++) {
+ FIRGBF *pixel = (FIRGBF*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ result[0] = result[1] = result[2] = 0;
+ for (int i = 0; i < 3; i++) {
+ result[i] += RGB2XYZ[i][0] * pixel[x].red;
+ result[i] += RGB2XYZ[i][1] * pixel[x].green;
+ result[i] += RGB2XYZ[i][2] * pixel[x].blue;
+ }
+ const float W = result[0] + result[1] + result[2];
+ const float Y = result[1];
+ if(W > 0) {
+ pixel[x].red = Y; // Y
+ pixel[x].green = result[0] / W; // x
+ pixel[x].blue = result[1] / W; // y
+ } else {
+ pixel[x].red = pixel[x].green = pixel[x].blue = 0;
+ }
+ }
+ // next line
+ bits += pitch;
+ }
+
+ return TRUE;
+}
+
+/**
+Convert in-place Yxy image to floating point RGB data.<br>
+On input, pixel->red == Y, pixel->green == x, pixel->blue == y
+@param dib Input Yxy / Output RGBF image
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL
+ConvertInPlaceYxyToRGBF(FIBITMAP *dib) {
+ float result[3];
+ float X, Y, Z;
+
+ if(FreeImage_GetImageType(dib) != FIT_RGBF)
+ return FALSE;
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+ for(unsigned y = 0; y < height; y++) {
+ FIRGBF *pixel = (FIRGBF*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ Y = pixel[x].red; // Y
+ result[1] = pixel[x].green; // x
+ result[2] = pixel[x].blue; // y
+ if ((Y > EPSILON) && (result[1] > EPSILON) && (result[2] > EPSILON)) {
+ X = (result[1] * Y) / result[2];
+ Z = (X / result[1]) - X - Y;
+ } else {
+ X = Z = EPSILON;
+ }
+ pixel[x].red = X;
+ pixel[x].green = Y;
+ pixel[x].blue = Z;
+ result[0] = result[1] = result[2] = 0;
+ for (int i = 0; i < 3; i++) {
+ result[i] += XYZ2RGB[i][0] * pixel[x].red;
+ result[i] += XYZ2RGB[i][1] * pixel[x].green;
+ result[i] += XYZ2RGB[i][2] * pixel[x].blue;
+ }
+ pixel[x].red = result[0]; // R
+ pixel[x].green = result[1]; // G
+ pixel[x].blue = result[2]; // B
+ }
+ // next line
+ bits += pitch;
+ }
+
+ return TRUE;
+}
+
+/**
+Get the maximum, minimum and average luminance.<br>
+On input, pixel->red == Y, pixel->green == x, pixel->blue == y
+@param Yxy Source Yxy image to analyze
+@param maxLum Maximum luminance
+@param minLum Minimum luminance
+@param worldLum Average luminance (world adaptation luminance)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL
+LuminanceFromYxy(FIBITMAP *Yxy, float *maxLum, float *minLum, float *worldLum) {
+ if(FreeImage_GetImageType(Yxy) != FIT_RGBF)
+ return FALSE;
+
+ const unsigned width = FreeImage_GetWidth(Yxy);
+ const unsigned height = FreeImage_GetHeight(Yxy);
+ const unsigned pitch = FreeImage_GetPitch(Yxy);
+
+ float max_lum = 0, min_lum = 0;
+ double sum = 0;
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(Yxy);
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBF *pixel = (FIRGBF*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ const float Y = pixel[x].red;
+ max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene
+ min_lum = (min_lum < Y) ? min_lum : Y; // min Luminance in the scene
+ sum += log(2.3e-5F + Y); // contrast constant in Tumblin paper
+ }
+ // next line
+ bits += pitch;
+ }
+ // maximum luminance
+ *maxLum = max_lum;
+ // minimum luminance
+ *minLum = min_lum;
+ // average log luminance
+ double avgLogLum = (sum / (width * height));
+ // world adaptation luminance
+ *worldLum = (float)exp(avgLogLum);
+
+ return TRUE;
+}
+
+/**
+Clamp RGBF image highest values to display white,
+then convert to 24-bit RGB
+*/
+FIBITMAP*
+ClampConvertRGBFTo24(FIBITMAP *src) {
+ if(FreeImage_GetImageType(src) != FIT_RGBF)
+ return FALSE;
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+
+ FIBITMAP *dst = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if (!dst) return NULL;
+
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+ BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBF *src_pixel = (FIRGBF*)src_bits;
+ BYTE *dst_pixel = (BYTE*)dst_bits;
+ for(unsigned x = 0; x < width; x++) {
+ const float red = (src_pixel[x].red > 1) ? 1 : src_pixel[x].red;
+ const float green = (src_pixel[x].green > 1) ? 1 : src_pixel[x].green;
+ const float blue = (src_pixel[x].blue > 1) ? 1 : src_pixel[x].blue;
+
+ dst_pixel[FI_RGBA_RED] = (BYTE)(255.0F * red + 0.5F);
+ dst_pixel[FI_RGBA_GREEN] = (BYTE)(255.0F * green + 0.5F);
+ dst_pixel[FI_RGBA_BLUE] = (BYTE)(255.0F * blue + 0.5F);
+ dst_pixel += 3;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+
+ return dst;
+}
+
+/**
+Extract the luminance channel L from a RGBF image.
+Luminance is calculated from the sRGB model (RGB2XYZ matrix)
+using a D65 white point :
+L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b )
+Reference :
+A Standard Default Color Space for the Internet - sRGB.
+[online] http://www.w3.org/Graphics/Color/sRGB
+*/
+FIBITMAP*
+ConvertRGBFToY(FIBITMAP *src) {
+ if(FreeImage_GetImageType(src) != FIT_RGBF)
+ return FALSE;
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+
+ FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height);
+ if (!dst) return NULL;
+
+ const unsigned src_pitch = FreeImage_GetPitch(src);
+ const unsigned dst_pitch = FreeImage_GetPitch(dst);
+
+
+ BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
+
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGBF *src_pixel = (FIRGBF*)src_bits;
+ float *dst_pixel = (float*)dst_bits;
+ for(unsigned x = 0; x < width; x++) {
+ const float L = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
+ dst_pixel[x] = (L > 0) ? L : 0;
+ }
+ // next line
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+
+ return dst;
+}
+
+/**
+Get the maximum, minimum, average luminance and log average luminance from a Y image
+@param dib Source Y image to analyze
+@param maxLum Maximum luminance
+@param minLum Minimum luminance
+@param Lav Average luminance
+@param Llav Log average luminance (also known as 'world adaptation luminance')
+@return Returns TRUE if successful, returns FALSE otherwise
+@see ConvertRGBFToY, FreeImage_TmoReinhard05Ex
+*/
+BOOL
+LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav) {
+ if(FreeImage_GetImageType(dib) != FIT_FLOAT)
+ return FALSE;
+
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned pitch = FreeImage_GetPitch(dib);
+
+ float max_lum = -1e20F, min_lum = 1e20F;
+ double sumLum = 0, sumLogLum = 0;
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+ for(unsigned y = 0; y < height; y++) {
+ const float *pixel = (float*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ const float Y = pixel[x];
+ max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene
+ min_lum = ((Y > 0) && (min_lum < Y)) ? min_lum : Y; // min Luminance in the scene
+ sumLum += Y; // average luminance
+ sumLogLum += log(2.3e-5F + Y); // contrast constant in Tumblin paper
+ }
+ // next line
+ bits += pitch;
+ }
+
+ // maximum luminance
+ *maxLum = max_lum;
+ // minimum luminance
+ *minLum = min_lum;
+ // average luminance
+ *Lav = (float)(sumLum / (width * height));
+ // average log luminance, a.k.a. world adaptation luminance
+ *Llav = (float)exp(sumLogLum / (width * height));
+
+ return TRUE;
+}
+// --------------------------------------------------------------------------
+
+static void findMaxMinPercentile(FIBITMAP *Y, float minPrct, float *minLum, float maxPrct, float *maxLum) {
+ int x, y;
+ int width = FreeImage_GetWidth(Y);
+ int height = FreeImage_GetHeight(Y);
+ int pitch = FreeImage_GetPitch(Y);
+
+ std::vector<float> vY(width * height);
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+ for(y = 0; y < height; y++) {
+ float *pixel = (float*)bits;
+ for(x = 0; x < width; x++) {
+ if(pixel[x] != 0) {
+ vY.push_back(pixel[x]);
+ }
+ }
+ bits += pitch;
+ }
+
+ std::sort(vY.begin(), vY.end());
+
+ *minLum = vY.at( int(minPrct * vY.size()));
+ *maxLum = vY.at( int(maxPrct * vY.size()));
+}
+
+/**
+Clipping function<br>
+Remove any extremely bright and/or extremely dark pixels
+and normalize between 0 and 1.
+@param Y Input/Output image
+@param minPrct Minimum percentile
+@param maxPrct Maximum percentile
+*/
+void
+NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct) {
+ int x, y;
+ float maxLum, minLum;
+
+ if(minPrct > maxPrct) {
+ // swap values
+ float t = minPrct; minPrct = maxPrct; maxPrct = t;
+ }
+ if(minPrct < 0) minPrct = 0;
+ if(maxPrct > 1) maxPrct = 1;
+
+ int width = FreeImage_GetWidth(Y);
+ int height = FreeImage_GetHeight(Y);
+ int pitch = FreeImage_GetPitch(Y);
+
+ // find max & min luminance values
+ if ((minPrct > 0) || (maxPrct < 1)) {
+ maxLum = 0, minLum = 0;
+ findMaxMinPercentile(Y, minPrct, &minLum, maxPrct, &maxLum);
+ } else {
+ maxLum = -1e20F, minLum = 1e20F;
+ BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+ for(y = 0; y < height; y++) {
+ const float *pixel = (float*)bits;
+ for(x = 0; x < width; x++) {
+ const float value = pixel[x];
+ maxLum = (maxLum < value) ? value : maxLum; // max Luminance in the scene
+ minLum = (minLum < value) ? minLum : value; // min Luminance in the scene
+ }
+ // next line
+ bits += pitch;
+ }
+ }
+ if(maxLum == minLum) return;
+
+ // normalize to range 0..1
+ const float divider = maxLum - minLum;
+ BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+ for(y = 0; y < height; y++) {
+ float *pixel = (float*)bits;
+ for(x = 0; x < width; x++) {
+ pixel[x] = (pixel[x] - minLum) / divider;
+ if(pixel[x] <= 0) pixel[x] = EPSILON;
+ if(pixel[x] > 1) pixel[x] = 1;
+ }
+ // next line
+ bits += pitch;
+ }
+}
diff --git a/plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp b/plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp
new file mode 100644
index 0000000000..b820bb3577
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp
@@ -0,0 +1,295 @@
+// ==========================================================
+// Tone mapping operator (Drago, 2003)
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Logarithmic mapping operator
+// Reference:
+// [1] F. Drago, K. Myszkowski, T. Annen, and N. Chiba,
+// Adaptive Logarithmic Mapping for Displaying High Contrast Scenes,
+// Eurographics 2003.
+// ----------------------------------------------------------
+
+/**
+Bias function
+*/
+static inline double
+biasFunction(const double b, const double x) {
+ return pow (x, b); // pow(x, log(bias)/log(0.5)
+}
+
+/**
+Padé approximation of log(x + 1)
+x(6+x)/(6+4x) good if x < 1
+x*(6 + 0.7662x)/(5.9897 + 3.7658x) between 1 and 2
+See http://www.nezumi.demon.co.uk/consult/logx.htm
+*/
+static inline double
+pade_log(const double x) {
+ if(x < 1) {
+ return (x * (6 + x) / (6 + 4 * x));
+ } else if(x < 2) {
+ return (x * (6 + 0.7662 * x) / (5.9897 + 3.7658 * x));
+ }
+ return log(x + 1);
+}
+
+/**
+Log mapping operator
+@param dib Input / Output Yxy image
+@param maxLum Maximum luminance
+@param avgLum Average luminance (world adaptation luminance)
+@param biasParam Bias parameter (a zero value default to 0.85)
+@param exposure Exposure parameter (default to 0)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+ToneMappingDrago03(FIBITMAP *dib, const float maxLum, const float avgLum, float biasParam, const float exposure) {
+ const float LOG05 = -0.693147F; // log(0.5)
+
+ double Lmax, divider, interpol, biasP;
+ unsigned x, y;
+ double L;
+
+ if(FreeImage_GetImageType(dib) != FIT_RGBF)
+ return FALSE;
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+
+
+ // arbitrary Bias Parameter
+ if(biasParam == 0)
+ biasParam = 0.85F;
+
+ // normalize maximum luminance by average luminance
+ Lmax = maxLum / avgLum;
+
+ divider = log10(Lmax+1);
+ biasP = log(biasParam)/LOG05;
+
+#if !defined(DRAGO03_FAST)
+
+ /**
+ Normal tone mapping of every pixel
+ further acceleration is obtained by a Padé approximation of log(x + 1)
+ */
+ BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+ for(y = 0; y < height; y++) {
+ FIRGBF *pixel = (FIRGBF*)bits;
+ for(x = 0; x < width; x++) {
+ double Yw = pixel[x].red / avgLum;
+ Yw *= exposure;
+ interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8);
+ L = pade_log(Yw);// log(Yw + 1)
+ pixel[x].red = (float)((L / interpol) / divider);
+ }
+ // next line
+ bits += pitch;
+ }
+
+#else
+ unsigned index;
+ int i, j;
+
+ unsigned max_width = width - (width % 3);
+ unsigned max_height = height - (height % 3);
+ unsigned fpitch = pitch / sizeof(FIRGBF);
+
+ /**
+ fast tone mapping
+ split the image into 3x3 pixel tiles and perform the computation for each group of 9 pixels
+ further acceleration is obtained by a Padé approximation of log(x + 1)
+ => produce artifacts and not so faster, so the code has been disabled
+ */
+#define PIXEL(x, y) image[y*fpitch + x].red
+
+ FIRGBF *image = (FIRGBF*)FreeImage_GetBits(dib);
+ for(y = 0; y < max_height; y += 3) {
+ for(x = 0; x < max_width; x += 3) {
+ double average = 0;
+ for(i = 0; i < 3; i++) {
+ for(j = 0; j < 3; j++) {
+ index = (y + i)*fpitch + (x + j);
+ image[index].red /= (float)avgLum;
+ image[index].red *= exposure;
+ average += image[index].red;
+ }
+ }
+ average = average / 9 - PIXEL(x, y);
+ if(average > -1 && average < 1) {
+ interpol = log(2 + pow(PIXEL(x + 1, y + 1) / Lmax, biasP) * 8);
+ for(i = 0; i < 3; i++) {
+ for(j = 0; j < 3; j++) {
+ index = (y + i)*fpitch + (x + j);
+ L = pade_log(image[index].red);// log(image[index].red + 1)
+ image[index].red = (float)((L / interpol) / divider);
+ }
+ }
+ }
+ else {
+ for(i = 0; i < 3; i++) {
+ for(j = 0; j < 3; j++) {
+ index = (y + i)*fpitch + (x + j);
+ interpol = log(2 + pow(image[index].red / Lmax, biasP) * 8);
+ L = pade_log(image[index].red);// log(image[index].red + 1)
+ image[index].red = (float)((L / interpol) / divider);
+ }
+ }
+ }
+ } //x
+ } // y
+
+ /**
+ Normal tone mapping of every pixel for the remaining right and bottom bands
+ */
+ BYTE *bits;
+
+ // right band
+ bits = (BYTE*)FreeImage_GetBits(dib);
+ for(y = 0; y < height; y++) {
+ FIRGBF *pixel = (FIRGBF*)bits;
+ for(x = max_width; x < width; x++) {
+ double Yw = pixel[x].red / avgLum;
+ Yw *= exposure;
+ interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8);
+ L = pade_log(Yw);// log(Yw + 1)
+ pixel[x].red = (float)((L / interpol) / divider);
+ }
+ // next line
+ bits += pitch;
+ }
+ // bottom band
+ bits = (BYTE*)FreeImage_GetBits(dib);
+ for(y = max_height; y < height; y++) {
+ FIRGBF *pixel = (FIRGBF*)bits;
+ for(x = 0; x < max_width; x++) {
+ double Yw = pixel[x].red / avgLum;
+ Yw *= exposure;
+ interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8);
+ L = pade_log(Yw);// log(Yw + 1)
+ pixel[x].red = (float)((L / interpol) / divider);
+ }
+ // next line
+ bits += pitch;
+ }
+
+#endif // DRAGO03_FAST
+
+ return TRUE;
+}
+
+/**
+Custom gamma correction based on the ITU-R BT.709 standard
+@param dib RGBF image to be corrected
+@param gammaval Gamma value (2.2 is a good default value)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+REC709GammaCorrection(FIBITMAP *dib, const float gammaval) {
+ if(FreeImage_GetImageType(dib) != FIT_RGBF)
+ return FALSE;
+
+ float slope = 4.5F;
+ float start = 0.018F;
+
+ const float fgamma = (float)((0.45 / gammaval) * 2);
+ if(gammaval >= 2.1F) {
+ start = (float)(0.018 / ((gammaval - 2) * 7.5));
+ slope = (float)(4.5 * ((gammaval - 2) * 7.5));
+ } else if (gammaval <= 1.9F) {
+ start = (float)(0.018 * ((2 - gammaval) * 7.5));
+ slope = (float)(4.5 / ((2 - gammaval) * 7.5));
+ }
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+ const unsigned pitch = FreeImage_GetPitch(dib);
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
+ for(unsigned y = 0; y < height; y++) {
+ float *pixel = (float*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ for(int i = 0; i < 3; i++) {
+ *pixel = (*pixel <= start) ? *pixel * slope : (1.099F * pow(*pixel, fgamma) - 0.099F);
+ pixel++;
+ }
+ }
+ bits += pitch;
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// Main algorithm
+// ----------------------------------------------------------
+
+/**
+Apply the Adaptive Logarithmic Mapping operator to a HDR image and convert to 24-bit RGB
+@param src Input RGB16 or RGB[A]F image
+@param gamma Gamma correction (gamma > 0). 1 means no correction, 2.2 in the original paper.
+@param exposure Exposure parameter (0 means no correction, 0 in the original paper)
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV
+FreeImage_TmoDrago03(FIBITMAP *src, double gamma, double exposure) {
+ float maxLum, minLum, avgLum;
+
+ if (!FreeImage_HasPixels(src)) return NULL;
+
+ // working RGBF variable
+ FIBITMAP *dib = NULL;
+
+ dib = FreeImage_ConvertToRGBF(src);
+ if (!dib) return NULL;
+
+ // default algorithm parameters
+ const float biasParam = 0.85F;
+ const float expoParam = (float)pow(2.0, exposure); //default exposure is 1, 2^0
+
+ // convert to Yxy
+ ConvertInPlaceRGBFToYxy(dib);
+ // get the luminance
+ LuminanceFromYxy(dib, &maxLum, &minLum, &avgLum);
+ // perform the tone mapping
+ ToneMappingDrago03(dib, maxLum, avgLum, biasParam, expoParam);
+ // convert back to RGBF
+ ConvertInPlaceYxyToRGBF(dib);
+ if(gamma != 1) {
+ // perform gamma correction
+ REC709GammaCorrection(dib, (float)gamma);
+ }
+ // clamp image highest values to display white, then convert to 24-bit RGB
+ FIBITMAP *dst = ClampConvertRGBFTo24(dib);
+
+ // clean-up and return
+ FreeImage_Unload(dib);
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ return dst;
+}
diff --git a/plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp b/plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp
new file mode 100644
index 0000000000..bcd4524523
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp
@@ -0,0 +1,689 @@
+// ==========================================================
+// Tone mapping operator (Fattal, 2002)
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Gradient domain HDR compression
+// Reference:
+// [1] R. Fattal, D. Lischinski, and M.Werman,
+// Gradient domain high dynamic range compression,
+// ACM Transactions on Graphics, special issue on Proc. of ACM SIGGRAPH 2002,
+// San Antonio, Texas, vol. 21(3), pp. 257-266, 2002.
+// ----------------------------------------------------------
+
+static const float EPSILON = 1e-4F;
+
+/**
+Performs a 5 by 5 gaussian filtering using two 1D convolutions,
+followed by a subsampling by 2.
+@param dib Input image
+@return Returns a blurred image of size SIZE(dib)/2
+@see GaussianPyramid
+*/
+static FIBITMAP* GaussianLevel5x5(FIBITMAP *dib) {
+ FIBITMAP *h_dib = NULL, *v_dib = NULL, *dst = NULL;
+ float *src_pixel, *dst_pixel;
+
+ try {
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ if(image_type != FIT_FLOAT) throw(1);
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+
+ h_dib = FreeImage_AllocateT(image_type, width, height);
+ v_dib = FreeImage_AllocateT(image_type, width, height);
+ if (!h_dib || !v_dib) throw(1);
+
+ const unsigned pitch = FreeImage_GetPitch(dib) / sizeof(float);
+
+ // horizontal convolution dib -> h_dib
+
+ src_pixel = (float*)FreeImage_GetBits(dib);
+ dst_pixel = (float*)FreeImage_GetBits(h_dib);
+
+ for(unsigned y = 0; y < height; y++) {
+ // work on line y
+ for(unsigned x = 2; x < width - 2; x++) {
+ dst_pixel[x] = src_pixel[x-2] + src_pixel[x+2] + 4 * (src_pixel[x-1] + src_pixel[x+1]) + 6 * src_pixel[x];
+ dst_pixel[x] /= 16;
+ }
+ // boundary mirroring
+ dst_pixel[0] = (2 * src_pixel[2] + 8 * src_pixel[1] + 6 * src_pixel[0]) / 16;
+ dst_pixel[1] = (src_pixel[3] + 4 * (src_pixel[0] + src_pixel[2]) + 7 * src_pixel[1]) / 16;
+ dst_pixel[width-2] = (src_pixel[width-4] + 5 * src_pixel[width-1] + 4 * src_pixel[width-3] + 6 * src_pixel[width-2]) / 16;
+ dst_pixel[width-1] = (src_pixel[width-3] + 5 * src_pixel[width-2] + 10 * src_pixel[width-1]) / 16;
+
+ // next line
+ src_pixel += pitch;
+ dst_pixel += pitch;
+ }
+
+ // vertical convolution h_dib -> v_dib
+
+ src_pixel = (float*)FreeImage_GetBits(h_dib);
+ dst_pixel = (float*)FreeImage_GetBits(v_dib);
+
+ for(unsigned x = 0; x < width; x++) {
+ // work on column x
+ for(unsigned y = 2; y < height - 2; y++) {
+ const unsigned index = y*pitch + x;
+ dst_pixel[index] = src_pixel[index-2*pitch] + src_pixel[index+2*pitch] + 4 * (src_pixel[index-pitch] + src_pixel[index+pitch]) + 6 * src_pixel[index];
+ dst_pixel[index] /= 16;
+ }
+ // boundary mirroring
+ dst_pixel[x] = (2 * src_pixel[x+2*pitch] + 8 * src_pixel[x+pitch] + 6 * src_pixel[x]) / 16;
+ dst_pixel[x+pitch] = (src_pixel[x+3*pitch] + 4 * (src_pixel[x] + src_pixel[x+2*pitch]) + 7 * src_pixel[x+pitch]) / 16;
+ dst_pixel[(height-2)*pitch+x] = (src_pixel[(height-4)*pitch+x] + 5 * src_pixel[(height-1)*pitch+x] + 4 * src_pixel[(height-3)*pitch+x] + 6 * src_pixel[(height-2)*pitch+x]) / 16;
+ dst_pixel[(height-1)*pitch+x] = (src_pixel[(height-3)*pitch+x] + 5 * src_pixel[(height-2)*pitch+x] + 10 * src_pixel[(height-1)*pitch+x]) / 16;
+ }
+
+ FreeImage_Unload(h_dib); h_dib = NULL;
+
+ // perform downsampling
+
+ dst = FreeImage_Rescale(v_dib, width/2, height/2, FILTER_BILINEAR);
+
+ FreeImage_Unload(v_dib);
+
+ return dst;
+
+ } catch(int) {
+ if(h_dib) FreeImage_Unload(h_dib);
+ if(v_dib) FreeImage_Unload(v_dib);
+ if(dst) FreeImage_Unload(dst);
+ return NULL;
+ }
+}
+
+/**
+Compute a Gaussian pyramid using the specified number of levels.
+@param H Original bitmap
+@param pyramid Resulting pyramid array
+@param nlevels Number of resolution levels
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL GaussianPyramid(FIBITMAP *H, FIBITMAP **pyramid, int nlevels) {
+ try {
+ // first level is the original image
+ pyramid[0] = FreeImage_Clone(H);
+ if(pyramid[0] == NULL) throw(1);
+ // compute next levels
+ for(int k = 1; k < nlevels; k++) {
+ pyramid[k] = GaussianLevel5x5(pyramid[k-1]);
+ if(pyramid[k] == NULL) throw(1);
+ }
+ return TRUE;
+ } catch(int) {
+ for(int k = 0; k < nlevels; k++) {
+ if(pyramid[k] != NULL) {
+ FreeImage_Unload(pyramid[k]);
+ pyramid[k] = NULL;
+ }
+ }
+ return FALSE;
+ }
+}
+
+/**
+Compute the gradient magnitude of an input image H using central differences,
+and returns the average gradient.
+@param H Input image
+@param avgGrad [out] Average gradient
+@param k Level number
+@return Returns the gradient magnitude if successful, returns NULL otherwise
+@see GradientPyramid
+*/
+static FIBITMAP* GradientLevel(FIBITMAP *H, float *avgGrad, int k) {
+ FIBITMAP *G = NULL;
+
+ try {
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H);
+ if(image_type != FIT_FLOAT) throw(1);
+
+ const unsigned width = FreeImage_GetWidth(H);
+ const unsigned height = FreeImage_GetHeight(H);
+
+ G = FreeImage_AllocateT(image_type, width, height);
+ if (!G) throw(1);
+
+ const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float);
+
+ const float divider = (float)(1 << (k + 1));
+ float average = 0;
+
+ float *src_pixel = (float*)FreeImage_GetBits(H);
+ float *dst_pixel = (float*)FreeImage_GetBits(G);
+
+ for(unsigned y = 0; y < height; y++) {
+ const unsigned n = (y == 0 ? 0 : y-1);
+ const unsigned s = (y+1 == height ? y : y+1);
+ for(unsigned x = 0; x < width; x++) {
+ const unsigned w = (x == 0 ? 0 : x-1);
+ const unsigned e = (x+1 == width ? x : x+1);
+ // central difference
+ const float gx = (src_pixel[y*pitch+e] - src_pixel[y*pitch+w]) / divider; // [Hk(x+1, y) - Hk(x-1, y)] / 2**(k+1)
+ const float gy = (src_pixel[s*pitch+x] - src_pixel[n*pitch+x]) / divider; // [Hk(x, y+1) - Hk(x, y-1)] / 2**(k+1)
+ // gradient
+ dst_pixel[x] = sqrt(gx*gx + gy*gy);
+ // average gradient
+ average += dst_pixel[x];
+ }
+ // next line
+ dst_pixel += pitch;
+ }
+
+ *avgGrad = average / (width * height);
+
+ return G;
+
+ } catch(int) {
+ if(G) FreeImage_Unload(G);
+ return NULL;
+ }
+}
+
+/**
+Calculate gradient magnitude and its average value on each pyramid level
+@param pyramid Gaussian pyramid (nlevels levels)
+@param nlevels Number of levels
+@param gradients [out] Gradient pyramid (nlevels levels)
+@param avgGrad [out] Average gradient on each level (array of size nlevels)
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL GradientPyramid(FIBITMAP **pyramid, int nlevels, FIBITMAP **gradients, float *avgGrad) {
+ try {
+ for(int k = 0; k < nlevels; k++) {
+ FIBITMAP *Hk = pyramid[k];
+ gradients[k] = GradientLevel(Hk, &avgGrad[k], k);
+ if(gradients[k] == NULL) throw(1);
+ }
+ return TRUE;
+ } catch(int) {
+ for(int k = 0; k < nlevels; k++) {
+ if(gradients[k] != NULL) {
+ FreeImage_Unload(gradients[k]);
+ gradients[k] = NULL;
+ }
+ }
+ return FALSE;
+ }
+}
+
+/**
+Compute the gradient attenuation function PHI(x, y)
+@param gradients Gradient pyramid (nlevels levels)
+@param avgGrad Average gradient on each level (array of size nlevels)
+@param nlevels Number of levels
+@param alpha Parameter alpha in the paper
+@param beta Parameter beta in the paper
+@return Returns the attenuation matrix Phi if successful, returns NULL otherwise
+*/
+static FIBITMAP* PhiMatrix(FIBITMAP **gradients, float *avgGrad, int nlevels, float alpha, float beta) {
+ float *src_pixel, *dst_pixel;
+ FIBITMAP **phi = NULL;
+
+ try {
+ phi = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*));
+ if (!phi) throw(1);
+ memset(phi, 0, nlevels * sizeof(FIBITMAP*));
+
+ for(int k = nlevels-1; k >= 0; k--) {
+ // compute phi(k)
+
+ FIBITMAP *Gk = gradients[k];
+
+ const unsigned width = FreeImage_GetWidth(Gk);
+ const unsigned height = FreeImage_GetHeight(Gk);
+ const unsigned pitch = FreeImage_GetPitch(Gk) / sizeof(float);
+
+ // parameter alpha is 0.1 times the average gradient magnitude
+ // also, note the factor of 2**k in the denominator;
+ // that is there to correct for the fact that an average gradient avgGrad(H) over 2**k pixels
+ // in the original image will appear as a gradient grad(Hk) = 2**k*avgGrad(H) over a single pixel in Hk.
+ float ALPHA = alpha * avgGrad[k] * (float)((int)1 << k);
+ if(ALPHA == 0) ALPHA = EPSILON;
+
+ phi[k] = FreeImage_AllocateT(FIT_FLOAT, width, height);
+ if (!phi[k]) throw(1);
+
+ src_pixel = (float*)FreeImage_GetBits(Gk);
+ dst_pixel = (float*)FreeImage_GetBits(phi[k]);
+ for(unsigned y = 0; y < height; y++) {
+ for(unsigned x = 0; x < width; x++) {
+ // compute (alpha / grad) * (grad / alpha) ** beta
+ const float v = src_pixel[x] / ALPHA;
+ const float value = (float)pow((float)v, (float)(beta-1));
+ dst_pixel[x] = (value > 1) ? 1 : value;
+ }
+ // next line
+ src_pixel += pitch;
+ dst_pixel += pitch;
+ }
+
+ if(k < nlevels-1) {
+ // compute PHI(k) = L( PHI(k+1)) * phi(k)
+ FIBITMAP *L = FreeImage_Rescale(phi[k+1], width, height, FILTER_BILINEAR);
+ if (!L) throw(1);
+
+ src_pixel = (float*)FreeImage_GetBits(L);
+ dst_pixel = (float*)FreeImage_GetBits(phi[k]);
+ for(unsigned y = 0; y < height; y++) {
+ for(unsigned x = 0; x < width; x++) {
+ dst_pixel[x] *= src_pixel[x];
+ }
+ // next line
+ src_pixel += pitch;
+ dst_pixel += pitch;
+ }
+
+ FreeImage_Unload(L);
+
+ // PHI(k+1) is no longer needed
+ FreeImage_Unload(phi[k+1]);
+ phi[k+1] = NULL;
+ }
+
+ // next level
+ }
+
+ // get the final result and return
+ FIBITMAP *dst = phi[0];
+
+ free(phi);
+
+ return dst;
+
+ } catch(int) {
+ if(phi) {
+ for(int k = nlevels-1; k >= 0; k--) {
+ if(phi[k]) FreeImage_Unload(phi[k]);
+ }
+ free(phi);
+ }
+ return NULL;
+ }
+}
+
+/**
+Compute gradients in x and y directions, attenuate them with the attenuation matrix,
+then compute the divergence div G from the attenuated gradient.
+@param H Normalized luminance
+@param PHI Attenuation matrix
+@return Returns the divergence matrix if successful, returns NULL otherwise
+*/
+static FIBITMAP* Divergence(FIBITMAP *H, FIBITMAP *PHI) {
+ FIBITMAP *Gx = NULL, *Gy = NULL, *divG = NULL;
+ float *phi, *h, *gx, *gy, *divg;
+
+ try {
+ const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H);
+ if(image_type != FIT_FLOAT) throw(1);
+
+ const unsigned width = FreeImage_GetWidth(H);
+ const unsigned height = FreeImage_GetHeight(H);
+
+ Gx = FreeImage_AllocateT(image_type, width, height);
+ if (!Gx) throw(1);
+ Gy = FreeImage_AllocateT(image_type, width, height);
+ if (!Gy) throw(1);
+
+ const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float);
+
+ // perform gradient attenuation
+
+ phi = (float*)FreeImage_GetBits(PHI);
+ h = (float*)FreeImage_GetBits(H);
+ gx = (float*)FreeImage_GetBits(Gx);
+ gy = (float*)FreeImage_GetBits(Gy);
+
+ for(unsigned y = 0; y < height; y++) {
+ const unsigned s = (y+1 == height ? y : y+1);
+ for(unsigned x = 0; x < width; x++) {
+ const unsigned e = (x+1 == width ? x : x+1);
+ // forward difference
+ const unsigned index = y*pitch + x;
+ const float phi_xy = phi[index];
+ const float h_xy = h[index];
+ gx[x] = (h[y*pitch+e] - h_xy) * phi_xy; // [H(x+1, y) - H(x, y)] * PHI(x, y)
+ gy[x] = (h[s*pitch+x] - h_xy) * phi_xy; // [H(x, y+1) - H(x, y)] * PHI(x, y)
+ }
+ // next line
+ gx += pitch;
+ gy += pitch;
+ }
+
+ // calculate the divergence
+
+ divG = FreeImage_AllocateT(image_type, width, height);
+ if (!divG) throw(1);
+
+ gx = (float*)FreeImage_GetBits(Gx);
+ gy = (float*)FreeImage_GetBits(Gy);
+ divg = (float*)FreeImage_GetBits(divG);
+
+ for(unsigned y0 = 0; y0 < height; y0++) {
+ for(unsigned x = 0; x < width; x++) {
+ // backward difference approximation
+ // divG = Gx(x, y) - Gx(x-1, y) + Gy(x, y) - Gy(x, y-1)
+ const unsigned index = y0*pitch + x;
+ divg[index] = gx[index] + gy[index];
+ if(x > 0) divg[index] -= gx[index-1];
+ if(y0 > 0) divg[index] -= gy[index-pitch];
+ }
+ }
+
+ // no longer needed ...
+ FreeImage_Unload(Gx);
+ FreeImage_Unload(Gy);
+
+ // return the divergence
+ return divG;
+
+ } catch(int) {
+ if(Gx) FreeImage_Unload(Gx);
+ if(Gy) FreeImage_Unload(Gy);
+ if(divG) FreeImage_Unload(divG);
+ return NULL;
+ }
+}
+
+/**
+Given the luminance channel, find max & min luminance values,
+normalize to range 0..100 and take the logarithm.
+@param Y Image luminance
+@return Returns the normalized luminance H if successful, returns NULL otherwise
+*/
+static FIBITMAP* LogLuminance(FIBITMAP *Y) {
+ FIBITMAP *H = NULL;
+
+ try {
+ // get the luminance channel
+ FIBITMAP *H = FreeImage_Clone(Y);
+ if (!H) throw(1);
+
+ const unsigned width = FreeImage_GetWidth(H);
+ const unsigned height = FreeImage_GetHeight(H);
+ const unsigned pitch = FreeImage_GetPitch(H);
+
+ // find max & min luminance values
+ float maxLum = -1e20F, minLum = 1e20F;
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(H);
+ for(unsigned y = 0; y < height; y++) {
+ const float *pixel = (float*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ const float value = pixel[x];
+ maxLum = (maxLum < value) ? value : maxLum; // max Luminance in the scene
+ minLum = (minLum < value) ? minLum : value; // min Luminance in the scene
+ }
+ // next line
+ bits += pitch;
+ }
+ if(maxLum == minLum) throw(1);
+
+ // normalize to range 0..100 and take the logarithm
+ const float scale = 100.F / (maxLum - minLum);
+ bits = (BYTE*)FreeImage_GetBits(H);
+ for(unsigned y0 = 0; y0 < height; y0++) {
+ float *pixel = (float*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ const float value = (pixel[x] - minLum) * scale;
+ pixel[x] = log(value + EPSILON);
+ }
+ // next line
+ bits += pitch;
+ }
+
+ return H;
+
+ } catch(int) {
+ if(H) FreeImage_Unload(H);
+ return NULL;
+ }
+}
+
+/**
+Given a normalized luminance, perform exponentiation and recover the log compressed image
+@param Y Input/Output luminance image
+*/
+static void ExpLuminance(FIBITMAP *Y) {
+ const unsigned width = FreeImage_GetWidth(Y);
+ const unsigned height = FreeImage_GetHeight(Y);
+ const unsigned pitch = FreeImage_GetPitch(Y);
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
+ for(unsigned y = 0; y < height; y++) {
+ float *pixel = (float*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ pixel[x] = exp(pixel[x]) - EPSILON;
+ }
+ bits += pitch;
+ }
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Gradient Domain HDR tone mapping operator
+@param Y Image luminance values
+@param alpha Parameter alpha of the paper (suggested value is 0.1)
+@param beta Parameter beta of the paper (suggested value is between 0.8 and 0.9)
+@return returns the tone mapped luminance
+*/
+static FIBITMAP* tmoFattal02(FIBITMAP *Y, float alpha, float beta) {
+ const unsigned MIN_PYRAMID_SIZE = 32; // minimun size (width or height) of the coarsest level of the pyramid
+
+ FIBITMAP *H = NULL;
+ FIBITMAP **pyramid = NULL;
+ FIBITMAP **gradients = NULL;
+ FIBITMAP *phy = NULL;
+ FIBITMAP *divG = NULL;
+ FIBITMAP *U = NULL;
+ float *avgGrad = NULL;
+
+ int k;
+ int nlevels = 0;
+
+ try {
+ // get the normalized luminance
+ FIBITMAP *H = LogLuminance(Y);
+ if (!H) throw(1);
+
+ // get the number of levels for the pyramid
+ const unsigned width = FreeImage_GetWidth(H);
+ const unsigned height = FreeImage_GetHeight(H);
+ unsigned minsize = MIN(width, height);
+ while(minsize >= MIN_PYRAMID_SIZE) {
+ nlevels++;
+ minsize /= 2;
+ }
+
+ // create the Gaussian pyramid
+ pyramid = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*));
+ if (!pyramid) throw(1);
+ memset(pyramid, 0, nlevels * sizeof(FIBITMAP*));
+
+ if (!GaussianPyramid(H, pyramid, nlevels)) throw(1);
+
+ // calculate gradient magnitude and its average value on each pyramid level
+ gradients = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*));
+ if (!gradients) throw(1);
+ memset(gradients, 0, nlevels * sizeof(FIBITMAP*));
+ avgGrad = (float*)malloc(nlevels * sizeof(float));
+ if (!avgGrad) throw(1);
+
+ if (!GradientPyramid(pyramid, nlevels, gradients, avgGrad)) throw(1);
+
+ // free the Gaussian pyramid
+ for(k = 0; k < nlevels; k++) {
+ if(pyramid[k]) FreeImage_Unload(pyramid[k]);
+ }
+ free(pyramid); pyramid = NULL;
+
+ // compute the gradient attenuation function PHI(x, y)
+ phy = PhiMatrix(gradients, avgGrad, nlevels, alpha, beta);
+ if (!phy) throw(1);
+
+ // free the gradient pyramid
+ for(k = 0; k < nlevels; k++) {
+ if(gradients[k]) FreeImage_Unload(gradients[k]);
+ }
+ free(gradients); gradients = NULL;
+ free(avgGrad); avgGrad = NULL;
+
+ // compute gradients in x and y directions, attenuate them with the attenuation matrix,
+ // then compute the divergence div G from the attenuated gradient.
+ divG = Divergence(H, phy);
+ if (!divG) throw(1);
+
+ // H & phy no longer needed
+ FreeImage_Unload(H); H = NULL;
+ FreeImage_Unload(phy); phy = NULL;
+
+ // solve the PDE (Poisson equation) using a multigrid solver and 3 cycles
+ FIBITMAP *U = FreeImage_MultigridPoissonSolver(divG, 3);
+ if (!U) throw(1);
+
+ FreeImage_Unload(divG);
+
+ // perform exponentiation and recover the log compressed image
+ ExpLuminance(U);
+
+ return U;
+
+ } catch(int) {
+ if(H) FreeImage_Unload(H);
+ if(pyramid) {
+ for(int i = 0; i < nlevels; i++) {
+ if(pyramid[i]) FreeImage_Unload(pyramid[i]);
+ }
+ free(pyramid);
+ }
+ if(gradients) {
+ for(int i = 0; i < nlevels; i++) {
+ if(gradients[i]) FreeImage_Unload(gradients[i]);
+ }
+ free(gradients);
+ }
+ if(avgGrad) free(avgGrad);
+ if(phy) FreeImage_Unload(phy);
+ if(divG) FreeImage_Unload(divG);
+ if(U) FreeImage_Unload(U);
+
+ return NULL;
+ }
+}
+
+// ----------------------------------------------------------
+// Main algorithm
+// ----------------------------------------------------------
+
+/**
+Apply the Gradient Domain High Dynamic Range Compression to a RGBF image and convert to 24-bit RGB
+@param dib Input RGBF / RGB16 image
+@param color_saturation Color saturation (s parameter in the paper) in [0.4..0.6]
+@param attenuation Atenuation factor (beta parameter in the paper) in [0.8..0.9]
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV
+FreeImage_TmoFattal02(FIBITMAP *dib, double color_saturation, double attenuation) {
+ const float alpha = 0.1F; // parameter alpha = 0.1
+ const float beta = (float)MAX(0.8, MIN(0.9, attenuation)); // parameter beta = [0.8..0.9]
+ const float s = (float)MAX(0.4, MIN(0.6, color_saturation));// exponent s controls color saturation = [0.4..0.6]
+
+ FIBITMAP *src = NULL;
+ FIBITMAP *Yin = NULL;
+ FIBITMAP *Yout = NULL;
+ FIBITMAP *dst = NULL;
+
+ if (!FreeImage_HasPixels(dib)) return NULL;
+
+ try {
+
+ // convert to RGBF
+ src = FreeImage_ConvertToRGBF(dib);
+ if (!src) throw(1);
+
+ // get the luminance channel
+ Yin = ConvertRGBFToY(src);
+ if (!Yin) throw(1);
+
+ // perform the tone mapping
+ Yout = tmoFattal02(Yin, alpha, beta);
+ if (!Yout) throw(1);
+
+ // clip low and high values and normalize to [0..1]
+ //NormalizeY(Yout, 0.001F, 0.995F);
+ NormalizeY(Yout, 0, 1);
+
+ // compress the dynamic range
+
+ const unsigned width = FreeImage_GetWidth(src);
+ const unsigned height = FreeImage_GetHeight(src);
+
+ const unsigned rgb_pitch = FreeImage_GetPitch(src);
+ const unsigned y_pitch = FreeImage_GetPitch(Yin);
+
+ BYTE *bits = (BYTE*)FreeImage_GetBits(src);
+ BYTE *bits_yin = (BYTE*)FreeImage_GetBits(Yin);
+ BYTE *bits_yout = (BYTE*)FreeImage_GetBits(Yout);
+
+ for(unsigned y = 0; y < height; y++) {
+ float *Lin = (float*)bits_yin;
+ float *Lout = (float*)bits_yout;
+ float *color = (float*)bits;
+ for(unsigned x = 0; x < width; x++) {
+ for(unsigned c = 0; c < 3; c++) {
+ *color = (Lin[x] > 0) ? pow(*color/Lin[x], s) * Lout[x] : 0;
+ color++;
+ }
+ }
+ bits += rgb_pitch;
+ bits_yin += y_pitch;
+ bits_yout += y_pitch;
+ }
+
+ // not needed anymore
+ FreeImage_Unload(Yin); Yin = NULL;
+ FreeImage_Unload(Yout); Yout = NULL;
+
+ // clamp image highest values to display white, then convert to 24-bit RGB
+ dst = ClampConvertRGBFTo24(src);
+
+ // clean-up and return
+ FreeImage_Unload(src); src = NULL;
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+
+ return dst;
+
+ } catch(int) {
+ if(src) FreeImage_Unload(src);
+ if(Yin) FreeImage_Unload(Yin);
+ if(Yout) FreeImage_Unload(Yout);
+ return NULL;
+ }
+}
diff --git a/plugins/AdvaImg/src/FreeImage/tmoReinhard05.cpp b/plugins/AdvaImg/src/FreeImage/tmoReinhard05.cpp
new file mode 100644
index 0000000000..969aa4c950
--- /dev/null
+++ b/plugins/AdvaImg/src/FreeImage/tmoReinhard05.cpp
@@ -0,0 +1,260 @@
+// ==========================================================
+// Tone mapping operator (Reinhard, 2005)
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
+//
+// This file is part of FreeImage 3
+//
+// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
+// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
+// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
+// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
+// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
+// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
+// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
+// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+// THIS DISCLAIMER.
+//
+// Use at your own risk!
+// ==========================================================
+
+#include "FreeImage.h"
+#include "Utilities.h"
+#include "ToneMapping.h"
+
+// ----------------------------------------------------------
+// Global and/or local tone mapping operator
+// References:
+// [1] Erik Reinhard and Kate Devlin, 'Dynamic Range Reduction Inspired by Photoreceptor Physiology',
+// IEEE Transactions on Visualization and Computer Graphics, 11(1), Jan/Feb 2005.
+// [2] Erik Reinhard, 'Parameter estimation for photographic tone reproduction',
+// Journal of Graphics Tools, vol. 7, no. 1, pp. 45–51, 2003.
+// ----------------------------------------------------------
+
+/**
+Tone mapping operator
+@param dib Input / Output RGBF image
+@param Y Input luminance image version of dib
+@param f Overall intensity in range [-8:8] : default to 0
+@param m Contrast in range [0.3:1) : default to 0
+@param a Adaptation in range [0:1] : default to 1
+@param c Color correction in range [0:1] : default to 0
+@return Returns TRUE if successful, returns FALSE otherwise
+@see LuminanceFromY
+*/
+static BOOL
+ToneMappingReinhard05(FIBITMAP *dib, FIBITMAP *Y, float f, float m, float a, float c) {
+ float Cav[3]; // channel average
+ float Lav = 0; // average luminance
+ float Llav = 0; // log average luminance
+ float minLum = 1; // min luminance
+ float maxLum = 1; // max luminance
+
+ float L; // pixel luminance
+ float I_g, I_l; // global and local light adaptation
+ float I_a; // interpolated pixel light adaptation
+ float k; // key (low-key means overall dark image, high-key means overall light image)
+
+ // check input parameters
+
+ if ((FreeImage_GetImageType(dib) != FIT_RGBF) || (FreeImage_GetImageType(Y) != FIT_FLOAT)) {
+ return FALSE;
+ }
+
+ if(f < -8) f = -8; if(f > 8) f = 8;
+ if(m < 0) m = 0; if(m > 1) m = 1;
+ if(a < 0) a = 0; if(a > 1) a = 1;
+ if(c < 0) c = 0; if(c > 1) c = 1;
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+
+ const unsigned dib_pitch = FreeImage_GetPitch(dib);
+ const unsigned y_pitch = FreeImage_GetPitch(Y);
+
+ int i;
+ unsigned x, y;
+ BYTE *bits = NULL, *Ybits = NULL;
+
+ // get statistics about the data (but only if its really needed)
+
+ f = exp(-f);
+ if ((m == 0) || (a != 1) && (c != 1)) {
+ // avoid these calculations if its not needed after ...
+ LuminanceFromY(Y, &maxLum, &minLum, &Lav, &Llav);
+ k = (log(maxLum) - Llav) / (log(maxLum) - log(minLum));
+ if(k < 0) {
+ // pow(k, 1.4F) is undefined ...
+ // there's an ambiguity about the calculation of Llav between Reinhard papers and the various implementations ...
+ // try another world adaptation luminance formula using instead 'worldLum = log(Llav)'
+ k = (log(maxLum) - log(Llav)) / (log(maxLum) - log(minLum));
+ if(k < 0) m = 0.3F;
+ }
+ }
+ m = (m > 0) ? m : (float)(0.3 + 0.7 * pow(k, 1.4F));
+
+ float max_color = -1e6F;
+ float min_color = +1e6F;
+
+ // tone map image
+
+ bits = (BYTE*)FreeImage_GetBits(dib);
+ Ybits = (BYTE*)FreeImage_GetBits(Y);
+
+ if ((a == 1) && (c == 0)) {
+ // when using default values, use a fastest code
+
+ for(y = 0; y < height; y++) {
+ float *Y = (float*)Ybits;
+ float *color = (float*)bits;
+
+ for(x = 0; x < width; x++) {
+ I_a = Y[x]; // luminance(x, y)
+ for (i = 0; i < 3; i++) {
+ *color /= ( *color + pow(f * I_a, m));
+
+ max_color = (*color > max_color) ? *color : max_color;
+ min_color = (*color < min_color) ? *color : min_color;
+
+ color++;
+ }
+ }
+ // next line
+ bits += dib_pitch;
+ Ybits += y_pitch;
+ }
+ } else {
+ // complete algorithm
+
+ // channel averages
+
+ Cav[0] = Cav[1] = Cav[2] = 0;
+ if ((a != 1) && (c != 0)) {
+ // channel averages are not needed when (a == 1) or (c == 0)
+ bits = (BYTE*)FreeImage_GetBits(dib);
+ for(y = 0; y < height; y++) {
+ float *color = (float*)bits;
+ for(x = 0; x < width; x++) {
+ for(i = 0; i < 3; i++) {
+ Cav[i] += *color;
+ color++;
+ }
+ }
+ // next line
+ bits += dib_pitch;
+ }
+ const float image_size = (float)width * height;
+ for(i = 0; i < 3; i++) {
+ Cav[i] /= image_size;
+ }
+ }
+
+ // perform tone mapping
+
+ bits = (BYTE*)FreeImage_GetBits(dib);
+ for(y = 0; y < height; y++) {
+ const float *Y = (float*)Ybits;
+ float *color = (float*)bits;
+
+ for(x = 0; x < width; x++) {
+ L = Y[x]; // luminance(x, y)
+ for (i = 0; i < 3; i++) {
+ I_l = c * *color + (1-c) * L;
+ I_g = c * Cav[i] + (1-c) * Lav;
+ I_a = a * I_l + (1-a) * I_g;
+ *color /= ( *color + pow(f * I_a, m));
+
+ max_color = (*color > max_color) ? *color : max_color;
+ min_color = (*color < min_color) ? *color : min_color;
+
+ color++;
+ }
+ }
+ // next line
+ bits += dib_pitch;
+ Ybits += y_pitch;
+ }
+ }
+
+ // normalize intensities
+
+ if(max_color != min_color) {
+ bits = (BYTE*)FreeImage_GetBits(dib);
+ const float range = max_color - min_color;
+ for(y = 0; y < height; y++) {
+ float *color = (float*)bits;
+ for(x = 0; x < width; x++) {
+ for(i = 0; i < 3; i++) {
+ *color = (*color - min_color) / range;
+ color++;
+ }
+ }
+ // next line
+ bits += dib_pitch;
+ }
+ }
+
+ return TRUE;
+}
+
+// ----------------------------------------------------------
+// Main algorithm
+// ----------------------------------------------------------
+
+/**
+Apply the global/local tone mapping operator to a RGBF image and convert to 24-bit RGB<br>
+User parameters control intensity, contrast, and level of adaptation
+@param src Input RGBF image
+@param intensity Overall intensity in range [-8:8] : default to 0
+@param contrast Contrast in range [0.3:1) : default to 0
+@param adaptation Adaptation in range [0:1] : default to 1
+@param color_correction Color correction in range [0:1] : default to 0
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV
+FreeImage_TmoReinhard05Ex(FIBITMAP *src, double intensity, double contrast, double adaptation, double color_correction) {
+ if (!FreeImage_HasPixels(src)) return NULL;
+
+ // working RGBF variable
+ FIBITMAP *dib = NULL, *Y = NULL;
+
+ dib = FreeImage_ConvertToRGBF(src);
+ if (!dib) return NULL;
+
+ // get the Luminance channel
+ Y = ConvertRGBFToY(dib);
+ if (!Y) {
+ FreeImage_Unload(dib);
+ return NULL;
+ }
+
+ // perform the tone mapping
+ ToneMappingReinhard05(dib, Y, (float)intensity, (float)contrast, (float)adaptation, (float)color_correction);
+ // not needed anymore
+ FreeImage_Unload(Y);
+ // clamp image highest values to display white, then convert to 24-bit RGB
+ FIBITMAP *dst = ClampConvertRGBFTo24(dib);
+
+ // clean-up and return
+ FreeImage_Unload(dib);
+
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, src);
+
+ return dst;
+}
+
+/**
+Apply the global tone mapping operator to a RGBF image and convert to 24-bit RGB<br>
+User parameters control intensity and contrast
+@param src Input RGBF image
+@param intensity Overall intensity in range [-8:8] : default to 0
+@param contrast Contrast in range [0.3:1) : default to 0
+@return Returns a 24-bit RGB image if successful, returns NULL otherwise
+*/
+FIBITMAP* DLL_CALLCONV
+FreeImage_TmoReinhard05(FIBITMAP *src, double intensity, double contrast) {
+ return FreeImage_TmoReinhard05Ex(src, intensity, contrast, 1, 0);
+}