summaryrefslogtreecommitdiff
path: root/libs/freeimage/src/FreeImage
diff options
context:
space:
mode:
Diffstat (limited to 'libs/freeimage/src/FreeImage')
-rw-r--r--libs/freeimage/src/FreeImage/BitmapAccess.cpp1567
-rw-r--r--libs/freeimage/src/FreeImage/CacheFile.cpp271
-rw-r--r--libs/freeimage/src/FreeImage/ColorLookup.cpp784
-rw-r--r--libs/freeimage/src/FreeImage/Conversion.cpp549
-rw-r--r--libs/freeimage/src/FreeImage/Conversion16_555.cpp208
-rw-r--r--libs/freeimage/src/FreeImage/Conversion16_565.cpp203
-rw-r--r--libs/freeimage/src/FreeImage/Conversion24.cpp251
-rw-r--r--libs/freeimage/src/FreeImage/Conversion32.cpp344
-rw-r--r--libs/freeimage/src/FreeImage/Conversion4.cpp245
-rw-r--r--libs/freeimage/src/FreeImage/Conversion8.cpp304
-rw-r--r--libs/freeimage/src/FreeImage/ConversionFloat.cpp193
-rw-r--r--libs/freeimage/src/FreeImage/ConversionRGB16.cpp143
-rw-r--r--libs/freeimage/src/FreeImage/ConversionRGBA16.cpp146
-rw-r--r--libs/freeimage/src/FreeImage/ConversionRGBAF.cpp249
-rw-r--r--libs/freeimage/src/FreeImage/ConversionRGBF.cpp242
-rw-r--r--libs/freeimage/src/FreeImage/ConversionType.cpp698
-rw-r--r--libs/freeimage/src/FreeImage/ConversionUINT16.cpp133
-rw-r--r--libs/freeimage/src/FreeImage/FreeImage.cpp220
-rw-r--r--libs/freeimage/src/FreeImage/FreeImageIO.cpp173
-rw-r--r--libs/freeimage/src/FreeImage/GetType.cpp88
-rw-r--r--libs/freeimage/src/FreeImage/Halftoning.cpp473
-rw-r--r--libs/freeimage/src/FreeImage/LFPQuantizer.cpp206
-rw-r--r--libs/freeimage/src/FreeImage/MNGHelper.cpp1319
-rw-r--r--libs/freeimage/src/FreeImage/MemoryIO.cpp235
-rw-r--r--libs/freeimage/src/FreeImage/MultiPage.cpp986
-rw-r--r--libs/freeimage/src/FreeImage/NNQuantizer.cpp504
-rw-r--r--libs/freeimage/src/FreeImage/PixelAccess.cpp196
-rw-r--r--libs/freeimage/src/FreeImage/Plugin.cpp812
-rw-r--r--libs/freeimage/src/FreeImage/PluginBMP.cpp1457
-rw-r--r--libs/freeimage/src/FreeImage/PluginGIF.cpp1405
-rw-r--r--libs/freeimage/src/FreeImage/PluginICO.cpp823
-rw-r--r--libs/freeimage/src/FreeImage/PluginJPEG.cpp1705
-rw-r--r--libs/freeimage/src/FreeImage/PluginPNG.cpp1114
-rw-r--r--libs/freeimage/src/FreeImage/ToneMapping.cpp74
-rw-r--r--libs/freeimage/src/FreeImage/WuQuantizer.cpp557
-rw-r--r--libs/freeimage/src/FreeImage/ZLibInterface.cpp223
-rw-r--r--libs/freeimage/src/FreeImage/tmoColorConvert.cpp477
-rw-r--r--libs/freeimage/src/FreeImage/tmoDrago03.cpp293
-rw-r--r--libs/freeimage/src/FreeImage/tmoFattal02.cpp687
-rw-r--r--libs/freeimage/src/FreeImage/tmoReinhard05.cpp258
40 files changed, 20815 insertions, 0 deletions
diff --git a/libs/freeimage/src/FreeImage/BitmapAccess.cpp b/libs/freeimage/src/FreeImage/BitmapAccess.cpp
new file mode 100644
index 0000000000..c3be7a1742
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/BitmapAccess.cpp
@@ -0,0 +1,1567 @@
+// ==========================================================
+// 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 "../stdafx.h"
+
+#include "../MapIntrospector.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+/**
+Constants for the BITMAPINFOHEADER::biCompression field
+BI_RGB:
+The bitmap is in uncompressed red green blue (RGB) format that is not compressed and does not use color masks.
+BI_BITFIELDS:
+The bitmap is not compressed and the color table consists of three DWORD color masks that specify the red, green, and blue components,
+respectively, of each pixel. This is valid when used with 16 and 32-bits per pixel bitmaps.
+*/
+#ifndef _WINGDI_
+#define BI_RGB 0L
+#define BI_BITFIELDS 3L
+#endif // _WINGDI_
+
+// ----------------------------------------------------------
+// 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
+// ----------------------------------------------------------
+
+/**
+FreeImage header structure
+*/
+FI_STRUCT (FREEIMAGEHEADER) {
+ /** data type - bitmap, array of long, double, complex, etc */
+ FREE_IMAGE_TYPE type;
+
+ /** background color used for RGB transparency */
+ RGBQUAD bkgnd_color;
+
+ /**@name transparency management */
+ //@{
+ /**
+ why another table ? for easy transparency table retrieval !
+ transparency could be stored in the palette, which is better
+ overall, but it requires quite some changes and it will render
+ FreeImage_GetTransparencyTable obsolete in its current form;
+ */
+ BYTE transparent_table[256];
+ /** number of transparent colors */
+ int transparency_count;
+ /** TRUE if the image is transparent */
+ BOOL transparent;
+ //@}
+
+ /** space to hold ICC profile */
+ FIICCPROFILE iccProfile;
+
+ /** contains a list of metadata models attached to the bitmap */
+ METADATAMAP *metadata;
+
+ /** FALSE if the FIBITMAP only contains the header and no pixel data */
+ BOOL has_pixels;
+
+ /** optionally contains a thumbnail attached to the bitmap */
+ FIBITMAP *thumbnail;
+
+ /**@name external pixel buffer management */
+ //@{
+ /** pointer to user provided pixels, NULL otherwise */
+ BYTE *external_bits;
+ /** user provided pitch, 0 otherwise */
+ unsigned external_pitch;
+ //@}
+
+ //BYTE filler[1]; // fill to 32-bit alignment
+};
+
+// ----------------------------------------------------------
+// FREEIMAGERGBMASKS definition
+// ----------------------------------------------------------
+
+/**
+RGB mask structure - mainly used for 16-bit RGB555 / RGB 565 FIBITMAP
+*/
+FI_STRUCT (FREEIMAGERGBMASKS) {
+ 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
+};
+
+// ----------------------------------------------------------
+// Memory allocation on a specified alignment boundary
+// ----------------------------------------------------------
+
+#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__)
+
+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
+
+// ----------------------------------------------------------
+// FIBITMAP memory management
+// ----------------------------------------------------------
+
+/**
+Calculate the size of a FreeImage image.
+Align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary.
+This function includes a protection against malicious images, based on a KISS integer overflow detection mechanism.
+
+@param header_only If TRUE, calculate a 'header only' FIBITMAP size, otherwise calculate a full FIBITMAP size
+@param width Image width
+@param height Image height
+@param bpp Number of bits-per-pixel
+@param need_masks We only store the masks (and allocate memory for them) for 16-bit images of type FIT_BITMAP
+@return Returns a size in BYTE units
+@see FreeImage_AllocateBitmap
+*/
+static size_t
+FreeImage_GetInternalImageSize(BOOL header_only, unsigned width, unsigned height, unsigned bpp, BOOL need_masks) {
+ 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);
+ // we both add palette size and masks size if need_masks is true, since CalculateUsedPaletteEntries
+ // always returns 0 if need_masks is true (which is only true for 16 bit images).
+ dib_size += need_masks ? sizeof(DWORD) * 3 : 0;
+ 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
+ {
+ 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;
+ }
+
+ /*
+ 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;
+
+ if(dImageSize > FIBITMAP_MAX_MEMORY) {
+ // avoid possible overflow inside C allocation functions
+ return 0;
+ }
+ }
+ }
+
+ return dib_size;
+}
+
+/**
+Helper for 16-bit FIT_BITMAP
+Returns a pointer to the bitmap's red-, green- and blue masks.
+@param dib The bitmap to obtain masks from.
+@return Returns a pointer to the bitmap's red-, green- and blue masks
+or NULL, if no masks are present (e.g. for 24 bit images).
+*/
+static FREEIMAGERGBMASKS *
+FreeImage_GetRGBMasks(FIBITMAP *dib) {
+ return FreeImage_HasRGBMasks(dib) ? (FREEIMAGERGBMASKS *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL;
+}
+
+/**
+Internal FIBITMAP allocation.
+
+This function accepts (ext_bits, ext_pitch) arguments. If these are provided the FIBITMAP
+will be allocated as "header only", but bits and pitch will be stored within the FREEIMAGEHEADER
+and the resulting FIBITMAP will have pixels, i.e. HasPixels() will return TRUE.
+- GetBits() and GetPitch return the correct values - either offsets or the stored values (user-provided bits and pitch).
+- Clone() creates a new FIBITMAP with copy of the user pixel data.
+- Unload's implementation does not need to change - it just release a "header only" dib.
+Note that when using external data, the data does not need to have the same alignment as the default 4-byte alignment.
+This enables the possibility to access buffers with, for instance, stricter alignment,
+like the ones used in low-level APIs like OpenCL or intrinsics.
+
+@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP
+@param ext_bits Pointer to external user's pixel buffer if using wrapped buffer, NULL otherwise
+@param ext_pitch Pointer to external user's pixel buffer pitch if using wrapped buffer, 0 otherwise
+@param type Image type
+@param width Image width
+@param height Image height
+@param bpp Number of bits per pixel
+@param red_mask Image red mask
+@param green_mask Image green mask
+@param blue_mask Image blue mask
+@return Returns the allocated FIBITMAP if successful, returns NULL otherwise
+*/
+static FIBITMAP *
+FreeImage_AllocateBitmap(BOOL header_only, BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+
+ // check input variables
+ width = abs(width);
+ height = abs(height);
+ if(!((width > 0) && (height > 0))) {
+ return NULL;
+ }
+ if(ext_bits) {
+ if(ext_pitch == 0) {
+ return NULL;
+ }
+ assert(header_only == FALSE);
+ }
+
+ // we only store the masks (and allocate memory for them) for 16-bit images of type FIT_BITMAP
+ BOOL need_masks = FALSE;
+
+ // check pixel bit depth
+ switch(type) {
+ case FIT_BITMAP:
+ switch(bpp) {
+ case 1:
+ case 4:
+ case 8:
+ break;
+ case 16:
+ need_masks = TRUE;
+ break;
+ 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:
+ return NULL;
+ }
+
+ FIBITMAP *bitmap = (FIBITMAP *)malloc(sizeof(FIBITMAP));
+
+ if (bitmap != 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
+
+ // when using a user provided pixel buffer, force a 'header only' allocation
+
+ size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks);
+
+ 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;
+
+ 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;
+
+ // store a pointer to user provided pixel buffer (if any)
+
+ fih->external_bits = ext_bits;
+ fih->external_pitch = ext_pitch;
+
+ // write out the BITMAPINFOHEADER
+
+ BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(bitmap);
+ bih->biSize = sizeof(BITMAPINFOHEADER);
+ bih->biWidth = width;
+ bih->biHeight = height;
+ bih->biPlanes = 1;
+ bih->biCompression = need_masks ? BI_BITFIELDS : BI_RGB;
+ 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;
+ }
+ }
+
+ // just setting the masks (only if needed) just like the palette.
+ if (need_masks) {
+ FREEIMAGERGBMASKS *masks = FreeImage_GetRGBMasks(bitmap);
+ masks->red_mask = red_mask;
+ masks->green_mask = green_mask;
+ masks->blue_mask = blue_mask;
+ }
+
+ return bitmap;
+ }
+
+ free(bitmap);
+ }
+
+ return NULL;
+}
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_AllocateHeaderForBits(BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) {
+ return FreeImage_AllocateBitmap(FALSE, ext_bits, ext_pitch, type, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+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) {
+ return FreeImage_AllocateBitmap(header_only, NULL, 0, type, width, height, bpp, red_mask, green_mask, blue_mask);
+}
+
+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_AllocateBitmap(header_only, NULL, 0, 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_AllocateBitmap(FALSE, NULL, 0, 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_AllocateBitmap(FALSE, NULL, 0, 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;
+ }
+
+ FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib);
+ unsigned width = FreeImage_GetWidth(dib);
+ unsigned height = FreeImage_GetHeight(dib);
+ unsigned bpp = FreeImage_GetBPP(dib);
+
+ const BYTE *ext_bits = ((FREEIMAGEHEADER *)dib->data)->external_bits;
+
+ // check for pixel availability ...
+ BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE;
+
+ // check whether this image has masks defined ...
+ BOOL need_masks = (bpp == 16 && type == FIT_BITMAP) ? TRUE : FALSE;
+
+ // allocate a new dib
+ FIBITMAP *new_dib = FreeImage_AllocateHeaderT(header_only, type, 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 the src 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
+
+ // when using a user provided pixel buffer, force a 'header only' calculation
+
+ size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks);
+
+ // 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));
+
+ // copy user provided pixel buffer (if any)
+ if(ext_bits) {
+ const unsigned pitch = FreeImage_GetPitch(dib);
+ const unsigned linesize = FreeImage_GetLine(dib);
+ for(unsigned y = 0; y < height; y++) {
+ memcpy(FreeImage_GetScanLine(new_dib, y), ext_bits, linesize);
+ ext_bits += pitch;
+ }
+ }
+
+ return new_dib;
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------
+
+BYTE * DLL_CALLCONV
+FreeImage_GetBits(FIBITMAP *dib) {
+ if(!FreeImage_HasPixels(dib)) {
+ return NULL;
+ }
+
+ if(((FREEIMAGEHEADER *)dib->data)->external_bits) {
+ return ((FREEIMAGEHEADER *)dib->data)->external_bits;
+ }
+
+ // 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 += FreeImage_HasRGBMasks(dib) ? sizeof(DWORD) * 3 : 0;
+ lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0);
+ return (BYTE *)lp;
+}
+
+// ----------------------------------------------------------
+// DIB information functions
+// ----------------------------------------------------------
+
+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_UINT16:
+ {
+ // 16-bit greyscale TIF can be either FIC_MINISBLACK (the most common case) or FIC_MINISWHITE
+ // you can check this using EXIF_MAIN metadata
+ FITAG *photometricTag = NULL;
+ if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "PhotometricInterpretation", &photometricTag)) {
+ const short *value = (short*)FreeImage_GetTagValue(photometricTag);
+ // PHOTOMETRIC_MINISWHITE = 0 => min value is white
+ // PHOTOMETRIC_MINISBLACK = 1 => min value is black
+ return (*value == 0) ? FIC_MINISWHITE : FIC_MINISBLACK;
+ }
+ return FIC_MINISBLACK;
+ }
+ break;
+ 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;
+ }
+
+ if( FreeImage_HasPixels(dib) ) {
+ // check for fully opaque alpha layer
+ 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;
+ }
+
+ return FIC_RGBALPHA;
+ }
+
+ 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;
+}
+
+// ----------------------------------------------------------
+
+BOOL DLL_CALLCONV
+FreeImage_HasRGBMasks(FIBITMAP *dib) {
+ return dib && FreeImage_GetInfoHeader(dib)->biCompression == BI_BITFIELDS;
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetRedMask(FIBITMAP *dib) {
+ FREEIMAGERGBMASKS *masks = NULL;
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ switch(image_type) {
+ case FIT_BITMAP:
+ // check for 16-bit RGB (565 or 555)
+ masks = FreeImage_GetRGBMasks(dib);
+ if (masks) {
+ return masks->red_mask;
+ }
+ return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_RED_MASK : 0;
+ default:
+ return 0;
+ }
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetGreenMask(FIBITMAP *dib) {
+ FREEIMAGERGBMASKS *masks = NULL;
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ switch(image_type) {
+ case FIT_BITMAP:
+ // check for 16-bit RGB (565 or 555)
+ masks = FreeImage_GetRGBMasks(dib);
+ if (masks) {
+ return masks->green_mask;
+ }
+ return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_GREEN_MASK : 0;
+ default:
+ return 0;
+ }
+}
+
+unsigned DLL_CALLCONV
+FreeImage_GetBlueMask(FIBITMAP *dib) {
+ FREEIMAGERGBMASKS *masks = NULL;
+ FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ switch(image_type) {
+ case FIT_BITMAP:
+ // check for 16-bit RGB (565 or 555)
+ masks = FreeImage_GetRGBMasks(dib);
+ if (masks) {
+ return masks->blue_mask;
+ }
+ return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_BLUE_MASK : 0;
+ default:
+ return 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 = MAX(0, MIN(count, 256));
+ if (FreeImage_GetBPP(dib) <= 8) {
+ ((FREEIMAGEHEADER *)dib->data)->transparent = (count > 0) ? TRUE : FALSE;
+ ((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) {
+ if(dib) {
+ FREEIMAGEHEADER *fih = (FREEIMAGEHEADER *)dib->data;
+ return fih->external_bits ? fih->external_pitch : (FreeImage_GetLine(dib) + 3 & ~3);
+ }
+ return 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;
+}
+
+/**
+Build and set a FITAG whose type is FIDT_ASCII.
+@param model Metadata model to be filled
+@param dib Image to be filled
+@param key Tag key
+@param value Tag value as a ASCII string
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL DLL_CALLCONV
+FreeImage_SetMetadataKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) {
+ if(!dib || !key || !value) {
+ return FALSE;
+ }
+ // create a tag
+ FITAG *tag = FreeImage_CreateTag();
+ if(tag) {
+ BOOL bSuccess = TRUE;
+ // fill the tag
+ DWORD tag_length = (DWORD)(strlen(value) + 1);
+ bSuccess &= FreeImage_SetTagKey(tag, key);
+ bSuccess &= FreeImage_SetTagLength(tag, tag_length);
+ bSuccess &= FreeImage_SetTagCount(tag, tag_length);
+ bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII);
+ bSuccess &= FreeImage_SetTagValue(tag, value);
+ if(bSuccess) {
+ // set the tag
+ bSuccess &= FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag);
+ }
+ // delete the tag
+ FreeImage_DeleteTag(tag);
+
+ return bSuccess;
+ }
+
+ return 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();
+}
+
+// ----------------------------------------------------------
+
+unsigned DLL_CALLCONV
+FreeImage_GetMemorySize(FIBITMAP *dib) {
+ if (!dib) {
+ return 0;
+ }
+ FREEIMAGEHEADER *header = (FREEIMAGEHEADER *)dib->data;
+ BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(dib);
+
+ BOOL header_only = !header->has_pixels || header->external_bits != NULL;
+ BOOL need_masks = bih->biCompression == BI_BITFIELDS;
+ unsigned width = bih->biWidth;
+ unsigned height = bih->biHeight;
+ unsigned bpp = bih->biBitCount;
+
+ // start off with the size of the FIBITMAP structure
+ size_t size = sizeof(FIBITMAP);
+
+ // add sizes of FREEIMAGEHEADER, BITMAPINFOHEADER, palette and DIB data
+ size += FreeImage_GetInternalImageSize(header_only, width, height, bpp, need_masks);
+
+ // add ICC profile size
+ size += header->iccProfile.size;
+
+ // add thumbnail image size
+ if (header->thumbnail) {
+ // we assume a thumbnail not having a thumbnail as well,
+ // so this recursive call should not create an infinite loop
+ size += FreeImage_GetMemorySize(header->thumbnail);
+ }
+
+ // add metadata size
+ METADATAMAP *md = header->metadata;
+ if (!md) {
+ return (unsigned)size;
+ }
+
+ // add size of METADATAMAP
+ size += sizeof(METADATAMAP);
+
+ const size_t models = md->size();
+ if (models == 0) {
+ return (unsigned)size;
+ }
+
+ unsigned tags = 0;
+
+ for (METADATAMAP::iterator i = md->begin(); i != md->end(); i++) {
+ TAGMAP *tm = i->second;
+ if (tm) {
+ for (TAGMAP::iterator j = tm->begin(); j != tm->end(); j++) {
+ ++tags;
+ const std::string & key = j->first;
+ size += key.capacity();
+ size += FreeImage_GetTagMemorySize(j->second);
+ }
+ }
+ }
+
+ // add size of all TAGMAP instances
+ size += models * sizeof(TAGMAP);
+ // add size of tree nodes in METADATAMAP
+ size += MapIntrospector<METADATAMAP>::GetNodesMemorySize(models);
+ // add size of tree nodes in TAGMAP
+ size += MapIntrospector<TAGMAP>::GetNodesMemorySize(tags);
+
+ return (unsigned)size;
+}
+
diff --git a/libs/freeimage/src/FreeImage/CacheFile.cpp b/libs/freeimage/src/FreeImage/CacheFile.cpp
new file mode 100644
index 0000000000..086393bf03
--- /dev/null
+++ b/libs/freeimage/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 "../stdafx.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/libs/freeimage/src/FreeImage/ColorLookup.cpp b/libs/freeimage/src/FreeImage/ColorLookup.cpp
new file mode 100644
index 0000000000..a96bead208
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ColorLookup.cpp
@@ -0,0 +1,784 @@
+// ==========================================================
+// 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 "../stdafx.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 },
+ { "green", 0, 128, 0 },
+ { "greenyellow", 173, 255, 47 },
+ { "grey", 128, 128, 128 },
+ { "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/libs/freeimage/src/FreeImage/Conversion.cpp b/libs/freeimage/src/FreeImage/Conversion.cpp
new file mode 100644
index 0000000000..9ef49f393b
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Conversion.cpp
@@ -0,0 +1,549 @@
+// ==========================================================
+// 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)
+// - Carsten Klein (cklein05@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 "../stdafx.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)) {
+ const unsigned bpp = FreeImage_GetBPP(dib);
+ if((FreeImage_GetImageType(dib) == FIT_BITMAP) && (bpp == 24 || bpp == 32)) {
+ 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;
+ }
+ break;
+ }
+ case FIQ_NNQUANT :
+ {
+ if (bpp == 32) {
+ // 32-bit images not supported by NNQUANT
+ return NULL;
+ }
+ // 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;
+ }
+ case FIQ_LFPQUANT :
+ {
+ LFPQuantizer Q(PaletteSize);
+ FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette);
+ if(dst) {
+ // copy metadata from src to dst
+ FreeImage_CloneMetadata(dst, dib);
+ }
+ return dst;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+// ==========================================================
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertFromRawBitsEx(BOOL copySource, BYTE *bits, FREE_IMAGE_TYPE type, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
+ FIBITMAP *dib = NULL;
+
+ if(copySource) {
+ // allocate a FIBITMAP with internally managed pixel buffer
+ dib = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask);
+ if(!dib) {
+ return NULL;
+ }
+ // copy user provided pixel buffer into the dib
+ const unsigned linesize = FreeImage_GetLine(dib);
+ for(int y = 0; y < height; y++) {
+ memcpy(FreeImage_GetScanLine(dib, y), bits, linesize);
+ // next line in user's buffer
+ bits += pitch;
+ }
+ // flip pixels vertically if needed
+ if(topdown) {
+ FreeImage_FlipVertical(dib);
+ }
+ }
+ else {
+ // allocate a FIBITMAP using a wrapper to user provided pixel buffer
+ dib = FreeImage_AllocateHeaderForBits(bits, pitch, type, width, height, bpp, red_mask, green_mask, blue_mask);
+ if(!dib) {
+ return NULL;
+ }
+ // flip pixels vertically if needed
+ if(topdown) {
+ FreeImage_FlipVertical(dib);
+ }
+ }
+
+ return dib;
+}
+
+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) {
+ return FreeImage_ConvertFromRawBitsEx(TRUE /* copySource */, bits, FIT_BITMAP, width, height, pitch, bpp, red_mask, green_mask, blue_mask, topdown);
+}
+
+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/libs/freeimage/src/FreeImage/Conversion16_555.cpp b/libs/freeimage/src/FreeImage/Conversion16_555.cpp
new file mode 100644
index 0000000000..a51d9ee6a5
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Conversion16_555.cpp
@@ -0,0 +1,208 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/Conversion16_565.cpp b/libs/freeimage/src/FreeImage/Conversion16_565.cpp
new file mode 100644
index 0000000000..54de0c54c3
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Conversion16_565.cpp
@@ -0,0 +1,203 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/Conversion24.cpp b/libs/freeimage/src/FreeImage/Conversion24.cpp
new file mode 100644
index 0000000000..c0afe98a7c
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Conversion24.cpp
@@ -0,0 +1,251 @@
+// ==========================================================
+// 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 "../stdafx.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) && (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 == 24) {
+ return FreeImage_Clone(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;
+ }
+ }
+
+ } else if(image_type == FIT_RGB16) {
+ 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);
+
+ 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;
+
+ } else if(image_type == FIT_RGBA16) {
+ 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);
+
+ 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;
+ 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 NULL;
+}
diff --git a/libs/freeimage/src/FreeImage/Conversion32.cpp b/libs/freeimage/src/FreeImage/Conversion32.cpp
new file mode 100644
index 0000000000..f5eb0c2103
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Conversion32.cpp
@@ -0,0 +1,344 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/Conversion4.cpp b/libs/freeimage/src/FreeImage/Conversion4.cpp
new file mode 100644
index 0000000000..165bdc2a00
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Conversion4.cpp
@@ -0,0 +1,245 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/Conversion8.cpp b/libs/freeimage/src/FreeImage/Conversion8.cpp
new file mode 100644
index 0000000000..1602c7705c
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Conversion8.cpp
@@ -0,0 +1,304 @@
+// ==========================================================
+// 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)
+// - Carsten Klein (cklein05@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 "../stdafx.h"
+
+// ----------------------------------------------------------
+// internal conversions X to 8 bits
+// ----------------------------------------------------------
+
+void DLL_CALLCONV
+FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels) {
+ for (unsigned cols = 0; cols < (unsigned)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) {
+ unsigned count_new = 0;
+ unsigned count_org = 0;
+ BOOL hinibble = TRUE;
+
+ while (count_new < (unsigned)width_in_pixels) {
+ if (hinibble) {
+ target[count_new] = (source[count_org] >> 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) {
+ const WORD *const bits = (WORD *)source;
+ for (unsigned cols = 0; cols < (unsigned)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) {
+ const WORD *const bits = (WORD *)source;
+ for (unsigned cols = 0; cols < (unsigned)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 (unsigned cols = 0; cols < (unsigned)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 (unsigned cols = 0; cols < (unsigned)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 FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
+ if (image_type != FIT_BITMAP && image_type != FIT_UINT16) {
+ return NULL;
+ }
+
+ const unsigned bpp = FreeImage_GetBPP(dib);
+
+ if (bpp != 8) {
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+
+ // Allocate a destination image
+ 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);
+
+ // Palette of destination image has already been initialized
+ RGBQUAD *new_pal = FreeImage_GetPalette(new_dib);
+
+ const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+
+ if (image_type == FIT_BITMAP) {
+
+ switch(bpp) {
+ case 1:
+ {
+ if (color_type == FIC_PALETTE) {
+ // Copy the palette
+ RGBQUAD *old_pal = FreeImage_GetPalette(dib);
+ new_pal[0] = old_pal[0];
+ new_pal[255] = old_pal[1];
+
+ } else if (color_type == FIC_MINISWHITE) {
+ // Create a reverse grayscale palette
+ CREATE_GREYSCALE_PALETTE_REVERSE(new_pal, 256);
+ }
+
+ // Expand and copy the bitmap data
+ for (unsigned rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine1To8(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ return new_dib;
+ }
+
+ case 4 :
+ {
+ if (color_type == FIC_PALETTE) {
+ // Copy the palette
+ memcpy(new_pal, FreeImage_GetPalette(dib), 16 * sizeof(RGBQUAD));
+ }
+
+ // Expand and copy the bitmap data
+ for (unsigned 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
+ if (IS_FORMAT_RGB565(dib)) {
+ for (unsigned rows = 0; rows < height; rows++) {
+ FreeImage_ConvertLine16To8_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
+ }
+ } else {
+ for (unsigned rows = 0; rows < height; rows++) {
+ 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 (unsigned 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 (unsigned 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 (unsigned rows = 0; rows < height; rows++) {
+ const WORD *const src_pixel = (WORD*)src_bits;
+ BYTE *dst_pixel = (BYTE*)dst_bits;
+ for(unsigned 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);
+
+ if (color_type == FIC_PALETTE || color_type == FIC_MINISWHITE) {
+
+ const unsigned bpp = FreeImage_GetBPP(dib);
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned 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);
+
+ // Create a greyscale palette
+ BYTE grey_pal[256];
+ const RGBQUAD *pal = FreeImage_GetPalette(dib);
+ const unsigned size = CalculateUsedPaletteEntries(bpp);
+ for (unsigned i = 0; i < size; i++) {
+ grey_pal[i] = GREY(pal->rgbRed, pal->rgbGreen, pal->rgbBlue);
+ pal++;
+ }
+
+ const BYTE *src_bits = FreeImage_GetBits(dib);
+ BYTE *dst_bits = FreeImage_GetBits(new_dib);
+
+ const unsigned src_pitch = FreeImage_GetPitch(dib);
+ const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
+
+ switch(bpp) {
+ case 1:
+ {
+ for (unsigned y = 0; y < height; y++) {
+ for (unsigned x = 0; x < width; x++) {
+ const unsigned pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0;
+ dst_bits[x] = grey_pal[pixel];
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case 4:
+ {
+ for (unsigned y = 0; y < height; y++) {
+ for (unsigned x = 0; x < width; x++) {
+ const unsigned pixel = x & 0x01 ? src_bits[x >> 1] & 0x0F : src_bits[x >> 1] >> 4;
+ dst_bits[x] = grey_pal[pixel];
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case 8:
+ {
+ for (unsigned y = 0; y < height; y++) {
+ for (unsigned x = 0; x < width; x++) {
+ dst_bits[x] = grey_pal[src_bits[x]];
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+ }
+ return new_dib;
+ }
+
+ // Convert the bitmap to 8-bit greyscale
+ return FreeImage_ConvertTo8Bits(dib);
+}
diff --git a/libs/freeimage/src/FreeImage/ConversionFloat.cpp b/libs/freeimage/src/FreeImage/ConversionFloat.cpp
new file mode 100644
index 0000000000..83114fd381
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ConversionFloat.cpp
@@ -0,0 +1,193 @@
+// ==========================================================
+// 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 "../stdafx.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);
+ dst_pixel[x] = CLAMP(dst_pixel[x], 0.0F, 1.0F);
+ }
+ 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);
+ dst_pixel[x] = CLAMP(dst_pixel[x], 0.0F, 1.0F);
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/libs/freeimage/src/FreeImage/ConversionRGB16.cpp b/libs/freeimage/src/FreeImage/ConversionRGB16.cpp
new file mode 100644
index 0000000000..9e6fbc0bec
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ConversionRGB16.cpp
@@ -0,0 +1,143 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/ConversionRGBA16.cpp b/libs/freeimage/src/FreeImage/ConversionRGBA16.cpp
new file mode 100644
index 0000000000..7e1259668b
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ConversionRGBA16.cpp
@@ -0,0 +1,146 @@
+// ==========================================================
+// 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 "../stdafx.h"
+
+// ----------------------------------------------------------
+// smart convert X to RGBA16
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGBA16(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 32-bit if needed
+ if(FreeImage_GetBPP(dib) == 32) {
+ src = dib;
+ } else {
+ src = FreeImage_ConvertTo32Bits(dib);
+ if(!src) return NULL;
+ }
+ break;
+ }
+ case FIT_UINT16:
+ // allow conversion from unsigned 16-bit
+ src = dib;
+ break;
+ case FIT_RGB16:
+ // allow conversion from 48-bit RGB
+ src = dib;
+ break;
+ case FIT_RGBA16:
+ // RGBA16 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_RGBA16, 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 RGBA16
+
+ switch(src_type) {
+ case FIT_BITMAP:
+ {
+ // Calculate the number of bytes per pixel (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);
+ FIRGBA16 *dst_bits = (FIRGBA16*)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;
+ dst_bits[x].alpha = src_bits[FI_RGBA_ALPHA] << 8;
+ src_bits += bytespp;
+ }
+ }
+ }
+ break;
+
+ case FIT_UINT16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y);
+ FIRGBA16 *dst_bits = (FIRGBA16*)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];
+ dst_bits[x].alpha = 0xFFFF;
+ }
+ }
+ }
+ break;
+
+ case FIT_RGB16:
+ {
+ for(unsigned y = 0; y < height; y++) {
+ const FIRGB16 *src_bits = (FIRGB16*)FreeImage_GetScanLine(src, y);
+ FIRGBA16 *dst_bits = (FIRGBA16*)FreeImage_GetScanLine(dst, y);
+ for(unsigned x = 0; x < width; x++) {
+ // convert pixels directly, while adding a "dummy" alpha of 1.0
+ dst_bits[x].red = src_bits[x].red;
+ dst_bits[x].green = src_bits[x].green;
+ dst_bits[x].blue = src_bits[x].blue;
+ dst_bits[x].alpha = 0xFFFF;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/libs/freeimage/src/FreeImage/ConversionRGBAF.cpp b/libs/freeimage/src/FreeImage/ConversionRGBAF.cpp
new file mode 100644
index 0000000000..f2bcaa7072
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ConversionRGBAF.cpp
@@ -0,0 +1,249 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Tanner Helland (tannerhelland@users.sf.net)
+// - 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 "../stdafx.h"
+
+// ----------------------------------------------------------
+// smart convert X to RGBAF
+// ----------------------------------------------------------
+
+FIBITMAP * DLL_CALLCONV
+FreeImage_ConvertToRGBAF(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 32-bit
+ const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
+ if(color_type != FIC_RGBALPHA) {
+ src = FreeImage_ConvertTo32Bits(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
+ src = dib;
+ break;
+ case FIT_FLOAT:
+ // allow conversion from 32-bit float
+ src = dib;
+ break;
+ case FIT_RGBF:
+ // allow conversion from 96-bit RGBF
+ src = dib;
+ break;
+ case FIT_RGBAF:
+ // RGBAF 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_RGBAF, 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 RGBAF
+
+ 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 (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;
+ FIRGBAF *dst_pixel = (FIRGBAF*)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;
+ dst_pixel->alpha = (float)(src_pixel[FI_RGBA_ALPHA]) / 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;
+ FIRGBAF *dst_pixel = (FIRGBAF*)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;
+ dst_pixel[x].alpha = 1.0F;
+ }
+ 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;
+ FIRGBAF *dst_pixel = (FIRGBAF*)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;
+ dst_pixel[x].alpha = 1.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;
+ FIRGBAF *dst_pixel = (FIRGBAF*)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;
+ dst_pixel[x].alpha = (float)(src_pixel[x].alpha) / 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;
+ FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert by copying greyscale channel to each R, G, B channels
+ // assume float values are in [0..1]
+ const float value = CLAMP(src_pixel[x], 0.0F, 1.0F);
+ dst_pixel[x].red = value;
+ dst_pixel[x].green = value;
+ dst_pixel[x].blue = value;
+ dst_pixel[x].alpha = 1.0F;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+
+ case FIT_RGBF:
+ {
+ const 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;
+ FIRGBAF *dst_pixel = (FIRGBAF*)dst_bits;
+
+ for(unsigned x = 0; x < width; x++) {
+ // convert pixels directly, while adding a "dummy" alpha of 1.0
+ dst_pixel[x].red = CLAMP(src_pixel[x].red, 0.0F, 1.0F);
+ dst_pixel[x].green = CLAMP(src_pixel[x].green, 0.0F, 1.0F);
+ dst_pixel[x].blue = CLAMP(src_pixel[x].blue, 0.0F, 1.0F);
+ dst_pixel[x].alpha = 1.0F;
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/libs/freeimage/src/FreeImage/ConversionRGBF.cpp b/libs/freeimage/src/FreeImage/ConversionRGBF.cpp
new file mode 100644
index 0000000000..cd0e875ddc
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ConversionRGBF.cpp
@@ -0,0 +1,242 @@
+// ==========================================================
+// 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 "../stdafx.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
+ // assume float values are in [0..1]
+ const float value = CLAMP(src_pixel[x], 0.0F, 1.0F);
+ dst_pixel[x].red = value;
+ dst_pixel[x].green = value;
+ dst_pixel[x].blue = value;
+ }
+ 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 = CLAMP(src_pixel[x].red, 0.0F, 1.0F);
+ dst_pixel[x].green = CLAMP(src_pixel[x].green, 0.0F, 1.0F);
+ dst_pixel[x].blue = CLAMP(src_pixel[x].blue, 0.0F, 1.0F);
+ }
+ src_bits += src_pitch;
+ dst_bits += dst_pitch;
+ }
+ }
+ break;
+ }
+
+ if(src != dib) {
+ FreeImage_Unload(src);
+ }
+
+ return dst;
+}
+
diff --git a/libs/freeimage/src/FreeImage/ConversionType.cpp b/libs/freeimage/src/FreeImage/ConversionType.cpp
new file mode 100644
index 0000000000..b3eedd25a8
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ConversionType.cpp
@@ -0,0 +1,698 @@
+// ==========================================================
+// Bitmap conversion routines
+//
+// Design and implementation by
+// - Hervé Drolon (drolon@infonie.fr)
+// - Tanner Helland (tannerhelland@users.sf.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 "../stdafx.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:
+ dst = FreeImage_ConvertToRGBA16(src);
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ dst = FreeImage_ConvertToRGBAF(src);
+ 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:
+ dst = FreeImage_ConvertToRGBA16(src);
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ dst = FreeImage_ConvertToRGBAF(src);
+ 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:
+ dst = FreeImage_ConvertToRGBAF(src);
+ 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:
+ dst = FreeImage_ConvertToRGBA16(src);
+ break;
+ case FIT_RGBF:
+ dst = FreeImage_ConvertToRGBF(src);
+ break;
+ case FIT_RGBAF:
+ dst = FreeImage_ConvertToRGBAF(src);
+ 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:
+ dst = FreeImage_ConvertToRGBAF(src);
+ 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:
+ dst = FreeImage_ConvertToRGBAF(src);
+ 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/libs/freeimage/src/FreeImage/ConversionUINT16.cpp b/libs/freeimage/src/FreeImage/ConversionUINT16.cpp
new file mode 100644
index 0000000000..37f0450f31
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ConversionUINT16.cpp
@@ -0,0 +1,133 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/FreeImage.cpp b/libs/freeimage/src/FreeImage/FreeImage.cpp
new file mode 100644
index 0000000000..0ecf730cd0
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/FreeImage.cpp
@@ -0,0 +1,220 @@
+// ==========================================================
+// 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!
+// ==========================================================
+
+#include "../stdafx.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/libs/freeimage/src/FreeImage/FreeImageIO.cpp b/libs/freeimage/src/FreeImage/FreeImageIO.cpp
new file mode 100644
index 0000000000..1cba25818e
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/FreeImageIO.cpp
@@ -0,0 +1,173 @@
+// ==========================================================
+// 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 "../stdafx.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++) {
+ long remaining_bytes = mem_header->file_length - mem_header->current_position;
+ //if there isn't size bytes left to read, set pos to eof and return a short count
+ if( remaining_bytes < (long)size ) {
+ if(remaining_bytes > 0) {
+ memcpy( buffer, (char *)mem_header->data + mem_header->current_position, remaining_bytes );
+ }
+ mem_header->current_position = mem_header->file_length;
+ break;
+ }
+ //copy size bytes count times
+ memcpy( buffer, (char *)mem_header->data + mem_header->current_position, size );
+ mem_header->current_position += 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->current_position + (long)(size * count)) >= mem_header->data_length ) {
+ //if we are at or above 1G, we cant double without going negative
+ if( mem_header->data_length & 0x40000000 ) {
+ //max 2G
+ if( mem_header->data_length == 0x7FFFFFFF ) {
+ return 0;
+ }
+ newdatalen = 0x7FFFFFFF;
+ } else if( mem_header->data_length == 0 ) {
+ //default to 4K if nothing yet
+ newdatalen = 4096;
+ } else {
+ //double size
+ newdatalen = mem_header->data_length << 1;
+ }
+ newdata = realloc( mem_header->data, newdatalen );
+ if( !newdata ) {
+ return 0;
+ }
+ mem_header->data = newdata;
+ mem_header->data_length = newdatalen;
+ }
+ memcpy( (char *)mem_header->data + mem_header->current_position, buffer, size * count );
+ mem_header->current_position += size * count;
+ if( mem_header->current_position > mem_header->file_length ) {
+ mem_header->file_length = mem_header->current_position;
+ }
+ return count;
+}
+
+int DLL_CALLCONV
+_MemorySeekProc(fi_handle handle, long offset, int origin) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+ // you can use _MemorySeekProc to reposition the pointer anywhere in a file
+ // the pointer can also be positioned beyond the end of the file
+
+ 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->current_position = offset;
+ return 0;
+ }
+ break;
+
+ case SEEK_CUR:
+ if( mem_header->current_position + offset >= 0 ) {
+ mem_header->current_position += offset;
+ return 0;
+ }
+ break;
+
+ case SEEK_END:
+ if( mem_header->file_length + offset >= 0 ) {
+ mem_header->current_position = mem_header->file_length + offset;
+ return 0;
+ }
+ break;
+ }
+
+ return -1;
+}
+
+long DLL_CALLCONV
+_MemoryTellProc(fi_handle handle) {
+ FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data);
+
+ return mem_header->current_position;
+}
+
+// ----------------------------------------------------------
+
+void
+SetMemoryIO(FreeImageIO *io) {
+ io->read_proc = _MemoryReadProc;
+ io->seek_proc = _MemorySeekProc;
+ io->tell_proc = _MemoryTellProc;
+ io->write_proc = _MemoryWriteProc;
+}
diff --git a/libs/freeimage/src/FreeImage/GetType.cpp b/libs/freeimage/src/FreeImage/GetType.cpp
new file mode 100644
index 0000000000..e5eff97d0b
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/GetType.cpp
@@ -0,0 +1,88 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/Halftoning.cpp b/libs/freeimage/src/FreeImage/Halftoning.cpp
new file mode 100644
index 0000000000..19195ad27a
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Halftoning.cpp
@@ -0,0 +1,473 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/LFPQuantizer.cpp b/libs/freeimage/src/FreeImage/LFPQuantizer.cpp
new file mode 100644
index 0000000000..88d9944a9d
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/LFPQuantizer.cpp
@@ -0,0 +1,206 @@
+// ==========================================================
+// LFPQuantizer class implementation
+//
+// Design and implementation by
+// - Carsten Klein (cklein05@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 "../stdafx.h"
+
+LFPQuantizer::LFPQuantizer(unsigned PaletteSize) :
+ m_size(0), m_limit(PaletteSize), m_index(0) {
+ m_map = new MapEntry[MAP_SIZE];
+ memset(m_map, 0xFF, MAP_SIZE * sizeof(MapEntry));
+}
+
+LFPQuantizer::~LFPQuantizer() {
+ delete[] m_map;
+}
+
+FIBITMAP* LFPQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette) {
+
+ if (ReserveSize > 0 && ReservePalette != NULL) {
+ AddReservePalette(ReservePalette, ReserveSize);
+ }
+
+ const unsigned width = FreeImage_GetWidth(dib);
+ const unsigned height = FreeImage_GetHeight(dib);
+
+ FIBITMAP *dib8 = FreeImage_Allocate(width, height, 8);
+ if (dib8 == NULL) {
+ return NULL;
+ }
+
+ const unsigned src_pitch = FreeImage_GetPitch(dib);
+ const unsigned dst_pitch = FreeImage_GetPitch(dib8);
+
+ const BYTE * const src_bits = FreeImage_GetBits(dib);
+ BYTE * const dst_bits = FreeImage_GetBits(dib8);
+
+ unsigned last_color = -1;
+ int last_index = 0;
+
+ if (FreeImage_GetBPP(dib) == 24) {
+
+ // Getting the source pixel as an unsigned int is much faster than
+ // working with FI_RGBA_xxx and shifting. However, this may fail
+ // for the very last pixel, since its rgbReserved member (alpha)
+ // may actually point to an address beyond the bitmap's memory. So,
+ // we do not process the last scanline in the first loop.
+
+ // Process all but the last scanline.
+ for (unsigned y = 0; y < height - 1; ++y) {
+ BYTE *dst_line = dst_bits + y * dst_pitch;
+ const BYTE *src_line = src_bits + y * src_pitch;
+ for (unsigned x = 0; x < width; ++x) {
+ const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF;
+ if (color != last_color) {
+ last_color = color;
+ last_index = GetIndexForColor(color);
+ if (last_index == -1) {
+ FreeImage_Unload(dib8);
+ return NULL;
+ }
+ }
+ dst_line[x] = last_index;
+ src_line += 3;
+ }
+ }
+
+ // Process all but the last pixel of the last scanline.
+ BYTE *dst_line = dst_bits + (height - 1) * dst_pitch;
+ const BYTE *src_line = src_bits + (height - 1) * src_pitch;
+ for (unsigned x = 0; x < width - 1; ++x) {
+ const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF;
+ if (color != last_color) {
+ last_color = color;
+ last_index = GetIndexForColor(color);
+ if (last_index == -1) {
+ FreeImage_Unload(dib8);
+ return NULL;
+ }
+ }
+ dst_line[x] = last_index;
+ src_line += 3;
+ }
+
+ // Process the last pixel (src_line should already point to it).
+ const unsigned color = 0 | src_line[FI_RGBA_BLUE] << FI_RGBA_BLUE_SHIFT
+ | src_line[FI_RGBA_GREEN] << FI_RGBA_GREEN_SHIFT
+ | src_line[FI_RGBA_RED] << FI_RGBA_RED_SHIFT;
+ if (color != last_color) {
+ last_color = color;
+ last_index = GetIndexForColor(color);
+ if (last_index == -1) {
+ FreeImage_Unload(dib8);
+ return NULL;
+ }
+ }
+ dst_line[width - 1] = last_index;
+
+ } else {
+ for (unsigned y = 0; y < height; ++y) {
+ BYTE *dst_line = dst_bits + y * dst_pitch;
+ const BYTE *src_line = src_bits + y * src_pitch;
+ for (unsigned x = 0; x < width; ++x) {
+ const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF;
+ if (color != last_color) {
+ last_color = color;
+ last_index = GetIndexForColor(color);
+ if (last_index == -1) {
+ FreeImage_Unload(dib8);
+ return NULL;
+ }
+ }
+ dst_line[x] = last_index;
+ src_line += 4;
+ }
+ }
+ }
+
+ WritePalette(FreeImage_GetPalette(dib8));
+ return dib8;
+}
+
+/**
+ * Returns the palette index of the specified color. Tries to put the
+ * color into the map, if it's not already present in the map. In that
+ * case, a new index is used for the color. Returns -1, if adding the
+ * color would exceed the desired maximum number of colors in the
+ * palette.
+ * @param color the color to get the index from
+ * @return the palette index of the specified color or -1, if there
+ * is no space left in the palette
+ */
+inline int LFPQuantizer::GetIndexForColor(unsigned color) {
+ unsigned bucket = hash(color) & (MAP_SIZE - 1);
+ while (m_map[bucket].color != color) {
+ if (m_map[bucket].color == EMPTY_BUCKET) {
+ if (m_size == m_limit) {
+ return -1;
+ }
+ m_map[bucket].color = color;
+ m_map[bucket].index = m_index++;
+ ++m_size;
+ break;
+ }
+ bucket = (bucket + 1) % MAP_SIZE;
+ }
+ return m_map[bucket].index;
+}
+
+/**
+ * Adds the specified number of entries of the specified reserve
+ * palette to the newly created palette.
+ * @param *palette a pointer to the reserve palette to copy from
+ * @param size the number of entries to copy
+ */
+void LFPQuantizer::AddReservePalette(const void *palette, unsigned size) {
+ if (size > MAX_SIZE) {
+ size = MAX_SIZE;
+ }
+ unsigned *ppal = (unsigned *) palette;
+ const unsigned offset = m_limit - size;
+ for (unsigned i = 0; i < size; ++i) {
+ const unsigned color = *ppal++;
+ const unsigned index = i + offset;
+ unsigned bucket = hash(color) & (MAP_SIZE - 1);
+ while((m_map[bucket].color != EMPTY_BUCKET) && (m_map[bucket].color != color)) {
+ bucket = (bucket + 1) % MAP_SIZE;
+ }
+ if(m_map[bucket].color != color) {
+ m_map[bucket].color = color;
+ m_map[bucket].index = index;
+ }
+ }
+ m_size += size;
+}
+
+/**
+ * Copies the newly created palette into the specified destination
+ * palette. Although unused palette entries are not overwritten in
+ * the destination palette, it is assumed to have space for at
+ * least 256 entries.
+ * @param palette a pointer to the destination palette
+ */
+void LFPQuantizer::WritePalette(void *palette) {
+ for (unsigned i = 0; i < MAP_SIZE; ++i) {
+ if (m_map[i].color != EMPTY_BUCKET) {
+ ((unsigned *) palette)[m_map[i].index] = m_map[i].color;
+ }
+ }
+}
diff --git a/libs/freeimage/src/FreeImage/MNGHelper.cpp b/libs/freeimage/src/FreeImage/MNGHelper.cpp
new file mode 100644
index 0000000000..1a7e924751
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/MNGHelper.cpp
@@ -0,0 +1,1319 @@
+// ==========================================================
+// MNG / JNG 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 "../stdafx.h"
+
+/**
+References
+http://www.libpng.org/pub/mng/spec/jng.html
+http://www.w3.org/TR/PNG/
+http://libpng.org/pub/mng/spec/
+*/
+
+// --------------------------------------------------------------------------
+
+#define MNG_INCLUDE_JNG
+
+#ifdef MNG_INCLUDE_JNG
+#define MNG_COLORTYPE_JPEGGRAY 8 /* JHDR */
+#define MNG_COLORTYPE_JPEGCOLOR 10
+#define MNG_COLORTYPE_JPEGGRAYA 12
+#define MNG_COLORTYPE_JPEGCOLORA 14
+
+#define MNG_BITDEPTH_JPEG8 8 /* JHDR */
+#define MNG_BITDEPTH_JPEG12 12
+#define MNG_BITDEPTH_JPEG8AND12 20
+
+#define MNG_COMPRESSION_BASELINEJPEG 8 /* JHDR */
+
+#define MNG_INTERLACE_SEQUENTIAL 0 /* JHDR */
+#define MNG_INTERLACE_PROGRESSIVE 8
+#endif /* MNG_INCLUDE_JNG */
+
+// --------------------------------------------------------------------------
+
+#define JNG_SUPPORTED
+
+/** Size of a JDAT chunk on writing */
+const DWORD JPEG_CHUNK_SIZE = 8192;
+
+/** PNG signature */
+static const BYTE g_png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+/** JNG signature */
+static const BYTE g_jng_signature[8] = { 139, 74, 78, 71, 13, 10, 26, 10 };
+
+// --------------------------------------------------------------------------
+
+/** Chunk type converted to enum */
+enum eChunckType {
+ UNKNOWN_CHUNCK,
+ MHDR,
+ BACK,
+ BASI,
+ CLIP,
+ CLON,
+ DEFI,
+ DHDR,
+ DISC,
+ ENDL,
+ FRAM,
+ IEND,
+ IHDR,
+ JHDR,
+ LOOP,
+ MAGN,
+ MEND,
+ MOVE,
+ PAST,
+ PLTE,
+ SAVE,
+ SEEK,
+ SHOW,
+ TERM,
+ bKGD,
+ cHRM,
+ gAMA,
+ iCCP,
+ nEED,
+ pHYg,
+ vpAg,
+ pHYs,
+ sBIT,
+ sRGB,
+ tRNS,
+ IDAT,
+ JDAT,
+ JDAA,
+ JdAA,
+ JSEP,
+ oFFs,
+ hIST,
+ iTXt,
+ sPLT,
+ sTER,
+ tEXt,
+ tIME,
+ zTXt
+};
+
+/**
+Helper for map<key, value> where value is a pointer to a string.
+Used to store tEXt metadata.
+*/
+typedef std::map<std::string, std::string> tEXtMAP;
+
+// --------------------------------------------------------------------------
+
+/*
+ Constant strings for known chunk types. If you need to add a chunk,
+ add a string holding the name here. To make the code more
+ portable, we use ASCII numbers like this, not characters.
+*/
+
+static BYTE mng_MHDR[5]={ 77, 72, 68, 82, (BYTE) '\0'};
+static BYTE mng_BACK[5]={ 66, 65, 67, 75, (BYTE) '\0'};
+static BYTE mng_BASI[5]={ 66, 65, 83, 73, (BYTE) '\0'};
+static BYTE mng_CLIP[5]={ 67, 76, 73, 80, (BYTE) '\0'};
+static BYTE mng_CLON[5]={ 67, 76, 79, 78, (BYTE) '\0'};
+static BYTE mng_DEFI[5]={ 68, 69, 70, 73, (BYTE) '\0'};
+static BYTE mng_DHDR[5]={ 68, 72, 68, 82, (BYTE) '\0'};
+static BYTE mng_DISC[5]={ 68, 73, 83, 67, (BYTE) '\0'};
+static BYTE mng_ENDL[5]={ 69, 78, 68, 76, (BYTE) '\0'};
+static BYTE mng_FRAM[5]={ 70, 82, 65, 77, (BYTE) '\0'};
+static BYTE mng_IEND[5]={ 73, 69, 78, 68, (BYTE) '\0'};
+static BYTE mng_IHDR[5]={ 73, 72, 68, 82, (BYTE) '\0'};
+static BYTE mng_JHDR[5]={ 74, 72, 68, 82, (BYTE) '\0'};
+static BYTE mng_LOOP[5]={ 76, 79, 79, 80, (BYTE) '\0'};
+static BYTE mng_MAGN[5]={ 77, 65, 71, 78, (BYTE) '\0'};
+static BYTE mng_MEND[5]={ 77, 69, 78, 68, (BYTE) '\0'};
+static BYTE mng_MOVE[5]={ 77, 79, 86, 69, (BYTE) '\0'};
+static BYTE mng_PAST[5]={ 80, 65, 83, 84, (BYTE) '\0'};
+static BYTE mng_PLTE[5]={ 80, 76, 84, 69, (BYTE) '\0'};
+static BYTE mng_SAVE[5]={ 83, 65, 86, 69, (BYTE) '\0'};
+static BYTE mng_SEEK[5]={ 83, 69, 69, 75, (BYTE) '\0'};
+static BYTE mng_SHOW[5]={ 83, 72, 79, 87, (BYTE) '\0'};
+static BYTE mng_TERM[5]={ 84, 69, 82, 77, (BYTE) '\0'};
+static BYTE mng_bKGD[5]={ 98, 75, 71, 68, (BYTE) '\0'};
+static BYTE mng_cHRM[5]={ 99, 72, 82, 77, (BYTE) '\0'};
+static BYTE mng_gAMA[5]={103, 65, 77, 65, (BYTE) '\0'};
+static BYTE mng_iCCP[5]={105, 67, 67, 80, (BYTE) '\0'};
+static BYTE mng_nEED[5]={110, 69, 69, 68, (BYTE) '\0'};
+static BYTE mng_pHYg[5]={112, 72, 89, 103, (BYTE) '\0'};
+static BYTE mng_vpAg[5]={118, 112, 65, 103, (BYTE) '\0'};
+static BYTE mng_pHYs[5]={112, 72, 89, 115, (BYTE) '\0'};
+static BYTE mng_sBIT[5]={115, 66, 73, 84, (BYTE) '\0'};
+static BYTE mng_sRGB[5]={115, 82, 71, 66, (BYTE) '\0'};
+static BYTE mng_tRNS[5]={116, 82, 78, 83, (BYTE) '\0'};
+
+#if defined(JNG_SUPPORTED)
+static BYTE mng_IDAT[5]={ 73, 68, 65, 84, (BYTE) '\0'};
+static BYTE mng_JDAT[5]={ 74, 68, 65, 84, (BYTE) '\0'};
+static BYTE mng_JDAA[5]={ 74, 68, 65, 65, (BYTE) '\0'};
+static BYTE mng_JdAA[5]={ 74, 100, 65, 65, (BYTE) '\0'};
+static BYTE mng_JSEP[5]={ 74, 83, 69, 80, (BYTE) '\0'};
+static BYTE mng_oFFs[5]={111, 70, 70, 115, (BYTE) '\0'};
+#endif
+
+static BYTE mng_hIST[5]={104, 73, 83, 84, (BYTE) '\0'};
+static BYTE mng_iTXt[5]={105, 84, 88, 116, (BYTE) '\0'};
+static BYTE mng_sPLT[5]={115, 80, 76, 84, (BYTE) '\0'};
+static BYTE mng_sTER[5]={115, 84, 69, 82, (BYTE) '\0'};
+static BYTE mng_tEXt[5]={116, 69, 88, 116, (BYTE) '\0'};
+static BYTE mng_tIME[5]={116, 73, 77, 69, (BYTE) '\0'};
+static BYTE mng_zTXt[5]={122, 84, 88, 116, (BYTE) '\0'};
+
+
+// --------------------------------------------------------------------------
+
+/**
+Convert a chunk name to a unique ID
+*/
+static eChunckType
+mng_GetChunckType(const BYTE *mChunkName) {
+ if(memcmp(mChunkName, mng_MHDR, 4) == 0) {
+ return MHDR;
+ }
+ if(memcmp(mChunkName, mng_LOOP, 4) == 0) {
+ return LOOP;
+ }
+ if(memcmp(mChunkName, mng_DEFI, 4) == 0) {
+ return DEFI;
+ }
+ if(memcmp(mChunkName, mng_PLTE, 4) == 0) {
+ return PLTE;
+ }
+ if(memcmp(mChunkName, mng_tRNS, 4) == 0) {
+ return tRNS;
+ }
+ if(memcmp(mChunkName, mng_IHDR, 4) == 0) {
+ return IHDR;
+ }
+ if(memcmp(mChunkName, mng_JHDR, 4) == 0) {
+ return JHDR;
+ }
+ if(memcmp(mChunkName, mng_MEND, 4) == 0) {
+ return MEND;
+ }
+ if(memcmp(mChunkName, mng_IEND, 4) == 0) {
+ return IEND;
+ }
+ if(memcmp(mChunkName, mng_JDAT, 4) == 0) {
+ return JDAT;
+ }
+ if(memcmp(mChunkName, mng_IDAT, 4) == 0) {
+ return IDAT;
+ }
+ if(memcmp(mChunkName, mng_JDAA, 4) == 0) {
+ return JDAA;
+ }
+ if(memcmp(mChunkName, mng_gAMA, 4) == 0) {
+ return gAMA;
+ }
+ if(memcmp(mChunkName, mng_pHYs, 4) == 0) {
+ return pHYs;
+ }
+ if(memcmp(mChunkName, mng_bKGD, 4) == 0) {
+ return bKGD;
+ }
+ if(memcmp(mChunkName, mng_tEXt, 4) == 0) {
+ return tEXt;
+ }
+
+ return UNKNOWN_CHUNCK;
+}
+
+inline void
+mng_SwapShort(WORD *sp) {
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapShort(sp);
+#endif
+}
+
+inline void
+mng_SwapLong(DWORD *lp) {
+#ifndef FREEIMAGE_BIGENDIAN
+ SwapLong(lp);
+#endif
+}
+
+/**
+Returns the size, in bytes, of a FreeImageIO stream, from the current position.
+*/
+static long
+mng_LOF(FreeImageIO *io, fi_handle handle) {
+ long start_pos = io->tell_proc(handle);
+ io->seek_proc(handle, 0, SEEK_END);
+ long file_length = io->tell_proc(handle);
+ io->seek_proc(handle, start_pos, SEEK_SET);
+ return file_length;
+}
+
+/**
+Count the number of bytes in a PNG stream, from IHDR to IEND.
+If successful, the stream position, as given by io->tell_proc(handle),
+should be the end of the PNG stream at the return of the function.
+@param io
+@param handle
+@param inPos
+@param m_TotalBytesOfChunks
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+mng_CountPNGChunks(FreeImageIO *io, fi_handle handle, long inPos, unsigned *m_TotalBytesOfChunks) {
+ long mLOF;
+ long mPos;
+ BOOL mEnd = FALSE;
+ DWORD mLength = 0;
+ BYTE mChunkName[5];
+
+ *m_TotalBytesOfChunks = 0;
+
+ // get the length of the file
+ mLOF = mng_LOF(io, handle);
+
+ // go to the start of the file
+ io->seek_proc(handle, inPos, SEEK_SET);
+
+ try {
+ // parse chunks
+ while(mEnd == FALSE) {
+ // chunk length
+ mPos = io->tell_proc(handle);
+ if(mPos + 4 > mLOF) {
+ throw(1);
+ }
+ io->read_proc(&mLength, 1, 4, handle);
+ mng_SwapLong(&mLength);
+ // chunk name
+ mPos = io->tell_proc(handle);
+ if(mPos + 4 > mLOF) {
+ throw(1);
+ }
+ io->read_proc(&mChunkName[0], 1, 4, handle);
+ mChunkName[4] = '\0';
+
+ // go to next chunk
+ mPos = io->tell_proc(handle);
+ // 4 = size of the CRC
+ if(mPos + (long)mLength + 4 > mLOF) {
+ throw(1);
+ }
+ io->seek_proc(handle, mLength + 4, SEEK_CUR);
+
+ switch( mng_GetChunckType(mChunkName) ) {
+ case IHDR:
+ if(mLength != 13) {
+ throw(1);
+ }
+ break;
+
+ case IEND:
+ mEnd = TRUE;
+ // the length below includes 4 bytes CRC, but no bytes for Length
+ *m_TotalBytesOfChunks = io->tell_proc(handle) - inPos;
+ break;
+
+ case UNKNOWN_CHUNCK:
+ default:
+ break;
+ }
+
+ } // while(!mEnd)
+
+ return TRUE;
+
+ } catch(int) {
+ return FALSE;
+ }
+}
+
+/**
+Retrieve the position of a chunk in a PNG stream
+@param hPngMemory PNG stream handle
+@param chunk_name Name of the chunk to be found
+@param offset Start of the search in the stream
+@param start_pos [returned value] Start position of the chunk
+@param next_pos [returned value] Start position of the next chunk
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+mng_FindChunk(FIMEMORY *hPngMemory, BYTE *chunk_name, long offset, DWORD *start_pos, DWORD *next_pos) {
+ DWORD mLength = 0;
+
+ BYTE *data = NULL;
+ DWORD size_in_bytes = 0;
+
+ *start_pos = 0;
+ *next_pos = 0;
+
+ // get a pointer to the stream buffer
+ FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
+ if(!(data && size_in_bytes) || (size_in_bytes < 20) || (size_in_bytes - offset < 20)) {
+ // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
+ return FALSE;
+ }
+
+ try {
+
+ // skip the signature and/or any following chunk(s)
+ DWORD chunk_pos = offset;
+
+ while(1) {
+ // get chunk length
+ if(chunk_pos + 4 > size_in_bytes) {
+ break;
+ }
+
+ memcpy(&mLength, &data[chunk_pos], 4);
+ mng_SwapLong(&mLength);
+ chunk_pos += 4;
+
+ const DWORD next_chunk_pos = chunk_pos + 4 + mLength + 4;
+ if(next_chunk_pos > size_in_bytes) {
+ break;
+ }
+
+ // get chunk name
+ if(memcmp(&data[chunk_pos], chunk_name, 4) == 0) {
+ chunk_pos -= 4; // found chunk
+ *start_pos = chunk_pos;
+ *next_pos = next_chunk_pos;
+ return TRUE;
+ }
+
+ chunk_pos = next_chunk_pos;
+ }
+
+ return FALSE;
+
+ } catch(int) {
+ return FALSE;
+ }
+}
+
+/**
+Remove a chunk located at (start_pos, next_pos) in the PNG stream
+@param hPngMemory PNG stream handle
+@param start_pos Start position of the chunk
+@param next_pos Start position of the next chunk
+@return Returns TRUE if successfull, returns FALSE otherwise
+*/
+static BOOL
+mng_CopyRemoveChunks(FIMEMORY *hPngMemory, DWORD start_pos, DWORD next_pos) {
+ BYTE *data = NULL;
+ DWORD size_in_bytes = 0;
+
+ // length of the chunk to remove
+ DWORD chunk_length = next_pos - start_pos;
+ if(chunk_length == 0) {
+ return TRUE;
+ }
+
+ // get a pointer to the stream buffer
+ FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
+ if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) {
+ // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
+ return FALSE;
+ }
+
+ // new file length
+ unsigned buffer_size = size_in_bytes + chunk_length;
+
+ BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE));
+ if(!buffer) {
+ return FALSE;
+ }
+ memcpy(&buffer[0], &data[0], start_pos);
+ memcpy(&buffer[start_pos], &data[next_pos], size_in_bytes - next_pos);
+
+ // seek to the start of the stream
+ FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
+ // re-write the stream
+ FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory);
+
+ free(buffer);
+
+ return TRUE;
+}
+
+/**
+Insert a chunk just before the inNextChunkName chunk
+@param hPngMemory PNG stream handle
+@param start_pos Start position of the inNextChunkName chunk
+@param next_pos Start position of the next chunk
+@return Returns TRUE if successfull, returns FALSE otherwise
+*/
+static BOOL
+mng_CopyInsertChunks(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, DWORD inChunkLength, DWORD start_pos, DWORD next_pos) {
+ BYTE *data = NULL;
+ DWORD size_in_bytes = 0;
+
+ // length of the chunk to check
+ DWORD chunk_length = next_pos - start_pos;
+ if(chunk_length == 0) {
+ return TRUE;
+ }
+
+ // get a pointer to the stream buffer
+ FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
+ if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) {
+ // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
+ return FALSE;
+ }
+
+ // new file length
+ unsigned buffer_size = inChunkLength + size_in_bytes;
+
+ BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE));
+ if(!buffer) {
+ return FALSE;
+ }
+ unsigned p = 0;
+ memcpy(&buffer[p], &data[0], start_pos);
+ p += start_pos;
+ memcpy(&buffer[p], inInsertChunk, inChunkLength);
+ p += inChunkLength;
+ memcpy(&buffer[p], &data[start_pos], size_in_bytes - start_pos);
+
+ // seek to the start of the stream
+ FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
+ // re-write the stream
+ FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory);
+
+ free(buffer);
+
+ return TRUE;
+}
+
+static BOOL
+mng_RemoveChunk(FIMEMORY *hPngMemory, BYTE *chunk_name) {
+ BOOL bResult = FALSE;
+
+ DWORD start_pos = 0;
+ DWORD next_pos = 0;
+
+ bResult = mng_FindChunk(hPngMemory, chunk_name, 8, &start_pos, &next_pos);
+ if(!bResult) {
+ return FALSE;
+ }
+
+ bResult = mng_CopyRemoveChunks(hPngMemory, start_pos, next_pos);
+ if(!bResult) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL
+mng_InsertChunk(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, unsigned chunk_length) {
+ BOOL bResult = FALSE;
+
+ DWORD start_pos = 0;
+ DWORD next_pos = 0;
+
+ bResult = mng_FindChunk(hPngMemory, inNextChunkName, 8, &start_pos, &next_pos);
+ if(!bResult) {
+ return FALSE;
+ }
+
+ bResult = mng_CopyInsertChunks(hPngMemory, inNextChunkName, inInsertChunk, chunk_length, start_pos, next_pos);
+ if(!bResult) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static FIBITMAP*
+mng_LoadFromMemoryHandle(FIMEMORY *hmem, int flags = 0) {
+ long offset = 0;
+ FIBITMAP *dib = NULL;
+
+ if(hmem) {
+ // seek to the start of the stream
+ FreeImage_SeekMemory(hmem, offset, SEEK_SET);
+
+ // check the file signature and deduce its format
+ // (the second argument is currently not used by FreeImage)
+ FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
+ if(fif != FIF_UNKNOWN) {
+ dib = FreeImage_LoadFromMemory(fif, hmem, flags);
+ }
+ }
+
+ return dib;
+}
+
+/**
+Write a chunk in a PNG stream from the current position.
+@param chunk_name Name of the chunk
+@param chunk_data Chunk array
+@param length Chunk length
+@param hPngMemory PNG stream handle
+*/
+static void
+mng_WriteChunk(BYTE *chunk_name, BYTE *chunk_data, DWORD length, FIMEMORY *hPngMemory) {
+ DWORD crc_file = 0;
+ // write a PNG chunk ...
+ // - length
+ mng_SwapLong(&length);
+ FreeImage_WriteMemory(&length, 1, 4, hPngMemory);
+ mng_SwapLong(&length);
+ // - chunk name
+ FreeImage_WriteMemory(chunk_name, 1, 4, hPngMemory);
+ if(chunk_data && length) {
+ // - chunk data
+ FreeImage_WriteMemory(chunk_data, 1, length, hPngMemory);
+ // - crc
+ crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
+ crc_file = FreeImage_ZLibCRC32(crc_file, chunk_data, length);
+ mng_SwapLong(&crc_file);
+ FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
+ } else {
+ // - crc
+ crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
+ mng_SwapLong(&crc_file);
+ FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
+ }
+
+}
+
+/**
+Wrap a IDAT chunk as a PNG stream.
+The stream has the structure { g_png_signature, IHDR, IDAT, IEND }
+The image is assumed to be a greyscale image.
+
+@param jng_width Image width
+@param jng_height Image height
+@param jng_alpha_sample_depth Bits per pixel
+@param mChunk PNG grayscale IDAT format
+@param mLength IDAT chunk length
+@param hPngMemory Output memory stream
+*/
+static void
+mng_WritePNGStream(DWORD jng_width, DWORD jng_height, BYTE jng_alpha_sample_depth, BYTE *mChunk, DWORD mLength, FIMEMORY *hPngMemory) {
+ // PNG grayscale IDAT format
+
+ BYTE data[14];
+
+ // wrap the IDAT chunk as a PNG stream
+
+ // write PNG file signature
+ FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);
+
+ // write a IHDR chunk ...
+ /*
+ The IHDR chunk must appear FIRST. It contains:
+ Width: 4 bytes
+ Height: 4 bytes
+ Bit depth: 1 byte
+ Color type: 1 byte
+ Compression method: 1 byte
+ Filter method: 1 byte
+ Interlace method: 1 byte
+ */
+ // - chunk data
+ mng_SwapLong(&jng_width);
+ mng_SwapLong(&jng_height);
+ memcpy(&data[0], &jng_width, 4);
+ memcpy(&data[4], &jng_height, 4);
+ mng_SwapLong(&jng_width);
+ mng_SwapLong(&jng_height);
+ data[8] = jng_alpha_sample_depth;
+ data[9] = 0; // color_type gray (jng_color_type)
+ data[10] = 0; // compression method 0 (jng_alpha_compression_method)
+ data[11] = 0; // filter_method 0 (jng_alpha_filter_method)
+ data[12] = 0; // interlace_method 0 (jng_alpha_interlace_method)
+
+ mng_WriteChunk(mng_IHDR, &data[0], 13, hPngMemory);
+
+ // write a IDAT chunk ...
+ mng_WriteChunk(mng_IDAT, mChunk, mLength, hPngMemory);
+
+ // write a IEND chunk ...
+ mng_WriteChunk(mng_IEND, NULL, 0, hPngMemory);
+
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Build and set a FITAG whose type is FIDT_ASCII.
+The tag must be destroyed by the caller using FreeImage_DeleteTag.
+@param model Metadata model to be filled
+@param dib Image to be filled
+@param key Tag key
+@param value Tag value
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+mng_SetKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) {
+ if(!dib || !key || !value) {
+ return FALSE;
+ }
+ // create a tag
+ FITAG *tag = FreeImage_CreateTag();
+ if(tag) {
+ BOOL bSuccess = TRUE;
+ // fill the tag
+ DWORD tag_length = (DWORD)(strlen(value) + 1);
+ bSuccess &= FreeImage_SetTagKey(tag, key);
+ bSuccess &= FreeImage_SetTagLength(tag, tag_length);
+ bSuccess &= FreeImage_SetTagCount(tag, tag_length);
+ bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII);
+ bSuccess &= FreeImage_SetTagValue(tag, value);
+ if(bSuccess) {
+ // set the tag
+ FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag);
+ }
+ FreeImage_DeleteTag(tag);
+ return bSuccess;
+ }
+
+ return FALSE;
+}
+
+/**
+Read a tEXt chunk and extract the key/value pair.
+@param key_value_pair [returned value] Array of key/value pairs
+@param mChunk Chunk data
+@param mLength Chunk length
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+static BOOL
+mng_SetMetadata_tEXt(tEXtMAP &key_value_pair, const BYTE *mChunk, DWORD mLength) {
+ std::string key;
+ std::string value;
+ BYTE *buffer = (BYTE*)malloc(mLength * sizeof(BYTE));
+ if(!buffer) {
+ return FALSE;
+ }
+ DWORD pos = 0;
+
+ memset(buffer, 0, mLength * sizeof(BYTE));
+
+ for(DWORD i = 0; i < mLength; i++) {
+ buffer[pos++] = mChunk[i];
+ if(mChunk[i] == '\0') {
+ if(key.size() == 0) {
+ key = (char*)buffer;
+ pos = 0;
+ memset(buffer, 0, mLength * sizeof(BYTE));
+ } else {
+ break;
+ }
+ }
+ }
+ value = (char*)buffer;
+ free(buffer);
+
+ key_value_pair[key] = value;
+
+ return TRUE;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Load a FIBITMAP from a MNG or a JNG stream
+@param format_id ID of the caller
+@param io Stream i/o functions
+@param handle Stream handle
+@param Offset Start of the first chunk
+@param flags Loading flags
+@return Returns a dib if successful, returns NULL otherwise
+*/
+FIBITMAP*
+mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0) {
+ DWORD mLength = 0;
+ BYTE mChunkName[5];
+ BYTE *mChunk = NULL;
+ DWORD crc_file;
+ long LastOffset;
+ long mOrigPos;
+ BYTE *PLTE_file_chunk = NULL; // whole PLTE chunk (lentgh, name, array, crc)
+ DWORD PLTE_file_size = 0; // size of PLTE chunk
+
+ BOOL m_HasGlobalPalette = FALSE; // may turn to TRUE in PLTE chunk
+ unsigned m_TotalBytesOfChunks = 0;
+ FIBITMAP *dib = NULL;
+ FIBITMAP *dib_alpha = NULL;
+
+ FIMEMORY *hJpegMemory = NULL;
+ FIMEMORY *hPngMemory = NULL;
+ FIMEMORY *hIDATMemory = NULL;
+
+ // ---
+ DWORD jng_width = 0;
+ DWORD jng_height = 0;
+ BYTE jng_color_type = 0;
+ BYTE jng_image_sample_depth = 0;
+ BYTE jng_image_compression_method = 0;
+
+ BYTE jng_alpha_sample_depth = 0;
+ BYTE jng_alpha_compression_method = 0;
+ BYTE jng_alpha_filter_method = 0;
+ BYTE jng_alpha_interlace_method = 0;
+
+ DWORD mng_frame_width = 0;
+ DWORD mng_frame_height = 0;
+ DWORD mng_ticks_per_second = 0;
+ DWORD mng_nominal_layer_count = 0;
+ DWORD mng_nominal_frame_count = 0;
+ DWORD mng_nominal_play_time = 0;
+ DWORD mng_simplicity_profile = 0;
+
+
+ DWORD res_x = 2835; // 72 dpi
+ DWORD res_y = 2835; // 72 dpi
+ RGBQUAD rgbBkColor = {0, 0, 0, 0};
+ WORD bk_red, bk_green, bk_blue;
+ BOOL hasBkColor = FALSE;
+ BOOL mHasIDAT = FALSE;
+
+ tEXtMAP key_value_pair;
+
+ // ---
+
+ BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
+
+ // get the file size
+ const long mLOF = mng_LOF(io, handle);
+ // go to the first chunk
+ io->seek_proc(handle, Offset, SEEK_SET);
+
+ try {
+ BOOL mEnd = FALSE;
+
+ while(mEnd == FALSE) {
+ // start of the chunk
+ LastOffset = io->tell_proc(handle);
+ // read length
+ mLength = 0;
+ io->read_proc(&mLength, 1, sizeof(mLength), handle);
+ mng_SwapLong(&mLength);
+ // read name
+ io->read_proc(&mChunkName[0], 1, 4, handle);
+ mChunkName[4] = '\0';
+
+ if(mLength > 0) {
+ mChunk = (BYTE*)realloc(mChunk, mLength);
+ if(!mChunk) {
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
+ throw (const char*)NULL;
+ }
+ Offset = io->tell_proc(handle);
+ if(Offset + (long)mLength > mLOF) {
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of file", mChunkName);
+ throw (const char*)NULL;
+ }
+ // read chunk
+ io->read_proc(mChunk, 1, mLength, handle);
+ }
+ // read crc
+ io->read_proc(&crc_file, 1, sizeof(crc_file), handle);
+ mng_SwapLong(&crc_file);
+ // check crc
+ DWORD crc_check = FreeImage_ZLibCRC32(0, &mChunkName[0], 4);
+ crc_check = FreeImage_ZLibCRC32(crc_check, mChunk, mLength);
+ if(crc_check != crc_file) {
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: bad CRC", mChunkName);
+ throw (const char*)NULL;
+ }
+
+ switch( mng_GetChunckType(mChunkName) ) {
+ case MHDR:
+ // The MHDR chunk is always first in all MNG datastreams except for those
+ // that consist of a single PNG or JNG datastream with a PNG or JNG signature.
+ if(mLength == 28) {
+ memcpy(&mng_frame_width, &mChunk[0], 4);
+ memcpy(&mng_frame_height, &mChunk[4], 4);
+ memcpy(&mng_ticks_per_second, &mChunk[8], 4);
+ memcpy(&mng_nominal_layer_count, &mChunk[12], 4);
+ memcpy(&mng_nominal_frame_count, &mChunk[16], 4);
+ memcpy(&mng_nominal_play_time, &mChunk[20], 4);
+ memcpy(&mng_simplicity_profile, &mChunk[24], 4);
+
+ mng_SwapLong(&mng_frame_width);
+ mng_SwapLong(&mng_frame_height);
+ mng_SwapLong(&mng_ticks_per_second);
+ mng_SwapLong(&mng_nominal_layer_count);
+ mng_SwapLong(&mng_nominal_frame_count);
+ mng_SwapLong(&mng_nominal_play_time);
+ mng_SwapLong(&mng_simplicity_profile);
+
+ } else {
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: size is %d instead of 28", mChunkName, mLength);
+ }
+ break;
+
+ case MEND:
+ mEnd = TRUE;
+ break;
+
+ case LOOP:
+ case ENDL:
+ break;
+ case DEFI:
+ break;
+ case SAVE:
+ case SEEK:
+ case TERM:
+ break;
+ case BACK:
+ break;
+
+ // Global "PLTE" and "tRNS" (if any). PNG "PLTE" will be of 0 byte, as it uses global data.
+ case PLTE: // Global
+ m_HasGlobalPalette = TRUE;
+ PLTE_file_size = mLength + 12; // (lentgh, name, array, crc) = (4, 4, mLength, 4)
+ PLTE_file_chunk = (BYTE*)realloc(PLTE_file_chunk, PLTE_file_size);
+ if(!PLTE_file_chunk) {
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
+ throw (const char*)NULL;
+ } else {
+ mOrigPos = io->tell_proc(handle);
+ // seek to the start of the chunk
+ io->seek_proc(handle, LastOffset, SEEK_SET);
+ // load the whole chunk
+ io->read_proc(PLTE_file_chunk, 1, PLTE_file_size, handle);
+ // go to the start of the next chunk
+ io->seek_proc(handle, mOrigPos, SEEK_SET);
+ }
+ break;
+
+ case tRNS: // Global
+ break;
+
+ case IHDR:
+ Offset = LastOffset;
+ // parse the PNG file and get its file size
+ if(mng_CountPNGChunks(io, handle, Offset, &m_TotalBytesOfChunks) == FALSE) {
+ // reach an unexpected end of file
+ mEnd = TRUE;
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of PNG file", mChunkName);
+ break;
+ }
+
+ // wrap the { IHDR, ..., IEND } chunks as a PNG stream
+ if(hPngMemory == NULL) {
+ hPngMemory = FreeImage_OpenMemory();
+ }
+
+ mOrigPos = io->tell_proc(handle);
+
+ // write PNG file signature
+ FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
+ FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);
+
+ mChunk = (BYTE*)realloc(mChunk, m_TotalBytesOfChunks);
+ if(!mChunk) {
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
+ throw (const char*)NULL;
+ }
+
+ // on calling CountPNGChunks earlier, we were in Offset pos,
+ // go back there
+ io->seek_proc(handle, Offset, SEEK_SET);
+ io->read_proc(mChunk, 1, m_TotalBytesOfChunks, handle);
+ // Put back to original pos
+ io->seek_proc(handle, mOrigPos, SEEK_SET);
+ // write the PNG chunks
+ FreeImage_WriteMemory(mChunk, 1, m_TotalBytesOfChunks, hPngMemory);
+
+ // plug in global PLTE if local PLTE exists
+ if(m_HasGlobalPalette) {
+ // ensure we remove some local chunks, so that global
+ // "PLTE" can be inserted right before "IDAT".
+ mng_RemoveChunk(hPngMemory, mng_PLTE);
+ mng_RemoveChunk(hPngMemory, mng_tRNS);
+ mng_RemoveChunk(hPngMemory, mng_bKGD);
+ // insert global "PLTE" chunk in its entirety before "IDAT"
+ mng_InsertChunk(hPngMemory, mng_IDAT, PLTE_file_chunk, PLTE_file_size);
+ }
+
+ if(dib) FreeImage_Unload(dib);
+ dib = mng_LoadFromMemoryHandle(hPngMemory, flags);
+
+ // stop after the first image
+ mEnd = TRUE;
+ break;
+
+ case JHDR:
+ if(mLength == 16) {
+ memcpy(&jng_width, &mChunk[0], 4);
+ memcpy(&jng_height, &mChunk[4], 4);
+ mng_SwapLong(&jng_width);
+ mng_SwapLong(&jng_height);
+
+ jng_color_type = mChunk[8];
+ jng_image_sample_depth = mChunk[9];
+ jng_image_compression_method = mChunk[10];
+ //BYTE jng_image_interlace_method = mChunk[11]; // for debug only
+
+ jng_alpha_sample_depth = mChunk[12];
+ jng_alpha_compression_method = mChunk[13];
+ jng_alpha_filter_method = mChunk[14];
+ jng_alpha_interlace_method = mChunk[15];
+ } else {
+ FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: invalid chunk length", mChunkName);
+ throw (const char*)NULL;
+ }
+ break;
+
+ case JDAT:
+ if(hJpegMemory == NULL) {
+ hJpegMemory = FreeImage_OpenMemory();
+ }
+ // as there may be several JDAT chunks, concatenate them
+ FreeImage_WriteMemory(mChunk, 1, mLength, hJpegMemory);
+ break;
+
+ case IDAT:
+ if(!header_only && (jng_alpha_compression_method == 0)) {
+ // PNG grayscale IDAT format
+ if(hIDATMemory == NULL) {
+ hIDATMemory = FreeImage_OpenMemory();
+ mHasIDAT = TRUE;
+ }
+ // as there may be several IDAT chunks, concatenate them
+ FreeImage_WriteMemory(mChunk, 1, mLength, hIDATMemory);
+ }
+ break;
+
+ case IEND:
+ if(!hJpegMemory) {
+ mEnd = TRUE;
+ break;
+ }
+ // load the JPEG
+ if(dib) {
+ FreeImage_Unload(dib);
+ }
+ dib = mng_LoadFromMemoryHandle(hJpegMemory, flags);
+
+ // load the PNG alpha layer
+ if(mHasIDAT) {
+ BYTE *data = NULL;
+ DWORD size_in_bytes = 0;
+
+ // get a pointer to the IDAT buffer
+ FreeImage_AcquireMemory(hIDATMemory, &data, &size_in_bytes);
+ if(data && size_in_bytes) {
+ // wrap the IDAT chunk as a PNG stream
+ if(hPngMemory == NULL) {
+ hPngMemory = FreeImage_OpenMemory();
+ }
+ mng_WritePNGStream(jng_width, jng_height, jng_alpha_sample_depth, data, size_in_bytes, hPngMemory);
+ // load the PNG
+ if(dib_alpha) {
+ FreeImage_Unload(dib_alpha);
+ }
+ dib_alpha = mng_LoadFromMemoryHandle(hPngMemory, flags);
+ }
+ }
+ // stop the parsing
+ mEnd = TRUE;
+ break;
+
+ case JDAA:
+ break;
+
+ case gAMA:
+ break;
+
+ case pHYs:
+ // unit is pixels per meter
+ memcpy(&res_x, &mChunk[0], 4);
+ mng_SwapLong(&res_x);
+ memcpy(&res_y, &mChunk[4], 4);
+ mng_SwapLong(&res_y);
+ break;
+
+ case bKGD:
+ memcpy(&bk_red, &mChunk[0], 2);
+ mng_SwapShort(&bk_red);
+ rgbBkColor.rgbRed = (BYTE)bk_red;
+ memcpy(&bk_green, &mChunk[2], 2);
+ mng_SwapShort(&bk_green);
+ rgbBkColor.rgbGreen = (BYTE)bk_green;
+ memcpy(&bk_blue, &mChunk[4], 2);
+ mng_SwapShort(&bk_blue);
+ rgbBkColor.rgbBlue = (BYTE)bk_blue;
+ hasBkColor = TRUE;
+ break;
+
+ case tEXt:
+ mng_SetMetadata_tEXt(key_value_pair, mChunk, mLength);
+ break;
+
+ case UNKNOWN_CHUNCK:
+ default:
+ break;
+
+
+ } // switch( GetChunckType )
+ } // while(!mEnd)
+
+ FreeImage_CloseMemory(hJpegMemory);
+ FreeImage_CloseMemory(hPngMemory);
+ FreeImage_CloseMemory(hIDATMemory);
+ free(mChunk);
+ free(PLTE_file_chunk);
+
+ // convert to 32-bit if a transparent layer is available
+ if(!header_only && dib_alpha) {
+ FIBITMAP *dst = FreeImage_ConvertTo32Bits(dib);
+ if((FreeImage_GetBPP(dib_alpha) == 8) && (FreeImage_GetImageType(dib_alpha) == FIT_BITMAP)) {
+ FreeImage_SetChannel(dst, dib_alpha, FICC_ALPHA);
+ } else {
+ FIBITMAP *dst_alpha = FreeImage_ConvertTo8Bits(dib_alpha);
+ FreeImage_SetChannel(dst, dst_alpha, FICC_ALPHA);
+ FreeImage_Unload(dst_alpha);
+ }
+ FreeImage_Unload(dib);
+ dib = dst;
+ }
+ FreeImage_Unload(dib_alpha);
+
+ if(dib) {
+ // set metadata
+ FreeImage_SetDotsPerMeterX(dib, res_x);
+ FreeImage_SetDotsPerMeterY(dib, res_y);
+ if(hasBkColor) {
+ FreeImage_SetBackgroundColor(dib, &rgbBkColor);
+ }
+ if(key_value_pair.size()) {
+ for(tEXtMAP::iterator j = key_value_pair.begin(); j != key_value_pair.end(); j++) {
+ std::string key = (*j).first;
+ std::string value = (*j).second;
+ mng_SetKeyValue(FIMD_COMMENTS, dib, key.c_str(), value.c_str());
+ }
+ }
+ }
+
+ return dib;
+
+ } catch(const char *text) {
+ FreeImage_CloseMemory(hJpegMemory);
+ FreeImage_CloseMemory(hPngMemory);
+ FreeImage_CloseMemory(hIDATMemory);
+ free(mChunk);
+ free(PLTE_file_chunk);
+ FreeImage_Unload(dib);
+ FreeImage_Unload(dib_alpha);
+ if(text) {
+ FreeImage_OutputMessageProc(format_id, text);
+ }
+ return NULL;
+ }
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Write a FIBITMAP to a JNG stream
+@param format_id ID of the caller
+@param io Stream i/o functions
+@param dib Image to be saved
+@param handle Stream handle
+@param flags Saving flags
+@return Returns TRUE if successful, returns FALSE otherwise
+*/
+BOOL
+mng_WriteJNG(int format_id, FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int flags) {
+ DWORD jng_width = 0;
+ DWORD jng_height = 0;
+ BYTE jng_color_type = 0;
+ BYTE jng_image_sample_depth = 8;
+ BYTE jng_image_compression_method = 8; // 8: ISO-10918-1 Huffman-coded baseline JPEG.
+ BYTE jng_image_interlace_method = 0;
+
+ BYTE jng_alpha_sample_depth = 0;
+ BYTE jng_alpha_compression_method = 0;
+ BYTE jng_alpha_filter_method = 0;
+ BYTE jng_alpha_interlace_method = 0;
+
+ BYTE buffer[16];
+
+ FIMEMORY *hJngMemory = NULL;
+ FIMEMORY *hJpegMemory = NULL;
+ FIMEMORY *hPngMemory = NULL;
+
+ FIBITMAP *dib_rgb = NULL;
+ FIBITMAP *dib_alpha = NULL;
+
+ if(!dib || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
+ return FALSE;
+ }
+
+ unsigned bpp = FreeImage_GetBPP(dib);
+
+ switch(bpp) {
+ case 8:
+ if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
+ dib_rgb = dib;
+ jng_color_type = MNG_COLORTYPE_JPEGGRAY;
+ } else {
+ // JPEG plugin will convert other types (FIC_MINISWHITE, FIC_PALETTE) to 24-bit on the fly
+ //dib_rgb = FreeImage_ConvertTo24Bits(dib);
+ dib_rgb = dib;
+ jng_color_type = MNG_COLORTYPE_JPEGCOLOR;
+
+ }
+ break;
+ case 24:
+ dib_rgb = dib;
+ jng_color_type = MNG_COLORTYPE_JPEGCOLOR;
+ break;
+ case 32:
+ dib_rgb = FreeImage_ConvertTo24Bits(dib);
+ jng_color_type = MNG_COLORTYPE_JPEGCOLORA;
+ jng_alpha_sample_depth = 8;
+ break;
+ default:
+ return FALSE;
+ }
+
+ jng_width = (DWORD)FreeImage_GetWidth(dib);
+ jng_height = (DWORD)FreeImage_GetHeight(dib);
+
+ try {
+ hJngMemory = FreeImage_OpenMemory();
+
+ // --- write JNG file signature ---
+ FreeImage_WriteMemory(g_jng_signature, 1, 8, hJngMemory);
+
+ // --- write a JHDR chunk ---
+ SwapLong(&jng_width);
+ SwapLong(&jng_height);
+ memcpy(&buffer[0], &jng_width, 4);
+ memcpy(&buffer[4], &jng_height, 4);
+ SwapLong(&jng_width);
+ SwapLong(&jng_height);
+ buffer[8] = jng_color_type;
+ buffer[9] = jng_image_sample_depth;
+ buffer[10] = jng_image_compression_method;
+ buffer[11] = jng_image_interlace_method;
+ buffer[12] = jng_alpha_sample_depth;
+ buffer[13] = jng_alpha_compression_method;
+ buffer[14] = jng_alpha_filter_method;
+ buffer[15] = jng_alpha_interlace_method;
+ mng_WriteChunk(mng_JHDR, &buffer[0], 16, hJngMemory);
+
+ // --- write a sequence of JDAT chunks ---
+ hJpegMemory = FreeImage_OpenMemory();
+ flags |= JPEG_BASELINE;
+ if(!FreeImage_SaveToMemory(FIF_JPEG, dib_rgb, hJpegMemory, flags)) {
+ throw (const char*)NULL;
+ }
+ if(dib_rgb != dib) {
+ FreeImage_Unload(dib_rgb);
+ dib_rgb = NULL;
+ }
+ {
+ BYTE *jpeg_data = NULL;
+ DWORD size_in_bytes = 0;
+
+ // get a pointer to the stream buffer
+ FreeImage_AcquireMemory(hJpegMemory, &jpeg_data, &size_in_bytes);
+ // write chunks
+ for(DWORD k = 0; k < size_in_bytes;) {
+ DWORD bytes_left = size_in_bytes - k;
+ DWORD chunk_size = MIN(JPEG_CHUNK_SIZE, bytes_left);
+ mng_WriteChunk(mng_JDAT, &jpeg_data[k], chunk_size, hJngMemory);
+ k += chunk_size;
+ }
+ }
+ FreeImage_CloseMemory(hJpegMemory);
+ hJpegMemory = NULL;
+
+ // --- write alpha layer as a sequence of IDAT chunk ---
+ if((bpp == 32) && (jng_color_type == MNG_COLORTYPE_JPEGCOLORA)) {
+ dib_alpha = FreeImage_GetChannel(dib, FICC_ALPHA);
+
+ hPngMemory = FreeImage_OpenMemory();
+ if(!FreeImage_SaveToMemory(FIF_PNG, dib_alpha, hPngMemory, PNG_DEFAULT)) {
+ throw (const char*)NULL;
+ }
+ FreeImage_Unload(dib_alpha);
+ dib_alpha = NULL;
+ // get the IDAT chunk
+ {
+ BOOL bResult = FALSE;
+ DWORD start_pos = 0;
+ DWORD next_pos = 0;
+ long offset = 8;
+
+ do {
+ // find the next IDAT chunk from 'offset' position
+ bResult = mng_FindChunk(hPngMemory, mng_IDAT, offset, &start_pos, &next_pos);
+ if(!bResult) break;
+
+ BYTE *png_data = NULL;
+ DWORD size_in_bytes = 0;
+
+ // get a pointer to the stream buffer
+ FreeImage_AcquireMemory(hPngMemory, &png_data, &size_in_bytes);
+ // write the IDAT chunk
+ mng_WriteChunk(mng_IDAT, &png_data[start_pos+8], next_pos - start_pos - 12, hJngMemory);
+
+ offset = next_pos;
+
+ } while(bResult);
+ }
+
+ FreeImage_CloseMemory(hPngMemory);
+ hPngMemory = NULL;
+ }
+
+ // --- write a IEND chunk ---
+ mng_WriteChunk(mng_IEND, NULL, 0, hJngMemory);
+
+ // write the JNG on output stream
+ {
+ BYTE *jng_data = NULL;
+ DWORD size_in_bytes = 0;
+ FreeImage_AcquireMemory(hJngMemory, &jng_data, &size_in_bytes);
+ io->write_proc(jng_data, 1, size_in_bytes, handle);
+ }
+
+ FreeImage_CloseMemory(hJngMemory);
+ FreeImage_CloseMemory(hJpegMemory);
+ FreeImage_CloseMemory(hPngMemory);
+
+ return TRUE;
+
+ } catch(const char *text) {
+ FreeImage_CloseMemory(hJngMemory);
+ FreeImage_CloseMemory(hJpegMemory);
+ FreeImage_CloseMemory(hPngMemory);
+ if(dib_rgb && (dib_rgb != dib)) {
+ FreeImage_Unload(dib_rgb);
+ }
+ FreeImage_Unload(dib_alpha);
+ if(text) {
+ FreeImage_OutputMessageProc(format_id, text);
+ }
+ return FALSE;
+ }
+}
diff --git a/libs/freeimage/src/FreeImage/MemoryIO.cpp b/libs/freeimage/src/FreeImage/MemoryIO.cpp
new file mode 100644
index 0000000000..c04612373a
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/MemoryIO.cpp
@@ -0,0 +1,235 @@
+// ==========================================================
+// 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 "../stdafx.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->data_length = mem_header->file_length = 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->file_length;
+ 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/libs/freeimage/src/FreeImage/MultiPage.cpp b/libs/freeimage/src/FreeImage/MultiPage.cpp
new file mode 100644
index 0000000000..0d49f46633
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/MultiPage.cpp
@@ -0,0 +1,986 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/NNQuantizer.cpp b/libs/freeimage/src/FreeImage/NNQuantizer.cpp
new file mode 100644
index 0000000000..dac4a2cdec
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/NNQuantizer.cpp
@@ -0,0 +1,504 @@
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/PixelAccess.cpp b/libs/freeimage/src/FreeImage/PixelAccess.cpp
new file mode 100644
index 0000000000..c0582a540a
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/PixelAccess.cpp
@@ -0,0 +1,196 @@
+// ==========================================================
+// 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 "../stdafx.h"
+
+// ----------------------------------------------------------
+
+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/libs/freeimage/src/FreeImage/Plugin.cpp b/libs/freeimage/src/FreeImage/Plugin.cpp
new file mode 100644
index 0000000000..048237a540
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/Plugin.cpp
@@ -0,0 +1,812 @@
+// =====================================================================
+// FreeImage Plugin Interface
+//
+// Design and implementation by
+// - Floris van den Berg (floris@geekhq.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
+
+#include "../stdafx.h"
+
+#include "../Metadata/FreeImageTag.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(std::nothrow) PluginNode;
+ Plugin *plugin = new(std::nothrow) Plugin;
+ if(!node || !plugin) {
+ if(node) delete node;
+ if(plugin) delete plugin;
+ FreeImage_OutputMessageProc(FIF_UNKNOWN, FI_MSG_ERROR_MEMORY);
+ return FIF_UNKNOWN;
+ }
+
+ 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) {
+ 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) {
+ 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 ((*i).second->m_enabled) {
+ if (FreeImage_stricmp(the_format, format) == 0) {
+ return (*i).second;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+PluginNode *
+PluginList::FindNodeFromMime(const char *mime) {
+ 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 ((*i).second->m_enabled) {
+ if ((the_mime != NULL) && (strcmp(the_mime, mime) == 0)) {
+ return (*i).second;
+ }
+ }
+ }
+
+ 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) {
+
+ /*
+ Note: initialize all singletons here
+ in order to avoid race conditions with multi-threading
+ */
+
+ // initialise the TagLib singleton
+ TagLib& s = TagLib::instance();
+
+ // 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(InitPNG);
+ s_plugins->AddNode(InitGIF);
+ //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(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(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);
+ //s_plugins->AddNode(InitWEBP);
+//#if !(defined(_MSC_VER) && (_MSC_VER <= 1310))
+ //s_plugins->AddNode(InitJXR);
+//#endif // unsupported by MS Visual Studio 2003 !!!
+
+ // external plugin initialization
+
+#ifdef _WIN32
+ if (!load_local_plugins_only) {
+ int count = 0;
+ char buffer[MAX_PATH + 200];
+ wchar_t current_dir[2 * _MAX_PATH], module[2 * _MAX_PATH];
+ BOOL bOk = FALSE;
+
+ // store the current directory. then set the directory to the application location
+
+ if (GetCurrentDirectoryW(2 * _MAX_PATH, current_dir) != 0) {
+ if (GetModuleFileNameW(NULL, module, 2 * _MAX_PATH) != 0) {
+ wchar_t *last_point = wcsrchr(module, L'\\');
+
+ if (last_point) {
+ *last_point = L'\0';
+
+ bOk = SetCurrentDirectoryW(module);
+ }
+ }
+ }
+
+ // search for plugins
+
+ while (count < s_search_list_size) {
+ _finddata_t find_data;
+ long find_handle;
+
+ strcpy(buffer, s_search_list[count]);
+ strcat(buffer, "*.fip");
+
+ if ((find_handle = (long)_findfirst(buffer, &find_data)) != -1L) {
+ do {
+ strcpy(buffer, s_search_list[count]);
+ strncat(buffer, find_data.name, MAX_PATH + 200);
+
+ HINSTANCE instance = LoadLibraryA(buffer);
+
+ if (instance != NULL) {
+ FARPROC proc_address = GetProcAddress(instance, "_Init@8");
+
+ if (proc_address != NULL) {
+ s_plugins->AddNode((FI_InitProc)proc_address, (void *)instance);
+ } else {
+ FreeLibrary(instance);
+ }
+ }
+ } while (_findnext(find_handle, &find_data) != -1L);
+
+ _findclose(find_handle);
+ }
+
+ count++;
+ }
+
+ // restore the current directory
+
+ if (bOk) {
+ SetCurrentDirectoryW(current_dir);
+ }
+ }
+#endif // _WIN32
+ }
+ }
+}
+
+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_plugin->load_proc != NULL) {
+ void *data = FreeImage_Open(node, io, handle, TRUE);
+
+ FIBITMAP *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_plugin->save_proc != NULL) {
+ void *data = FreeImage_Open(node, io, handle, FALSE);
+
+ BOOL 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) ? (FREE_IMAGE_FORMAT)node->m_id : 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) ? (FREE_IMAGE_FORMAT)node->m_id : 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/libs/freeimage/src/FreeImage/PluginBMP.cpp b/libs/freeimage/src/FreeImage/PluginBMP.cpp
new file mode 100644
index 0000000000..eb176ff325
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/PluginBMP.cpp
@@ -0,0 +1,1457 @@
+// ==========================================================
+// 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 "../stdafx.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_ALPHABITFIELDS = 6; // compression: Bit field (this value is valid in Windows CE .NET 4.0 and later)
+
+typedef struct tagBITMAPINFOOS2_1X_HEADER {
+ DWORD biSize;
+ WORD biWidth;
+ WORD biHeight;
+ WORD biPlanes;
+ WORD biBitCount;
+} BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER;
+
+// ==========================================================
+// 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, int type) {
+ 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);
+
+ // seek to the end of the header (depending on the BMP header version)
+ // type == sizeof(BITMAPVxINFOHEADER)
+ switch(type) {
+ case 40: // sizeof(BITMAPINFOHEADER) - all Windows versions since Windows 3.0
+ break;
+ case 52: // sizeof(BITMAPV2INFOHEADER) (undocumented)
+ case 56: // sizeof(BITMAPV3INFOHEADER) (undocumented)
+ case 108: // sizeof(BITMAPV4HEADER) - all Windows versions since Windows 95/NT4 (not supported)
+ case 124: // sizeof(BITMAPV5HEADER) - Windows 98/2000 and newer (not supported)
+ io->seek_proc(handle, (long)(type - sizeof(BITMAPINFOHEADER)), SEEK_CUR);
+ break;
+ }
+
+ // 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
+ 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 :
+ {
+ int use_bitfields = 0;
+ if (bih.biCompression == BI_BITFIELDS) use_bitfields = 3;
+ else if (bih.biCompression == BI_ALPHABITFIELDS) use_bitfields = 4;
+ else if (type == 52) use_bitfields = 3;
+ else if (type >= 56) use_bitfields = 4;
+
+ if (use_bitfields > 0) {
+ DWORD bitfields[4];
+ io->read_proc(bitfields, use_bitfields * 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;
+ }
+
+ // seek to the actual pixel data
+ 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;
+ }
+ break; // 16-bit
+
+ case 24 :
+ case 32 :
+ {
+ int use_bitfields = 0;
+ if (bih.biCompression == BI_BITFIELDS) use_bitfields = 3;
+ else if (bih.biCompression == BI_ALPHABITFIELDS) use_bitfields = 4;
+ else if (type == 52) use_bitfields = 3;
+ else if (type >= 56) use_bitfields = 4;
+
+ if (use_bitfields > 0) {
+ DWORD bitfields[4];
+ io->read_proc(bitfields, use_bitfields * 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
+ // i.e. you can have (FreeImage_GetColorsUsed(dib) > 0)
+
+ // seek to the actual pixel data
+ 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
+ case 52: // BITMAPV2INFOHEADER (undocumented, partially supported)
+ case 56: // BITMAPV3INFOHEADER (undocumented, partially supported)
+ case 108: // BITMAPV4HEADER - all Windows versions since Windows 95/NT4 (partially supported)
+ case 124: // BITMAPV5HEADER - Windows 98/2000 and newer (partially supported)
+ return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits, type);
+
+ 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/libs/freeimage/src/FreeImage/PluginGIF.cpp b/libs/freeimage/src/FreeImage/PluginGIF.cpp
new file mode 100644
index 0000000000..1a7a84041b
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/PluginGIF.cpp
@@ -0,0 +1,1405 @@
+// ==========================================================
+// GIF Loader and Writer
+//
+// Design and implementation by
+// - Ryan Rubley <ryan@lostreality.org>
+// - Raphaлl Gaquer <raphael.gaquer@alcer.com>
+// - Aaron Shumate <aaron@shumate.us>
+//
+// 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 "../stdafx.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_nextCode < 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++ ) {
+ const int scanidx = logicalheight - (y + info.top) - 1;
+ if ( scanidx < 0 ) {
+ break; // If data is corrupt, don't calculate in invalid scanline
+ }
+ scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + 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++ ) {
+ const int scanidx = logicalheight - (y + info.top) - 1;
+ if ( scanidx < 0 ) {
+ break; // If data is corrupt, don't calculate in invalid scanline
+ }
+ scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + 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", (int)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/libs/freeimage/src/FreeImage/PluginICO.cpp b/libs/freeimage/src/FreeImage/PluginICO.cpp
new file mode 100644
index 0000000000..85fc57c391
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/PluginICO.cpp
@@ -0,0 +1,823 @@
+// ==========================================================
+// 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 "../stdafx.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;
+}
+
+/**
+Vista icon support
+@return Returns TRUE if the bitmap data is stored in PNG format
+*/
+static BOOL
+IsPNG(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 };
+
+ long tell = io->tell_proc(handle);
+ io->read_proc(&signature, 1, 8, handle);
+ BOOL bIsPNG = (memcmp(png_signature, signature, 8) == 0);
+ io->seek_proc(handle, tell, SEEK_SET);
+
+ return bIsPNG;
+}
+
+#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
+ // apply the AND mask only if the image is not 32 bpp
+ if(((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) && (bit_count < 32)) {
+ 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, icon_list[page].dwImageOffset, SEEK_SET);
+
+ if( IsPNG(io, handle) ) {
+ // Vista icon support
+ // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
+ 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
+ // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/18/10077133.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/libs/freeimage/src/FreeImage/PluginJPEG.cpp b/libs/freeimage/src/FreeImage/PluginJPEG.cpp
new file mode 100644
index 0000000000..49e653d9e0
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/PluginJPEG.cpp
@@ -0,0 +1,1705 @@
+// ==========================================================
+// 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
+
+#include "../stdafx.h"
+
+extern "C" {
+#define XMD_H
+#undef FAR
+#include <setjmp.h>
+
+#include "../LibJPEG/jinclude.h"
+#include "../LibJPEG/jpeglib.h"
+#include "../LibJPEG/jerror.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 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);
+ }
+}
+
+// ==========================================================
+// 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;
+ }
+
+ if ((flags & JPEG_GREYSCALE) == JPEG_GREYSCALE) {
+ // force loading as a 8-bit greyscale image
+ cinfo.out_color_space = JCS_GRAYSCALE;
+ }
+
+ // step 5a: start decompressor and calculate output width and height
+
+ jpeg_start_decompress(&cinfo);
+
+ // step 5b: allocate dib and init header
+
+ if((cinfo.output_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.output_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
+
+ if (cinfo.output_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)) {
+ RotateExif(&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/libs/freeimage/src/FreeImage/PluginPNG.cpp b/libs/freeimage/src/FreeImage/PluginPNG.cpp
new file mode 100644
index 0000000000..0753192140
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/PluginPNG.cpp
@@ -0,0 +1,1114 @@
+// ==========================================================
+// 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)
+// - Tanner Helland (tannerhelland@users.sf.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 "../stdafx.h"
+
+#include "../Metadata/FreeImageTag.h"
+
+// ----------------------------------------------------------
+
+#define PNG_BYTES_TO_CHECK 8
+
+#undef PNG_Z_DEFAULT_COMPRESSION // already used in ../LibPNG/pnglibconf.h
+
+// ----------------------------------------------------------
+
+#include "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;
+ png_timep mod_time = 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);
+ }
+ }
+
+ // timestamp chunk
+ if(png_get_tIME(png_ptr, info_ptr, &mod_time)) {
+ char timestamp[32];
+ // create a tag
+ tag = FreeImage_CreateTag();
+ if(!tag) return FALSE;
+
+ // convert as 'yyyy:MM:dd hh:mm:ss'
+ sprintf(timestamp, "%4d:%02d:%02d %2d:%02d:%02d", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second);
+
+ DWORD tag_length = (DWORD)strlen(timestamp) + 1;
+ FreeImage_SetTagLength(tag, tag_length);
+ FreeImage_SetTagCount(tag, tag_length);
+ FreeImage_SetTagType(tag, FIDT_ASCII);
+ FreeImage_SetTagID(tag, TAG_DATETIME);
+ FreeImage_SetTagValue(tag, timestamp);
+
+ // store the tag as Exif-TIFF
+ FreeImage_SetTagKey(tag, "DateTime");
+ FreeImage_SetMetadata(FIMD_EXIF_MAIN, 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;
+ png_time mod_time;
+
+ // 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;
+ }
+
+ // set the Exif-TIFF 'DateTime' metadata as a tIME chunk
+ tag = NULL;
+ FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "DateTime", &tag);
+ if(tag && FreeImage_GetTagLength(tag)) {
+ int year, month, day, hour, minute, second;
+ const char *value = (char*)FreeImage_GetTagValue(tag);
+ if(sscanf(value, "%4d:%02d:%02d %2d:%02d:%02d", &year, &month, &day, &hour, &minute, &second) == 6) {
+ mod_time.year = (png_uint_16)year;
+ mod_time.month = (png_byte)month;
+ mod_time.day = (png_byte)day;
+ mod_time.hour = (png_byte)hour;
+ mod_time.minute = (png_byte)minute;
+ mod_time.second = (png_byte)second;
+ png_set_tIME (png_ptr, info_ptr, &mod_time);
+ }
+ }
+
+ 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;
+}
+
+// --------------------------------------------------------------------------
+
+/**
+Configure the decoder so that decoded pixels are compatible with a FREE_IMAGE_TYPE format.
+Set conversion instructions as needed.
+@param png_ptr PNG handle
+@param info_ptr PNG info handle
+@param flags Decoder flags
+@param output_image_type Returned FreeImage converted image type
+@return Returns TRUE if successful, returns FALSE otherwise
+@see png_read_update_info
+*/
+static BOOL
+ConfigureDecoder(png_structp png_ptr, png_infop info_ptr, int flags, FREE_IMAGE_TYPE *output_image_type) {
+ // get original image info
+ const int color_type = png_get_color_type(png_ptr, info_ptr);
+ const int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+ const int pixel_depth = bit_depth * png_get_channels(png_ptr, info_ptr);
+
+ FREE_IMAGE_TYPE image_type = FIT_BITMAP; // assume standard image type
+
+ // check for transparency table or single transparent color
+ BOOL bIsTransparent = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) == PNG_INFO_tRNS ? TRUE : FALSE;
+
+ // check allowed combinations of colour type and bit depth
+ // then get converted FreeImage type
+
+ switch(color_type) {
+ case PNG_COLOR_TYPE_GRAY: // color type '0', bitdepth = 1, 2, 4, 8, 16
+ switch(bit_depth) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ // expand grayscale images to the full 8-bit from 2-bit/pixel
+ if (pixel_depth == 2) {
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ }
+
+ // if a tRNS chunk is provided, we must also expand the grayscale data to 8-bits,
+ // this allows us to make use of the transparency table with existing FreeImage methods
+ if (bIsTransparent && (pixel_depth < 8)) {
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ }
+ break;
+
+ case 16:
+ image_type = (pixel_depth == 16) ? FIT_UINT16 : FIT_UNKNOWN;
+
+ // 16-bit grayscale images can contain a transparent value (shade)
+ // if found, expand the transparent value to a full alpha channel
+ if (bIsTransparent && (image_type != FIT_UNKNOWN)) {
+ // expand tRNS to a full alpha channel
+ png_set_tRNS_to_alpha(png_ptr);
+
+ // expand new 16-bit gray + 16-bit alpha to full 64-bit RGBA
+ png_set_gray_to_rgb(png_ptr);
+
+ image_type = FIT_RGBA16;
+ }
+ break;
+
+ default:
+ image_type = FIT_UNKNOWN;
+ break;
+ }
+ break;
+
+ case PNG_COLOR_TYPE_RGB: // color type '2', bitdepth = 8, 16
+ switch(bit_depth) {
+ case 8:
+ image_type = (pixel_depth == 24) ? FIT_BITMAP : FIT_UNKNOWN;
+ break;
+ case 16:
+ image_type = (pixel_depth == 48) ? FIT_RGB16 : FIT_UNKNOWN;
+ break;
+ default:
+ image_type = FIT_UNKNOWN;
+ break;
+ }
+ // sometimes, 24- or 48-bit images may contain transparency information
+ // check for this use case and convert to an alpha-compatible format
+ if (bIsTransparent && (image_type != FIT_UNKNOWN)) {
+ // if the image is 24-bit RGB, mark it as 32-bit; if it is 48-bit, mark it as 64-bit
+ image_type = (pixel_depth == 24) ? FIT_BITMAP : (pixel_depth == 48) ? FIT_RGBA16 : FIT_UNKNOWN;
+ // expand tRNS chunk to alpha channel
+ png_set_tRNS_to_alpha(png_ptr);
+ }
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE: // color type '3', bitdepth = 1, 2, 4, 8
+ switch(bit_depth) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ // expand palette images to the full 8 bits from 2 bits/pixel
+ if (pixel_depth == 2) {
+ png_set_packing(png_ptr);
+ }
+
+ // if a tRNS chunk is provided, we must also expand the palletized data to 8-bits,
+ // this allows us to make use of the transparency table with existing FreeImage methods
+ if (bIsTransparent && (pixel_depth < 8)) {
+ png_set_packing(png_ptr);
+ }
+ break;
+
+ default:
+ image_type = FIT_UNKNOWN;
+ break;
+ }
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA: // color type '4', bitdepth = 8, 16
+ switch(bit_depth) {
+ case 8:
+ // 8-bit grayscale + 8-bit alpha => convert to 32-bit RGBA
+ image_type = (pixel_depth == 16) ? FIT_BITMAP : FIT_UNKNOWN;
+ break;
+ case 16:
+ // 16-bit grayscale + 16-bit alpha => convert to 64-bit RGBA
+ image_type = (pixel_depth == 32) ? FIT_RGBA16 : FIT_UNKNOWN;
+ break;
+ default:
+ image_type = FIT_UNKNOWN;
+ break;
+ }
+ // expand 8-bit greyscale + 8-bit alpha to 32-bit
+ // expand 16-bit greyscale + 16-bit alpha to 64-bit
+ png_set_gray_to_rgb(png_ptr);
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA: // color type '6', bitdepth = 8, 16
+ switch(bit_depth) {
+ case 8:
+ break;
+ case 16:
+ image_type = (pixel_depth == 64) ? FIT_RGBA16 : FIT_UNKNOWN;
+ break;
+ default:
+ image_type = FIT_UNKNOWN;
+ break;
+ }
+ break;
+ }
+
+ // check for unknown or invalid formats
+ if(image_type == FIT_UNKNOWN) {
+ *output_image_type = image_type;
+ return FALSE;
+ }
+
+#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
+
+#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
+ if((image_type == FIT_BITMAP) && ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA))) {
+ // flip the RGB pixels to BGR (or RGBA to BGRA)
+ png_set_bgr(png_ptr);
+ }
+#endif
+
+ // gamma correction
+ // 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);
+
+ // return the output image type
+ *output_image_type = image_type;
+
+ 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 = NULL;
+ png_uint_32 width, height;
+ int color_type;
+ int bit_depth;
+ int pixel_depth = 0; // pixel_depth = bit_depth * channels
+
+ FIBITMAP *dib = NULL;
+ png_bytepp row_pointers = NULL;
+
+ 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);
+
+ // configure the decoder
+
+ FREE_IMAGE_TYPE image_type = FIT_BITMAP;
+
+ if(!ConfigureDecoder(png_ptr, info_ptr, flags, &image_type)) {
+ throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
+ }
+
+ // update image info
+
+ color_type = png_get_color_type(png_ptr, info_ptr);
+ bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+ pixel_depth = bit_depth * png_get_channels(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:
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE:
+ dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+ if(dib) {
+ png_colorp png_palette = NULL;
+ int palette_entries = 0;
+
+ png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries);
+
+ palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib));
+
+ // store the palette
+
+ RGBQUAD *palette = FreeImage_GetPalette(dib);
+ for(int 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, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
+
+ if(dib && (pixel_depth <= 8)) {
+ RGBQUAD *palette = FreeImage_GetPalette(dib);
+ const int palette_entries = 1 << pixel_depth;
+
+ for(int 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;
+ }
+
+ if(!dib) {
+ throw FI_MSG_ERROR_DIB_MEMORY;
+ }
+
+ // 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 < 256) {
+ BYTE table[256];
+ memset(table, 0xFF, 256);
+ table[trans_color->gray] = 0;
+ FreeImage_SetTransparencyTable(dib, table, 256);
+ }
+ // check for a full transparency table, too
+ else if ((trans_alpha) && (pixel_depth <= 8)) {
+ FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans);
+ }
+
+ } else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) {
+ // transparency table
+ FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans);
+ }
+ }
+
+ // store the background color (only supported for FIT_BITMAP types)
+
+ if ((image_type == FIT_BITMAP) && 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;
+ }
+
+ // check for transparent images
+ BOOL bIsTransparent =
+ (image_type == FIT_BITMAP) && FreeImage_IsTransparent(dib) && (FreeImage_GetTransparencyCount(dib) > 0) ? TRUE : FALSE;
+
+ switch (FreeImage_GetColorType(dib)) {
+ case FIC_MINISWHITE:
+ if(!bIsTransparent) {
+ // Invert monochrome files to have 0 as black and 1 as white (no break here)
+ png_set_invert_mono(png_ptr);
+ }
+ // (fall through)
+
+ case FIC_MINISBLACK:
+ if(!bIsTransparent) {
+ 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;
+ }
+ // If a monochrome image is transparent, save it with a palette
+ // (fall through)
+
+ 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 (bIsTransparent) {
+ 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) {
+ if(png_ptr) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ }
+ 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/libs/freeimage/src/FreeImage/ToneMapping.cpp b/libs/freeimage/src/FreeImage/ToneMapping.cpp
new file mode 100644
index 0000000000..91f82f5104
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/ToneMapping.cpp
@@ -0,0 +1,74 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/WuQuantizer.cpp b/libs/freeimage/src/FreeImage/WuQuantizer.cpp
new file mode 100644
index 0000000000..3704b2b922
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/WuQuantizer.cpp
@@ -0,0 +1,557 @@
+///////////////////////////////////////////////////////////////////////
+// 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 "../stdafx.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;
+
+ if (FreeImage_GetBPP(m_dib) == 24) {
+ 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;
+ }
+ }
+ } else {
+ 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 += 4;
+ }
+ }
+ }
+
+ 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/libs/freeimage/src/FreeImage/ZLibInterface.cpp b/libs/freeimage/src/FreeImage/ZLibInterface.cpp
new file mode 100644
index 0000000000..8a79c2590a
--- /dev/null
+++ b/libs/freeimage/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 "../stdafx.h"
+
+#include "zlib.h"
+#include "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/libs/freeimage/src/FreeImage/tmoColorConvert.cpp b/libs/freeimage/src/FreeImage/tmoColorConvert.cpp
new file mode 100644
index 0000000000..5323830bda
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/tmoColorConvert.cpp
@@ -0,0 +1,477 @@
+// ==========================================================
+// 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 "../stdafx.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 = MAX(0.0F, pixel[x].red);// avoid negative values
+ 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/libs/freeimage/src/FreeImage/tmoDrago03.cpp b/libs/freeimage/src/FreeImage/tmoDrago03.cpp
new file mode 100644
index 0000000000..de7639db61
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/tmoDrago03.cpp
@@ -0,0 +1,293 @@
+// ==========================================================
+// 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 "../stdafx.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/libs/freeimage/src/FreeImage/tmoFattal02.cpp b/libs/freeimage/src/FreeImage/tmoFattal02.cpp
new file mode 100644
index 0000000000..e543fa5f3d
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/tmoFattal02.cpp
@@ -0,0 +1,687 @@
+// ==========================================================
+// 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 "../stdafx.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 y = 0; y < height; y++) {
+ 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 = y*pitch + x;
+ divg[index] = gx[index] + gy[index];
+ if(x > 0) divg[index] -= gx[index-1];
+ if(y > 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 y = 0; y < height; y++) {
+ 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/libs/freeimage/src/FreeImage/tmoReinhard05.cpp b/libs/freeimage/src/FreeImage/tmoReinhard05.cpp
new file mode 100644
index 0000000000..34c5c67a83
--- /dev/null
+++ b/libs/freeimage/src/FreeImage/tmoReinhard05.cpp
@@ -0,0 +1,258 @@
+// ==========================================================
+// 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 "../stdafx.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);
+}