diff options
Diffstat (limited to 'libs/freeimage/src/FreeImage')
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); +} |