summaryrefslogtreecommitdiff
path: root/libs/freeimage/src/FreeImage/BitmapAccess.cpp
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2018-01-26 17:38:31 +0300
committerGeorge Hazan <ghazan@miranda.im>2018-01-26 17:38:31 +0300
commitdea9c030340e50324eba97c72a27c151bed12e1c (patch)
tree6fc156f40f52a9fc6e6b29e60001959477ee1a5e /libs/freeimage/src/FreeImage/BitmapAccess.cpp
parentc6e8f8223cab9d799593b7b2cfa22134aa9745d6 (diff)
AdvaImg:
- freeimage extracted to the separate library; - FI_INTERFACE removed, all references to it are replaced with direct calls of FreeImage_* functions; - unified project for AdvaImg
Diffstat (limited to 'libs/freeimage/src/FreeImage/BitmapAccess.cpp')
-rw-r--r--libs/freeimage/src/FreeImage/BitmapAccess.cpp1567
1 files changed, 1567 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;
+}
+