From d2d798d0f11abcbf141db69869e76e8d123ec1eb Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Sat, 13 Dec 2014 20:28:24 +0000 Subject: FreeImage updated to 3.16 git-svn-id: http://svn.miranda-ng.org/main/trunk@11379 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp | 2573 ++++++++++---------- plugins/AdvaImg/src/FreeImage/CacheFile.h | 92 - plugins/AdvaImg/src/FreeImage/Conversion24.cpp | 504 ++-- plugins/AdvaImg/src/FreeImage/Conversion32.cpp | 690 +++--- plugins/AdvaImg/src/FreeImage/Conversion8.cpp | 610 ++--- plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp | 386 +-- plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp | 288 +-- plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp | 484 ++-- plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp | 268 +- plugins/AdvaImg/src/FreeImage/J2KHelper.cpp | 1091 +++++---- plugins/AdvaImg/src/FreeImage/J2KHelper.h | 36 + plugins/AdvaImg/src/FreeImage/MultiPage.cpp | 1980 +++++++-------- plugins/AdvaImg/src/FreeImage/PSDParser.cpp | 4 + plugins/AdvaImg/src/FreeImage/PixelAccess.cpp | 420 ++-- plugins/AdvaImg/src/FreeImage/Plugin.cpp | 77 +- plugins/AdvaImg/src/FreeImage/PluginBMP.cpp | 118 +- plugins/AdvaImg/src/FreeImage/PluginGIF.cpp | 4 +- plugins/AdvaImg/src/FreeImage/PluginHDR.cpp | 2 +- plugins/AdvaImg/src/FreeImage/PluginICO.cpp | 3 +- plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp | 666 +++-- plugins/AdvaImg/src/FreeImage/PluginJP2.cpp | 666 +++-- plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp | 106 +- plugins/AdvaImg/src/FreeImage/PluginJXR.cpp | 1309 ++++++++++ plugins/AdvaImg/src/FreeImage/PluginPNG.cpp | 2 + plugins/AdvaImg/src/FreeImage/PluginSGI.cpp | 2 +- plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp | 112 +- plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp | 8 +- plugins/AdvaImg/src/FreeImage/PluginWebP.cpp | 698 ++++++ plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp | 958 ++++---- plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp | 590 ++--- plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp | 1378 +++++------ 31 files changed, 9104 insertions(+), 7021 deletions(-) delete mode 100644 plugins/AdvaImg/src/FreeImage/CacheFile.h create mode 100644 plugins/AdvaImg/src/FreeImage/J2KHelper.h create mode 100644 plugins/AdvaImg/src/FreeImage/PluginJXR.cpp create mode 100644 plugins/AdvaImg/src/FreeImage/PluginWebP.cpp (limited to 'plugins/AdvaImg/src/FreeImage') diff --git a/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp b/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp index 5b90ed8607..2ba5539d4c 100644 --- a/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp +++ b/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp @@ -1,1269 +1,1306 @@ -// ========================================================== -// 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 -#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) -#include -#endif // _WIN32 || _WIN64 || __MINGW32__ - -#include "FreeImage.h" -#include "FreeImageIO.h" -#include "Utilities.h" - -#include "../Metadata/FreeImageTag.h" - -/** Constants for the BITMAPINFOHEADER::biCompression field */ -#ifndef _WINGDI_ -#define BI_RGB 0L -#define BI_BITFIELDS 3L -#endif // _WINGDI_ - -// ---------------------------------------------------------- -// Metadata definitions -// ---------------------------------------------------------- - -// helper for map where value is a pointer to a FreeImage tag -typedef std::map TAGMAP; - -// helper for map -typedef std::map METADATAMAP; - -// helper for metadata iterator -FI_STRUCT (METADATAHEADER) { - long pos; // current position when iterating the map - TAGMAP *tagmap; // pointer to the tag map -}; - -// ---------------------------------------------------------- -// FIBITMAP definition -// ---------------------------------------------------------- - -FI_STRUCT (FREEIMAGEHEADER) { - FREE_IMAGE_TYPE type; // data type - bitmap, array of long, double, complex, etc - - RGBQUAD bkgnd_color; // background color used for RGB transparency - - BOOL transparent; // why another table? for easy transparency table retrieval! - int transparency_count; // transparency could be stored in the palette, which is better - BYTE transparent_table[256];// overall, but it requires quite some changes and it will render - // FreeImage_GetTransparencyTable obsolete in its current form; - FIICCPROFILE iccProfile; // space to hold ICC profile - - METADATAMAP *metadata; // contains a list of metadata models attached to the bitmap - - BOOL has_pixels; // FALSE if the FIBITMAP only contains the header and no pixel data - - FIBITMAP *thumbnail; // optionally contains a thumbnail attached to the bitmap - - //BYTE filler[1]; // fill to 32-bit alignment -}; - -// ---------------------------------------------------------- -// FREEIMAGERGBMASKS definition -// ---------------------------------------------------------- - -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 - -// ---------------------------------------------------------- -// DIB information functions -// ---------------------------------------------------------- - -/** -Calculate the size of a FreeImage image. -Align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary. - -@param header_only If TRUE, calculate a 'header only' FIBITMAP size, otherwise calculate a full FIBITMAP size -@param width -@param height -@param bpp -@param need_masks -@see FreeImage_AllocateHeaderT -*/ -static size_t -FreeImage_GetImageSizeHeader(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 - { - /* - The following constant take into account the additionnal memory used by - aligned malloc functions as well as debug malloc functions. - It is supposed here that using a (8 * FIBITMAP_ALIGNMENT) risk margin will be enough - for the target compiler. - */ - const double FIBITMAP_MAX_MEMORY = (double)((size_t)-1) - 8 * FIBITMAP_ALIGNMENT; - const double dPitch = floor( ((double)bpp * width + 31.0) / 32.0 ) * 4.0; - const double dImageSize = (double)header_size + dPitch * height; - if(dImageSize != (double)dib_size) { - // here, we are sure to encounter a malloc overflow: try to avoid it ... - return 0; - } - if(dImageSize > FIBITMAP_MAX_MEMORY) { - // avoid possible overflow inside C allocation functions - return 0; - } - } - } - - return dib_size; -} - -/** +// ========================================================== +// 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 +#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) +#include +#endif // _WIN32 || _WIN64 || __MINGW32__ + +#include "FreeImage.h" +#include "FreeImageIO.h" +#include "Utilities.h" + +#include "../Metadata/FreeImageTag.h" + +/** Constants for the BITMAPINFOHEADER::biCompression field */ +#ifndef _WINGDI_ +#define BI_RGB 0L +#define BI_BITFIELDS 3L +#endif // _WINGDI_ + +// ---------------------------------------------------------- +// Metadata definitions +// ---------------------------------------------------------- + +// helper for map where value is a pointer to a FreeImage tag +typedef std::map TAGMAP; + +// helper for map +typedef std::map METADATAMAP; + +// helper for metadata iterator +FI_STRUCT (METADATAHEADER) { + long pos; // current position when iterating the map + TAGMAP *tagmap; // pointer to the tag map +}; + +// ---------------------------------------------------------- +// FIBITMAP definition +// ---------------------------------------------------------- + +FI_STRUCT (FREEIMAGEHEADER) { + FREE_IMAGE_TYPE type; // data type - bitmap, array of long, double, complex, etc + + RGBQUAD bkgnd_color; // background color used for RGB transparency + + BOOL transparent; // why another table? for easy transparency table retrieval! + int transparency_count; // transparency could be stored in the palette, which is better + BYTE transparent_table[256];// overall, but it requires quite some changes and it will render + // FreeImage_GetTransparencyTable obsolete in its current form; + FIICCPROFILE iccProfile; // space to hold ICC profile + + METADATAMAP *metadata; // contains a list of metadata models attached to the bitmap + + BOOL has_pixels; // FALSE if the FIBITMAP only contains the header and no pixel data + + FIBITMAP *thumbnail; // optionally contains a thumbnail attached to the bitmap + + //BYTE filler[1]; // fill to 32-bit alignment +}; + +// ---------------------------------------------------------- +// FREEIMAGERGBMASKS definition +// ---------------------------------------------------------- + +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 + +// ---------------------------------------------------------- +// DIB information functions +// ---------------------------------------------------------- + +/** +Calculate the size of a FreeImage image. +Align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary. + +@param header_only If TRUE, calculate a 'header only' FIBITMAP size, otherwise calculate a full FIBITMAP size +@param width +@param height +@param bpp +@param need_masks +@see FreeImage_AllocateHeaderT +*/ +static size_t +FreeImage_GetImageSizeHeader(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 + { + /* + The following constant take into account the additionnal memory used by + aligned malloc functions as well as debug malloc functions. + It is supposed here that using a (8 * FIBITMAP_ALIGNMENT) risk margin will be enough + for the target compiler. + */ + const double FIBITMAP_MAX_MEMORY = (double)((size_t)-1) - 8 * FIBITMAP_ALIGNMENT; + const double dPitch = floor( ((double)bpp * width + 31.0) / 32.0 ) * 4.0; + const double dImageSize = (double)header_size + dPitch * height; + if(dImageSize != (double)dib_size) { + // here, we are sure to encounter a malloc overflow: try to avoid it ... + return 0; + } + if(dImageSize > FIBITMAP_MAX_MEMORY) { + // avoid possible overflow inside C allocation functions + return 0; + } + } + } + + return dib_size; +} + +/** 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; -} - -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) { - - // check input variables - width = abs(width); - height = abs(height); - if(!((width > 0) && (height > 0))) { - return NULL; - } - - // we only store the masks (and allocate memory for - // them) for 16 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 - - size_t dib_size = FreeImage_GetImageSizeHeader(header_only, 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; - - // 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_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { - return FreeImage_AllocateHeaderT(header_only, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); -} - -FIBITMAP * DLL_CALLCONV -FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { - return FreeImage_AllocateHeaderT(FALSE, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); -} - -FIBITMAP * DLL_CALLCONV -FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { - return FreeImage_AllocateHeaderT(FALSE, type, width, height, bpp, red_mask, green_mask, blue_mask); -} - -void DLL_CALLCONV -FreeImage_Unload(FIBITMAP *dib) { - if (NULL != dib) { - if (NULL != dib->data) { - // delete possible icc profile ... - if (FreeImage_GetICCProfile(dib)->data) - free(FreeImage_GetICCProfile(dib)->data); - - // delete metadata models - METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; - - for(METADATAMAP::iterator i = (*metadata).begin(); i != (*metadata).end(); i++) { - TAGMAP *tagmap = (*i).second; - - if(tagmap) { - for(TAGMAP::iterator j = tagmap->begin(); j != tagmap->end(); j++) { - FITAG *tag = (*j).second; - FreeImage_DeleteTag(tag); - } - - delete tagmap; - } - } - - delete metadata; - - // delete embedded thumbnail - FreeImage_Unload(FreeImage_GetThumbnail(dib)); - - // delete bitmap ... - FreeImage_Aligned_Free(dib->data); - } - free(dib); // ... and the wrapper - } -} - -// ---------------------------------------------------------- - -FIBITMAP * DLL_CALLCONV -FreeImage_Clone(FIBITMAP *dib) { - if(!dib) return NULL; - - FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib); - unsigned width = FreeImage_GetWidth(dib); - unsigned height = FreeImage_GetHeight(dib); - unsigned bpp = FreeImage_GetBPP(dib); - - // check for pixel availability ... - BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE; - // 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 a FreeImage image - // align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary - // palette is aligned on a 16 bytes boundary - // pixels are aligned on a 16 bytes boundary - - size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp, 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)); - - return new_dib; - } - - return NULL; -} - -// ---------------------------------------------------------- - -FIBITMAP* DLL_CALLCONV -FreeImage_GetThumbnail(FIBITMAP *dib) { - return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->thumbnail : NULL; -} - -BOOL DLL_CALLCONV -FreeImage_SetThumbnail(FIBITMAP *dib, FIBITMAP *thumbnail) { - if(dib == NULL) { - return FALSE; - } - FIBITMAP *currentThumbnail = ((FREEIMAGEHEADER *)dib->data)->thumbnail; - if(currentThumbnail == thumbnail) { - return TRUE; - } - FreeImage_Unload(currentThumbnail); - - ((FREEIMAGEHEADER *)dib->data)->thumbnail = FreeImage_HasPixels(thumbnail) ? FreeImage_Clone(thumbnail) : NULL; - - return TRUE; -} - -// ---------------------------------------------------------- - -FREE_IMAGE_COLOR_TYPE DLL_CALLCONV -FreeImage_GetColorType(FIBITMAP *dib) { - RGBQUAD *rgb; - - const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); - - // special bitmap type - if(image_type != FIT_BITMAP) { - switch(image_type) { - case FIT_RGB16: - case FIT_RGBF: - return FIC_RGB; - case FIT_RGBA16: - case FIT_RGBAF: - return FIC_RGBALPHA; - } - - return FIC_MINISBLACK; - } - - // standard image type - switch (FreeImage_GetBPP(dib)) { - case 1: - { - rgb = FreeImage_GetPalette(dib); - - if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) { - rgb++; - - if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) - return FIC_MINISBLACK; - } - - if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) { - rgb++; - - if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) - return FIC_MINISWHITE; - } - - return FIC_PALETTE; - } - - case 4: - case 8: // Check if the DIB has a color or a greyscale palette - { - int ncolors = FreeImage_GetColorsUsed(dib); - int minisblack = 1; - rgb = FreeImage_GetPalette(dib); - - for (int i = 0; i < ncolors; i++) { - if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) - return FIC_PALETTE; - - // The DIB has a color palette if the greyscale isn't a linear ramp - // Take care of reversed grey images - if (rgb->rgbRed != i) { - if ((ncolors-i-1) != rgb->rgbRed) - return FIC_PALETTE; - else - minisblack = 0; - } - - rgb++; - } - - return minisblack ? FIC_MINISBLACK : FIC_MINISWHITE; - } - - case 16: - case 24: - return FIC_RGB; - - case 32: - { - if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK) - return FIC_CMYK; - - 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 = FreeImage_GetRGBMasks(dib); - return masks ? masks->red_mask : 0; -} - -unsigned DLL_CALLCONV -FreeImage_GetGreenMask(FIBITMAP *dib) { - FREEIMAGERGBMASKS *masks = FreeImage_GetRGBMasks(dib); - return masks ? masks->green_mask : 0; -} - -unsigned DLL_CALLCONV -FreeImage_GetBlueMask(FIBITMAP *dib) { - FREEIMAGERGBMASKS *masks = FreeImage_GetRGBMasks(dib); - return masks ? masks->blue_mask : 0; -} - -// ---------------------------------------------------------- - -BOOL DLL_CALLCONV -FreeImage_HasBackgroundColor(FIBITMAP *dib) { - if(dib) { - RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color; - return (bkgnd_color->rgbReserved != 0) ? TRUE : FALSE; - } - return FALSE; -} - -BOOL DLL_CALLCONV -FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) { - if(dib && bkcolor) { - if(FreeImage_HasBackgroundColor(dib)) { - // get the background color - RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color; - memcpy(bkcolor, bkgnd_color, sizeof(RGBQUAD)); - // get the background index - if(FreeImage_GetBPP(dib) == 8) { - RGBQUAD *pal = FreeImage_GetPalette(dib); - for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) { - if(bkgnd_color->rgbRed == pal[i].rgbRed) { - if(bkgnd_color->rgbGreen == pal[i].rgbGreen) { - if(bkgnd_color->rgbBlue == pal[i].rgbBlue) { - bkcolor->rgbReserved = (BYTE)i; - return TRUE; - } - } - } - } - } - - bkcolor->rgbReserved = 0; - - return TRUE; - } - } - - return FALSE; -} - -BOOL DLL_CALLCONV -FreeImage_SetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) { - if(dib) { - RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color; - if(bkcolor) { - // set the background color - memcpy(bkgnd_color, bkcolor, sizeof(RGBQUAD)); - // enable the file background color - bkgnd_color->rgbReserved = 1; - } else { - // clear and disable the file background color - memset(bkgnd_color, 0, sizeof(RGBQUAD)); - } - return TRUE; - } - - return FALSE; -} - -// ---------------------------------------------------------- - -BOOL DLL_CALLCONV -FreeImage_IsTransparent(FIBITMAP *dib) { - if(dib) { - FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); - switch(image_type) { - case FIT_BITMAP: - if(FreeImage_GetBPP(dib) == 32) { - if(FreeImage_GetColorType(dib) == FIC_RGBALPHA) { - return TRUE; - } - } else { - return ((FREEIMAGEHEADER *)dib->data)->transparent ? TRUE : FALSE; - } - break; - case FIT_RGBA16: - case FIT_RGBAF: - return TRUE; - default: - break; - } - } - return FALSE; -} - -BYTE * DLL_CALLCONV -FreeImage_GetTransparencyTable(FIBITMAP *dib) { - return dib ? ((FREEIMAGEHEADER *)dib->data)->transparent_table : NULL; -} - -void DLL_CALLCONV -FreeImage_SetTransparent(FIBITMAP *dib, BOOL enabled) { - if (dib) { - if ((FreeImage_GetBPP(dib) <= 8) || (FreeImage_GetBPP(dib) == 32)) { - ((FREEIMAGEHEADER *)dib->data)->transparent = enabled; - } else { - ((FREEIMAGEHEADER *)dib->data)->transparent = FALSE; - } - } -} - -unsigned DLL_CALLCONV -FreeImage_GetTransparencyCount(FIBITMAP *dib) { - return dib ? ((FREEIMAGEHEADER *)dib->data)->transparency_count : 0; -} - -void DLL_CALLCONV -FreeImage_SetTransparencyTable(FIBITMAP *dib, BYTE *table, int count) { - if (dib) { - count = 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) { - return dib ? FreeImage_GetLine(dib) + 3 & ~3 : 0; -} - -unsigned DLL_CALLCONV -FreeImage_GetColorsUsed(FIBITMAP *dib) { - return dib ? FreeImage_GetInfoHeader(dib)->biClrUsed : 0; -} - -unsigned DLL_CALLCONV -FreeImage_GetDIBSize(FIBITMAP *dib) { - return (dib) ? sizeof(BITMAPINFOHEADER) + (FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD)) + (FreeImage_GetPitch(dib) * FreeImage_GetHeight(dib)) : 0; -} - -RGBQUAD * DLL_CALLCONV -FreeImage_GetPalette(FIBITMAP *dib) { - return (dib && FreeImage_GetBPP(dib) < 16) ? (RGBQUAD *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL; -} - -unsigned DLL_CALLCONV -FreeImage_GetDotsPerMeterX(FIBITMAP *dib) { - return (dib) ? FreeImage_GetInfoHeader(dib)->biXPelsPerMeter : 0; -} - -unsigned DLL_CALLCONV -FreeImage_GetDotsPerMeterY(FIBITMAP *dib) { - return (dib) ? FreeImage_GetInfoHeader(dib)->biYPelsPerMeter : 0; -} - -void DLL_CALLCONV -FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res) { - if(dib) { - FreeImage_GetInfoHeader(dib)->biXPelsPerMeter = res; - } -} - -void DLL_CALLCONV -FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res) { - if(dib) { - FreeImage_GetInfoHeader(dib)->biYPelsPerMeter = res; - } -} - -BITMAPINFOHEADER * DLL_CALLCONV -FreeImage_GetInfoHeader(FIBITMAP *dib) { - if(!dib) return NULL; - size_t lp = (size_t)dib->data + sizeof(FREEIMAGEHEADER); - lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0); - lp += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT; - return (BITMAPINFOHEADER *)lp; -} - -BITMAPINFO * DLL_CALLCONV -FreeImage_GetInfo(FIBITMAP *dib) { - return (BITMAPINFO *)FreeImage_GetInfoHeader(dib); -} - -// ---------------------------------------------------------- -// Metadata routines -// ---------------------------------------------------------- - -FIMETADATA * DLL_CALLCONV -FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag) { - if(!dib) - return NULL; - - // get the metadata model - METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; - TAGMAP *tagmap = NULL; - if( (*metadata).find(model) != (*metadata).end() ) { - tagmap = (*metadata)[model]; - } - if(tagmap) { - // allocate a handle - FIMETADATA *handle = (FIMETADATA *)malloc(sizeof(FIMETADATA)); - if(handle) { - // calculate the size of a METADATAHEADER - int header_size = sizeof(METADATAHEADER); - - handle->data = (BYTE *)malloc(header_size * sizeof(BYTE)); - - if(handle->data) { - memset(handle->data, 0, header_size * sizeof(BYTE)); - - // write out the METADATAHEADER - METADATAHEADER *mdh = (METADATAHEADER *)handle->data; - - mdh->pos = 1; - mdh->tagmap = tagmap; - - // get the first element - TAGMAP::iterator i = tagmap->begin(); - *tag = (*i).second; - - return handle; - } - - free(handle); - } - } - - return NULL; -} - -BOOL DLL_CALLCONV -FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag) { - if(!mdhandle) - return FALSE; - - METADATAHEADER *mdh = (METADATAHEADER *)mdhandle->data; - TAGMAP *tagmap = mdh->tagmap; - - int current_pos = mdh->pos; - int mapsize = (int)tagmap->size(); - - if(current_pos < mapsize) { - // get the tag element at position pos - int count = 0; - - for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) { - if(count == current_pos) { - *tag = (*i).second; - mdh->pos++; - break; - } - count++; - } - - return TRUE; - } - - return FALSE; -} - -void DLL_CALLCONV -FreeImage_FindCloseMetadata(FIMETADATA *mdhandle) { - if (NULL != mdhandle) { // delete the handle - if (NULL != mdhandle->data) { - free(mdhandle->data); - } - free(mdhandle); // ... and the wrapper - } -} - - -// ---------------------------------------------------------- - -BOOL DLL_CALLCONV -FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) { - if(!src || !dst) return FALSE; - - // get metadata links - METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)src->data)->metadata; - METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)dst->data)->metadata; - - // copy metadata models, *except* the FIMD_ANIMATION model - for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) { - int model = (*i).first; - if(model == (int)FIMD_ANIMATION) { - continue; - } - TAGMAP *src_tagmap = (*i).second; - - if(src_tagmap) { - if( dst_metadata->find(model) != dst_metadata->end() ) { - // destroy dst model - FreeImage_SetMetadata((FREE_IMAGE_MDMODEL)model, dst, NULL, NULL); - } - - // create a metadata model - TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP(); - - if(dst_tagmap) { - // fill the model - for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) { - std::string dst_key = (*j).first; - FITAG *dst_tag = FreeImage_CloneTag( (*j).second ); - - // assign key and tag value - (*dst_tagmap)[dst_key] = dst_tag; - } - - // assign model and tagmap - (*dst_metadata)[model] = dst_tagmap; - } - } - } - - // clone resolution - FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src)); - FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src)); - - return TRUE; -} - -// ---------------------------------------------------------- - -BOOL DLL_CALLCONV -FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) { - if(!dib) - return FALSE; - - TAGMAP *tagmap = NULL; - - // get the metadata model - METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; - METADATAMAP::iterator model_iterator = metadata->find(model); - if (model_iterator != metadata->end()) { - tagmap = model_iterator->second; - } - - if(key != NULL) { - - if(!tagmap) { - // this model, doesn't exist: create it - tagmap = new(std::nothrow) TAGMAP(); - (*metadata)[model] = tagmap; - } - - if(tag) { - // first check the tag - if(FreeImage_GetTagKey(tag) == NULL) { - FreeImage_SetTagKey(tag, key); - } else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) { - // set the tag key - FreeImage_SetTagKey(tag, key); - } - if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) { - FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key); - return FALSE; - } - - // fill the tag ID if possible and if it's needed - TagLib& tag_lib = TagLib::instance(); - switch(model) { - case FIMD_IPTC: - { - int id = tag_lib.getTagID(TagLib::IPTC, key); - /* - if(id == -1) { - FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key); - } - */ - FreeImage_SetTagID(tag, (WORD)id); - } - break; - - default: - break; - } - - // delete existing tag - FITAG *old_tag = (*tagmap)[key]; - if(old_tag) { - FreeImage_DeleteTag(old_tag); - } - - // create a new tag - (*tagmap)[key] = FreeImage_CloneTag(tag); - } - else { - // delete existing tag - TAGMAP::iterator i = tagmap->find(key); - if(i != tagmap->end()) { - FITAG *old_tag = (*i).second; - FreeImage_DeleteTag(old_tag); - tagmap->erase(key); - } - } - } - else { - // destroy the metadata model - if(tagmap) { - for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) { - FITAG *tag = (*i).second; - FreeImage_DeleteTag(tag); - } - - delete tagmap; - metadata->erase(model_iterator); - } - } - - return TRUE; -} - -BOOL DLL_CALLCONV -FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag) { - if(!dib || !key || !tag) - return FALSE; - - TAGMAP *tagmap = NULL; - *tag = NULL; - - // get the metadata model - METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; - if(!(*metadata).empty()) { - METADATAMAP::iterator model_iterator = metadata->find(model); - if (model_iterator != metadata->end() ) { - // this model exists : try to get the requested tag - tagmap = model_iterator->second; - TAGMAP::iterator tag_iterator = tagmap->find(key); - if (tag_iterator != tagmap->end() ) { - // get the requested tag - *tag = tag_iterator->second; - } - } - } - - return (*tag != NULL) ? TRUE : FALSE; -} - -// ---------------------------------------------------------- - -unsigned DLL_CALLCONV -FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib) { - if(!dib) - return FALSE; - - TAGMAP *tagmap = NULL; - - // get the metadata model - METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; - if( (*metadata).find(model) != (*metadata).end() ) { - tagmap = (*metadata)[model]; - } - if(!tagmap) { - // this model, doesn't exist: return - return 0; - } - - // get the tag count - return (unsigned)tagmap->size(); -} - -// ---------------------------------------------------------- - - +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; +} + +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) { + + // check input variables + width = abs(width); + height = abs(height); + if(!((width > 0) && (height > 0))) { + return NULL; + } + + // 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 + + size_t dib_size = FreeImage_GetImageSizeHeader(header_only, 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; + + // 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_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { + return FreeImage_AllocateHeaderT(header_only, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); +} + +FIBITMAP * DLL_CALLCONV +FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { + return FreeImage_AllocateHeaderT(FALSE, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); +} + +FIBITMAP * DLL_CALLCONV +FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { + return FreeImage_AllocateHeaderT(FALSE, type, width, height, bpp, red_mask, green_mask, blue_mask); +} + +void DLL_CALLCONV +FreeImage_Unload(FIBITMAP *dib) { + if (NULL != dib) { + if (NULL != dib->data) { + // delete possible icc profile ... + if (FreeImage_GetICCProfile(dib)->data) + free(FreeImage_GetICCProfile(dib)->data); + + // delete metadata models + METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; + + for(METADATAMAP::iterator i = (*metadata).begin(); i != (*metadata).end(); i++) { + TAGMAP *tagmap = (*i).second; + + if(tagmap) { + for(TAGMAP::iterator j = tagmap->begin(); j != tagmap->end(); j++) { + FITAG *tag = (*j).second; + FreeImage_DeleteTag(tag); + } + + delete tagmap; + } + } + + delete metadata; + + // delete embedded thumbnail + FreeImage_Unload(FreeImage_GetThumbnail(dib)); + + // delete bitmap ... + FreeImage_Aligned_Free(dib->data); + } + free(dib); // ... and the wrapper + } +} + +// ---------------------------------------------------------- + +FIBITMAP * DLL_CALLCONV +FreeImage_Clone(FIBITMAP *dib) { + if(!dib) return NULL; + + FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib); + unsigned width = FreeImage_GetWidth(dib); + unsigned height = FreeImage_GetHeight(dib); + unsigned bpp = FreeImage_GetBPP(dib); + + // check for pixel availability ... + BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE; + // 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 a FreeImage image + // align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary + // palette is aligned on a 16 bytes boundary + // pixels are aligned on a 16 bytes boundary + + size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp, 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)); + + return new_dib; + } + + return NULL; +} + +// ---------------------------------------------------------- + +FIBITMAP* DLL_CALLCONV +FreeImage_GetThumbnail(FIBITMAP *dib) { + return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->thumbnail : NULL; +} + +BOOL DLL_CALLCONV +FreeImage_SetThumbnail(FIBITMAP *dib, FIBITMAP *thumbnail) { + if(dib == NULL) { + return FALSE; + } + FIBITMAP *currentThumbnail = ((FREEIMAGEHEADER *)dib->data)->thumbnail; + if(currentThumbnail == thumbnail) { + return TRUE; + } + FreeImage_Unload(currentThumbnail); + + ((FREEIMAGEHEADER *)dib->data)->thumbnail = FreeImage_HasPixels(thumbnail) ? FreeImage_Clone(thumbnail) : NULL; + + return TRUE; +} + +// ---------------------------------------------------------- + +FREE_IMAGE_COLOR_TYPE DLL_CALLCONV +FreeImage_GetColorType(FIBITMAP *dib) { + RGBQUAD *rgb; + + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + + // special bitmap type + if(image_type != FIT_BITMAP) { + switch(image_type) { + case FIT_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); + return masks ? masks->red_mask : FI_RGBA_RED_MASK; + 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); + return masks ? masks->green_mask : FI_RGBA_GREEN_MASK; + 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); + return masks ? masks->blue_mask : FI_RGBA_BLUE_MASK; + 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) { + return dib ? FreeImage_GetLine(dib) + 3 & ~3 : 0; +} + +unsigned DLL_CALLCONV +FreeImage_GetColorsUsed(FIBITMAP *dib) { + return dib ? FreeImage_GetInfoHeader(dib)->biClrUsed : 0; +} + +unsigned DLL_CALLCONV +FreeImage_GetDIBSize(FIBITMAP *dib) { + return (dib) ? sizeof(BITMAPINFOHEADER) + (FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD)) + (FreeImage_GetPitch(dib) * FreeImage_GetHeight(dib)) : 0; +} + +RGBQUAD * DLL_CALLCONV +FreeImage_GetPalette(FIBITMAP *dib) { + return (dib && FreeImage_GetBPP(dib) < 16) ? (RGBQUAD *)(((BYTE *)FreeImage_GetInfoHeader(dib)) + sizeof(BITMAPINFOHEADER)) : NULL; +} + +unsigned DLL_CALLCONV +FreeImage_GetDotsPerMeterX(FIBITMAP *dib) { + return (dib) ? FreeImage_GetInfoHeader(dib)->biXPelsPerMeter : 0; +} + +unsigned DLL_CALLCONV +FreeImage_GetDotsPerMeterY(FIBITMAP *dib) { + return (dib) ? FreeImage_GetInfoHeader(dib)->biYPelsPerMeter : 0; +} + +void DLL_CALLCONV +FreeImage_SetDotsPerMeterX(FIBITMAP *dib, unsigned res) { + if(dib) { + FreeImage_GetInfoHeader(dib)->biXPelsPerMeter = res; + } +} + +void DLL_CALLCONV +FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res) { + if(dib) { + FreeImage_GetInfoHeader(dib)->biYPelsPerMeter = res; + } +} + +BITMAPINFOHEADER * DLL_CALLCONV +FreeImage_GetInfoHeader(FIBITMAP *dib) { + if(!dib) return NULL; + size_t lp = (size_t)dib->data + sizeof(FREEIMAGEHEADER); + lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0); + lp += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT; + return (BITMAPINFOHEADER *)lp; +} + +BITMAPINFO * DLL_CALLCONV +FreeImage_GetInfo(FIBITMAP *dib) { + return (BITMAPINFO *)FreeImage_GetInfoHeader(dib); +} + +// ---------------------------------------------------------- +// Metadata routines +// ---------------------------------------------------------- + +FIMETADATA * DLL_CALLCONV +FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag) { + if(!dib) + return NULL; + + // get the metadata model + METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; + TAGMAP *tagmap = NULL; + if( (*metadata).find(model) != (*metadata).end() ) { + tagmap = (*metadata)[model]; + } + if(tagmap) { + // allocate a handle + FIMETADATA *handle = (FIMETADATA *)malloc(sizeof(FIMETADATA)); + if(handle) { + // calculate the size of a METADATAHEADER + int header_size = sizeof(METADATAHEADER); + + handle->data = (BYTE *)malloc(header_size * sizeof(BYTE)); + + if(handle->data) { + memset(handle->data, 0, header_size * sizeof(BYTE)); + + // write out the METADATAHEADER + METADATAHEADER *mdh = (METADATAHEADER *)handle->data; + + mdh->pos = 1; + mdh->tagmap = tagmap; + + // get the first element + TAGMAP::iterator i = tagmap->begin(); + *tag = (*i).second; + + return handle; + } + + free(handle); + } + } + + return NULL; +} + +BOOL DLL_CALLCONV +FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag) { + if(!mdhandle) + return FALSE; + + METADATAHEADER *mdh = (METADATAHEADER *)mdhandle->data; + TAGMAP *tagmap = mdh->tagmap; + + int current_pos = mdh->pos; + int mapsize = (int)tagmap->size(); + + if(current_pos < mapsize) { + // get the tag element at position pos + int count = 0; + + for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) { + if(count == current_pos) { + *tag = (*i).second; + mdh->pos++; + break; + } + count++; + } + + return TRUE; + } + + return FALSE; +} + +void DLL_CALLCONV +FreeImage_FindCloseMetadata(FIMETADATA *mdhandle) { + if (NULL != mdhandle) { // delete the handle + if (NULL != mdhandle->data) { + free(mdhandle->data); + } + free(mdhandle); // ... and the wrapper + } +} + + +// ---------------------------------------------------------- + +BOOL DLL_CALLCONV +FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) { + if(!src || !dst) return FALSE; + + // get metadata links + METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)src->data)->metadata; + METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)dst->data)->metadata; + + // copy metadata models, *except* the FIMD_ANIMATION model + for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) { + int model = (*i).first; + if(model == (int)FIMD_ANIMATION) { + continue; + } + TAGMAP *src_tagmap = (*i).second; + + if(src_tagmap) { + if( dst_metadata->find(model) != dst_metadata->end() ) { + // destroy dst model + FreeImage_SetMetadata((FREE_IMAGE_MDMODEL)model, dst, NULL, NULL); + } + + // create a metadata model + TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP(); + + if(dst_tagmap) { + // fill the model + for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) { + std::string dst_key = (*j).first; + FITAG *dst_tag = FreeImage_CloneTag( (*j).second ); + + // assign key and tag value + (*dst_tagmap)[dst_key] = dst_tag; + } + + // assign model and tagmap + (*dst_metadata)[model] = dst_tagmap; + } + } + } + + // clone resolution + FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src)); + FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src)); + + return TRUE; +} + +// ---------------------------------------------------------- + +BOOL DLL_CALLCONV +FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) { + if(!dib) + return FALSE; + + TAGMAP *tagmap = NULL; + + // get the metadata model + METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; + METADATAMAP::iterator model_iterator = metadata->find(model); + if (model_iterator != metadata->end()) { + tagmap = model_iterator->second; + } + + if(key != NULL) { + + if(!tagmap) { + // this model, doesn't exist: create it + tagmap = new(std::nothrow) TAGMAP(); + (*metadata)[model] = tagmap; + } + + if(tag) { + // first check the tag + if(FreeImage_GetTagKey(tag) == NULL) { + FreeImage_SetTagKey(tag, key); + } else if(strcmp(key, FreeImage_GetTagKey(tag)) != 0) { + // set the tag key + FreeImage_SetTagKey(tag, key); + } + if(FreeImage_GetTagCount(tag) * FreeImage_TagDataWidth(FreeImage_GetTagType(tag)) != FreeImage_GetTagLength(tag)) { + FreeImage_OutputMessageProc(FIF_UNKNOWN, "Invalid data count for tag '%s'", key); + return FALSE; + } + + // fill the tag ID if possible and if it's needed + TagLib& tag_lib = TagLib::instance(); + switch(model) { + case FIMD_IPTC: + { + int id = tag_lib.getTagID(TagLib::IPTC, key); + /* + if(id == -1) { + FreeImage_OutputMessageProc(FIF_UNKNOWN, "IPTC: Invalid key '%s'", key); + } + */ + FreeImage_SetTagID(tag, (WORD)id); + } + break; + + default: + break; + } + + // delete existing tag + FITAG *old_tag = (*tagmap)[key]; + if(old_tag) { + FreeImage_DeleteTag(old_tag); + } + + // create a new tag + (*tagmap)[key] = FreeImage_CloneTag(tag); + } + else { + // delete existing tag + TAGMAP::iterator i = tagmap->find(key); + if(i != tagmap->end()) { + FITAG *old_tag = (*i).second; + FreeImage_DeleteTag(old_tag); + tagmap->erase(key); + } + } + } + else { + // destroy the metadata model + if(tagmap) { + for(TAGMAP::iterator i = tagmap->begin(); i != tagmap->end(); i++) { + FITAG *tag = (*i).second; + FreeImage_DeleteTag(tag); + } + + delete tagmap; + metadata->erase(model_iterator); + } + } + + return TRUE; +} + +BOOL DLL_CALLCONV +FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag) { + if(!dib || !key || !tag) + return FALSE; + + TAGMAP *tagmap = NULL; + *tag = NULL; + + // get the metadata model + METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; + if(!(*metadata).empty()) { + METADATAMAP::iterator model_iterator = metadata->find(model); + if (model_iterator != metadata->end() ) { + // this model exists : try to get the requested tag + tagmap = model_iterator->second; + TAGMAP::iterator tag_iterator = tagmap->find(key); + if (tag_iterator != tagmap->end() ) { + // get the requested tag + *tag = tag_iterator->second; + } + } + } + + return (*tag != NULL) ? TRUE : FALSE; +} + +// ---------------------------------------------------------- + +unsigned DLL_CALLCONV +FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib) { + if(!dib) + return FALSE; + + TAGMAP *tagmap = NULL; + + // get the metadata model + METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; + if( (*metadata).find(model) != (*metadata).end() ) { + tagmap = (*metadata)[model]; + } + if(!tagmap) { + // this model, doesn't exist: return + return 0; + } + + // get the tag count + return (unsigned)tagmap->size(); +} + +// ---------------------------------------------------------- + + diff --git a/plugins/AdvaImg/src/FreeImage/CacheFile.h b/plugins/AdvaImg/src/FreeImage/CacheFile.h deleted file mode 100644 index a1e5e782c9..0000000000 --- a/plugins/AdvaImg/src/FreeImage/CacheFile.h +++ /dev/null @@ -1,92 +0,0 @@ -// ========================================================== -// Multi-Page 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! -// ========================================================== - -#ifndef CACHEFILE_H -#define CACHEFILE_H - -// ---------------------------------------------------------- - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- - -static const int CACHE_SIZE = 32; -static const int BLOCK_SIZE = (64 * 1024) - 8; - -// ---------------------------------------------------------- - -#ifdef _WIN32 -#pragma pack(push, 1) -#else -#pragma pack(1) -#endif // _WIN32 - -struct Block { - unsigned nr; - unsigned next; - BYTE *data; -}; - -#ifdef _WIN32 -#pragma pack(pop) -#else -#pragma pack() -#endif // _WIN32 - -// ---------------------------------------------------------- - -class CacheFile { - typedef std::list PageCache; - typedef std::list::iterator PageCacheIt; - typedef std::map PageMap; - typedef std::map::iterator PageMapIt; - -public : - CacheFile(const std::string filename, BOOL keep_in_memory); - ~CacheFile(); - - BOOL open(); - void close(); - BOOL readFile(BYTE *data, int nr, int size); - int writeFile(BYTE *data, int size); - void deleteFile(int nr); - -private : - void cleanupMemCache(); - int allocateBlock(); - Block *lockBlock(int nr); - BOOL unlockBlock(int nr); - BOOL deleteBlock(int nr); - -private : - FILE *m_file; - std::string m_filename; - std::list m_free_pages; - PageCache m_page_cache_mem; - PageCache m_page_cache_disk; - PageMap m_page_map; - int m_page_count; - Block *m_current_block; - BOOL m_keep_in_memory; -}; - -#endif // CACHEFILE_H diff --git a/plugins/AdvaImg/src/FreeImage/Conversion24.cpp b/plugins/AdvaImg/src/FreeImage/Conversion24.cpp index 1d5bba7f02..3b7a800696 100644 --- a/plugins/AdvaImg/src/FreeImage/Conversion24.cpp +++ b/plugins/AdvaImg/src/FreeImage/Conversion24.cpp @@ -1,252 +1,252 @@ -// ========================================================== -// Bitmap conversion routines -// -// Design and implementation by -// - Floris van den Berg (flvdberg@wxs.nl) -// - Dale Larson (dlarson@norsesoft.com) -// - Hervé Drolon (drolon@infonie.fr) -// - Jani Kajala (janik@remedy.fi) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- -// internal conversions X to 24 bits -// ---------------------------------------------------------- - -void DLL_CALLCONV -FreeImage_ConvertLine1To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { - for (int cols = 0; cols < width_in_pixels; cols++) { - BYTE index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0; - - target[FI_RGBA_BLUE] = palette[index].rgbBlue; - target[FI_RGBA_GREEN] = palette[index].rgbGreen; - target[FI_RGBA_RED] = palette[index].rgbRed; - - target += 3; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine4To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { - BOOL low_nibble = FALSE; - int x = 0; - - for (int cols = 0; cols < width_in_pixels; ++cols ) { - if (low_nibble) { - target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue; - target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen; - target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed; - - x++; - } else { - target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue; - target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen; - target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed; - } - - low_nibble = !low_nibble; - - target += 3; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine8To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue; - target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen; - target[FI_RGBA_RED] = palette[source[cols]].rgbRed; - - target += 3; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine16To24_555(BYTE *target, BYTE *source, int width_in_pixels) { - WORD *bits = (WORD *)source; - - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); - - target += 3; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine16To24_565(BYTE *target, BYTE *source, int width_in_pixels) { - WORD *bits = (WORD *)source; - - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F); - target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F); - - target += 3; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine32To24(BYTE *target, BYTE *source, int width_in_pixels) { - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE]; - target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN]; - target[FI_RGBA_RED] = source[FI_RGBA_RED]; - - target += 3; - source += 4; - } -} - -// ---------------------------------------------------------- -// smart convert X to 24 bits -// ---------------------------------------------------------- - -FIBITMAP * DLL_CALLCONV -FreeImage_ConvertTo24Bits(FIBITMAP *dib) { - if(!FreeImage_HasPixels(dib)) return NULL; - - const unsigned bpp = FreeImage_GetBPP(dib); - const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); - - if((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (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; -} +// ========================================================== +// Bitmap conversion routines +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Dale Larson (dlarson@norsesoft.com) +// - Hervé Drolon (drolon@infonie.fr) +// - Jani Kajala (janik@remedy.fi) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// internal conversions X to 24 bits +// ---------------------------------------------------------- + +void DLL_CALLCONV +FreeImage_ConvertLine1To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { + for (int cols = 0; cols < width_in_pixels; cols++) { + BYTE index = (source[cols >> 3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0; + + target[FI_RGBA_BLUE] = palette[index].rgbBlue; + target[FI_RGBA_GREEN] = palette[index].rgbGreen; + target[FI_RGBA_RED] = palette[index].rgbRed; + + target += 3; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine4To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { + BOOL low_nibble = FALSE; + int x = 0; + + for (int cols = 0; cols < width_in_pixels; ++cols ) { + if (low_nibble) { + target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue; + target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen; + target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed; + + x++; + } else { + target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue; + target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen; + target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed; + } + + low_nibble = !low_nibble; + + target += 3; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine8To24(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue; + target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen; + target[FI_RGBA_RED] = palette[source[cols]].rgbRed; + + target += 3; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine16To24_555(BYTE *target, BYTE *source, int width_in_pixels) { + WORD *bits = (WORD *)source; + + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); + + target += 3; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine16To24_565(BYTE *target, BYTE *source, int width_in_pixels) { + WORD *bits = (WORD *)source; + + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F); + target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F); + + target += 3; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine32To24(BYTE *target, BYTE *source, int width_in_pixels) { + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE]; + target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN]; + target[FI_RGBA_RED] = source[FI_RGBA_RED]; + + target += 3; + source += 4; + } +} + +// ---------------------------------------------------------- +// smart convert X to 24 bits +// ---------------------------------------------------------- + +FIBITMAP * DLL_CALLCONV +FreeImage_ConvertTo24Bits(FIBITMAP *dib) { + if(!FreeImage_HasPixels(dib)) return NULL; + + const unsigned bpp = FreeImage_GetBPP(dib); + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + + if((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (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/plugins/AdvaImg/src/FreeImage/Conversion32.cpp b/plugins/AdvaImg/src/FreeImage/Conversion32.cpp index b8bd518471..4874dcfaca 100644 --- a/plugins/AdvaImg/src/FreeImage/Conversion32.cpp +++ b/plugins/AdvaImg/src/FreeImage/Conversion32.cpp @@ -1,345 +1,345 @@ -// ========================================================== -// Bitmap conversion routines -// -// Design and implementation by -// - Floris van den Berg (flvdberg@wxs.nl) -// - Hervé Drolon (drolon@infonie.fr) -// - Jani Kajala (janik@remedy.fi) -// - Detlev Vendt (detlev.vendt@brillit.de) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- -// internal conversions X to 32 bits -// ---------------------------------------------------------- - -void DLL_CALLCONV -FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { - for (int cols = 0; cols < width_in_pixels; cols++) { - int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0; - - target[FI_RGBA_BLUE] = palette[index].rgbBlue; - target[FI_RGBA_GREEN] = palette[index].rgbGreen; - target[FI_RGBA_RED] = palette[index].rgbRed; - target[FI_RGBA_ALPHA] = 0xFF; - target += 4; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { - BOOL low_nibble = FALSE; - int x = 0; - - for (int cols = 0 ; cols < width_in_pixels ; ++cols) { - if (low_nibble) { - target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue; - target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen; - target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed; - - x++; - } else { - target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue; - target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen; - target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed; - } - - low_nibble = !low_nibble; - - target[FI_RGBA_ALPHA] = 0xFF; - target += 4; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue; - target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen; - target[FI_RGBA_RED] = palette[source[cols]].rgbRed; - target[FI_RGBA_ALPHA] = 0xFF; - target += 4; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels) { - WORD *bits = (WORD *)source; - - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_ALPHA] = 0xFF; - target += 4; - } -} - -void DLL_CALLCONV -FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels) { - WORD *bits = (WORD *)source; - - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F); - target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F); - target[FI_RGBA_ALPHA] = 0xFF; - target += 4; - } -} -/* -void DLL_CALLCONV -FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) { - for (int cols = 0; cols < width_in_pixels; cols++) { - *(DWORD *)target = (*(DWORD *) source & FI_RGBA_RGB_MASK) | FI_RGBA_ALPHA_MASK; - target += 4; - source += 3; - } -} -*/ -/** -This unoptimized version of the conversion function avoid an undetermined bug with VC++ SP6. -The bug occurs in release mode only, when the image height is equal to 537 -(try e.g. a size of 432x537 to reproduce the bug with the optimized function). -*/ -void DLL_CALLCONV -FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) { - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_RED] = source[FI_RGBA_RED]; - target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN]; - target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE]; - target[FI_RGBA_ALPHA] = 0xFF; - target += 4; - source += 3; - } -} - -// ---------------------------------------------------------- - -inline void -FreeImage_ConvertLine1To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) { - for (int cols = 0; cols < width_in_pixels; cols++) { - int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0; - - target[FI_RGBA_BLUE] = palette[index].rgbBlue; - target[FI_RGBA_GREEN] = palette[index].rgbGreen; - target[FI_RGBA_RED] = palette[index].rgbRed; - target[FI_RGBA_ALPHA] = (index < transparent_pixels) ? table[index] : 255; - target += 4; - } -} - -inline void -FreeImage_ConvertLine4To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) { - BOOL low_nibble = FALSE; - int x = 0; - - for (int cols = 0 ; cols < width_in_pixels ; ++cols) { - if (low_nibble) { - target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue; - target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen; - target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed; - target[FI_RGBA_ALPHA] = (LOWNIBBLE(source[x]) < transparent_pixels) ? table[LOWNIBBLE(source[x])] : 255; - - x++; - } else { - target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue; - target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen; - target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed; - target[FI_RGBA_ALPHA] = (HINIBBLE(source[x] >> 4) < transparent_pixels) ? table[HINIBBLE(source[x]) >> 4] : 255; - } - - low_nibble = !low_nibble; - - target += 4; - } -} - -inline void -FreeImage_ConvertLine8To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) { - for (int cols = 0; cols < width_in_pixels; cols++) { - target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue; - target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen; - target[FI_RGBA_RED] = palette[source[cols]].rgbRed; - target[FI_RGBA_ALPHA] = (source[cols] < transparent_pixels) ? table[source[cols]] : 255; - target += 4; - } -} - -// ---------------------------------------------------------- - -FIBITMAP * DLL_CALLCONV -FreeImage_ConvertTo32Bits(FIBITMAP *dib) { - if(!FreeImage_HasPixels(dib)) return NULL; - - const int bpp = FreeImage_GetBPP(dib); - const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); - - if((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (image_type != FIT_RGBA16)) { - return NULL; - } - - const int width = FreeImage_GetWidth(dib); - const int height = FreeImage_GetHeight(dib); - - if(image_type == FIT_BITMAP) { - - if(bpp == 32) { - return FreeImage_Clone(dib); - } - - FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - if(new_dib == NULL) { - return NULL; - } - - // copy metadata from src to dst - FreeImage_CloneMetadata(new_dib, dib); - - BOOL bIsTransparent = FreeImage_IsTransparent(dib); - - switch(bpp) { - case 1: - { - if(bIsTransparent) { - for (int rows = 0; rows < height; rows++) { - FreeImage_ConvertLine1To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); - } - } else { - for (int rows = 0; rows < height; rows++) { - FreeImage_ConvertLine1To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib)); - } - } - - return new_dib; - } - - case 4: - { - if(bIsTransparent) { - for (int rows = 0; rows < height; rows++) { - FreeImage_ConvertLine4To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); - } - } else { - for (int rows = 0; rows < height; rows++) { - FreeImage_ConvertLine4To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib)); - } - } - - return new_dib; - } - - case 8: - { - if(bIsTransparent) { - for (int rows = 0; rows < height; rows++) { - FreeImage_ConvertLine8To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); - } - } else { - for (int rows = 0; rows < height; rows++) { - FreeImage_ConvertLine8To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib)); - } - } - - return new_dib; - } - - case 16: - { - for (int rows = 0; rows < height; rows++) { - if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { - FreeImage_ConvertLine16To32_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width); - } else { - // includes case where all the masks are 0 - FreeImage_ConvertLine16To32_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width); - } - } - - return new_dib; - } - - case 24: - { - for (int rows = 0; rows < height; rows++) { - FreeImage_ConvertLine24To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width); - } - - return new_dib; - } - } - - } else if(image_type == FIT_RGB16) { - FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - if(new_dib == NULL) { - return NULL; - } - - // copy metadata from src to dst - FreeImage_CloneMetadata(new_dib, dib); - - const unsigned src_pitch = FreeImage_GetPitch(dib); - const unsigned dst_pitch = FreeImage_GetPitch(new_dib); - const BYTE *src_bits = FreeImage_GetBits(dib); - BYTE *dst_bits = FreeImage_GetBits(new_dib); - for (int rows = 0; rows < height; rows++) { - const FIRGB16 *src_pixel = (FIRGB16*)src_bits; - RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits; - for(int cols = 0; cols < width; cols++) { - dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8); - dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8); - dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8); - dst_pixel[cols].rgbReserved = (BYTE)0xFF; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - - return new_dib; - - } else if(image_type == FIT_RGBA16) { - FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - if(new_dib == NULL) { - return NULL; - } - - // copy metadata from src to dst - FreeImage_CloneMetadata(new_dib, dib); - - const unsigned src_pitch = FreeImage_GetPitch(dib); - const unsigned dst_pitch = FreeImage_GetPitch(new_dib); - const BYTE *src_bits = FreeImage_GetBits(dib); - BYTE *dst_bits = FreeImage_GetBits(new_dib); - for (int rows = 0; rows < height; rows++) { - const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits; - RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits; - for(int cols = 0; cols < width; cols++) { - dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8); - dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8); - dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8); - dst_pixel[cols].rgbReserved = (BYTE)(src_pixel[cols].alpha >> 8); - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - - return new_dib; - } - - return NULL; -} +// ========================================================== +// Bitmap conversion routines +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Hervé Drolon (drolon@infonie.fr) +// - Jani Kajala (janik@remedy.fi) +// - Detlev Vendt (detlev.vendt@brillit.de) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// internal conversions X to 32 bits +// ---------------------------------------------------------- + +void DLL_CALLCONV +FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { + for (int cols = 0; cols < width_in_pixels; cols++) { + int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0; + + target[FI_RGBA_BLUE] = palette[index].rgbBlue; + target[FI_RGBA_GREEN] = palette[index].rgbGreen; + target[FI_RGBA_RED] = palette[index].rgbRed; + target[FI_RGBA_ALPHA] = 0xFF; + target += 4; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { + BOOL low_nibble = FALSE; + int x = 0; + + for (int cols = 0 ; cols < width_in_pixels ; ++cols) { + if (low_nibble) { + target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue; + target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen; + target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed; + + x++; + } else { + target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue; + target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen; + target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed; + } + + low_nibble = !low_nibble; + + target[FI_RGBA_ALPHA] = 0xFF; + target += 4; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) { + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue; + target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen; + target[FI_RGBA_RED] = palette[source[cols]].rgbRed; + target[FI_RGBA_ALPHA] = 0xFF; + target += 4; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels) { + WORD *bits = (WORD *)source; + + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_ALPHA] = 0xFF; + target += 4; + } +} + +void DLL_CALLCONV +FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels) { + WORD *bits = (WORD *)source; + + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F); + target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F); + target[FI_RGBA_ALPHA] = 0xFF; + target += 4; + } +} +/* +void DLL_CALLCONV +FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) { + for (int cols = 0; cols < width_in_pixels; cols++) { + *(DWORD *)target = (*(DWORD *) source & FI_RGBA_RGB_MASK) | FI_RGBA_ALPHA_MASK; + target += 4; + source += 3; + } +} +*/ +/** +This unoptimized version of the conversion function avoid an undetermined bug with VC++ SP6. +The bug occurs in release mode only, when the image height is equal to 537 +(try e.g. a size of 432x537 to reproduce the bug with the optimized function). +*/ +void DLL_CALLCONV +FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) { + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_RED] = source[FI_RGBA_RED]; + target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN]; + target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE]; + target[FI_RGBA_ALPHA] = 0xFF; + target += 4; + source += 3; + } +} + +// ---------------------------------------------------------- + +inline void +FreeImage_ConvertLine1To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) { + for (int cols = 0; cols < width_in_pixels; cols++) { + int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0; + + target[FI_RGBA_BLUE] = palette[index].rgbBlue; + target[FI_RGBA_GREEN] = palette[index].rgbGreen; + target[FI_RGBA_RED] = palette[index].rgbRed; + target[FI_RGBA_ALPHA] = (index < transparent_pixels) ? table[index] : 255; + target += 4; + } +} + +inline void +FreeImage_ConvertLine4To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) { + BOOL low_nibble = FALSE; + int x = 0; + + for (int cols = 0 ; cols < width_in_pixels ; ++cols) { + if (low_nibble) { + target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue; + target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen; + target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed; + target[FI_RGBA_ALPHA] = (LOWNIBBLE(source[x]) < transparent_pixels) ? table[LOWNIBBLE(source[x])] : 255; + + x++; + } else { + target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue; + target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen; + target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed; + target[FI_RGBA_ALPHA] = (HINIBBLE(source[x] >> 4) < transparent_pixels) ? table[HINIBBLE(source[x]) >> 4] : 255; + } + + low_nibble = !low_nibble; + + target += 4; + } +} + +inline void +FreeImage_ConvertLine8To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) { + for (int cols = 0; cols < width_in_pixels; cols++) { + target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue; + target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen; + target[FI_RGBA_RED] = palette[source[cols]].rgbRed; + target[FI_RGBA_ALPHA] = (source[cols] < transparent_pixels) ? table[source[cols]] : 255; + target += 4; + } +} + +// ---------------------------------------------------------- + +FIBITMAP * DLL_CALLCONV +FreeImage_ConvertTo32Bits(FIBITMAP *dib) { + if(!FreeImage_HasPixels(dib)) return NULL; + + const int bpp = FreeImage_GetBPP(dib); + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + + if((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (image_type != FIT_RGBA16)) { + return NULL; + } + + const int width = FreeImage_GetWidth(dib); + const int height = FreeImage_GetHeight(dib); + + if(image_type == FIT_BITMAP) { + + if(bpp == 32) { + return FreeImage_Clone(dib); + } + + FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if(new_dib == NULL) { + return NULL; + } + + // copy metadata from src to dst + FreeImage_CloneMetadata(new_dib, dib); + + BOOL bIsTransparent = FreeImage_IsTransparent(dib); + + switch(bpp) { + case 1: + { + if(bIsTransparent) { + for (int rows = 0; rows < height; rows++) { + FreeImage_ConvertLine1To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); + } + } else { + for (int rows = 0; rows < height; rows++) { + FreeImage_ConvertLine1To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib)); + } + } + + return new_dib; + } + + case 4: + { + if(bIsTransparent) { + for (int rows = 0; rows < height; rows++) { + FreeImage_ConvertLine4To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); + } + } else { + for (int rows = 0; rows < height; rows++) { + FreeImage_ConvertLine4To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib)); + } + } + + return new_dib; + } + + case 8: + { + if(bIsTransparent) { + for (int rows = 0; rows < height; rows++) { + FreeImage_ConvertLine8To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); + } + } else { + for (int rows = 0; rows < height; rows++) { + FreeImage_ConvertLine8To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib)); + } + } + + return new_dib; + } + + case 16: + { + for (int rows = 0; rows < height; rows++) { + if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { + FreeImage_ConvertLine16To32_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width); + } else { + // includes case where all the masks are 0 + FreeImage_ConvertLine16To32_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width); + } + } + + return new_dib; + } + + case 24: + { + for (int rows = 0; rows < height; rows++) { + FreeImage_ConvertLine24To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width); + } + + return new_dib; + } + } + + } else if(image_type == FIT_RGB16) { + FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if(new_dib == NULL) { + return NULL; + } + + // copy metadata from src to dst + FreeImage_CloneMetadata(new_dib, dib); + + const unsigned src_pitch = FreeImage_GetPitch(dib); + const unsigned dst_pitch = FreeImage_GetPitch(new_dib); + const BYTE *src_bits = FreeImage_GetBits(dib); + BYTE *dst_bits = FreeImage_GetBits(new_dib); + for (int rows = 0; rows < height; rows++) { + const FIRGB16 *src_pixel = (FIRGB16*)src_bits; + RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits; + for(int cols = 0; cols < width; cols++) { + dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8); + dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8); + dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8); + dst_pixel[cols].rgbReserved = (BYTE)0xFF; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + + return new_dib; + + } else if(image_type == FIT_RGBA16) { + FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if(new_dib == NULL) { + return NULL; + } + + // copy metadata from src to dst + FreeImage_CloneMetadata(new_dib, dib); + + const unsigned src_pitch = FreeImage_GetPitch(dib); + const unsigned dst_pitch = FreeImage_GetPitch(new_dib); + const BYTE *src_bits = FreeImage_GetBits(dib); + BYTE *dst_bits = FreeImage_GetBits(new_dib); + for (int rows = 0; rows < height; rows++) { + const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits; + RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits; + for(int cols = 0; cols < width; cols++) { + dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8); + dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8); + dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8); + dst_pixel[cols].rgbReserved = (BYTE)(src_pixel[cols].alpha >> 8); + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + + return new_dib; + } + + return NULL; +} diff --git a/plugins/AdvaImg/src/FreeImage/Conversion8.cpp b/plugins/AdvaImg/src/FreeImage/Conversion8.cpp index 1c331a6552..c4f9b22441 100644 --- a/plugins/AdvaImg/src/FreeImage/Conversion8.cpp +++ b/plugins/AdvaImg/src/FreeImage/Conversion8.cpp @@ -1,305 +1,305 @@ -// ========================================================== -// 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 "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- -// internal conversions X to 8 bits -// ---------------------------------------------------------- - -void DLL_CALLCONV -FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels) { - for (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); -} +// ========================================================== +// 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 "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// internal conversions X to 8 bits +// ---------------------------------------------------------- + +void DLL_CALLCONV +FreeImage_ConvertLine1To8(BYTE *target, BYTE *source, int width_in_pixels) { + for (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/plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp b/plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp index e72d14cb83..a36a6d423f 100644 --- a/plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp +++ b/plugins/AdvaImg/src/FreeImage/ConversionFloat.cpp @@ -1,192 +1,194 @@ -// ========================================================== -// Bitmap conversion routines -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- -// smart convert X to Float -// ---------------------------------------------------------- - -FIBITMAP * DLL_CALLCONV -FreeImage_ConvertToFloat(FIBITMAP *dib) { - FIBITMAP *src = NULL; - FIBITMAP *dst = NULL; - - if(!FreeImage_HasPixels(dib)) return NULL; - - FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); - - // check for allowed conversions - switch(src_type) { - case FIT_BITMAP: - { - // allow conversion from 8-bit - if((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) { - src = dib; - } else { - src = FreeImage_ConvertToGreyscale(dib); - if(!src) return NULL; - } - break; - } - case FIT_UINT16: - case FIT_RGB16: - case FIT_RGBA16: - case FIT_RGBF: - case FIT_RGBAF: - src = dib; - break; - case FIT_FLOAT: - // float type : clone the src - return FreeImage_Clone(dib); - default: - return NULL; - } - - // allocate dst image - - const unsigned width = FreeImage_GetWidth(src); - const unsigned height = FreeImage_GetHeight(src); - - dst = FreeImage_AllocateT(FIT_FLOAT, width, height); - if(!dst) { - if(src != dib) { - FreeImage_Unload(src); - } - return NULL; - } - - // copy metadata from src to dst - FreeImage_CloneMetadata(dst, src); - - // convert from src type to float - - const unsigned src_pitch = FreeImage_GetPitch(src); - const unsigned dst_pitch = FreeImage_GetPitch(dst); - - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - switch(src_type) { - case FIT_BITMAP: - { - for(unsigned y = 0; y < height; y++) { - const BYTE *src_pixel = (BYTE*)src_bits; - float *dst_pixel = (float*)dst_bits; - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - dst_pixel[x] = (float)(src_pixel[x]) / 255; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_UINT16: - { - for(unsigned y = 0; y < height; y++) { - const WORD *src_pixel = (WORD*)src_bits; - float *dst_pixel = (float*)dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - dst_pixel[x] = (float)(src_pixel[x]) / 65535; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_RGB16: - { - for(unsigned y = 0; y < height; y++) { - const FIRGB16 *src_pixel = (FIRGB16*)src_bits; - float *dst_pixel = (float*)dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_RGBA16: - { - for(unsigned y = 0; y < height; y++) { - const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits; - float *dst_pixel = (float*)dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_RGBF: - { - for(unsigned y = 0; y < height; y++) { - const FIRGBF *src_pixel = (FIRGBF*)src_bits; - float *dst_pixel = (float*)dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert (assume pixel values are in the range [0..1]) - dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue); - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_RGBAF: - { - for(unsigned y = 0; y < height; y++) { - const FIRGBAF *src_pixel = (FIRGBAF*)src_bits; - float *dst_pixel = (float*)dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert (assume pixel values are in the range [0..1]) - dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue); - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - } - - if(src != dib) { - FreeImage_Unload(src); - } - - return dst; -} - +// ========================================================== +// Bitmap conversion routines +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// smart convert X to Float +// ---------------------------------------------------------- + +FIBITMAP * DLL_CALLCONV +FreeImage_ConvertToFloat(FIBITMAP *dib) { + FIBITMAP *src = NULL; + FIBITMAP *dst = NULL; + + if(!FreeImage_HasPixels(dib)) return NULL; + + FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); + + // check for allowed conversions + switch(src_type) { + case FIT_BITMAP: + { + // allow conversion from 8-bit + if((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) { + src = dib; + } else { + src = FreeImage_ConvertToGreyscale(dib); + if(!src) return NULL; + } + break; + } + case FIT_UINT16: + case FIT_RGB16: + case FIT_RGBA16: + case FIT_RGBF: + case FIT_RGBAF: + src = dib; + break; + case FIT_FLOAT: + // float type : clone the src + return FreeImage_Clone(dib); + default: + return NULL; + } + + // allocate dst image + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + dst = FreeImage_AllocateT(FIT_FLOAT, width, height); + if(!dst) { + if(src != dib) { + FreeImage_Unload(src); + } + return NULL; + } + + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, src); + + // convert from src type to float + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + switch(src_type) { + case FIT_BITMAP: + { + for(unsigned y = 0; y < height; y++) { + const BYTE *src_pixel = (BYTE*)src_bits; + float *dst_pixel = (float*)dst_bits; + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + dst_pixel[x] = (float)(src_pixel[x]) / 255; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_UINT16: + { + for(unsigned y = 0; y < height; y++) { + const WORD *src_pixel = (WORD*)src_bits; + float *dst_pixel = (float*)dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + dst_pixel[x] = (float)(src_pixel[x]) / 65535; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_RGB16: + { + for(unsigned y = 0; y < height; y++) { + const FIRGB16 *src_pixel = (FIRGB16*)src_bits; + float *dst_pixel = (float*)dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_RGBA16: + { + for(unsigned y = 0; y < height; y++) { + const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits; + float *dst_pixel = (float*)dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue) / 65535.0F; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_RGBF: + { + for(unsigned y = 0; y < height; y++) { + const FIRGBF *src_pixel = (FIRGBF*)src_bits; + float *dst_pixel = (float*)dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert (assume pixel values are in the range [0..1]) + dst_pixel[x] = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue); + 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/plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp b/plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp index bd38504a1e..9e28205688 100644 --- a/plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp +++ b/plugins/AdvaImg/src/FreeImage/ConversionRGB16.cpp @@ -1,144 +1,144 @@ -// ========================================================== -// Bitmap conversion routines -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- -// smart convert X to RGB16 -// ---------------------------------------------------------- - -FIBITMAP * DLL_CALLCONV -FreeImage_ConvertToRGB16(FIBITMAP *dib) { - FIBITMAP *src = NULL; - FIBITMAP *dst = NULL; - - if(!FreeImage_HasPixels(dib)) return NULL; - - const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); - - // check for allowed conversions - switch(src_type) { - case FIT_BITMAP: - { - // convert to 24-bit if needed - if((FreeImage_GetBPP(dib) == 24) || (FreeImage_GetBPP(dib) == 32)) { - src = dib; - } else { - src = FreeImage_ConvertTo24Bits(dib); - if(!src) return NULL; - } - break; - } - case FIT_UINT16: - // allow conversion from unsigned 16-bit - src = dib; - break; - case FIT_RGB16: - // RGB16 type : clone the src - return FreeImage_Clone(dib); - break; - case FIT_RGBA16: - // allow conversion from 64-bit RGBA (ignore the alpha channel) - src = dib; - break; - default: - return NULL; - } - - // allocate dst image - - const unsigned width = FreeImage_GetWidth(src); - const unsigned height = FreeImage_GetHeight(src); - - dst = FreeImage_AllocateT(FIT_RGB16, width, height); - if(!dst) { - if(src != dib) { - FreeImage_Unload(src); - } - return NULL; - } - - // copy metadata from src to dst - FreeImage_CloneMetadata(dst, src); - - // convert from src type to RGB16 - - switch(src_type) { - case FIT_BITMAP: - { - // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit) - const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); - - for(unsigned y = 0; y < height; y++) { - const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y); - FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); - for(unsigned x = 0; x < width; x++) { - dst_bits[x].red = src_bits[FI_RGBA_RED] << 8; - dst_bits[x].green = src_bits[FI_RGBA_GREEN] << 8; - dst_bits[x].blue = src_bits[FI_RGBA_BLUE] << 8; - src_bits += bytespp; - } - } - } - break; - - case FIT_UINT16: - { - for(unsigned y = 0; y < height; y++) { - const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y); - FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); - for(unsigned x = 0; x < width; x++) { - // convert by copying greyscale channel to each R, G, B channels - dst_bits[x].red = src_bits[x]; - dst_bits[x].green = src_bits[x]; - dst_bits[x].blue = src_bits[x]; - } - } - } - break; - - case FIT_RGBA16: - { - for(unsigned y = 0; y < height; y++) { - const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y); - FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); - for(unsigned x = 0; x < width; x++) { - // convert and skip alpha channel - dst_bits[x].red = src_bits[x].red; - dst_bits[x].green = src_bits[x].green; - dst_bits[x].blue = src_bits[x].blue; - } - } - } - break; - - default: - break; - } - - if(src != dib) { - FreeImage_Unload(src); - } - - return dst; -} - +// ========================================================== +// Bitmap conversion routines +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// smart convert X to RGB16 +// ---------------------------------------------------------- + +FIBITMAP * DLL_CALLCONV +FreeImage_ConvertToRGB16(FIBITMAP *dib) { + FIBITMAP *src = NULL; + FIBITMAP *dst = NULL; + + if(!FreeImage_HasPixels(dib)) return NULL; + + const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); + + // check for allowed conversions + switch(src_type) { + case FIT_BITMAP: + { + // convert to 24-bit if needed + if((FreeImage_GetBPP(dib) == 24) || (FreeImage_GetBPP(dib) == 32)) { + src = dib; + } else { + src = FreeImage_ConvertTo24Bits(dib); + if(!src) return NULL; + } + break; + } + case FIT_UINT16: + // allow conversion from unsigned 16-bit + src = dib; + break; + case FIT_RGB16: + // RGB16 type : clone the src + return FreeImage_Clone(dib); + break; + case FIT_RGBA16: + // allow conversion from 64-bit RGBA (ignore the alpha channel) + src = dib; + break; + default: + return NULL; + } + + // allocate dst image + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + dst = FreeImage_AllocateT(FIT_RGB16, width, height); + if(!dst) { + if(src != dib) { + FreeImage_Unload(src); + } + return NULL; + } + + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, src); + + // convert from src type to RGB16 + + switch(src_type) { + case FIT_BITMAP: + { + // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit) + const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); + + for(unsigned y = 0; y < height; y++) { + const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y); + FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); + for(unsigned x = 0; x < width; x++) { + dst_bits[x].red = src_bits[FI_RGBA_RED] << 8; + dst_bits[x].green = src_bits[FI_RGBA_GREEN] << 8; + dst_bits[x].blue = src_bits[FI_RGBA_BLUE] << 8; + src_bits += bytespp; + } + } + } + break; + + case FIT_UINT16: + { + for(unsigned y = 0; y < height; y++) { + const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y); + FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); + for(unsigned x = 0; x < width; x++) { + // convert by copying greyscale channel to each R, G, B channels + dst_bits[x].red = src_bits[x]; + dst_bits[x].green = src_bits[x]; + dst_bits[x].blue = src_bits[x]; + } + } + } + break; + + case FIT_RGBA16: + { + for(unsigned y = 0; y < height; y++) { + const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y); + FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); + for(unsigned x = 0; x < width; x++) { + // convert and skip alpha channel + dst_bits[x].red = src_bits[x].red; + dst_bits[x].green = src_bits[x].green; + dst_bits[x].blue = src_bits[x].blue; + } + } + } + break; + + default: + break; + } + + if(src != dib) { + FreeImage_Unload(src); + } + + return dst; +} + diff --git a/plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp b/plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp index b5161d566d..230dd7b11b 100644 --- a/plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp +++ b/plugins/AdvaImg/src/FreeImage/ConversionRGBF.cpp @@ -1,241 +1,243 @@ -// ========================================================== -// Bitmap conversion routines -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- -// smart convert X to RGBF -// ---------------------------------------------------------- - -FIBITMAP * DLL_CALLCONV -FreeImage_ConvertToRGBF(FIBITMAP *dib) { - FIBITMAP *src = NULL; - FIBITMAP *dst = NULL; - - if(!FreeImage_HasPixels(dib)) return NULL; - - const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); - - // check for allowed conversions - switch(src_type) { - case FIT_BITMAP: - { - // allow conversion from 24- and 32-bit - const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); - if((color_type != FIC_RGB) && (color_type != FIC_RGBALPHA)) { - src = FreeImage_ConvertTo24Bits(dib); - if(!src) return NULL; - } else { - src = dib; - } - break; - } - case FIT_UINT16: - // allow conversion from 16-bit - src = dib; - break; - case FIT_RGB16: - // allow conversion from 48-bit RGB - src = dib; - break; - case FIT_RGBA16: - // allow conversion from 64-bit RGBA (ignore the alpha channel) - src = dib; - break; - case FIT_FLOAT: - // allow conversion from 32-bit float - src = dib; - break; - case FIT_RGBAF: - // allow conversion from 128-bit RGBAF - src = dib; - break; - case FIT_RGBF: - // RGBF type : clone the src - return FreeImage_Clone(dib); - break; - default: - return NULL; - } - - // allocate dst image - - const unsigned width = FreeImage_GetWidth(src); - const unsigned height = FreeImage_GetHeight(src); - - dst = FreeImage_AllocateT(FIT_RGBF, width, height); - if(!dst) { - if(src != dib) { - FreeImage_Unload(src); - } - return NULL; - } - - // copy metadata from src to dst - FreeImage_CloneMetadata(dst, src); - - // convert from src type to RGBF - - const unsigned src_pitch = FreeImage_GetPitch(src); - const unsigned dst_pitch = FreeImage_GetPitch(dst); - - switch(src_type) { - case FIT_BITMAP: - { - // calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit) - const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); - - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const BYTE *src_pixel = (BYTE*)src_bits; - FIRGBF *dst_pixel = (FIRGBF*)dst_bits; - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - dst_pixel->red = (float)(src_pixel[FI_RGBA_RED]) / 255.0F; - dst_pixel->green = (float)(src_pixel[FI_RGBA_GREEN]) / 255.0F; - dst_pixel->blue = (float)(src_pixel[FI_RGBA_BLUE]) / 255.0F; - - src_pixel += bytespp; - dst_pixel ++; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_UINT16: - { - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const WORD *src_pixel = (WORD*)src_bits; - FIRGBF *dst_pixel = (FIRGBF*)dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - const float dst_value = (float)src_pixel[x] / 65535.0F; - dst_pixel[x].red = dst_value; - dst_pixel[x].green = dst_value; - dst_pixel[x].blue = dst_value; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_RGB16: - { - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const FIRGB16 *src_pixel = (FIRGB16*) src_bits; - FIRGBF *dst_pixel = (FIRGBF*) dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F; - dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F; - dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_RGBA16: - { - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const FIRGBA16 *src_pixel = (FIRGBA16*) src_bits; - FIRGBF *dst_pixel = (FIRGBF*) dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert and scale to the range [0..1] - dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F; - dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F; - dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_FLOAT: - { - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const float *src_pixel = (float*) src_bits; - FIRGBF *dst_pixel = (FIRGBF*) dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert by copying greyscale channel to each R, G, B channels - dst_pixel[x].red = src_pixel[x]; - dst_pixel[x].green = src_pixel[x]; - dst_pixel[x].blue = src_pixel[x]; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - - case FIT_RGBAF: - { - const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const FIRGBAF *src_pixel = (FIRGBAF*) src_bits; - FIRGBF *dst_pixel = (FIRGBF*) dst_bits; - - for(unsigned x = 0; x < width; x++) { - // convert and skip alpha channel - dst_pixel[x].red = src_pixel[x].red; - dst_pixel[x].green = src_pixel[x].green; - dst_pixel[x].blue = src_pixel[x].blue; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - } - break; - } - - if(src != dib) { - FreeImage_Unload(src); - } - - return dst; -} - +// ========================================================== +// Bitmap conversion routines +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// smart convert X to RGBF +// ---------------------------------------------------------- + +FIBITMAP * DLL_CALLCONV +FreeImage_ConvertToRGBF(FIBITMAP *dib) { + FIBITMAP *src = NULL; + FIBITMAP *dst = NULL; + + if(!FreeImage_HasPixels(dib)) return NULL; + + const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); + + // check for allowed conversions + switch(src_type) { + case FIT_BITMAP: + { + // allow conversion from 24- and 32-bit + const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); + if((color_type != FIC_RGB) && (color_type != FIC_RGBALPHA)) { + src = FreeImage_ConvertTo24Bits(dib); + if(!src) return NULL; + } else { + src = dib; + } + break; + } + case FIT_UINT16: + // allow conversion from 16-bit + src = dib; + break; + case FIT_RGB16: + // allow conversion from 48-bit RGB + src = dib; + break; + case FIT_RGBA16: + // allow conversion from 64-bit RGBA (ignore the alpha channel) + src = dib; + break; + case FIT_FLOAT: + // allow conversion from 32-bit float + src = dib; + break; + case FIT_RGBAF: + // allow conversion from 128-bit RGBAF + src = dib; + break; + case FIT_RGBF: + // RGBF type : clone the src + return FreeImage_Clone(dib); + break; + default: + return NULL; + } + + // allocate dst image + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + dst = FreeImage_AllocateT(FIT_RGBF, width, height); + if(!dst) { + if(src != dib) { + FreeImage_Unload(src); + } + return NULL; + } + + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, src); + + // convert from src type to RGBF + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + switch(src_type) { + case FIT_BITMAP: + { + // calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit) + const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); + + const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const BYTE *src_pixel = (BYTE*)src_bits; + FIRGBF *dst_pixel = (FIRGBF*)dst_bits; + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + dst_pixel->red = (float)(src_pixel[FI_RGBA_RED]) / 255.0F; + dst_pixel->green = (float)(src_pixel[FI_RGBA_GREEN]) / 255.0F; + dst_pixel->blue = (float)(src_pixel[FI_RGBA_BLUE]) / 255.0F; + + src_pixel += bytespp; + dst_pixel ++; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_UINT16: + { + const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const WORD *src_pixel = (WORD*)src_bits; + FIRGBF *dst_pixel = (FIRGBF*)dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + const float dst_value = (float)src_pixel[x] / 65535.0F; + dst_pixel[x].red = dst_value; + dst_pixel[x].green = dst_value; + dst_pixel[x].blue = dst_value; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_RGB16: + { + const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const FIRGB16 *src_pixel = (FIRGB16*) src_bits; + FIRGBF *dst_pixel = (FIRGBF*) dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F; + dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F; + dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_RGBA16: + { + const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const FIRGBA16 *src_pixel = (FIRGBA16*) src_bits; + FIRGBF *dst_pixel = (FIRGBF*) dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert and scale to the range [0..1] + dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F; + dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F; + dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + } + break; + + case FIT_FLOAT: + { + const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const float *src_pixel = (float*) src_bits; + FIRGBF *dst_pixel = (FIRGBF*) dst_bits; + + for(unsigned x = 0; x < width; x++) { + // convert by copying greyscale channel to each R, G, B channels + // 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/plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp b/plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp index ed4691f3a2..e6492aacd2 100644 --- a/plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp +++ b/plugins/AdvaImg/src/FreeImage/ConversionUINT16.cpp @@ -1,134 +1,134 @@ -// ========================================================== -// Bitmap conversion routines -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- -// smart convert X to UINT16 -// ---------------------------------------------------------- - -FIBITMAP * DLL_CALLCONV -FreeImage_ConvertToUINT16(FIBITMAP *dib) { - FIBITMAP *src = NULL; - FIBITMAP *dst = NULL; - - if(!FreeImage_HasPixels(dib)) return NULL; - - const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); - - // check for allowed conversions - switch(src_type) { - case FIT_BITMAP: - { - // convert to greyscale if needed - if((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) { - src = dib; - } else { - src = FreeImage_ConvertToGreyscale(dib); - if(!src) return NULL; - } - break; - } - case FIT_UINT16: - // UINT16 type : clone the src - return FreeImage_Clone(dib); - break; - case FIT_RGB16: - // allow conversion from 48-bit RGB - src = dib; - break; - case FIT_RGBA16: - // allow conversion from 64-bit RGBA (ignore the alpha channel) - src = dib; - break; - default: - return NULL; - } - - // allocate dst image - - const unsigned width = FreeImage_GetWidth(src); - const unsigned height = FreeImage_GetHeight(src); - - dst = FreeImage_AllocateT(FIT_UINT16, width, height); - if(!dst) { - if(src != dib) { - FreeImage_Unload(src); - } - return NULL; - } - - // copy metadata from src to dst - FreeImage_CloneMetadata(dst, src); - - // convert from src type to UINT16 - - switch(src_type) { - case FIT_BITMAP: - { - for(unsigned y = 0; y < height; y++) { - const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y); - WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); - for(unsigned x = 0; x < width; x++) { - dst_bits[x] = src_bits[x] << 8; - } - } - } - break; - - case FIT_RGB16: - { - for(unsigned y = 0; y < height; y++) { - const FIRGB16 *src_bits = (FIRGB16*)FreeImage_GetScanLine(src, y); - WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); - for(unsigned x = 0; x < width; x++) { - // convert to grey - dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue); - } - } - } - break; - - case FIT_RGBA16: - { - for(unsigned y = 0; y < height; y++) { - const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y); - WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); - for(unsigned x = 0; x < width; x++) { - // convert to grey - dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue); - } - } - } - break; - - default: - break; - } - - if(src != dib) { - FreeImage_Unload(src); - } - - return dst; -} - +// ========================================================== +// Bitmap conversion routines +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- +// smart convert X to UINT16 +// ---------------------------------------------------------- + +FIBITMAP * DLL_CALLCONV +FreeImage_ConvertToUINT16(FIBITMAP *dib) { + FIBITMAP *src = NULL; + FIBITMAP *dst = NULL; + + if(!FreeImage_HasPixels(dib)) return NULL; + + const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); + + // check for allowed conversions + switch(src_type) { + case FIT_BITMAP: + { + // convert to greyscale if needed + if((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) { + src = dib; + } else { + src = FreeImage_ConvertToGreyscale(dib); + if(!src) return NULL; + } + break; + } + case FIT_UINT16: + // UINT16 type : clone the src + return FreeImage_Clone(dib); + break; + case FIT_RGB16: + // allow conversion from 48-bit RGB + src = dib; + break; + case FIT_RGBA16: + // allow conversion from 64-bit RGBA (ignore the alpha channel) + src = dib; + break; + default: + return NULL; + } + + // allocate dst image + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + dst = FreeImage_AllocateT(FIT_UINT16, width, height); + if(!dst) { + if(src != dib) { + FreeImage_Unload(src); + } + return NULL; + } + + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, src); + + // convert from src type to UINT16 + + switch(src_type) { + case FIT_BITMAP: + { + for(unsigned y = 0; y < height; y++) { + const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y); + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); + for(unsigned x = 0; x < width; x++) { + dst_bits[x] = src_bits[x] << 8; + } + } + } + break; + + case FIT_RGB16: + { + for(unsigned y = 0; y < height; y++) { + const FIRGB16 *src_bits = (FIRGB16*)FreeImage_GetScanLine(src, y); + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); + for(unsigned x = 0; x < width; x++) { + // convert to grey + dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue); + } + } + } + break; + + case FIT_RGBA16: + { + for(unsigned y = 0; y < height; y++) { + const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y); + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); + for(unsigned x = 0; x < width; x++) { + // convert to grey + dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue); + } + } + } + break; + + default: + break; + } + + if(src != dib) { + FreeImage_Unload(src); + } + + return dst; +} + diff --git a/plugins/AdvaImg/src/FreeImage/J2KHelper.cpp b/plugins/AdvaImg/src/FreeImage/J2KHelper.cpp index c9f8fa57af..1776c3bfd2 100644 --- a/plugins/AdvaImg/src/FreeImage/J2KHelper.cpp +++ b/plugins/AdvaImg/src/FreeImage/J2KHelper.cpp @@ -1,500 +1,591 @@ -// ========================================================== -// JPEG2000 helpers -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" -#include "../LibOpenJPEG/openjpeg.h" - -/** -Divide an integer by a power of 2 and round upwards -@return Returns a divided by 2^b -*/ -static int int_ceildivpow2(int a, int b) { - return (a + (1 << b) - 1) >> b; -} - -/** -Convert a OpenJPEG image to a FIBITMAP -@param format_id Plugin ID -@param image OpenJPEG image -@return Returns the converted image if successful, returns NULL otherwise -*/ -FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image) { - FIBITMAP *dib = NULL; - - try { - // compute image width and height - - //int w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx); - int wr = image->comps[0].w; - int wrr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor); - - //int h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy); - //int hr = image->comps[0].h; - int hrr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor); - - // check the number of components - - int numcomps = image->numcomps; - - BOOL bIsValid = TRUE; - for(int c = 0; c < numcomps - 1; c++) { - if( (image->comps[c].dx == image->comps[c+1].dx) && - (image->comps[c].dy == image->comps[c+1].dy) && - (image->comps[c].prec == image->comps[c+1].prec) ) { - continue; - } else { - bIsValid = FALSE; - break; - } - } - bIsValid &= ((numcomps == 1) || (numcomps == 3) || (numcomps == 4)); - if(!bIsValid) { - if(numcomps) { - FreeImage_OutputMessageProc(format_id, "Warning: image contains %d greyscale components. Only the first will be loaded.\n", numcomps); - numcomps = 1; - } else { - // unknown type - throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; - } - } - - // create a new DIB - - if(image->comps[0].prec <= 8) { - switch(numcomps) { - case 1: - dib = FreeImage_Allocate(wrr, hrr, 8); - break; - case 3: - dib = FreeImage_Allocate(wrr, hrr, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - break; - case 4: - dib = FreeImage_Allocate(wrr, hrr, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - break; - } - } else if(image->comps[0].prec <= 16) { - switch(numcomps) { - case 1: - dib = FreeImage_AllocateT(FIT_UINT16, wrr, hrr); - break; - case 3: - dib = FreeImage_AllocateT(FIT_RGB16, wrr, hrr); - break; - case 4: - dib = FreeImage_AllocateT(FIT_RGBA16, wrr, hrr); - break; - } - } else { - throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; - } - if(!dib) { - throw FI_MSG_ERROR_DIB_MEMORY; - } - - if(image->comps[0].prec <= 8) { - if(numcomps == 1) { - // 8-bit greyscale - // ---------------------------------------------------------- - - // build a greyscale palette - - RGBQUAD *pal = FreeImage_GetPalette(dib); - for (int i = 0; i < 256; i++) { - pal[i].rgbRed = (BYTE)i; - pal[i].rgbGreen = (BYTE)i; - pal[i].rgbBlue = (BYTE)i; - } - - // load pixel data - - unsigned pixel_count = 0; - - for(int y = 0; y < hrr; y++) { - BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); - - for(int x = 0; x < wrr; x++) { - const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; - - int index = image->comps[0].data[pixel_pos]; - index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); - - bits[x] = (BYTE)index; - - pixel_count++; - } - } - } - else if(numcomps == 3) { - - // 24-bit RGB - // ---------------------------------------------------------- - - // load pixel data - - unsigned pixel_count = 0; - - for(int y = 0; y < hrr; y++) { - BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); - - for(int x = 0; x < wrr; x++) { - const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; - - int r = image->comps[0].data[pixel_pos]; - r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); - - int g = image->comps[1].data[pixel_pos]; - g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); - - int b = image->comps[2].data[pixel_pos]; - b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); - - bits[FI_RGBA_RED] = (BYTE)r; - bits[FI_RGBA_GREEN] = (BYTE)g; - bits[FI_RGBA_BLUE] = (BYTE)b; - bits += 3; - - pixel_count++; - } - } - } - else if(numcomps == 4) { - - // 32-bit RGBA - // ---------------------------------------------------------- - - // load pixel data - - unsigned pixel_count = 0; - - for(int y = 0; y < hrr; y++) { - BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); - - for(int x = 0; x < wrr; x++) { - const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; - - int r = image->comps[0].data[pixel_pos]; - r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); - - int g = image->comps[1].data[pixel_pos]; - g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); - - int b = image->comps[2].data[pixel_pos]; - b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); - - int a = image->comps[3].data[pixel_pos]; - a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); - - bits[FI_RGBA_RED] = (BYTE)r; - bits[FI_RGBA_GREEN] = (BYTE)g; - bits[FI_RGBA_BLUE] = (BYTE)b; - bits[FI_RGBA_ALPHA] = (BYTE)a; - bits += 4; - - pixel_count++; - } - } - } - } - else if(image->comps[0].prec <= 16) { - if(numcomps == 1) { - // 16-bit greyscale - // ---------------------------------------------------------- - - // load pixel data - - unsigned pixel_count = 0; - - for(int y = 0; y < hrr; y++) { - unsigned short *bits = (unsigned short*)FreeImage_GetScanLine(dib, hrr - 1 - y); - - for(int x = 0; x < wrr; x++) { - const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; - - int index = image->comps[0].data[pixel_pos]; - index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); - - bits[x] = (unsigned short)index; - - pixel_count++; - } - } - } - else if(numcomps == 3) { - - // 48-bit RGB - // ---------------------------------------------------------- - - // load pixel data - - unsigned pixel_count = 0; - - for(int y = 0; y < hrr; y++) { - FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, hrr - 1 - y); - - for(int x = 0; x < wrr; x++) { - const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; - - int r = image->comps[0].data[pixel_pos]; - r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); - - int g = image->comps[1].data[pixel_pos]; - g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); - - int b = image->comps[2].data[pixel_pos]; - b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); - - bits[x].red = (WORD)r; - bits[x].green = (WORD)g; - bits[x].blue = (WORD)b; - - pixel_count++; - } - } - } - else if(numcomps == 4) { - - // 64-bit RGBA - // ---------------------------------------------------------- - - // load pixel data - - unsigned pixel_count = 0; - - for(int y = 0; y < hrr; y++) { - FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, hrr - 1 - y); - - for(int x = 0; x < wrr; x++) { - const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; - - int r = image->comps[0].data[pixel_pos]; - r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); - - int g = image->comps[1].data[pixel_pos]; - g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); - - int b = image->comps[2].data[pixel_pos]; - b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); - - int a = image->comps[3].data[pixel_pos]; - a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); - - bits[x].red = (WORD)r; - bits[x].green = (WORD)g; - bits[x].blue = (WORD)b; - bits[x].alpha = (WORD)a; - - pixel_count++; - } - } - } - } - - return dib; - - } catch(const char *text) { - if(dib) FreeImage_Unload(dib); - FreeImage_OutputMessageProc(format_id, text); - return NULL; - } - -} - -/** -Convert a FIBITMAP to a OpenJPEG image -@param format_id Plugin ID -@param dib FreeImage image -@param parameters Compression parameters -@return Returns the converted image if successful, returns NULL otherwise -*/ -opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters) { - int prec, numcomps, x, y, index; - OPJ_COLOR_SPACE color_space; - opj_image_cmptparm_t cmptparm[4]; // maximum of 4 components - opj_image_t *image = NULL; // image to encode - - try { - int w = FreeImage_GetWidth(dib); - int h = FreeImage_GetHeight(dib); - - // get image characteristics - FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); - - if(image_type == FIT_BITMAP) { - // standard image ... - prec = 8; - switch(FreeImage_GetColorType(dib)) { - case FIC_MINISBLACK: - numcomps = 1; - color_space = CLRSPC_GRAY; - break; - case FIC_RGB: - if(FreeImage_GetBPP(dib) == 32) { - // 32-bit image with a fully opaque layer - numcomps = 4; - color_space = CLRSPC_SRGB; - } else { - // 24-bit image - numcomps = 3; - color_space = CLRSPC_SRGB; - } - break; - case FIC_RGBALPHA: - numcomps = 4; - color_space = CLRSPC_SRGB; - break; - default: - return NULL; - } - } else { - // HDR image ... - prec = 16; - switch(image_type) { - case FIT_UINT16: - numcomps = 1; - color_space = CLRSPC_GRAY; - break; - case FIT_RGB16: - numcomps = 3; - color_space = CLRSPC_SRGB; - break; - case FIT_RGBA16: - numcomps = 4; - color_space = CLRSPC_SRGB; - break; - default: - return NULL; - } - } - - // initialize image components - memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t)); - for(int i = 0; i < numcomps; i++) { - cmptparm[i].dx = parameters->subsampling_dx; - cmptparm[i].dy = parameters->subsampling_dy; - cmptparm[i].w = w; - cmptparm[i].h = h; - cmptparm[i].prec = prec; - cmptparm[i].bpp = prec; - cmptparm[i].sgnd = 0; - } - // create the image - image = opj_image_create(numcomps, &cmptparm[0], color_space); - if(!image) { - throw FI_MSG_ERROR_DIB_MEMORY; - } - - // set image offset and reference grid - image->x0 = parameters->image_offset_x0; - image->y0 = parameters->image_offset_y0; - image->x1 = parameters->image_offset_x0 + (w - 1) * parameters->subsampling_dx + 1; - image->y1 = parameters->image_offset_y0 + (h - 1) * parameters->subsampling_dy + 1; - - // set image data - if(prec == 8) { - switch(numcomps) { - case 1: - index = 0; - for(y = 0; y < h; y++) { - BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); - for(x = 0; x < w; x++) { - image->comps[0].data[index] = bits[x]; - index++; - } - } - break; - case 3: - index = 0; - for(y = 0; y < h; y++) { - BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); - for(x = 0; x < w; x++) { - image->comps[0].data[index] = bits[FI_RGBA_RED]; - image->comps[1].data[index] = bits[FI_RGBA_GREEN]; - image->comps[2].data[index] = bits[FI_RGBA_BLUE]; - bits += 3; - index++; - } - } - break; - case 4: - index = 0; - for(y = 0; y < h; y++) { - BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); - for(x = 0; x < w; x++) { - image->comps[0].data[index] = bits[FI_RGBA_RED]; - image->comps[1].data[index] = bits[FI_RGBA_GREEN]; - image->comps[2].data[index] = bits[FI_RGBA_BLUE]; - image->comps[3].data[index] = bits[FI_RGBA_ALPHA]; - bits += 4; - index++; - } - } - break; - } - } - else if(prec == 16) { - switch(numcomps) { - case 1: - index = 0; - for(y = 0; y < h; y++) { - WORD *bits = (WORD*)FreeImage_GetScanLine(dib, h - 1 - y); - for(x = 0; x < w; x++) { - image->comps[0].data[index] = bits[x]; - index++; - } - } - break; - case 3: - index = 0; - for(y = 0; y < h; y++) { - FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, h - 1 - y); - for(x = 0; x < w; x++) { - image->comps[0].data[index] = bits[x].red; - image->comps[1].data[index] = bits[x].green; - image->comps[2].data[index] = bits[x].blue; - index++; - } - } - break; - case 4: - index = 0; - for(y = 0; y < h; y++) { - FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, h - 1 - y); - for(x = 0; x < w; x++) { - image->comps[0].data[index] = bits[x].red; - image->comps[1].data[index] = bits[x].green; - image->comps[2].data[index] = bits[x].blue; - image->comps[3].data[index] = bits[x].alpha; - index++; - } - } - break; - } - } - - return image; - - } catch (const char *text) { - if(image) opj_image_destroy(image); - FreeImage_OutputMessageProc(format_id, text); - return NULL; - } -} +// ========================================================== +// JPEG2000 helpers +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "../LibOpenJPEG/openjpeg.h" +#include "J2KHelper.h" + +// -------------------------------------------------------------------------- + +static OPJ_UINT64 +_LengthProc(J2KFIO_t *fio) { + long start_pos = fio->io->tell_proc(fio->handle); + fio->io->seek_proc(fio->handle, 0, SEEK_END); + unsigned file_length = fio->io->tell_proc(fio->handle) - start_pos; + fio->io->seek_proc(fio->handle, start_pos, SEEK_SET); + return (OPJ_UINT64)file_length; +} + +static OPJ_SIZE_T +_ReadProc(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { + J2KFIO_t *fio = (J2KFIO_t*)p_user_data; + OPJ_SIZE_T l_nb_read = fio->io->read_proc(p_buffer, 1, (unsigned)p_nb_bytes, fio->handle); + return l_nb_read ? l_nb_read : (OPJ_SIZE_T)-1; +} + +static OPJ_SIZE_T +_WriteProc(void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data) { + J2KFIO_t *fio = (J2KFIO_t*)p_user_data; + return fio->io->write_proc(p_buffer, 1, (unsigned)p_nb_bytes, fio->handle); +} + +static OPJ_OFF_T +_SkipProc(OPJ_OFF_T p_nb_bytes, void *p_user_data) { + J2KFIO_t *fio = (J2KFIO_t*)p_user_data; + if( fio->io->seek_proc(fio->handle, (long)p_nb_bytes, SEEK_CUR) ) { + return -1; + } + return p_nb_bytes; +} + +static OPJ_BOOL +_SeekProc(OPJ_OFF_T p_nb_bytes, FILE * p_user_data) { + J2KFIO_t *fio = (J2KFIO_t*)p_user_data; + if( fio->io->seek_proc(fio->handle, (long)p_nb_bytes, SEEK_SET) ) { + return OPJ_FALSE; + } + return OPJ_TRUE; +} + +// -------------------------------------------------------------------------- + +J2KFIO_t* +opj_freeimage_stream_create(FreeImageIO *io, fi_handle handle, BOOL bRead) { + if(!handle) { + return NULL; + } + J2KFIO_t *fio = (J2KFIO_t*)malloc(sizeof(J2KFIO_t)); + if(fio) { + fio->io = io; + fio->handle = handle; + + opj_stream_t *l_stream = opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE, bRead ? OPJ_TRUE : OPJ_FALSE); + if (l_stream) { + opj_stream_set_user_data(l_stream, fio, NULL); + opj_stream_set_user_data_length(l_stream, _LengthProc(fio)); + opj_stream_set_read_function(l_stream, (opj_stream_read_fn)_ReadProc); + opj_stream_set_write_function(l_stream, (opj_stream_write_fn)_WriteProc); + opj_stream_set_skip_function(l_stream, (opj_stream_skip_fn)_SkipProc); + opj_stream_set_seek_function(l_stream, (opj_stream_seek_fn)_SeekProc); + fio->stream = l_stream; + return fio; + } else { + free(fio); + } + } + + return NULL; +} + +void +opj_freeimage_stream_destroy(J2KFIO_t* fio) { + if(fio) { + if(fio->stream) { + opj_stream_destroy(fio->stream); + } + free(fio); + } +} + +// -------------------------------------------------------------------------- + +/** +Divide an integer by a power of 2 and round upwards +@return Returns a divided by 2^b +*/ +static int int_ceildivpow2(int a, int b) { + return (a + (1 << b) - 1) >> b; +} + +/** +Convert a OpenJPEG image to a FIBITMAP +@param format_id Plugin ID +@param image OpenJPEG image +@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP +@return Returns the converted image if successful, returns NULL otherwise +*/ +FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image, BOOL header_only) { + FIBITMAP *dib = NULL; + + try { + // compute image width and height + + //int w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx); + int wr = image->comps[0].w; + int wrr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor); + + //int h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy); + //int hr = image->comps[0].h; + int hrr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor); + + // check the number of components + + int numcomps = image->numcomps; + + BOOL bIsValid = TRUE; + for(int c = 0; c < numcomps - 1; c++) { + if( (image->comps[c].dx == image->comps[c+1].dx) && + (image->comps[c].dy == image->comps[c+1].dy) && + (image->comps[c].prec == image->comps[c+1].prec) ) { + continue; + } else { + bIsValid = FALSE; + break; + } + } + bIsValid &= ((numcomps == 1) || (numcomps == 3) || (numcomps == 4)); + if(!bIsValid) { + if(numcomps) { + FreeImage_OutputMessageProc(format_id, "Warning: image contains %d greyscale components. Only the first will be loaded.\n", numcomps); + numcomps = 1; + } else { + // unknown type + throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; + } + } + + // create a new DIB + + if(image->comps[0].prec <= 8) { + switch(numcomps) { + case 1: + dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 8); + break; + case 3: + dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + break; + case 4: + dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + break; + } + } else if(image->comps[0].prec <= 16) { + switch(numcomps) { + case 1: + dib = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, wrr, hrr); + break; + case 3: + dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, wrr, hrr); + break; + case 4: + dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBA16, wrr, hrr); + break; + } + } else { + throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; + } + if(!dib) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + // "header only" FIBITMAP ? + if(header_only) { + return dib; + } + + if(image->comps[0].prec <= 8) { + if(numcomps == 1) { + // 8-bit greyscale + // ---------------------------------------------------------- + + // build a greyscale palette + + RGBQUAD *pal = FreeImage_GetPalette(dib); + for (int i = 0; i < 256; i++) { + pal[i].rgbRed = (BYTE)i; + pal[i].rgbGreen = (BYTE)i; + pal[i].rgbBlue = (BYTE)i; + } + + // load pixel data + + unsigned pixel_count = 0; + + for(int y = 0; y < hrr; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); + + for(int x = 0; x < wrr; x++) { + const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; + + int index = image->comps[0].data[pixel_pos]; + index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + + bits[x] = (BYTE)index; + + pixel_count++; + } + } + } + else if(numcomps == 3) { + + // 24-bit RGB + // ---------------------------------------------------------- + + // load pixel data + + unsigned pixel_count = 0; + + for(int y = 0; y < hrr; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); + + for(int x = 0; x < wrr; x++) { + const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; + + int r = image->comps[0].data[pixel_pos]; + r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + + int g = image->comps[1].data[pixel_pos]; + g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + + int b = image->comps[2].data[pixel_pos]; + b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); + + bits[FI_RGBA_RED] = (BYTE)r; + bits[FI_RGBA_GREEN] = (BYTE)g; + bits[FI_RGBA_BLUE] = (BYTE)b; + bits += 3; + + pixel_count++; + } + } + } + else if(numcomps == 4) { + + // 32-bit RGBA + // ---------------------------------------------------------- + + // load pixel data + + unsigned pixel_count = 0; + + for(int y = 0; y < hrr; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); + + for(int x = 0; x < wrr; x++) { + const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; + + int r = image->comps[0].data[pixel_pos]; + r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + + int g = image->comps[1].data[pixel_pos]; + g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + + int b = image->comps[2].data[pixel_pos]; + b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); + + int a = image->comps[3].data[pixel_pos]; + a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); + + bits[FI_RGBA_RED] = (BYTE)r; + bits[FI_RGBA_GREEN] = (BYTE)g; + bits[FI_RGBA_BLUE] = (BYTE)b; + bits[FI_RGBA_ALPHA] = (BYTE)a; + bits += 4; + + pixel_count++; + } + } + } + } + else if(image->comps[0].prec <= 16) { + if(numcomps == 1) { + // 16-bit greyscale + // ---------------------------------------------------------- + + // load pixel data + + unsigned pixel_count = 0; + + for(int y = 0; y < hrr; y++) { + WORD *bits = (WORD*)FreeImage_GetScanLine(dib, hrr - 1 - y); + + for(int x = 0; x < wrr; x++) { + const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; + + int index = image->comps[0].data[pixel_pos]; + index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + + bits[x] = (WORD)index; + + pixel_count++; + } + } + } + else if(numcomps == 3) { + + // 48-bit RGB + // ---------------------------------------------------------- + + // load pixel data + + unsigned pixel_count = 0; + + for(int y = 0; y < hrr; y++) { + FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, hrr - 1 - y); + + for(int x = 0; x < wrr; x++) { + const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; + + int r = image->comps[0].data[pixel_pos]; + r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + + int g = image->comps[1].data[pixel_pos]; + g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + + int b = image->comps[2].data[pixel_pos]; + b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); + + bits[x].red = (WORD)r; + bits[x].green = (WORD)g; + bits[x].blue = (WORD)b; + + pixel_count++; + } + } + } + else if(numcomps == 4) { + + // 64-bit RGBA + // ---------------------------------------------------------- + + // load pixel data + + unsigned pixel_count = 0; + + for(int y = 0; y < hrr; y++) { + FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, hrr - 1 - y); + + for(int x = 0; x < wrr; x++) { + const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; + + int r = image->comps[0].data[pixel_pos]; + r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + + int g = image->comps[1].data[pixel_pos]; + g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + + int b = image->comps[2].data[pixel_pos]; + b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); + + int a = image->comps[3].data[pixel_pos]; + a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); + + bits[x].red = (WORD)r; + bits[x].green = (WORD)g; + bits[x].blue = (WORD)b; + bits[x].alpha = (WORD)a; + + pixel_count++; + } + } + } + } + + return dib; + + } catch(const char *text) { + if(dib) FreeImage_Unload(dib); + FreeImage_OutputMessageProc(format_id, text); + return NULL; + } + +} + +/** +Convert a FIBITMAP to a OpenJPEG image +@param format_id Plugin ID +@param dib FreeImage image +@param parameters Compression parameters +@return Returns the converted image if successful, returns NULL otherwise +*/ +opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters) { + int prec, numcomps, x, y, index; + OPJ_COLOR_SPACE color_space; + opj_image_cmptparm_t cmptparm[4]; // maximum of 4 components + opj_image_t *image = NULL; // image to encode + + try { + int w = FreeImage_GetWidth(dib); + int h = FreeImage_GetHeight(dib); + + // get image characteristics + FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + + if(image_type == FIT_BITMAP) { + // standard image ... + prec = 8; + switch(FreeImage_GetColorType(dib)) { + case FIC_MINISBLACK: + numcomps = 1; + color_space = OPJ_CLRSPC_GRAY; + break; + case FIC_RGB: + if(FreeImage_GetBPP(dib) == 32) { + // 32-bit image with a fully opaque layer + numcomps = 4; + color_space = OPJ_CLRSPC_SRGB; + } else { + // 24-bit image + numcomps = 3; + color_space = OPJ_CLRSPC_SRGB; + } + break; + case FIC_RGBALPHA: + numcomps = 4; + color_space = OPJ_CLRSPC_SRGB; + break; + default: + return NULL; + } + } else { + // HDR image ... + prec = 16; + switch(image_type) { + case FIT_UINT16: + numcomps = 1; + color_space = OPJ_CLRSPC_GRAY; + break; + case FIT_RGB16: + numcomps = 3; + color_space = OPJ_CLRSPC_SRGB; + break; + case FIT_RGBA16: + numcomps = 4; + color_space = OPJ_CLRSPC_SRGB; + break; + default: + return NULL; + } + } + + // initialize image components + memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t)); + for(int i = 0; i < numcomps; i++) { + cmptparm[i].dx = parameters->subsampling_dx; + cmptparm[i].dy = parameters->subsampling_dy; + cmptparm[i].w = w; + cmptparm[i].h = h; + cmptparm[i].prec = prec; + cmptparm[i].bpp = prec; + cmptparm[i].sgnd = 0; + } + // create the image + image = opj_image_create(numcomps, &cmptparm[0], color_space); + if(!image) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + // set image offset and reference grid + image->x0 = parameters->image_offset_x0; + image->y0 = parameters->image_offset_y0; + image->x1 = parameters->image_offset_x0 + (w - 1) * parameters->subsampling_dx + 1; + image->y1 = parameters->image_offset_y0 + (h - 1) * parameters->subsampling_dy + 1; + + // set image data + if(prec == 8) { + switch(numcomps) { + case 1: + index = 0; + for(y = 0; y < h; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); + for(x = 0; x < w; x++) { + image->comps[0].data[index] = bits[x]; + index++; + } + } + break; + case 3: + index = 0; + for(y = 0; y < h; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); + for(x = 0; x < w; x++) { + image->comps[0].data[index] = bits[FI_RGBA_RED]; + image->comps[1].data[index] = bits[FI_RGBA_GREEN]; + image->comps[2].data[index] = bits[FI_RGBA_BLUE]; + bits += 3; + index++; + } + } + break; + case 4: + index = 0; + for(y = 0; y < h; y++) { + BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); + for(x = 0; x < w; x++) { + image->comps[0].data[index] = bits[FI_RGBA_RED]; + image->comps[1].data[index] = bits[FI_RGBA_GREEN]; + image->comps[2].data[index] = bits[FI_RGBA_BLUE]; + image->comps[3].data[index] = bits[FI_RGBA_ALPHA]; + bits += 4; + index++; + } + } + break; + } + } + else if(prec == 16) { + switch(numcomps) { + case 1: + index = 0; + for(y = 0; y < h; y++) { + WORD *bits = (WORD*)FreeImage_GetScanLine(dib, h - 1 - y); + for(x = 0; x < w; x++) { + image->comps[0].data[index] = bits[x]; + index++; + } + } + break; + case 3: + index = 0; + for(y = 0; y < h; y++) { + FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, h - 1 - y); + for(x = 0; x < w; x++) { + image->comps[0].data[index] = bits[x].red; + image->comps[1].data[index] = bits[x].green; + image->comps[2].data[index] = bits[x].blue; + index++; + } + } + break; + case 4: + index = 0; + for(y = 0; y < h; y++) { + FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, h - 1 - y); + for(x = 0; x < w; x++) { + image->comps[0].data[index] = bits[x].red; + image->comps[1].data[index] = bits[x].green; + image->comps[2].data[index] = bits[x].blue; + image->comps[3].data[index] = bits[x].alpha; + index++; + } + } + break; + } + } + + return image; + + } catch (const char *text) { + if(image) opj_image_destroy(image); + FreeImage_OutputMessageProc(format_id, text); + return NULL; + } +} diff --git a/plugins/AdvaImg/src/FreeImage/J2KHelper.h b/plugins/AdvaImg/src/FreeImage/J2KHelper.h new file mode 100644 index 0000000000..a8121f164e --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/J2KHelper.h @@ -0,0 +1,36 @@ +#ifndef J2K_HELPER_H +#define J2K_HELPER_H + +// ========================================================== +// Helper functions (see J2KHelper.cpp) +// ========================================================== + +/** +FreeImageIO wrapper +*/ +typedef struct tagJ2KFIO_t { + FreeImageIO *io; //! FreeImage IO + fi_handle handle; //! FreeImage handle + opj_stream_t *stream; //! OpenJPEG stream +} J2KFIO_t; + +/** +Stream constructor +*/ +J2KFIO_t* opj_freeimage_stream_create(FreeImageIO *io, fi_handle handle, BOOL bRead); + +/** +Stream destructor +*/ +void opj_freeimage_stream_destroy(J2KFIO_t* fio); + +/** +Conversion opj_image_t => FIBITMAP +*/ +FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image, BOOL header_only); +/** +Conversion FIBITMAP => opj_image_t +*/ +opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters); + +#endif // J2K_HELPER_H \ No newline at end of file diff --git a/plugins/AdvaImg/src/FreeImage/MultiPage.cpp b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp index 60ce85eb10..d48e11be94 100644 --- a/plugins/AdvaImg/src/FreeImage/MultiPage.cpp +++ b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp @@ -1,990 +1,990 @@ -// ========================================================== -// Multi-Page functions -// -// Design and implementation by -// - Floris van den Berg (flvdberg@wxs.nl) -// - Laurent Rocher (rocherl@club-internet.fr) -// - Steve Johnson (steve@parisgroup.net) -// - Petr Pytelka (pyta@lightcomp.com) -// - Hervé Drolon (drolon@infonie.fr) -// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net -// - Martin Dyring-Andersen (mda@spamfighter.com) -// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#ifdef _MSC_VER -#pragma warning (disable : 4786) // identifier was truncated to 'number' characters -#endif - -#include "CacheFile.h" -#include "FreeImageIO.h" -#include "Plugin.h" -#include "Utilities.h" -#include "FreeImage.h" - -// ---------------------------------------------------------- - -enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE }; - -// ---------------------------------------------------------- - -struct BlockTypeS { - BlockType m_type; - - BlockTypeS(BlockType type) : m_type(type) { - } - virtual ~BlockTypeS() {} -}; - -struct BlockContinueus : public BlockTypeS { - int m_start; - int m_end; - - BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS), - m_start(s), - m_end(e) { - } -}; - -struct BlockReference : public BlockTypeS { - int m_reference; - int m_size; - - BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE), - m_reference(r), - m_size(size) { - } -}; - -// ---------------------------------------------------------- - -typedef std::list BlockList; -typedef std::list::iterator BlockListIterator; - -// ---------------------------------------------------------- - -FI_STRUCT (MULTIBITMAPHEADER) { - PluginNode *node; - FREE_IMAGE_FORMAT fif; - FreeImageIO *io; - fi_handle handle; - CacheFile *m_cachefile; - std::map 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 io (new FreeImageIO); - - SetDefaultIO(io.get()); - - if (!create_new) { - handle = fopen(filename, "rb"); - if (handle == NULL) { - return NULL; - } - } - - std::auto_ptr bitmap (new FIMULTIBITMAP); - std::auto_ptr 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 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 bitmap (new FIMULTIBITMAP); - std::auto_ptr header (new MULTIBITMAPHEADER); - std::auto_ptr 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 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::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::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; -} +// ========================================================== +// Multi-Page functions +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Laurent Rocher (rocherl@club-internet.fr) +// - Steve Johnson (steve@parisgroup.net) +// - Petr Pytelka (pyta@lightcomp.com) +// - Hervé Drolon (drolon@infonie.fr) +// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net +// - Martin Dyring-Andersen (mda@spamfighter.com) +// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#ifdef _MSC_VER +#pragma warning (disable : 4786) // identifier was truncated to 'number' characters +#endif + +#include "CacheFile.h" +#include "FreeImageIO.h" +#include "Plugin.h" +#include "Utilities.h" +#include "FreeImage.h" + +// ---------------------------------------------------------- + +enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE }; + +// ---------------------------------------------------------- + +struct BlockTypeS { + BlockType m_type; + + BlockTypeS(BlockType type) : m_type(type) { + } + virtual ~BlockTypeS() {} +}; + +struct BlockContinueus : public BlockTypeS { + int m_start; + int m_end; + + BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS), + m_start(s), + m_end(e) { + } +}; + +struct BlockReference : public BlockTypeS { + int m_reference; + int m_size; + + BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE), + m_reference(r), + m_size(size) { + } +}; + +// ---------------------------------------------------------- + +typedef std::list BlockList; +typedef std::list::iterator BlockListIterator; + +// ---------------------------------------------------------- + +FI_STRUCT (MULTIBITMAPHEADER) { + PluginNode *node; + FREE_IMAGE_FORMAT fif; + FreeImageIO *io; + fi_handle handle; + CacheFile *m_cachefile; + std::map 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 io (new FreeImageIO); + + SetDefaultIO(io.get()); + + if (!create_new) { + handle = fopen(filename, "rb"); + if (handle == NULL) { + return NULL; + } + } + + std::auto_ptr bitmap (new FIMULTIBITMAP); + std::auto_ptr 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 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 bitmap (new FIMULTIBITMAP); + std::auto_ptr header (new MULTIBITMAPHEADER); + std::auto_ptr 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 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::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::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) { + pages[c] = i->second; + + c++; + + if (c == *count) + break; + } + } + + return TRUE; + } + + return FALSE; +} + +// ===================================================================== +// Memory IO Multipage functions +// ===================================================================== + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) { + BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache + + // retrieve the plugin list to find the node belonging to this plugin + + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if (node) { + FreeImageIO *io = new(std::nothrow) FreeImageIO; + + if (io) { + SetMemoryIO(io); + + FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP; + + if (bitmap) { + MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER; + + if (header) { + header->m_filename = NULL; + header->node = node; + header->fif = fif; + header->io = io; + header->handle = (fi_handle)stream; + header->changed = FALSE; + header->read_only = read_only; + header->m_cachefile = NULL; + header->cache_fif = fif; + header->load_flags = flags; + + // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure + + bitmap->data = header; + + // cache the page count + + header->page_count = FreeImage_InternalGetPageCount(bitmap); + + // allocate a continueus block to describe the bitmap + + header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1)); + + if (!read_only) { + // set up the cache + CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE); + + if (cache_file && cache_file->open()) { + header->m_cachefile = cache_file; + } + } + + return bitmap; + } + + delete bitmap; + } + + delete io; + } + } + } + + return NULL; +} + +BOOL DLL_CALLCONV +FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) { + if (stream && stream->data) { + FreeImageIO io; + SetMemoryIO(&io); + + return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags); + } + + return FALSE; +} diff --git a/plugins/AdvaImg/src/FreeImage/PSDParser.cpp b/plugins/AdvaImg/src/FreeImage/PSDParser.cpp index 57c703a06f..fba54c2421 100644 --- a/plugins/AdvaImg/src/FreeImage/PSDParser.cpp +++ b/plugins/AdvaImg/src/FreeImage/PSDParser.cpp @@ -506,6 +506,10 @@ bool psdParser::ReadImageResources(FreeImageIO *io, fi_handle handle, LONG lengt oResource.Reset(); n = (int)io->read_proc(&oResource._OSType, sizeof(oResource._OSType), 1, handle); + if(n != 1) { + FreeImage_OutputMessageProc(_fi_format_id, "This file contains damaged data causing an unexpected end-of-file - stop reading resources"); + return false; + } nBytes += n * sizeof(oResource._OSType); if( (nBytes % 2) != 0 ) { diff --git a/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp b/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp index e3dccfe1c9..c69463c316 100644 --- a/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp +++ b/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp @@ -1,210 +1,210 @@ -// ========================================================== -// Pixel access functions -// -// Design and implementation by -// - Floris van den Berg (flvdberg@wxs.nl) -// - Hervé Drolon (drolon@infonie.fr) -// - Ryan Rubley (ryan@lostreality.org) -// - Riley McNiff (rmcniff@marexgroup.com) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -// ---------------------------------------------------------- - -BYTE * DLL_CALLCONV -FreeImage_GetBits(FIBITMAP *dib) { - if(!FreeImage_HasPixels(dib)) { - return NULL; - } - // returns the pixels aligned on a FIBITMAP_ALIGNMENT bytes alignment boundary - size_t lp = (size_t)FreeImage_GetInfoHeader(dib); - lp += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * FreeImage_GetColorsUsed(dib); - lp += FreeImage_HasRGBMasks(dib) ? sizeof(DWORD) * 3 : 0; - lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0); - return (BYTE *)lp; -} - -BYTE * DLL_CALLCONV -FreeImage_GetScanLine(FIBITMAP *dib, int scanline) { - if(!FreeImage_HasPixels(dib)) { - return NULL; - } - return CalculateScanLine(FreeImage_GetBits(dib), FreeImage_GetPitch(dib), scanline); -} - -BOOL DLL_CALLCONV -FreeImage_GetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) { - BYTE shift; - - if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) - return FALSE; - - if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { - BYTE *bits = FreeImage_GetScanLine(dib, y); - - switch(FreeImage_GetBPP(dib)) { - case 1: - *value = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0; - break; - case 4: - shift = (BYTE)((1 - x % 2) << 2); - *value = (bits[x >> 1] & (0x0F << shift)) >> shift; - break; - case 8: - *value = bits[x]; - break; - default: - return FALSE; - } - - return TRUE; - } - - return FALSE; -} - -BOOL DLL_CALLCONV -FreeImage_GetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) { - if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) - return FALSE; - - if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { - BYTE *bits = FreeImage_GetScanLine(dib, y); - - switch(FreeImage_GetBPP(dib)) { - case 16: - { - bits += 2*x; - WORD *pixel = (WORD *)bits; - if((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { - value->rgbBlue = (BYTE)((((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F); - value->rgbGreen = (BYTE)((((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F); - value->rgbRed = (BYTE)((((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F); - value->rgbReserved = 0; - } else { - value->rgbBlue = (BYTE)((((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); - value->rgbGreen = (BYTE)((((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); - value->rgbRed = (BYTE)((((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); - value->rgbReserved = 0; - } - break; - } - case 24: - bits += 3*x; - value->rgbBlue = bits[FI_RGBA_BLUE]; // B - value->rgbGreen = bits[FI_RGBA_GREEN]; // G - value->rgbRed = bits[FI_RGBA_RED]; // R - value->rgbReserved = 0; - break; - case 32: - bits += 4*x; - value->rgbBlue = bits[FI_RGBA_BLUE]; // B - value->rgbGreen = bits[FI_RGBA_GREEN]; // G - value->rgbRed = bits[FI_RGBA_RED]; // R - value->rgbReserved = bits[FI_RGBA_ALPHA]; // A - break; - default: - return FALSE; - } - - return TRUE; - } - - return FALSE; -} - -BOOL DLL_CALLCONV -FreeImage_SetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) { - BYTE shift; - - if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) - return FALSE; - - if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { - BYTE *bits = FreeImage_GetScanLine(dib, y); - - switch(FreeImage_GetBPP(dib)) { - case 1: - *value ? bits[x >> 3] |= (0x80 >> (x & 0x7)) : bits[x >> 3] &= (0xFF7F >> (x & 0x7)); - break; - case 4: - shift = (BYTE)((1 - x % 2) << 2); - bits[x >> 1] &= ~(0x0F << shift); - bits[x >> 1] |= ((*value & 0x0F) << shift); - break; - case 8: - bits[x] = *value; - break; - default: - return FALSE; - } - - return TRUE; - } - - return FALSE; -} - -BOOL DLL_CALLCONV -FreeImage_SetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) { - if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) - return FALSE; - - if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { - BYTE *bits = FreeImage_GetScanLine(dib, y); - - switch(FreeImage_GetBPP(dib)) { - case 16: - { - bits += 2*x; - WORD *pixel = (WORD *)bits; - if((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { - *pixel = ((value->rgbBlue >> 3) << FI16_565_BLUE_SHIFT) | - ((value->rgbGreen >> 2) << FI16_565_GREEN_SHIFT) | - ((value->rgbRed >> 3) << FI16_565_RED_SHIFT); - } else { - *pixel = ((value->rgbBlue >> 3) << FI16_555_BLUE_SHIFT) | - ((value->rgbGreen >> 3) << FI16_555_GREEN_SHIFT) | - ((value->rgbRed >> 3) << FI16_555_RED_SHIFT); - } - break; - } - case 24: - bits += 3*x; - bits[FI_RGBA_BLUE] = value->rgbBlue; // B - bits[FI_RGBA_GREEN] = value->rgbGreen; // G - bits[FI_RGBA_RED] = value->rgbRed; // R - break; - case 32: - bits += 4*x; - bits[FI_RGBA_BLUE] = value->rgbBlue; // B - bits[FI_RGBA_GREEN] = value->rgbGreen; // G - bits[FI_RGBA_RED] = value->rgbRed; // R - bits[FI_RGBA_ALPHA] = value->rgbReserved; // A - break; - default: - return FALSE; - } - - return TRUE; - } - - return FALSE; -} - +// ========================================================== +// Pixel access functions +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Hervé Drolon (drolon@infonie.fr) +// - Ryan Rubley (ryan@lostreality.org) +// - Riley McNiff (rmcniff@marexgroup.com) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +// ---------------------------------------------------------- + +BYTE * DLL_CALLCONV +FreeImage_GetBits(FIBITMAP *dib) { + if(!FreeImage_HasPixels(dib)) { + return NULL; + } + // returns the pixels aligned on a FIBITMAP_ALIGNMENT bytes alignment boundary + size_t lp = (size_t)FreeImage_GetInfoHeader(dib); + lp += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * FreeImage_GetColorsUsed(dib); + lp += FreeImage_HasRGBMasks(dib) ? sizeof(DWORD) * 3 : 0; + lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0); + return (BYTE *)lp; +} + +BYTE * DLL_CALLCONV +FreeImage_GetScanLine(FIBITMAP *dib, int scanline) { + if(!FreeImage_HasPixels(dib)) { + return NULL; + } + return CalculateScanLine(FreeImage_GetBits(dib), FreeImage_GetPitch(dib), scanline); +} + +BOOL DLL_CALLCONV +FreeImage_GetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) { + BYTE shift; + + if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) + return FALSE; + + if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { + BYTE *bits = FreeImage_GetScanLine(dib, y); + + switch(FreeImage_GetBPP(dib)) { + case 1: + *value = (bits[x >> 3] & (0x80 >> (x & 0x07))) != 0; + break; + case 4: + shift = (BYTE)((1 - x % 2) << 2); + *value = (bits[x >> 1] & (0x0F << shift)) >> shift; + break; + case 8: + *value = bits[x]; + break; + default: + return FALSE; + } + + return TRUE; + } + + return FALSE; +} + +BOOL DLL_CALLCONV +FreeImage_GetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) { + if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) + return FALSE; + + if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { + BYTE *bits = FreeImage_GetScanLine(dib, y); + + switch(FreeImage_GetBPP(dib)) { + case 16: + { + bits += 2*x; + WORD *pixel = (WORD *)bits; + if((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { + value->rgbBlue = (BYTE)((((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F); + value->rgbGreen = (BYTE)((((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F); + value->rgbRed = (BYTE)((((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F); + value->rgbReserved = 0; + } else { + value->rgbBlue = (BYTE)((((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); + value->rgbGreen = (BYTE)((((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); + value->rgbRed = (BYTE)((((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); + value->rgbReserved = 0; + } + break; + } + case 24: + bits += 3*x; + value->rgbBlue = bits[FI_RGBA_BLUE]; // B + value->rgbGreen = bits[FI_RGBA_GREEN]; // G + value->rgbRed = bits[FI_RGBA_RED]; // R + value->rgbReserved = 0; + break; + case 32: + bits += 4*x; + value->rgbBlue = bits[FI_RGBA_BLUE]; // B + value->rgbGreen = bits[FI_RGBA_GREEN]; // G + value->rgbRed = bits[FI_RGBA_RED]; // R + value->rgbReserved = bits[FI_RGBA_ALPHA]; // A + break; + default: + return FALSE; + } + + return TRUE; + } + + return FALSE; +} + +BOOL DLL_CALLCONV +FreeImage_SetPixelIndex(FIBITMAP *dib, unsigned x, unsigned y, BYTE *value) { + BYTE shift; + + if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) + return FALSE; + + if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { + BYTE *bits = FreeImage_GetScanLine(dib, y); + + switch(FreeImage_GetBPP(dib)) { + case 1: + *value ? bits[x >> 3] |= (0x80 >> (x & 0x7)) : bits[x >> 3] &= (0xFF7F >> (x & 0x7)); + break; + case 4: + shift = (BYTE)((1 - x % 2) << 2); + bits[x >> 1] &= ~(0x0F << shift); + bits[x >> 1] |= ((*value & 0x0F) << shift); + break; + case 8: + bits[x] = *value; + break; + default: + return FALSE; + } + + return TRUE; + } + + return FALSE; +} + +BOOL DLL_CALLCONV +FreeImage_SetPixelColor(FIBITMAP *dib, unsigned x, unsigned y, RGBQUAD *value) { + if(!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) + return FALSE; + + if((x < FreeImage_GetWidth(dib)) && (y < FreeImage_GetHeight(dib))) { + BYTE *bits = FreeImage_GetScanLine(dib, y); + + switch(FreeImage_GetBPP(dib)) { + case 16: + { + bits += 2*x; + WORD *pixel = (WORD *)bits; + if((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { + *pixel = ((value->rgbBlue >> 3) << FI16_565_BLUE_SHIFT) | + ((value->rgbGreen >> 2) << FI16_565_GREEN_SHIFT) | + ((value->rgbRed >> 3) << FI16_565_RED_SHIFT); + } else { + *pixel = ((value->rgbBlue >> 3) << FI16_555_BLUE_SHIFT) | + ((value->rgbGreen >> 3) << FI16_555_GREEN_SHIFT) | + ((value->rgbRed >> 3) << FI16_555_RED_SHIFT); + } + break; + } + case 24: + bits += 3*x; + bits[FI_RGBA_BLUE] = value->rgbBlue; // B + bits[FI_RGBA_GREEN] = value->rgbGreen; // G + bits[FI_RGBA_RED] = value->rgbRed; // R + break; + case 32: + bits += 4*x; + bits[FI_RGBA_BLUE] = value->rgbBlue; // B + bits[FI_RGBA_GREEN] = value->rgbGreen; // G + bits[FI_RGBA_RED] = value->rgbRed; // R + bits[FI_RGBA_ALPHA] = value->rgbReserved; // A + break; + default: + return FALSE; + } + + return TRUE; + } + + return FALSE; +} + diff --git a/plugins/AdvaImg/src/FreeImage/Plugin.cpp b/plugins/AdvaImg/src/FreeImage/Plugin.cpp index 7ded36ebc2..6f88e47e68 100644 --- a/plugins/AdvaImg/src/FreeImage/Plugin.cpp +++ b/plugins/AdvaImg/src/FreeImage/Plugin.cpp @@ -218,7 +218,12 @@ FreeImage_GetPluginList() { 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(); @@ -257,7 +262,7 @@ FreeImage_Initialise(BOOL load_local_plugins_only) { //s_plugins->AddNode(InitXPM); //s_plugins->AddNode(InitDDS); s_plugins->AddNode(InitGIF); - //s_plugins->AddNode(InitHDR); + //s_plugins->AddNode(InitHDR); //s_plugins->AddNode(InitG3); //s_plugins->AddNode(InitSGI); //s_plugins->AddNode(InitEXR); @@ -266,6 +271,74 @@ FreeImage_Initialise(BOOL load_local_plugins_only) { //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]; + char 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 (GetCurrentDirectoryA(2 * _MAX_PATH, current_dir) != 0) { + if (GetModuleFileNameA(NULL, module, 2 * _MAX_PATH) != 0) { + char *last_point = strrchr(module, '\\'); + + if (last_point) { + *last_point = '\0'; + + bOk = SetCurrentDirectoryA(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) { + SetCurrentDirectoryA(current_dir); + } + } +#endif // _WIN32 } } } diff --git a/plugins/AdvaImg/src/FreeImage/PluginBMP.cpp b/plugins/AdvaImg/src/FreeImage/PluginBMP.cpp index 4041d859fa..0ee1c5db30 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginBMP.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginBMP.cpp @@ -35,10 +35,13 @@ static const BYTE RLE_ENDOFLINE = 0; static const BYTE RLE_ENDOFBITMAP = 1; static const BYTE RLE_DELTA = 2; -static const BYTE BI_RGB = 0; -static const BYTE BI_RLE8 = 1; -static const BYTE BI_RLE4 = 2; -static const BYTE BI_BITFIELDS = 3; +static const BYTE BI_RGB = 0; // compression: none +static const BYTE BI_RLE8 = 1; // compression: RLE 8-bit/pixel +static const BYTE BI_RLE4 = 2; // compression: RLE 4-bit/pixel +static const BYTE BI_BITFIELDS = 3; // compression: Bit field or Huffman 1D compression for BITMAPCOREHEADER2 +static const BYTE BI_JPEG = 4; // compression: JPEG or RLE-24 compression for BITMAPCOREHEADER2 +static const BYTE BI_PNG = 5; // compression: PNG +static const BYTE BI_ALPHABITFIELDS = 6; // compression: Bit field (this value is valid in Windows CE .NET 4.0 and later) // ---------------------------------------------------------- @@ -65,11 +68,11 @@ typedef struct tagBITMAPINFOOS2_1X_HEADER { } BITMAPINFOOS2_1X_HEADER, *PBITMAPINFOOS2_1X_HEADER; typedef struct tagBITMAPFILEHEADER { - WORD bfType; - DWORD bfSize; - WORD bfReserved1; - WORD bfReserved2; - DWORD bfOffBits; + WORD bfType; //! The file type + DWORD bfSize; //! The size, in bytes, of the bitmap file + WORD bfReserved1; //! Reserved; must be zero + WORD bfReserved2; //! Reserved; must be zero + DWORD bfOffBits; //! The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits } BITMAPFILEHEADER, *PBITMAPFILEHEADER; #ifdef _WIN32 @@ -452,7 +455,7 @@ LoadPixelDataRLE8(FreeImageIO *io, fi_handle handle, int width, int height, FIBI // -------------------------------------------------------------------------- static FIBITMAP * -LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) { +LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset, int type) { FIBITMAP *dib = NULL; try { @@ -481,8 +484,9 @@ LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bit case 4 : case 8 : { - if ((used_colors == 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) + 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 @@ -494,6 +498,19 @@ LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bit // 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 @@ -512,10 +529,8 @@ LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bit // 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); - if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * sizeof(RGBQUAD)))) - io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); - // read the pixel data switch (compression) { @@ -551,11 +566,15 @@ LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bit case 16 : { - if (bih.biCompression == BI_BITFIELDS) { - DWORD bitfields[3]; - - io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle); - + 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); @@ -573,14 +592,11 @@ LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bit // 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 - - if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) { - // seek to the actual pixel data - io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); - } - LoadPixelData(io, handle, dib, height, pitch, bit_count); return dib; @@ -590,11 +606,15 @@ LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bit case 24 : case 32 : { - if (bih.biCompression == BI_BITFIELDS) { - DWORD bitfields[3]; - - io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle); - + 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 ) { @@ -619,12 +639,10 @@ LoadWindowsBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bit // 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) - if (FreeImage_GetColorsUsed(dib) > 0) { - io->seek_proc(handle, FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD), SEEK_CUR); - } else if ((bih.biCompression != BI_BITFIELDS) && (bitmap_bits_offset > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER))) { - io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); - } + // 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 @@ -735,8 +753,9 @@ LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_ // 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))) + if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) { io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); + } // read the pixel data @@ -827,8 +846,9 @@ LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_ // 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))) + 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 @@ -1101,24 +1121,18 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { case 12: // OS/2 and also all Windows versions since Windows 3.0 return LoadOS21XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits); + case 64: // OS/2 return LoadOS22XBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits); - case 40: - // BITMAPINFOHEADER - all Windows versions since Windows 3.0 - return LoadWindowsBMP(io, handle, flags, offset_in_file + bitmapfileheader.bfOffBits); - case 52: - // BITMAPV2INFOHEADER (undocumented) - break; - case 56: - // BITMAPV3INFOHEADER (undocumented) - break; - case 108: - // BITMAPV4HEADER - all Windows versions since Windows 95/NT4 (not supported) - break; - case 124: - // BITMAPV5HEADER - Windows 98/2000 and newer (not supported) - break; + + 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; } diff --git a/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp b/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp index e8d84afac4..87c0185865 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp @@ -376,7 +376,7 @@ bool StringTable::Decompress(BYTE *buf, int *len) m_partial >>= m_codeSize; m_partialSize -= m_codeSize; - if( code > m_nextCode || (m_nextCode == MAX_LZW_CODE && code != m_clearCode) || code == m_endCode ) { + if( code > m_nextCode || /*(m_nextCode == MAX_LZW_CODE && code != m_clearCode) || */code == m_endCode ) { m_done = true; *len = (int)(bufpos - buf); return true; @@ -387,7 +387,7 @@ bool StringTable::Decompress(BYTE *buf, int *len) } //add new string to string table, if not the first pass since a clear code - if( m_oldCode != MAX_LZW_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]; } diff --git a/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp b/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp index d8c24adc31..0cde6139db 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp @@ -73,7 +73,7 @@ typedef enum { rgbe_read_error, rgbe_write_error, rgbe_format_error, - rgbe_memory_error, + rgbe_memory_error } rgbe_error_code; // ---------------------------------------------------------- diff --git a/plugins/AdvaImg/src/FreeImage/PluginICO.cpp b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp index 7f41a0d841..df5ecee91d 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginICO.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp @@ -339,7 +339,8 @@ LoadStandardIcon(FreeImageIO *io, fi_handle handle, int flags, BOOL header_only) // bitmap has been loaded successfully! // convert to 32bpp and generate an alpha channel - if((flags & ICO_MAKEALPHA) == ICO_MAKEALPHA) { + // 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); diff --git a/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp b/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp index ae8a6ae88a..5c23a7c6e1 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp @@ -1,339 +1,327 @@ -// ========================================================== -// JPEG2000 J2K codestream Loader and Writer -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" -#include "../LibOpenJPEG/openjpeg.h" - -// ========================================================== -// Plugin Interface -// ========================================================== - -static int s_format_id; - -// ========================================================== -// Helper functions (see J2KHelper.cpp) -// ========================================================== - -FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image); -opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters); - -// ========================================================== -// Internal functions -// ========================================================== - -/** -OpenJPEG Error callback -*/ -static void j2k_error_callback(const char *msg, void *client_data) { - FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg); -} -/** -OpenJPEG Warning callback -*/ -static void j2k_warning_callback(const char *msg, void *client_data) { - FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg); -} - -// ========================================================== -// Plugin Implementation -// ========================================================== - -static const char * DLL_CALLCONV -Format() { - return "J2K"; -} - -static const char * DLL_CALLCONV -Description() { - return "JPEG-2000 codestream"; -} - -static const char * DLL_CALLCONV -Extension() { - return "j2k,j2c"; -} - -static const char * DLL_CALLCONV -RegExpr() { - return NULL; -} - -static const char * DLL_CALLCONV -MimeType() { - return "image/j2k"; -} - -static BOOL DLL_CALLCONV -Validate(FreeImageIO *io, fi_handle handle) { - BYTE jpc_signature[] = { 0xFF, 0x4F }; - BYTE signature[2] = { 0, 0 }; - - long tell = io->tell_proc(handle); - io->read_proc(signature, 1, sizeof(jpc_signature), handle); - io->seek_proc(handle, tell, SEEK_SET); - - return (memcmp(jpc_signature, signature, sizeof(jpc_signature)) == 0); -} - -static BOOL DLL_CALLCONV -SupportsExportDepth(int depth) { - return ( - (depth == 8) || - (depth == 24) || - (depth == 32) - ); -} - -static BOOL DLL_CALLCONV -SupportsExportType(FREE_IMAGE_TYPE type) { - return ( - (type == FIT_BITMAP) || - (type == FIT_UINT16) || - (type == FIT_RGB16) || - (type == FIT_RGBA16) - ); -} - -// ---------------------------------------------------------- - -static void * DLL_CALLCONV -Open(FreeImageIO *io, fi_handle handle, BOOL read) { - return NULL; -} - -static void DLL_CALLCONV -Close(FreeImageIO *io, fi_handle handle, void *data) { -} - -// ---------------------------------------------------------- - -static FIBITMAP * DLL_CALLCONV -Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { - if (handle) { - opj_dparameters_t parameters; // decompression parameters - opj_event_mgr_t event_mgr; // event manager - opj_image_t *image = NULL; // decoded image - - BYTE *src = NULL; - long file_length; - - opj_dinfo_t* dinfo = NULL; // handle to a decompressor - opj_cio_t *cio = NULL; - - FIBITMAP *dib = NULL; - - // check the file format - if(!Validate(io, handle)) { - return NULL; - } - - // configure the event callbacks - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = j2k_error_callback; - event_mgr.warning_handler = j2k_warning_callback; - event_mgr.info_handler = NULL; - - // set decoding parameters to default values - opj_set_default_decoder_parameters(¶meters); - - try { - // read the input file and put it in memory - - long start_pos = io->tell_proc(handle); - io->seek_proc(handle, 0, SEEK_END); - file_length = io->tell_proc(handle) - start_pos; - io->seek_proc(handle, start_pos, SEEK_SET); - src = (BYTE*)malloc(file_length * sizeof(BYTE)); - if(!src) { - throw FI_MSG_ERROR_MEMORY; - } - if(io->read_proc(src, 1, file_length, handle) < 1) { - throw "Error while reading input stream"; - } - - // decode the JPEG-2000 codestream - - // get a decoder handle - dinfo = opj_create_decompress(CODEC_J2K); - - // catch events using our callbacks - opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, NULL); - - // setup the decoder decoding parameters using user parameters - opj_setup_decoder(dinfo, ¶meters); - - // open a byte stream - cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length); - - // decode the stream and fill the image structure - image = opj_decode(dinfo, cio); - if(!image) { - throw "Failed to decode image!\n"; - } - - // close the byte stream - opj_cio_close(cio); - cio = NULL; - - // free the memory containing the code-stream - free(src); - src = NULL; - - // free the codec context - opj_destroy_decompress(dinfo); - - // create output image - dib = J2KImageToFIBITMAP(s_format_id, image); - if(!dib) throw "Failed to import JPEG2000 image"; - - // free image data structure - opj_image_destroy(image); - - return dib; - - } catch (const char *text) { - if(src) free(src); - if(dib) FreeImage_Unload(dib); - // free remaining structures - opj_destroy_decompress(dinfo); - opj_image_destroy(image); - // close the byte stream - if(cio) opj_cio_close(cio); - - FreeImage_OutputMessageProc(s_format_id, text); - - return NULL; - } - } - - return NULL; -} - -static BOOL DLL_CALLCONV -Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { - if ((dib) && (handle)) { - BOOL bSuccess; - opj_cparameters_t parameters; // compression parameters - opj_event_mgr_t event_mgr; // event manager - opj_image_t *image = NULL; // image to encode - opj_cinfo_t* cinfo = NULL; // codec context - opj_cio_t *cio = NULL; // memory byte stream - - // configure the event callbacks - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = j2k_error_callback; - event_mgr.warning_handler = j2k_warning_callback; - event_mgr.info_handler = NULL; - - // set encoding parameters to default values - opj_set_default_encoder_parameters(¶meters); - - parameters.tcp_numlayers = 0; - // if no rate entered, apply a 16:1 rate by default - if(flags == J2K_DEFAULT) { - parameters.tcp_rates[0] = (float)16; - } else { - // for now, the flags parameter is only used to specify the rate - parameters.tcp_rates[0] = (float)flags; - } - parameters.tcp_numlayers++; - parameters.cp_disto_alloc = 1; - - try { - // convert the dib to a OpenJPEG image - image = FIBITMAPToJ2KImage(s_format_id, dib, ¶meters); - if(!image) return FALSE; - - // decide if MCT should be used - parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0; - - // encode the destination image - - // get a J2K compressor handle - cinfo = opj_create_compress(CODEC_J2K); - - // catch events using our callbacks - opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, NULL); - - // setup the encoder parameters using the current image and using user parameters - opj_setup_encoder(cinfo, ¶meters, image); - - // open a byte stream for writing, allocate memory for all tiles - cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0); - - // encode the image - bSuccess = opj_encode(cinfo, cio, image, NULL/*parameters.index*/); - if (!bSuccess) { - throw "Failed to encode image"; - } - int codestream_length = cio_tell(cio); - - // write the buffer to user's IO handle - io->write_proc(cio->buffer, 1, codestream_length, handle); - - // close and free the byte stream - opj_cio_close(cio); - - // free remaining compression structures - opj_destroy_compress(cinfo); - - // free image data - opj_image_destroy(image); - - return TRUE; - - } catch (const char *text) { - if(cio) opj_cio_close(cio); - if(cinfo) opj_destroy_compress(cinfo); - if(image) opj_image_destroy(image); - FreeImage_OutputMessageProc(s_format_id, text); - return FALSE; - } - } - - return FALSE; -} - -// ========================================================== -// Init -// ========================================================== - -void DLL_CALLCONV -InitJ2K(Plugin *plugin, int format_id) { - s_format_id = format_id; - - plugin->format_proc = Format; - plugin->description_proc = Description; - plugin->extension_proc = Extension; - plugin->regexpr_proc = RegExpr; - plugin->open_proc = Open; - plugin->close_proc = Close; - plugin->pagecount_proc = NULL; - plugin->pagecapability_proc = NULL; - plugin->load_proc = Load; - plugin->save_proc = Save; - plugin->validate_proc = Validate; - plugin->mime_proc = MimeType; - plugin->supports_export_bpp_proc = SupportsExportDepth; - plugin->supports_export_type_proc = SupportsExportType; - plugin->supports_icc_profiles_proc = NULL; -} +// ========================================================== +// JPEG2000 J2K codestream Loader and Writer +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "../LibOpenJPEG/openjpeg.h" +#include "J2KHelper.h" + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Internal functions +// ========================================================== + +/** +OpenJPEG Error callback +*/ +static void j2k_error_callback(const char *msg, void *client_data) { + FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg); +} +/** +OpenJPEG Warning callback +*/ +static void j2k_warning_callback(const char *msg, void *client_data) { + FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg); +} + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static const char * DLL_CALLCONV +Format() { + return "J2K"; +} + +static const char * DLL_CALLCONV +Description() { + return "JPEG-2000 codestream"; +} + +static const char * DLL_CALLCONV +Extension() { + return "j2k,j2c"; +} + +static const char * DLL_CALLCONV +RegExpr() { + return NULL; +} + +static const char * DLL_CALLCONV +MimeType() { + return "image/j2k"; +} + +static BOOL DLL_CALLCONV +Validate(FreeImageIO *io, fi_handle handle) { + BYTE jpc_signature[] = { 0xFF, 0x4F }; + BYTE signature[2] = { 0, 0 }; + + long tell = io->tell_proc(handle); + io->read_proc(signature, 1, sizeof(jpc_signature), handle); + io->seek_proc(handle, tell, SEEK_SET); + + return (memcmp(jpc_signature, signature, sizeof(jpc_signature)) == 0); +} + +static BOOL DLL_CALLCONV +SupportsExportDepth(int depth) { + return ( + (depth == 8) || + (depth == 24) || + (depth == 32) + ); +} + +static BOOL DLL_CALLCONV +SupportsExportType(FREE_IMAGE_TYPE type) { + return ( + (type == FIT_BITMAP) || + (type == FIT_UINT16) || + (type == FIT_RGB16) || + (type == FIT_RGBA16) + ); +} + +// ---------------------------------------------------------- + +static void * DLL_CALLCONV +Open(FreeImageIO *io, fi_handle handle, BOOL read) { + // create the stream wrapper + J2KFIO_t *fio = opj_freeimage_stream_create(io, handle, read); + return fio; +} + +static void DLL_CALLCONV +Close(FreeImageIO *io, fi_handle handle, void *data) { + // destroy the stream wrapper + J2KFIO_t *fio = (J2KFIO_t*)data; + opj_freeimage_stream_destroy(fio); +} + +// ---------------------------------------------------------- + +static FIBITMAP * DLL_CALLCONV +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + J2KFIO_t *fio = (J2KFIO_t*)data; + if (handle && fio) { + opj_codec_t *d_codec = NULL; // handle to a decompressor + opj_dparameters_t parameters; // decompression parameters + opj_image_t *image = NULL; // decoded image + + FIBITMAP *dib = NULL; + + // check the file format + if(!Validate(io, handle)) { + return NULL; + } + + BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; + + // get the OpenJPEG stream + opj_stream_t *d_stream = fio->stream; + + // set decoding parameters to default values + opj_set_default_decoder_parameters(¶meters); + + try { + // decode the JPEG-2000 codestream + + // get a decoder handle + d_codec = opj_create_decompress(OPJ_CODEC_J2K); + + // configure the event callbacks + // catch events using our callbacks (no local context needed here) + opj_set_info_handler(d_codec, NULL, NULL); + opj_set_warning_handler(d_codec, j2k_warning_callback, NULL); + opj_set_error_handler(d_codec, j2k_error_callback, NULL); + + // setup the decoder decoding parameters using user parameters + if( !opj_setup_decoder(d_codec, ¶meters) ) { + throw "Failed to setup the decoder\n"; + } + + // read the main header of the codestream and if necessary the JP2 boxes + if( !opj_read_header(d_stream, d_codec, &image)) { + throw "Failed to read the header\n"; + } + + // --- header only mode + + if (header_only) { + // create output image + dib = J2KImageToFIBITMAP(s_format_id, image, header_only); + if(!dib) throw "Failed to import JPEG2000 image"; + // clean-up and return header data + opj_destroy_codec(d_codec); + opj_image_destroy(image); + return dib; + } + + // decode the stream and fill the image structure + if( !( opj_decode(d_codec, d_stream, image) && opj_end_decompress(d_codec, d_stream) ) ) { + throw "Failed to decode image!\n"; + } + + // free the codec context + opj_destroy_codec(d_codec); + d_codec = NULL; + + // create output image + dib = J2KImageToFIBITMAP(s_format_id, image, header_only); + if(!dib) throw "Failed to import JPEG2000 image"; + + // free image data structure + opj_image_destroy(image); + + return dib; + + } catch (const char *text) { + if(dib) FreeImage_Unload(dib); + // free remaining structures + opj_destroy_codec(d_codec); + opj_image_destroy(image); + + 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) { + J2KFIO_t *fio = (J2KFIO_t*)data; + if (dib && handle && fio) { + BOOL bSuccess; + opj_codec_t *c_codec = NULL; // handle to a compressor + opj_cparameters_t parameters; // compression parameters + opj_image_t *image = NULL; // image to encode + + // get the OpenJPEG stream + opj_stream_t *c_stream = fio->stream; + + // set encoding parameters to default values + opj_set_default_encoder_parameters(¶meters); + + try { + parameters.numresolution = 1; + // check the resolution (i.e. parameters.numresolution) + int min_size = MIN(FreeImage_GetWidth(dib), FreeImage_GetHeight(dib)); + if(min_size < (1 << parameters.numresolution)) { + throw "Invalid image size - image is too small"; + } + + parameters.tcp_numlayers = 0; + // if no rate entered, apply a 16:1 rate by default + if(flags == J2K_DEFAULT) { + parameters.tcp_rates[0] = (float)16; + } else { + // for now, the flags parameter is only used to specify the rate + parameters.tcp_rates[0] = (float)flags; + } + parameters.tcp_numlayers++; + parameters.cp_disto_alloc = 1; + + // convert the dib to a OpenJPEG image + image = FIBITMAPToJ2KImage(s_format_id, dib, ¶meters); + if(!image) return FALSE; + + // decide if MCT should be used + parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0; + + // encode the destination image + + // get a J2K compressor handle + c_codec = opj_create_compress(OPJ_CODEC_J2K); + + // configure the event callbacks + // catch events using our callbacks (no local context needed here) + opj_set_info_handler(c_codec, NULL, NULL); + opj_set_warning_handler(c_codec, j2k_warning_callback, NULL); + opj_set_error_handler(c_codec, j2k_error_callback, NULL); + + // setup the encoder parameters using the current image and using user parameters + opj_setup_encoder(c_codec, ¶meters, image); + + // encode the image + bSuccess = opj_start_compress(c_codec, image, c_stream); + if(bSuccess) { + bSuccess = bSuccess && opj_encode(c_codec, c_stream); + if(bSuccess) { + bSuccess = bSuccess && opj_end_compress(c_codec, c_stream); + } + } + if (!bSuccess) { + throw "Failed to encode image"; + } + + // free remaining compression structures + opj_destroy_codec(c_codec); + + // free image data + opj_image_destroy(image); + + return TRUE; + + } catch (const char *text) { + if(c_codec) opj_destroy_codec(c_codec); + if(image) opj_image_destroy(image); + FreeImage_OutputMessageProc(s_format_id, text); + return FALSE; + } + } + + return FALSE; +} + +// ========================================================== +// Init +// ========================================================== + +void DLL_CALLCONV +InitJ2K(Plugin *plugin, int format_id) { + s_format_id = format_id; + + plugin->format_proc = Format; + plugin->description_proc = Description; + plugin->extension_proc = Extension; + plugin->regexpr_proc = RegExpr; + plugin->open_proc = Open; + plugin->close_proc = Close; + plugin->pagecount_proc = NULL; + plugin->pagecapability_proc = NULL; + plugin->load_proc = Load; + plugin->save_proc = Save; + plugin->validate_proc = Validate; + plugin->mime_proc = MimeType; + plugin->supports_export_bpp_proc = SupportsExportDepth; + plugin->supports_export_type_proc = SupportsExportType; + plugin->supports_icc_profiles_proc = NULL; +} diff --git a/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp b/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp index 261697fb5c..edf5b396c3 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp @@ -1,339 +1,327 @@ -// ========================================================== -// JPEG2000 JP2 file format Loader and Writer -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" -#include "../LibOpenJPEG/openjpeg.h" - -// ========================================================== -// Plugin Interface -// ========================================================== - -static int s_format_id; - -// ========================================================== -// Helper functions (see J2KHelper.cpp) -// ========================================================== - -FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image); -opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters); - -// ========================================================== -// Internal functions -// ========================================================== - -/** -OpenJPEG Error callback -*/ -static void jp2_error_callback(const char *msg, void *client_data) { - FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg); -} -/** -OpenJPEG Warning callback -*/ -static void jp2_warning_callback(const char *msg, void *client_data) { - FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg); -} - -// ========================================================== -// Plugin Implementation -// ========================================================== - -static const char * DLL_CALLCONV -Format() { - return "JP2"; -} - -static const char * DLL_CALLCONV -Description() { - return "JPEG-2000 File Format"; -} - -static const char * DLL_CALLCONV -Extension() { - return "jp2"; -} - -static const char * DLL_CALLCONV -RegExpr() { - return NULL; -} - -static const char * DLL_CALLCONV -MimeType() { - return "image/jp2"; -} - -static BOOL DLL_CALLCONV -Validate(FreeImageIO *io, fi_handle handle) { - BYTE jp2_signature[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A }; - BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - - long tell = io->tell_proc(handle); - io->read_proc(signature, 1, sizeof(jp2_signature), handle); - io->seek_proc(handle, tell, SEEK_SET); - - return (memcmp(jp2_signature, signature, sizeof(jp2_signature)) == 0); -} - -static BOOL DLL_CALLCONV -SupportsExportDepth(int depth) { - return ( - (depth == 8) || - (depth == 24) || - (depth == 32) - ); -} - -static BOOL DLL_CALLCONV -SupportsExportType(FREE_IMAGE_TYPE type) { - return ( - (type == FIT_BITMAP) || - (type == FIT_UINT16) || - (type == FIT_RGB16) || - (type == FIT_RGBA16) - ); -} - -// ---------------------------------------------------------- - -static void * DLL_CALLCONV -Open(FreeImageIO *io, fi_handle handle, BOOL read) { - return NULL; -} - -static void DLL_CALLCONV -Close(FreeImageIO *io, fi_handle handle, void *data) { -} - -// ---------------------------------------------------------- - -static FIBITMAP * DLL_CALLCONV -Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { - if (handle) { - opj_dparameters_t parameters; // decompression parameters - opj_event_mgr_t event_mgr; // event manager - opj_image_t *image = NULL; // decoded image - - BYTE *src = NULL; - long file_length; - - opj_dinfo_t* dinfo = NULL; // handle to a decompressor - opj_cio_t *cio = NULL; - - FIBITMAP *dib = NULL; - - // check the file format - if(!Validate(io, handle)) { - return NULL; - } - - // configure the event callbacks - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = jp2_error_callback; - event_mgr.warning_handler = jp2_warning_callback; - event_mgr.info_handler = NULL; - - // set decoding parameters to default values - opj_set_default_decoder_parameters(¶meters); - - try { - // read the input file and put it in memory - - long start_pos = io->tell_proc(handle); - io->seek_proc(handle, 0, SEEK_END); - file_length = io->tell_proc(handle) - start_pos; - io->seek_proc(handle, start_pos, SEEK_SET); - src = (BYTE*)malloc(file_length * sizeof(BYTE)); - if(!src) { - throw FI_MSG_ERROR_MEMORY; - } - if(io->read_proc(src, 1, file_length, handle) < 1) { - throw "Error while reading input stream"; - } - - // decode the JPEG-2000 file - - // get a decoder handle - dinfo = opj_create_decompress(CODEC_JP2); - - // catch events using our callbacks - opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, NULL); - - // setup the decoder decoding parameters using user parameters - opj_setup_decoder(dinfo, ¶meters); - - // open a byte stream - cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length); - - // decode the stream and fill the image structure - image = opj_decode(dinfo, cio); - if(!image) { - throw "Failed to decode image!\n"; - } - - // close the byte stream - opj_cio_close(cio); - cio = NULL; - - // free the memory containing the code-stream - free(src); - src = NULL; - - // free the codec context - opj_destroy_decompress(dinfo); - - // create output image - dib = J2KImageToFIBITMAP(s_format_id, image); - if(!dib) throw "Failed to import JPEG2000 image"; - - // free image data structure - opj_image_destroy(image); - - return dib; - - } catch (const char *text) { - if(src) free(src); - if(dib) FreeImage_Unload(dib); - // free remaining structures - opj_destroy_decompress(dinfo); - opj_image_destroy(image); - // close the byte stream - if(cio) opj_cio_close(cio); - - FreeImage_OutputMessageProc(s_format_id, text); - - return NULL; - } - } - - return NULL; -} - -static BOOL DLL_CALLCONV -Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { - if ((dib) && (handle)) { - BOOL bSuccess; - opj_cparameters_t parameters; // compression parameters - opj_event_mgr_t event_mgr; // event manager - opj_image_t *image = NULL; // image to encode - opj_cinfo_t* cinfo = NULL; // codec context - opj_cio_t *cio = NULL; // memory byte stream - - // configure the event callbacks - memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); - event_mgr.error_handler = jp2_error_callback; - event_mgr.warning_handler = jp2_warning_callback; - event_mgr.info_handler = NULL; - - // set encoding parameters to default values - opj_set_default_encoder_parameters(¶meters); - - parameters.tcp_numlayers = 0; - // if no rate entered, apply a 16:1 rate by default - if(flags == JP2_DEFAULT) { - parameters.tcp_rates[0] = (float)16; - } else { - // for now, the flags parameter is only used to specify the rate - parameters.tcp_rates[0] = (float)flags; - } - parameters.tcp_numlayers++; - parameters.cp_disto_alloc = 1; - - try { - // convert the dib to a OpenJPEG image - image = FIBITMAPToJ2KImage(s_format_id, dib, ¶meters); - if(!image) return FALSE; - - // decide if MCT should be used - parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0; - - // encode the destination image - - // get a J2K compressor handle - cinfo = opj_create_compress(CODEC_JP2); - - // catch events using our callbacks - opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, NULL); - - // setup the encoder parameters using the current image and using user parameters - opj_setup_encoder(cinfo, ¶meters, image); - - // open a byte stream for writing, allocate memory for all tiles - cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0); - - // encode the image - bSuccess = opj_encode(cinfo, cio, image, NULL/*parameters.index*/); - if (!bSuccess) { - throw "Failed to encode image"; - } - int codestream_length = cio_tell(cio); - - // write the buffer to user's IO handle - io->write_proc(cio->buffer, 1, codestream_length, handle); - - // close and free the byte stream - opj_cio_close(cio); - - // free remaining compression structures - opj_destroy_compress(cinfo); - - // free image data - opj_image_destroy(image); - - return TRUE; - - } catch (const char *text) { - if(cio) opj_cio_close(cio); - if(cinfo) opj_destroy_compress(cinfo); - if(image) opj_image_destroy(image); - FreeImage_OutputMessageProc(s_format_id, text); - return FALSE; - } - } - - return FALSE; -} - -// ========================================================== -// Init -// ========================================================== - -void DLL_CALLCONV -InitJP2(Plugin *plugin, int format_id) { - s_format_id = format_id; - - plugin->format_proc = Format; - plugin->description_proc = Description; - plugin->extension_proc = Extension; - plugin->regexpr_proc = RegExpr; - plugin->open_proc = Open; - plugin->close_proc = Close; - plugin->pagecount_proc = NULL; - plugin->pagecapability_proc = NULL; - plugin->load_proc = Load; - plugin->save_proc = Save; - plugin->validate_proc = Validate; - plugin->mime_proc = MimeType; - plugin->supports_export_bpp_proc = SupportsExportDepth; - plugin->supports_export_type_proc = SupportsExportType; - plugin->supports_icc_profiles_proc = NULL; -} +// ========================================================== +// JPEG2000 JP2 file format Loader and Writer +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "../LibOpenJPEG/openjpeg.h" +#include "J2KHelper.h" + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// Internal functions +// ========================================================== + +/** +OpenJPEG Error callback +*/ +static void jp2_error_callback(const char *msg, void *client_data) { + FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg); +} +/** +OpenJPEG Warning callback +*/ +static void jp2_warning_callback(const char *msg, void *client_data) { + FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg); +} + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static const char * DLL_CALLCONV +Format() { + return "JP2"; +} + +static const char * DLL_CALLCONV +Description() { + return "JPEG-2000 File Format"; +} + +static const char * DLL_CALLCONV +Extension() { + return "jp2"; +} + +static const char * DLL_CALLCONV +RegExpr() { + return NULL; +} + +static const char * DLL_CALLCONV +MimeType() { + return "image/jp2"; +} + +static BOOL DLL_CALLCONV +Validate(FreeImageIO *io, fi_handle handle) { + BYTE jp2_signature[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A }; + BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + long tell = io->tell_proc(handle); + io->read_proc(signature, 1, sizeof(jp2_signature), handle); + io->seek_proc(handle, tell, SEEK_SET); + + return (memcmp(jp2_signature, signature, sizeof(jp2_signature)) == 0); +} + +static BOOL DLL_CALLCONV +SupportsExportDepth(int depth) { + return ( + (depth == 8) || + (depth == 24) || + (depth == 32) + ); +} + +static BOOL DLL_CALLCONV +SupportsExportType(FREE_IMAGE_TYPE type) { + return ( + (type == FIT_BITMAP) || + (type == FIT_UINT16) || + (type == FIT_RGB16) || + (type == FIT_RGBA16) + ); +} + +// ---------------------------------------------------------- + +static void * DLL_CALLCONV +Open(FreeImageIO *io, fi_handle handle, BOOL read) { + // create the stream wrapper + J2KFIO_t *fio = opj_freeimage_stream_create(io, handle, read); + return fio; +} + +static void DLL_CALLCONV +Close(FreeImageIO *io, fi_handle handle, void *data) { + // destroy the stream wrapper + J2KFIO_t *fio = (J2KFIO_t*)data; + opj_freeimage_stream_destroy(fio); +} + +// ---------------------------------------------------------- + +static FIBITMAP * DLL_CALLCONV +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + J2KFIO_t *fio = (J2KFIO_t*)data; + if (handle && fio) { + opj_codec_t *d_codec = NULL; // handle to a decompressor + opj_dparameters_t parameters; // decompression parameters + opj_image_t *image = NULL; // decoded image + + FIBITMAP *dib = NULL; + + // check the file format + if(!Validate(io, handle)) { + return NULL; + } + + BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; + + // get the OpenJPEG stream + opj_stream_t *d_stream = fio->stream; + + // set decoding parameters to default values + opj_set_default_decoder_parameters(¶meters); + + try { + // decode the JPEG-2000 file + + // get a decoder handle + d_codec = opj_create_decompress(OPJ_CODEC_JP2); + + // configure the event callbacks + // catch events using our callbacks (no local context needed here) + opj_set_info_handler(d_codec, NULL, NULL); + opj_set_warning_handler(d_codec, jp2_warning_callback, NULL); + opj_set_error_handler(d_codec, jp2_error_callback, NULL); + + // setup the decoder decoding parameters using user parameters + if( !opj_setup_decoder(d_codec, ¶meters) ) { + throw "Failed to setup the decoder\n"; + } + + // read the main header of the codestream and if necessary the JP2 boxes + if( !opj_read_header(d_stream, d_codec, &image)) { + throw "Failed to read the header\n"; + } + + // --- header only mode + + if (header_only) { + // create output image + dib = J2KImageToFIBITMAP(s_format_id, image, header_only); + if(!dib) throw "Failed to import JPEG2000 image"; + // clean-up and return header data + opj_destroy_codec(d_codec); + opj_image_destroy(image); + return dib; + } + + // decode the stream and fill the image structure + if( !( opj_decode(d_codec, d_stream, image) && opj_end_decompress(d_codec, d_stream) ) ) { + throw "Failed to decode image!\n"; + } + + // free the codec context + opj_destroy_codec(d_codec); + d_codec = NULL; + + // create output image + dib = J2KImageToFIBITMAP(s_format_id, image, header_only); + if(!dib) throw "Failed to import JPEG2000 image"; + + // free image data structure + opj_image_destroy(image); + + return dib; + + } catch (const char *text) { + if(dib) FreeImage_Unload(dib); + // free remaining structures + opj_destroy_codec(d_codec); + opj_image_destroy(image); + + 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) { + J2KFIO_t *fio = (J2KFIO_t*)data; + if (dib && handle && fio) { + BOOL bSuccess; + opj_codec_t *c_codec = NULL; // handle to a compressor + opj_cparameters_t parameters; // compression parameters + opj_image_t *image = NULL; // image to encode + + // get the OpenJPEG stream + opj_stream_t *c_stream = fio->stream; + + // set encoding parameters to default values + opj_set_default_encoder_parameters(¶meters); + + try { + parameters.numresolution = 1; + // check the resolution (i.e. parameters.numresolution) + int min_size = MIN(FreeImage_GetWidth(dib), FreeImage_GetHeight(dib)); + if(min_size < (1 << parameters.numresolution)) { + throw "Invalid image size - image is too small"; + } + + parameters.tcp_numlayers = 0; + // if no rate entered, apply a 16:1 rate by default + if(flags == J2K_DEFAULT) { + parameters.tcp_rates[0] = (float)16; + } else { + // for now, the flags parameter is only used to specify the rate + parameters.tcp_rates[0] = (float)flags; + } + parameters.tcp_numlayers++; + parameters.cp_disto_alloc = 1; + + // convert the dib to a OpenJPEG image + image = FIBITMAPToJ2KImage(s_format_id, dib, ¶meters); + if(!image) return FALSE; + + // decide if MCT should be used + parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0; + + // encode the destination image + + // get a JP2 compressor handle + c_codec = opj_create_compress(OPJ_CODEC_JP2); + + // configure the event callbacks + // catch events using our callbacks (no local context needed here) + opj_set_info_handler(c_codec, NULL, NULL); + opj_set_warning_handler(c_codec, jp2_warning_callback, NULL); + opj_set_error_handler(c_codec, jp2_error_callback, NULL); + + // setup the encoder parameters using the current image and using user parameters + opj_setup_encoder(c_codec, ¶meters, image); + + // encode the image + bSuccess = opj_start_compress(c_codec, image, c_stream); + if(bSuccess) { + bSuccess = bSuccess && opj_encode(c_codec, c_stream); + if(bSuccess) { + bSuccess = bSuccess && opj_end_compress(c_codec, c_stream); + } + } + if (!bSuccess) { + throw "Failed to encode image"; + } + + // free remaining compression structures + opj_destroy_codec(c_codec); + + // free image data + opj_image_destroy(image); + + return TRUE; + + } catch (const char *text) { + if(c_codec) opj_destroy_codec(c_codec); + if(image) opj_image_destroy(image); + FreeImage_OutputMessageProc(s_format_id, text); + return FALSE; + } + } + + return FALSE; +} + +// ========================================================== +// Init +// ========================================================== + +void DLL_CALLCONV +InitJP2(Plugin *plugin, int format_id) { + s_format_id = format_id; + + plugin->format_proc = Format; + plugin->description_proc = Description; + plugin->extension_proc = Extension; + plugin->regexpr_proc = RegExpr; + plugin->open_proc = Open; + plugin->close_proc = Close; + plugin->pagecount_proc = NULL; + plugin->pagecapability_proc = NULL; + plugin->load_proc = Load; + plugin->save_proc = Save; + plugin->validate_proc = Validate; + plugin->mime_proc = MimeType; + plugin->supports_export_bpp_proc = SupportsExportDepth; + plugin->supports_export_type_proc = SupportsExportType; + plugin->supports_icc_profiles_proc = NULL; +} diff --git a/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp b/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp index 1e0237d115..c1b45e6347 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp @@ -655,46 +655,6 @@ jpeg_read_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) return FALSE; } -/** - Read JPEG_APP1 marker (Exif profile) - @param dib Input FIBITMAP - @param dataptr Pointer to the APP1 marker - @param datalen APP1 marker length - @return Returns TRUE if successful, FALSE otherwise -*/ -static BOOL -jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned int length) { - // marker identifying string for Exif = "Exif\0\0" - BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; - - // verify the identifying string - if(memcmp(exif_signature, profile, sizeof(exif_signature)) != 0) { - // not an Exif profile - return FALSE; - } - - // create a tag - FITAG *tag = FreeImage_CreateTag(); - if(tag) { - FreeImage_SetTagID(tag, EXIF_MARKER); // (JPEG_APP0 + 1) => EXIF marker / Adobe XMP marker - FreeImage_SetTagKey(tag, g_TagLib_ExifRawFieldName); - FreeImage_SetTagLength(tag, (DWORD)length); - FreeImage_SetTagCount(tag, (DWORD)length); - FreeImage_SetTagType(tag, FIDT_BYTE); - FreeImage_SetTagValue(tag, profile); - - // store the tag - FreeImage_SetMetadata(FIMD_EXIF_RAW, dib, FreeImage_GetTagKey(tag), tag); - - // destroy the tag - FreeImage_DeleteTag(tag); - - return TRUE; - } - - return FALSE; -} - /** Read JFIF "JFXX" extension APP0 marker @param dib Input FIBITMAP @@ -976,14 +936,14 @@ jpeg_write_exif_profile_raw(j_compress_ptr cinfo, FIBITMAP *dib) { 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 TRUE; } return FALSE; @@ -1126,66 +1086,6 @@ store_size_info(FIBITMAP *dib, JDIMENSION width, JDIMENSION height) { } } -// ------------------------------------------------------------ -// Rotate a dib according to Exif info -// ------------------------------------------------------------ - -static void -rotate_exif(FIBITMAP **dib) { - // check for Exif rotation - if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, *dib)) { - FIBITMAP *rotated = NULL; - // process Exif rotation - FITAG *tag = NULL; - FreeImage_GetMetadata(FIMD_EXIF_MAIN, *dib, "Orientation", &tag); - if(tag != NULL) { - if(FreeImage_GetTagID(tag) == TAG_ORIENTATION) { - unsigned short orientation = *((unsigned short *)FreeImage_GetTagValue(tag)); - switch (orientation) { - case 1: // "top, left side" => 0° - break; - case 2: // "top, right side" => flip left-right - FreeImage_FlipHorizontal(*dib); - break; - case 3: // "bottom, right side"; => -180° - rotated = FreeImage_Rotate(*dib, 180); - FreeImage_Unload(*dib); - *dib = rotated; - break; - case 4: // "bottom, left side" => flip up-down - FreeImage_FlipVertical(*dib); - break; - case 5: // "left side, top" => +90° + flip up-down - rotated = FreeImage_Rotate(*dib, 90); - FreeImage_Unload(*dib); - *dib = rotated; - FreeImage_FlipVertical(*dib); - break; - case 6: // "right side, top" => -90° - rotated = FreeImage_Rotate(*dib, -90); - FreeImage_Unload(*dib); - *dib = rotated; - break; - case 7: // "right side, bottom" => -90° + flip up-down - rotated = FreeImage_Rotate(*dib, -90); - FreeImage_Unload(*dib); - *dib = rotated; - FreeImage_FlipVertical(*dib); - break; - case 8: // "left side, bottom" => +90° - rotated = FreeImage_Rotate(*dib, 90); - FreeImage_Unload(*dib); - *dib = rotated; - break; - default: - break; - } - } - } - } -} - - // ========================================================== // Plugin Implementation // ========================================================== @@ -1473,7 +1373,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // check for automatic Exif rotation if(!header_only && ((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE)) { - rotate_exif(&dib); + RotateExif(&dib); } // everything went well. return the loaded dib diff --git a/plugins/AdvaImg/src/FreeImage/PluginJXR.cpp b/plugins/AdvaImg/src/FreeImage/PluginJXR.cpp new file mode 100644 index 0000000000..f5e4878c1d --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/PluginJXR.cpp @@ -0,0 +1,1309 @@ +// ========================================================== +// JPEG XR Loader & Writer +// +// Design and implementation by +// - Herve Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "../Metadata/FreeImageTag.h" + +#include "../LibJXR/jxrgluelib/JXRGlue.h" + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ========================================================== +// FreeImageIO interface (I/O streaming functions) +// ========================================================== + +/** +JXR wrapper for FreeImage I/O handle +*/ +typedef struct tagFreeImageJXRIO { + FreeImageIO *io; + fi_handle handle; +} FreeImageJXRIO; + +static ERR +_jxr_io_Read(WMPStream* pWS, void* pv, size_t cb) { + FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj; + return (fio->io->read_proc(pv, (unsigned)cb, 1, fio->handle) == 1) ? WMP_errSuccess : WMP_errFileIO; +} + +static ERR +_jxr_io_Write(WMPStream* pWS, const void* pv, size_t cb) { + FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj; + if(0 != cb) { + return (fio->io->write_proc((void*)pv, (unsigned)cb, 1, fio->handle) == 1) ? WMP_errSuccess : WMP_errFileIO; + } + return WMP_errFileIO; +} + +static ERR +_jxr_io_SetPos(WMPStream* pWS, size_t offPos) { + FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj; + return (fio->io->seek_proc(fio->handle, (long)offPos, SEEK_SET) == 0) ? WMP_errSuccess : WMP_errFileIO; +} + +static ERR +_jxr_io_GetPos(WMPStream* pWS, size_t* poffPos) { + FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj; + long lOff = fio->io->tell_proc(fio->handle); + if(lOff == -1) { + return WMP_errFileIO; + } + *poffPos = (size_t)lOff; + return WMP_errSuccess; +} + +static Bool +_jxr_io_EOS(WMPStream* pWS) { + FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj; + long currentPos = fio->io->tell_proc(fio->handle); + fio->io->seek_proc(fio->handle, 0, SEEK_END); + long fileRemaining = fio->io->tell_proc(fio->handle) - currentPos; + fio->io->seek_proc(fio->handle, currentPos, SEEK_SET); + return (fileRemaining > 0); +} + +static ERR +_jxr_io_Close(WMPStream** ppWS) { + WMPStream *pWS = *ppWS; + // HACK : we use fMem to avoid a stream destruction by the library + // because FreeImage MUST HAVE the ownership of the stream + // see _jxr_io_Create + if(pWS && pWS->fMem) { + free(pWS); + *ppWS = NULL; + } + return WMP_errSuccess; +} + +static ERR +_jxr_io_Create(WMPStream **ppWS, FreeImageJXRIO *jxr_io) { + *ppWS = (WMPStream*)calloc(1, sizeof(**ppWS)); + if(*ppWS) { + WMPStream *pWS = *ppWS; + + pWS->state.pvObj = jxr_io; + pWS->Close = _jxr_io_Close; + pWS->EOS = _jxr_io_EOS; + pWS->Read = _jxr_io_Read; + pWS->Write = _jxr_io_Write; + pWS->SetPos = _jxr_io_SetPos; + pWS->GetPos = _jxr_io_GetPos; + + // HACK : we use fMem to avoid a stream destruction by the library + // because FreeImage MUST HAVE the ownership of the stream + // see _jxr_io_Close + pWS->fMem = FALSE; + + return WMP_errSuccess; + } + return WMP_errOutOfMemory; +} + +// ========================================================== +// JPEG XR Error handling +// ========================================================== + +static const char* +JXR_ErrorMessage(const int error) { + switch(error) { + case WMP_errNotYetImplemented: + case WMP_errAbstractMethod: + return "Not yet implemented"; + case WMP_errOutOfMemory: + return "Out of memory"; + case WMP_errFileIO: + return "File I/O error"; + case WMP_errBufferOverflow: + return "Buffer overflow"; + case WMP_errInvalidParameter: + return "Invalid parameter"; + case WMP_errInvalidArgument: + return "Invalid argument"; + case WMP_errUnsupportedFormat: + return "Unsupported format"; + case WMP_errIncorrectCodecVersion: + return "Incorrect codec version"; + case WMP_errIndexNotFound: + return "Format converter: Index not found"; + case WMP_errOutOfSequence: + return "Metadata: Out of sequence"; + case WMP_errMustBeMultipleOf16LinesUntilLastCall: + return "Must be multiple of 16 lines until last call"; + case WMP_errPlanarAlphaBandedEncRequiresTempFile: + return "Planar alpha banded encoder requires temp files"; + case WMP_errAlphaModeCannotBeTranscoded: + return "Alpha mode cannot be transcoded"; + case WMP_errIncorrectCodecSubVersion: + return "Incorrect codec subversion"; + case WMP_errFail: + case WMP_errNotInitialized: + default: + return "Invalid instruction - please contact the FreeImage team"; + } + + return NULL; +} + +// ========================================================== +// Helper functions & macro +// ========================================================== + +#define JXR_CHECK(error_code) \ + if(error_code < 0) { \ + const char *error_message = JXR_ErrorMessage(error_code); \ + throw error_message; \ + } + +// -------------------------------------------------------------------------- + +/** +Input conversions natively understood by FreeImage +@see GetNativePixelFormat +*/ +typedef struct tagJXRInputConversion { + BITDEPTH_BITS bdBitDepth; + U32 cbitUnit; + FREE_IMAGE_TYPE image_type; + unsigned red_mask; + unsigned green_mask; + unsigned blue_mask; +} JXRInputConversion; + +/** +Conversion table for native FreeImage formats +@see GetNativePixelFormat +*/ +static JXRInputConversion s_FreeImagePixelInfo[] = { + // 1-bit bitmap + { BD_1, 1, FIT_BITMAP, 0, 0, 0 }, + // 8-, 24-, 32-bit bitmap + { BD_8, 8, FIT_BITMAP, 0, 0, 0 }, + { BD_8, 24, FIT_BITMAP, 0, 0, 0 }, + { BD_8, 32, FIT_BITMAP, 0, 0, 0 }, + // 16-bit RGB 565 + { BD_565, 16, FIT_BITMAP, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK }, + // 16-bit RGB 555 + { BD_5, 16, FIT_BITMAP, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK }, + // 16-bit greyscale, RGB16, RGBA16 bitmap + { BD_16, 16, FIT_UINT16, 0, 0, 0 }, + { BD_16, 48, FIT_RGB16, 0, 0, 0 }, + { BD_16, 64, FIT_RGBA16, 0, 0, 0 }, + // 32-bit float, RGBF, RGBAF bitmap + { BD_32F, 32, FIT_FLOAT, 0, 0, 0 }, + { BD_32F, 96, FIT_RGBF, 0, 0, 0 }, + { BD_32F, 128, FIT_RGBAF, 0, 0, 0 } +}; + +/** +Scan input pixelInfo specifications and return the equivalent FreeImage info for loading +@param pixelInfo Image specifications +@param out_guid_format (returned value) output pixel format +@param image_type (returned value) Image type +@param bpp (returned value) Image bit depth +@param red_mask (returned value) RGB mask +@param green_mask (returned value) RGB mask +@param blue_mask (returned value) RGB mask +@return Returns WMP_errSuccess if successful, returns WMP_errFail otherwise +@see GetInputPixelFormat +*/ +static ERR +GetNativePixelFormat(const PKPixelInfo *pixelInfo, PKPixelFormatGUID *out_guid_format, FREE_IMAGE_TYPE *image_type, unsigned *bpp, unsigned *red_mask, unsigned *green_mask, unsigned *blue_mask) { + const unsigned s_FreeImagePixelInfoSize = (unsigned)sizeof(s_FreeImagePixelInfo) / sizeof(*(s_FreeImagePixelInfo)); + + for(unsigned i = 0; i < s_FreeImagePixelInfoSize; i++) { + if(pixelInfo->bdBitDepth == s_FreeImagePixelInfo[i].bdBitDepth) { + if(pixelInfo->cbitUnit == s_FreeImagePixelInfo[i].cbitUnit) { + // found ! now get dst image format specifications + memcpy(out_guid_format, pixelInfo->pGUIDPixFmt, sizeof(PKPixelFormatGUID)); + *image_type = s_FreeImagePixelInfo[i].image_type; + *bpp = s_FreeImagePixelInfo[i].cbitUnit; + *red_mask = s_FreeImagePixelInfo[i].red_mask; + *green_mask = s_FreeImagePixelInfo[i].green_mask; + *blue_mask = s_FreeImagePixelInfo[i].blue_mask; + return WMP_errSuccess; + } + } + } + + // not found : need pixel format conversion + return WMP_errFail; +} + +/** +Scan input file guid format and return the equivalent FreeImage info & target guid format for loading +@param pDecoder Decoder handle +@param guid_format (returned value) Output pixel format +@param image_type (returned value) Image type +@param bpp (returned value) Image bit depth +@param red_mask (returned value) RGB mask +@param green_mask (returned value) RGB mask +@param blue_mask (returned value) RGB mask +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static ERR +GetInputPixelFormat(PKImageDecode *pDecoder, PKPixelFormatGUID *guid_format, FREE_IMAGE_TYPE *image_type, unsigned *bpp, unsigned *red_mask, unsigned *green_mask, unsigned *blue_mask) { + ERR error_code = 0; // error code as returned by the interface + PKPixelInfo pixelInfo; // image specifications + + try { + // get input file pixel format ... + PKPixelFormatGUID pguidSourcePF; + error_code = pDecoder->GetPixelFormat(pDecoder, &pguidSourcePF); + JXR_CHECK(error_code); + pixelInfo.pGUIDPixFmt = &pguidSourcePF; + // ... check for a supported format and get the format specifications + error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD); + JXR_CHECK(error_code); + + // search for an equivalent native FreeImage format + error_code = GetNativePixelFormat(&pixelInfo, guid_format, image_type, bpp, red_mask, green_mask, blue_mask); + + if(error_code != WMP_errSuccess) { + // try to find a suitable conversion function ... + const PKPixelFormatGUID *ppguidTargetPF = NULL; // target pixel format + unsigned iIndex = 0; // first available conversion function + do { + error_code = PKFormatConverter_EnumConversions(&pguidSourcePF, iIndex, &ppguidTargetPF); + if(error_code == WMP_errSuccess) { + // found a conversion function, is the converted format a native FreeImage format ? + pixelInfo.pGUIDPixFmt = ppguidTargetPF; + error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD); + JXR_CHECK(error_code); + error_code = GetNativePixelFormat(&pixelInfo, guid_format, image_type, bpp, red_mask, green_mask, blue_mask); + if(error_code == WMP_errSuccess) { + break; + } + } + // try next conversion function + iIndex++; + } while(error_code != WMP_errIndexNotFound); + + } + + return (error_code == WMP_errSuccess) ? WMP_errSuccess : WMP_errUnsupportedFormat; + + } catch(...) { + return error_code; + } +} + +// -------------------------------------------------------------------------- + +/** +Scan input dib format and return the equivalent PKPixelFormatGUID format for saving +@param dib Image to be saved +@param guid_format (returned value) GUID format +@param bHasAlpha (returned value) TRUE if an alpha layer is present +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static ERR +GetOutputPixelFormat(FIBITMAP *dib, PKPixelFormatGUID *guid_format, BOOL *bHasAlpha) { + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + const unsigned bpp = FreeImage_GetBPP(dib); + const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); + + *guid_format = GUID_PKPixelFormatDontCare; + *bHasAlpha = FALSE; + + switch(image_type) { + case FIT_BITMAP: // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit + switch(bpp) { + case 1: + // assume FIC_MINISBLACK + if(color_type == FIC_MINISBLACK) { + *guid_format = GUID_PKPixelFormatBlackWhite; + } + break; + case 8: + // assume FIC_MINISBLACK + if(color_type == FIC_MINISBLACK) { + *guid_format = GUID_PKPixelFormat8bppGray; + } + break; + case 16: + if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { + *guid_format = GUID_PKPixelFormat16bppRGB565; + } else { + // includes case where all the masks are 0 + *guid_format = GUID_PKPixelFormat16bppRGB555; + } + break; +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + case 24: + *guid_format = GUID_PKPixelFormat24bppBGR; + break; + case 32: + *guid_format = GUID_PKPixelFormat32bppBGRA; + *bHasAlpha = TRUE; + break; +#elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + case 24: + *guid_format = GUID_PKPixelFormat24bppRGB; + break; + case 32: + *guid_format = GUID_PKPixelFormat32bppRGBA; + *bHasAlpha = TRUE; + break; +#endif + case 4: + default: + // not supported + break; + } + break; + case FIT_UINT16: // array of unsigned short : unsigned 16-bit + *guid_format = GUID_PKPixelFormat16bppGray; + break; + case FIT_FLOAT: // array of float : 32-bit IEEE floating point + *guid_format = GUID_PKPixelFormat32bppGrayFloat; + break; + case FIT_RGB16: // 48-bit RGB image : 3 x 16-bit + *guid_format = GUID_PKPixelFormat48bppRGB; + break; + case FIT_RGBA16: // 64-bit RGBA image : 4 x 16-bit + *guid_format = GUID_PKPixelFormat64bppRGBA; + *bHasAlpha = TRUE; + break; + case FIT_RGBF: // 96-bit RGB float image : 3 x 32-bit IEEE floating point + *guid_format = GUID_PKPixelFormat96bppRGBFloat; + break; + case FIT_RGBAF: // 128-bit RGBA float image : 4 x 32-bit IEEE floating point + *guid_format = GUID_PKPixelFormat128bppRGBAFloat; + *bHasAlpha = TRUE; + break; + + case FIT_INT16: // array of short : signed 16-bit + case FIT_UINT32: // array of unsigned long : unsigned 32-bit + case FIT_INT32: // array of long : signed 32-bit + case FIT_DOUBLE: // array of double : 64-bit IEEE floating point + case FIT_COMPLEX: // array of FICOMPLEX : 2 x 64-bit IEEE floating point + + default: + // unsupported format + break; + } + + return (*guid_format != GUID_PKPixelFormatDontCare) ? WMP_errSuccess : WMP_errUnsupportedFormat; +} + +// ========================================================== +// Metadata loading & saving +// ========================================================== + +/** +Read a JPEG-XR IFD as a buffer +*/ +static ERR +ReadProfile(WMPStream* pStream, unsigned cbByteCount, unsigned uOffset, BYTE **ppbProfile) { + // (re-)allocate profile buffer + BYTE *pbProfile = *ppbProfile; + pbProfile = (BYTE*)realloc(pbProfile, cbByteCount); + if(!pbProfile) { + return WMP_errOutOfMemory; + } + // read the profile + if(WMP_errSuccess == pStream->SetPos(pStream, uOffset)) { + if(WMP_errSuccess == pStream->Read(pStream, pbProfile, cbByteCount)) { + *ppbProfile = pbProfile; + return WMP_errSuccess; + } + } + return WMP_errFileIO; +} + +/** +Convert a DPKPROPVARIANT to a FITAG, then store the tag as FIMD_EXIF_MAIN +*/ +static BOOL +ReadPropVariant(WORD tag_id, const DPKPROPVARIANT & varSrc, FIBITMAP *dib) { + DWORD dwSize; + + if(varSrc.vt == DPKVT_EMPTY) { + return FALSE; + } + + // get the tag key + TagLib& s = TagLib::instance(); + const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL); + if(!key) { + return FALSE; + } + + // create a tag + FITAG *tag = FreeImage_CreateTag(); + if(tag) { + // set tag ID + FreeImage_SetTagID(tag, tag_id); + // set tag type, count, length and value + switch (varSrc.vt) { + case DPKVT_LPSTR: + FreeImage_SetTagType(tag, FIDT_ASCII); + dwSize = (DWORD)strlen(varSrc.VT.pszVal) + 1; + FreeImage_SetTagCount(tag, dwSize); + FreeImage_SetTagLength(tag, dwSize); + FreeImage_SetTagValue(tag, varSrc.VT.pszVal); + break; + + case DPKVT_LPWSTR: + FreeImage_SetTagType(tag, FIDT_UNDEFINED); + dwSize = (DWORD)(sizeof(U16) * (wcslen((wchar_t *) varSrc.VT.pwszVal) + 1)); // +1 for NULL term + FreeImage_SetTagCount(tag, dwSize / 2); + FreeImage_SetTagLength(tag, dwSize); + FreeImage_SetTagValue(tag, varSrc.VT.pwszVal); + break; + + case DPKVT_UI2: + FreeImage_SetTagType(tag, FIDT_SHORT); + FreeImage_SetTagCount(tag, 1); + FreeImage_SetTagLength(tag, 2); + FreeImage_SetTagValue(tag, &varSrc.VT.uiVal); + break; + + case DPKVT_UI4: + FreeImage_SetTagType(tag, FIDT_LONG); + FreeImage_SetTagCount(tag, 1); + FreeImage_SetTagLength(tag, 4); + FreeImage_SetTagValue(tag, &varSrc.VT.ulVal); + break; + + default: + assert(FALSE); // This case is not handled + break; + } + // get the tag desctiption + const char *description = s.getTagDescription(TagLib::EXIF_MAIN, tag_id); + FreeImage_SetTagDescription(tag, description); + + // store the tag + FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, key, tag); + + FreeImage_DeleteTag(tag); + } + return TRUE; +} + +/** +Read JPEG-XR descriptive metadata and store as EXIF_MAIN metadata +@see ReadPropVariant +*/ +static ERR +ReadDescriptiveMetadata(PKImageDecode *pID, FIBITMAP *dib) { + // get Exif TIFF metadata + const DESCRIPTIVEMETADATA *pDescMetadata = &pID->WMP.sDescMetadata; + // convert metadata to FITAG and store into the EXIF_MAIN metadata model + ReadPropVariant(WMP_tagImageDescription, pDescMetadata->pvarImageDescription, dib); + ReadPropVariant(WMP_tagCameraMake, pDescMetadata->pvarCameraMake, dib); + ReadPropVariant(WMP_tagCameraModel, pDescMetadata->pvarCameraModel, dib); + ReadPropVariant(WMP_tagSoftware, pDescMetadata->pvarSoftware, dib); + ReadPropVariant(WMP_tagDateTime, pDescMetadata->pvarDateTime, dib); + ReadPropVariant(WMP_tagArtist, pDescMetadata->pvarArtist, dib); + ReadPropVariant(WMP_tagCopyright, pDescMetadata->pvarCopyright, dib); + ReadPropVariant(WMP_tagRatingStars, pDescMetadata->pvarRatingStars, dib); + ReadPropVariant(WMP_tagRatingValue, pDescMetadata->pvarRatingValue, dib); + ReadPropVariant(WMP_tagCaption, pDescMetadata->pvarCaption, dib); + ReadPropVariant(WMP_tagDocumentName, pDescMetadata->pvarDocumentName, dib); + ReadPropVariant(WMP_tagPageName, pDescMetadata->pvarPageName, dib); + ReadPropVariant(WMP_tagPageNumber, pDescMetadata->pvarPageNumber, dib); + ReadPropVariant(WMP_tagHostComputer, pDescMetadata->pvarHostComputer, dib); + return WMP_errSuccess; +} + +/** +Read ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata +*/ +static ERR +ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) { + ERR error_code = 0; // error code as returned by the interface + size_t currentPos = 0; // current stream position + + WMPStream *pStream = pID->pStream; + WmpDEMisc *wmiDEMisc = &pID->WMP.wmiDEMisc; + BYTE *pbProfile = NULL; + + try { + // save current position + error_code = pStream->GetPos(pStream, ¤tPos); + JXR_CHECK(error_code); + + // ICC profile + if(0 != wmiDEMisc->uColorProfileByteCount) { + unsigned cbByteCount = wmiDEMisc->uColorProfileByteCount; + unsigned uOffset = wmiDEMisc->uColorProfileOffset; + error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); + JXR_CHECK(error_code); + FreeImage_CreateICCProfile(dib, pbProfile, cbByteCount); + } + + // XMP metadata + if(0 != wmiDEMisc->uXMPMetadataByteCount) { + unsigned cbByteCount = wmiDEMisc->uXMPMetadataByteCount; + unsigned uOffset = wmiDEMisc->uXMPMetadataOffset; + error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); + JXR_CHECK(error_code); + // store the tag as XMP + FITAG *tag = FreeImage_CreateTag(); + if(tag) { + FreeImage_SetTagLength(tag, cbByteCount); + FreeImage_SetTagCount(tag, cbByteCount); + FreeImage_SetTagType(tag, FIDT_ASCII); + FreeImage_SetTagValue(tag, pbProfile); + FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); + FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); + FreeImage_DeleteTag(tag); + } + } + + // IPTC metadata + if(0 != wmiDEMisc->uIPTCNAAMetadataByteCount) { + unsigned cbByteCount = wmiDEMisc->uIPTCNAAMetadataByteCount; + unsigned uOffset = wmiDEMisc->uIPTCNAAMetadataOffset; + error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); + JXR_CHECK(error_code); + // decode the IPTC profile + read_iptc_profile(dib, pbProfile, cbByteCount); + } + + // Exif metadata + if(0 != wmiDEMisc->uEXIFMetadataByteCount) { + unsigned cbByteCount = wmiDEMisc->uEXIFMetadataByteCount; + unsigned uOffset = wmiDEMisc->uEXIFMetadataOffset; + error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); + JXR_CHECK(error_code); + // decode the Exif profile + jpegxr_read_exif_profile(dib, pbProfile, cbByteCount); + } + + // Exif-GPS metadata + if(0 != wmiDEMisc->uGPSInfoMetadataByteCount) { + unsigned cbByteCount = wmiDEMisc->uGPSInfoMetadataByteCount; + unsigned uOffset = wmiDEMisc->uGPSInfoMetadataOffset; + error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); + JXR_CHECK(error_code); + // decode the Exif-GPS profile + jpegxr_read_exif_gps_profile(dib, pbProfile, cbByteCount); + } + + // free profile buffer + free(pbProfile); + // restore initial position + error_code = pID->pStream->SetPos(pID->pStream, currentPos); + JXR_CHECK(error_code); + + // as a LAST STEP, read descriptive metadata + // these metadata overwrite possible identical Exif-TIFF metadata + // that could have been read inside the Exif IFD + + return ReadDescriptiveMetadata(pID, dib); + + } catch(...) { + // free profile buffer + free(pbProfile); + if(currentPos) { + // restore initial position + pStream->SetPos(pStream, currentPos); + } + return error_code; + } +} + +// ========================================================== +// Quantization tables (Y, U, V, YHP, UHP, VHP), +// optimized for PSNR +// ========================================================== + +static const int DPK_QPS_420[11][6] = { // for 8 bit only + { 66, 65, 70, 72, 72, 77 }, + { 59, 58, 63, 64, 63, 68 }, + { 52, 51, 57, 56, 56, 61 }, + { 48, 48, 54, 51, 50, 55 }, + { 43, 44, 48, 46, 46, 49 }, + { 37, 37, 42, 38, 38, 43 }, + { 26, 28, 31, 27, 28, 31 }, + { 16, 17, 22, 16, 17, 21 }, + { 10, 11, 13, 10, 10, 13 }, + { 5, 5, 6, 5, 5, 6 }, + { 2, 2, 3, 2, 2, 2 } +}; + +static const int DPK_QPS_8[12][6] = { + { 67, 79, 86, 72, 90, 98 }, + { 59, 74, 80, 64, 83, 89 }, + { 53, 68, 75, 57, 76, 83 }, + { 49, 64, 71, 53, 70, 77 }, + { 45, 60, 67, 48, 67, 74 }, + { 40, 56, 62, 42, 59, 66 }, + { 33, 49, 55, 35, 51, 58 }, + { 27, 44, 49, 28, 45, 50 }, + { 20, 36, 42, 20, 38, 44 }, + { 13, 27, 34, 13, 28, 34 }, + { 7, 17, 21, 8, 17, 21 }, // Photoshop 100% + { 2, 5, 6, 2, 5, 6 } +}; + +static const int DPK_QPS_16[11][6] = { + { 197, 203, 210, 202, 207, 213 }, + { 174, 188, 193, 180, 189, 196 }, + { 152, 167, 173, 156, 169, 174 }, + { 135, 152, 157, 137, 153, 158 }, + { 119, 137, 141, 119, 138, 142 }, + { 102, 120, 125, 100, 120, 124 }, + { 82, 98, 104, 79, 98, 103 }, + { 60, 76, 81, 58, 76, 81 }, + { 39, 52, 58, 36, 52, 58 }, + { 16, 27, 33, 14, 27, 33 }, + { 5, 8, 9, 4, 7, 8 } +}; + +static const int DPK_QPS_16f[11][6] = { + { 148, 177, 171, 165, 187, 191 }, + { 133, 155, 153, 147, 172, 181 }, + { 114, 133, 138, 130, 157, 167 }, + { 97, 118, 120, 109, 137, 144 }, + { 76, 98, 103, 85, 115, 121 }, + { 63, 86, 91, 62, 96, 99 }, + { 46, 68, 71, 43, 73, 75 }, + { 29, 48, 52, 27, 48, 51 }, + { 16, 30, 35, 14, 29, 34 }, + { 8, 14, 17, 7, 13, 17 }, + { 3, 5, 7, 3, 5, 6 } +}; + +static const int DPK_QPS_32f[11][6] = { + { 194, 206, 209, 204, 211, 217 }, + { 175, 187, 196, 186, 193, 205 }, + { 157, 170, 177, 167, 180, 190 }, + { 133, 152, 156, 144, 163, 168 }, + { 116, 138, 142, 117, 143, 148 }, + { 98, 120, 123, 96, 123, 126 }, + { 80, 99, 102, 78, 99, 102 }, + { 65, 79, 84, 63, 79, 84 }, + { 48, 61, 67, 45, 60, 66 }, + { 27, 41, 46, 24, 40, 45 }, + { 3, 22, 24, 2, 21, 22 } +}; + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static const char * DLL_CALLCONV +Format() { + return "JPEG-XR"; +} + +static const char * DLL_CALLCONV +Description() { + return "JPEG XR image format"; +} + +static const char * DLL_CALLCONV +Extension() { + return "jxr,wdp,hdp"; +} + +static const char * DLL_CALLCONV +RegExpr() { + return NULL; +} + +static const char * DLL_CALLCONV +MimeType() { + return "image/vnd.ms-photo"; +} + +static BOOL DLL_CALLCONV +Validate(FreeImageIO *io, fi_handle handle) { + BYTE jxr_signature[3] = { 0x49, 0x49, 0xBC }; + BYTE signature[3] = { 0, 0, 0 }; + + io->read_proc(&signature, 1, 3, handle); + + return (memcmp(jxr_signature, signature, 3) == 0); +} + +static BOOL DLL_CALLCONV +SupportsExportDepth(int depth) { + return ( + (depth == 1) || + (depth == 8) || + (depth == 16) || + (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) || + (type == FIT_FLOAT) || + (type == FIT_RGBF) || + (type == FIT_RGBAF) + ); +} + +static BOOL DLL_CALLCONV +SupportsICCProfiles() { + return TRUE; +} + +static BOOL DLL_CALLCONV +SupportsNoPixels() { + return TRUE; +} + +// ========================================================== +// Open & Close +// ========================================================== + +static void * DLL_CALLCONV +Open(FreeImageIO *io, fi_handle handle, BOOL read) { + WMPStream *pStream = NULL; // stream interface + if(io && handle) { + // allocate the FreeImageIO stream wrapper + FreeImageJXRIO *jxr_io = (FreeImageJXRIO*)malloc(sizeof(FreeImageJXRIO)); + if(jxr_io) { + jxr_io->io = io; + jxr_io->handle = handle; + // create a JXR stream wrapper + if(_jxr_io_Create(&pStream, jxr_io) != WMP_errSuccess) { + free(jxr_io); + return NULL; + } + } + } + return pStream; +} + +static void DLL_CALLCONV +Close(FreeImageIO *io, fi_handle handle, void *data) { + WMPStream *pStream = (WMPStream*)data; + if(pStream) { + // free the FreeImageIO stream wrapper + FreeImageJXRIO *jxr_io = (FreeImageJXRIO*)pStream->state.pvObj; + free(jxr_io); + // free the JXR stream wrapper + pStream->fMem = TRUE; + _jxr_io_Close(&pStream); + } +} + +// ========================================================== +// Load +// ========================================================== + +/** +Set decoder parameters +@param pDecoder Decoder handle +@param flags FreeImage load flags +*/ +static void +SetDecoderParameters(PKImageDecode *pDecoder, int flags) { + // load image & alpha for formats with alpha + pDecoder->WMP.wmiSCP.uAlphaMode = 2; + // more options to come ... +} + +/** +Copy or convert & copy decoded pixels into the dib +@param pDecoder Decoder handle +@param out_guid_format Target guid format +@param dib Output dib +@param width Image width +@param height Image height +@return Returns 0 if successful, returns ERR otherwise +*/ +static ERR +CopyPixels(PKImageDecode *pDecoder, PKPixelFormatGUID out_guid_format, FIBITMAP *dib, int width, int height) { + PKFormatConverter *pConverter = NULL; // pixel format converter + ERR error_code = 0; // error code as returned by the interface + BYTE *pb = NULL; // local buffer used for pixel format conversion + + // image dimensions + const PKRect rect = {0, 0, width, height}; + + try { + // get input file pixel format ... + PKPixelFormatGUID in_guid_format; + error_code = pDecoder->GetPixelFormat(pDecoder, &in_guid_format); + JXR_CHECK(error_code); + + // is a format conversion needed ? + + if(IsEqualGUID(out_guid_format, in_guid_format)) { + // no conversion, load bytes "as is" ... + + // get a pointer to dst pixel data + BYTE *dib_bits = FreeImage_GetBits(dib); + + // get dst pitch (count of BYTE for stride) + const unsigned cbStride = FreeImage_GetPitch(dib); + + // decode and copy bits to dst array + error_code = pDecoder->Copy(pDecoder, &rect, dib_bits, cbStride); + JXR_CHECK(error_code); + } + else { + // we need to use the conversion API ... + + // allocate the pixel format converter + error_code = PKCodecFactory_CreateFormatConverter(&pConverter); + JXR_CHECK(error_code); + + // set the conversion function + error_code = pConverter->Initialize(pConverter, pDecoder, NULL, out_guid_format); + JXR_CHECK(error_code); + + // get the maximum stride + unsigned cbStride = 0; + { + PKPixelInfo pPIFrom; + PKPixelInfo pPITo; + + pPIFrom.pGUIDPixFmt = &in_guid_format; + error_code = PixelFormatLookup(&pPIFrom, LOOKUP_FORWARD); + JXR_CHECK(error_code); + + pPITo.pGUIDPixFmt = &out_guid_format; + error_code = PixelFormatLookup(&pPITo, LOOKUP_FORWARD); + JXR_CHECK(error_code); + + unsigned cbStrideFrom = ((pPIFrom.cbitUnit + 7) >> 3) * width; + unsigned cbStrideTo = ((pPITo.cbitUnit + 7) >> 3) * width; + cbStride = MAX(cbStrideFrom, cbStrideTo); + } + + // allocate a local decoder / encoder buffer + error_code = PKAllocAligned((void **) &pb, cbStride * height, 128); + JXR_CHECK(error_code); + + // copy / convert pixels + error_code = pConverter->Copy(pConverter, &rect, pb, cbStride); + JXR_CHECK(error_code); + + // now copy pixels into the dib + const size_t line_size = FreeImage_GetLine(dib); + for(int y = 0; y < height; y++) { + BYTE *src_bits = (BYTE*)(pb + y * cbStride); + BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, y); + memcpy(dst_bits, src_bits, line_size); + } + + // free the local buffer + PKFreeAligned((void **) &pb); + + // free the pixel format converter + PKFormatConverter_Release(&pConverter); + } + + // FreeImage DIB are upside-down relative to usual graphic conventions + FreeImage_FlipVertical(dib); + + // post-processing ... + // ------------------- + + // swap RGB as needed + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppRGB) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppRGB)) { + SwapRedBlue32(dib); + } +#elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB + if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppBGR) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppBGR)) { + SwapRedBlue32(dib); + } +#endif + + return WMP_errSuccess; + + } catch(...) { + // free the local buffer + PKFreeAligned((void **) &pb); + // free the pixel format converter + PKFormatConverter_Release(&pConverter); + + return error_code; + } +} + +// -------------------------------------------------------------------------- + +static FIBITMAP * DLL_CALLCONV +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + PKImageDecode *pDecoder = NULL; // decoder interface + ERR error_code = 0; // error code as returned by the interface + PKPixelFormatGUID guid_format; // loaded pixel format (== input file pixel format if no conversion needed) + + FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; // input image type + unsigned bpp = 0; // input image bit depth + FIBITMAP *dib = NULL; + + // get the I/O stream wrapper + WMPStream *pDecodeStream = (WMPStream*)data; + + if(!handle || !pDecodeStream) { + return NULL; + } + + BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; + + try { + int width, height; // image dimensions (in pixels) + + // create a JXR decoder interface and initialize function pointers with *_WMP functions + error_code = PKImageDecode_Create_WMP(&pDecoder); + JXR_CHECK(error_code); + + // attach the stream to the decoder ... + // ... then read the image container and the metadata + error_code = pDecoder->Initialize(pDecoder, pDecodeStream); + JXR_CHECK(error_code); + + // set decoder parameters + SetDecoderParameters(pDecoder, flags); + + // get dst image format specifications + unsigned red_mask = 0, green_mask = 0, blue_mask = 0; + error_code = GetInputPixelFormat(pDecoder, &guid_format, &image_type, &bpp, &red_mask, &green_mask, &blue_mask); + JXR_CHECK(error_code); + + // get image dimensions + pDecoder->GetSize(pDecoder, &width, &height); + + // allocate dst image + { + dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, bpp, red_mask, green_mask, blue_mask); + if(!dib) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + if(FreeImage_GetBPP(dib) == 1) { + // BD_1 - build a FIC_MINISBLACK palette + RGBQUAD *pal = FreeImage_GetPalette(dib); + pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; + pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; + } + } + + // get image resolution + { + float resX, resY; // image resolution (in dots per inch) + // convert from English units, i.e. dots per inch to universal units, i.e. dots per meter + pDecoder->GetResolution(pDecoder, &resX, &resY); + FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX / 0.0254F + 0.5F)); + FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY / 0.0254F + 0.5F)); + } + + // get metadata & ICC profile + error_code = ReadMetadata(pDecoder, dib); + JXR_CHECK(error_code); + + if(header_only) { + // header only mode ... + + // free the decoder + pDecoder->Release(&pDecoder); + assert(pDecoder == NULL); + + return dib; + } + + // copy pixels into the dib, perform pixel conversion if needed + error_code = CopyPixels(pDecoder, guid_format, dib, width, height); + JXR_CHECK(error_code); + + // free the decoder + pDecoder->Release(&pDecoder); + assert(pDecoder == NULL); + + return dib; + + } catch (const char *message) { + // unload the dib + FreeImage_Unload(dib); + // free the decoder + pDecoder->Release(&pDecoder); + + if(NULL != message) { + FreeImage_OutputMessageProc(s_format_id, message); + } + } + + return NULL; +} + +// ========================================================== +// Save +// ========================================================== + +/** +Configure compression parameters + +ImageQuality Q (BD==1) Q (BD==8) Q (BD==16) Q (BD==32F) Subsample Overlap +[0.0, 0.4] 8-IQ*5 (see table) (see table) (see table) 4:4:4 2 +(0.4, 0.8) 8-IQ*5 (see table) (see table) (see table) 4:4:4 1 +[0.8, 1.0) 8-IQ*5 (see table) (see table) (see table) 4:4:4 1 +[1.0, 1.0] 1 1 1 1 4:4:4 0 + +@param wmiSCP Encoder parameters +@param pixelInfo Image specifications +@param fltImageQuality Image output quality in [0..1), 1 means lossless +*/ +static void +SetCompression(CWMIStrCodecParam *wmiSCP, const PKPixelInfo *pixelInfo, float fltImageQuality) { + if(fltImageQuality < 1.0F) { + // overlap + if(fltImageQuality >= 0.5F) { + wmiSCP->olOverlap = OL_ONE; + } else { + wmiSCP->olOverlap = OL_TWO; + } + // chroma sub-sampling + if(fltImageQuality >= 0.5F || pixelInfo->uBitsPerSample > 8) { + wmiSCP->cfColorFormat = YUV_444; + } else { + wmiSCP->cfColorFormat = YUV_420; + } + + // bit depth + if(pixelInfo->bdBitDepth == BD_1) { + wmiSCP->uiDefaultQPIndex = (U8)(8 - 5.0F * fltImageQuality + 0.5F); + } + else { + // remap [0.8, 0.866, 0.933, 1.0] to [0.8, 0.9, 1.0, 1.1] + // to use 8-bit DPK QP table (0.933 == Photoshop JPEG 100) + if(fltImageQuality > 0.8F && pixelInfo->bdBitDepth == BD_8 && wmiSCP->cfColorFormat != YUV_420 && wmiSCP->cfColorFormat != YUV_422) { + fltImageQuality = 0.8F + (fltImageQuality - 0.8F) * 1.5F; + } + + const int qi = (int) (10.0F * fltImageQuality); + const float qf = 10.0F * fltImageQuality - (float)qi; + + const int *pQPs = + (wmiSCP->cfColorFormat == YUV_420 || wmiSCP->cfColorFormat == YUV_422) ? + DPK_QPS_420[qi] : + (pixelInfo->bdBitDepth == BD_8 ? DPK_QPS_8[qi] : + (pixelInfo->bdBitDepth == BD_16 ? DPK_QPS_16[qi] : + (pixelInfo->bdBitDepth == BD_16F ? DPK_QPS_16f[qi] : + DPK_QPS_32f[qi]))); + + wmiSCP->uiDefaultQPIndex = (U8) (0.5F + (float) pQPs[0] * (1.0F - qf) + (float) (pQPs + 6)[0] * qf); + wmiSCP->uiDefaultQPIndexU = (U8) (0.5F + (float) pQPs[1] * (1.0F - qf) + (float) (pQPs + 6)[1] * qf); + wmiSCP->uiDefaultQPIndexV = (U8) (0.5F + (float) pQPs[2] * (1.0F - qf) + (float) (pQPs + 6)[2] * qf); + wmiSCP->uiDefaultQPIndexYHP = (U8) (0.5F + (float) pQPs[3] * (1.0F - qf) + (float) (pQPs + 6)[3] * qf); + wmiSCP->uiDefaultQPIndexUHP = (U8) (0.5F + (float) pQPs[4] * (1.0F - qf) + (float) (pQPs + 6)[4] * qf); + wmiSCP->uiDefaultQPIndexVHP = (U8) (0.5F + (float) pQPs[5] * (1.0F - qf) + (float) (pQPs + 6)[5] * qf); + } + } // fltImageQuality < 1.0F + else { + // lossless mode + wmiSCP->uiDefaultQPIndex = 1; + } +} + +/** +Set encoder parameters +@param wmiSCP Encoder parameters +@param pixelInfo Image specifications +@param flags FreeImage save flags +@param bHasAlpha TRUE if an alpha layer is present +*/ +static void +SetEncoderParameters(CWMIStrCodecParam *wmiSCP, const PKPixelInfo *pixelInfo, int flags, BOOL bHasAlpha) { + float fltImageQuality = 1.0F; + + // all values have been set to zero by the API + // update default values for some attributes + wmiSCP->cfColorFormat = YUV_444; // color format + wmiSCP->bdBitDepth = BD_LONG; // internal bit depth + wmiSCP->bfBitstreamFormat = SPATIAL; // compressed image data in spatial order + wmiSCP->bProgressiveMode = FALSE; // sequential mode + wmiSCP->olOverlap = OL_ONE; // single level overlap processing + wmiSCP->cNumOfSliceMinus1H = 0; // # of horizontal slices + wmiSCP->cNumOfSliceMinus1V = 0; // # of vertical slices + wmiSCP->sbSubband = SB_ALL; // keep all subbands + wmiSCP->uAlphaMode = 0; // 0:no alpha 1: alpha only else: something + alpha + wmiSCP->uiDefaultQPIndex = 1; // quantization for grey or rgb layer(s), 1: lossless + wmiSCP->uiDefaultQPIndexAlpha = 1; // quantization for alpha layer, 1: lossless + + // process the flags + // ----------------- + + // progressive mode + if((flags & JXR_PROGRESSIVE) == JXR_PROGRESSIVE) { + // turn on progressive mode (instead of sequential mode) + wmiSCP->bProgressiveMode = TRUE; + } + + // quality in [0.01 - 1.0), 1.0 means lossless - default is 0.80 + int quality = flags & 0x7F; + if(quality == 0) { + // defaut to 0.80 + fltImageQuality = 0.8F; + } else if((flags & JXR_LOSSLESS) == JXR_LOSSLESS) { + fltImageQuality = 1.0F; + } else { + quality = (quality >= 100) ? 100 : quality; + fltImageQuality = quality / 100.0F; + } + SetCompression(wmiSCP, pixelInfo, fltImageQuality); + + // alpha compression + if(bHasAlpha) { + wmiSCP->uAlphaMode = 2; // encode with a planar alpha channel + } +} + +// -------------------------------------------------------------------------- + +static BOOL DLL_CALLCONV +Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { + BOOL bIsFlipped = FALSE; // FreeImage DIB are upside-down relative to usual graphic conventions + PKPixelFormatGUID guid_format; // image format + PKPixelInfo pixelInfo; // image specifications + BOOL bHasAlpha = FALSE; // is alpha layer present ? + + PKImageEncode *pEncoder = NULL; // encoder interface + ERR error_code = 0; // error code as returned by the interface + + // get the I/O stream wrapper + WMPStream *pEncodeStream = (WMPStream*)data; + + if(!dib || !handle || !pEncodeStream) { + return FALSE; + } + + try { + // get image dimensions + unsigned width = FreeImage_GetWidth(dib); + unsigned height = FreeImage_GetHeight(dib); + + // check JPEG-XR limits + if((width < MB_WIDTH_PIXEL) || (height < MB_HEIGHT_PIXEL)) { + FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height); + throw (const char*)NULL; + } + + // get output pixel format + error_code = GetOutputPixelFormat(dib, &guid_format, &bHasAlpha); + JXR_CHECK(error_code); + pixelInfo.pGUIDPixFmt = &guid_format; + error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD); + JXR_CHECK(error_code); + + // create a JXR encoder interface and initialize function pointers with *_WMP functions + error_code = PKImageEncode_Create_WMP(&pEncoder); + JXR_CHECK(error_code); + + // attach the stream to the encoder and set all encoder parameters to zero ... + error_code = pEncoder->Initialize(pEncoder, pEncodeStream, &pEncoder->WMP.wmiSCP, sizeof(CWMIStrCodecParam)); + JXR_CHECK(error_code); + + // ... then configure the encoder + SetEncoderParameters(&pEncoder->WMP.wmiSCP, &pixelInfo, flags, bHasAlpha); + + // set pixel format + pEncoder->SetPixelFormat(pEncoder, guid_format); + + // set image size + pEncoder->SetSize(pEncoder, width, height); + + // set resolution (convert from universal units to English units) + float resX = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterX(dib)); + float resY = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterY(dib)); + pEncoder->SetResolution(pEncoder, resX, resY); + + // write pixels + // -------------- + + // dib coordinates are upside-down relative to usual conventions + bIsFlipped = FreeImage_FlipVertical(dib); + + // get a pointer to dst pixel data + BYTE *dib_bits = FreeImage_GetBits(dib); + + // get dst pitch (count of BYTE for stride) + const unsigned cbStride = FreeImage_GetPitch(dib); + + // write pixels on output + error_code = pEncoder->WritePixels(pEncoder, height, dib_bits, cbStride); + JXR_CHECK(error_code); + + // recover dib coordinates + FreeImage_FlipVertical(dib); + + // free the encoder + pEncoder->Release(&pEncoder); + assert(pEncoder == NULL); + + return TRUE; + + } catch (const char *message) { + if(bIsFlipped) { + // recover dib coordinates + FreeImage_FlipVertical(dib); + } + if(pEncoder) { + // free the encoder + pEncoder->Release(&pEncoder); + assert(pEncoder == NULL); + } + if(NULL != message) { + FreeImage_OutputMessageProc(s_format_id, message); + } + } + + return FALSE; +} + +// ========================================================== +// Init +// ========================================================== + +void DLL_CALLCONV +InitJXR(Plugin *plugin, int format_id) { + s_format_id = format_id; + + plugin->format_proc = Format; + plugin->description_proc = Description; + plugin->extension_proc = Extension; + plugin->regexpr_proc = RegExpr; + plugin->open_proc = Open; + plugin->close_proc = Close; + plugin->pagecount_proc = NULL; + plugin->pagecapability_proc = NULL; + plugin->load_proc = Load; + plugin->save_proc = Save; + plugin->validate_proc = Validate; + plugin->mime_proc = MimeType; + plugin->supports_export_bpp_proc = SupportsExportDepth; + plugin->supports_export_type_proc = SupportsExportType; + plugin->supports_icc_profiles_proc = SupportsICCProfiles; + plugin->supports_no_pixels_proc = SupportsNoPixels; +} + diff --git a/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp b/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp index 7a74fe5e81..f6b59e299b 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp @@ -35,6 +35,8 @@ #define PNG_BYTES_TO_CHECK 8 +#undef PNG_Z_DEFAULT_COMPRESSION // already used in ../LibPNG/pnglibconf.h + // ---------------------------------------------------------- #include "zlib.h" diff --git a/plugins/AdvaImg/src/FreeImage/PluginSGI.cpp b/plugins/AdvaImg/src/FreeImage/PluginSGI.cpp index 0fd162b1d4..35fd2ec611 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginSGI.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginSGI.cpp @@ -178,7 +178,7 @@ Description() { static const char * DLL_CALLCONV Extension() { - return "sgi"; + return "sgi,rgb,rgba,bw"; } static const char * DLL_CALLCONV diff --git a/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp b/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp index 5fb1f53ee0..f12d7286ce 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp @@ -358,6 +358,7 @@ MimeType() { static BOOL isTARGA20(FreeImageIO *io, fi_handle handle) { const unsigned sizeofSig = 18; + BYTE signature[sizeofSig]; // tga_signature = "TRUEVISION-XFILE." (TGA 2.0 only) BYTE tga_signature[sizeofSig] = { 84, 82, 85, 69, 86, 73, 83, 73, 79, 78, 45, 88, 70, 73, 76, 69, 46, 0 }; // get the start offset @@ -366,13 +367,10 @@ isTARGA20(FreeImageIO *io, fi_handle handle) { io->seek_proc(handle, 0, SEEK_END); const long eof = io->tell_proc(handle); // read the signature - io->seek_proc(handle, start_offset + eof - sizeofSig, SEEK_SET); - BYTE signature[sizeofSig]; - io->read_proc(&signature, 1, sizeofSig, handle); - - // rewind - io->seek_proc(handle, start_offset, SEEK_SET); + io->read_proc(&signature, 1, sizeofSig, handle); + // rewind + io->seek_proc(handle, start_offset, SEEK_SET); return (memcmp(tga_signature, signature, sizeofSig) == 0); } @@ -384,41 +382,61 @@ Validate(FreeImageIO *io, fi_handle handle) { } // not a 2.0 image, try testing if it's a valid TGA anyway (not robust) - BOOL bResult = FALSE; - - const long start_offset = io->tell_proc(handle); - - TGAHEADER header; - io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle); - - // rewind - io->seek_proc(handle, start_offset, SEEK_SET); + { + const long start_offset = io->tell_proc(handle); + + // get the header + TGAHEADER header; + io->read_proc(&header, sizeof(tagTGAHEADER), 1, handle); +#ifdef FREEIMAGE_BIGENDIAN + SwapHeader(&header); +#endif + // rewind + io->seek_proc(handle, start_offset, SEEK_SET); - switch(header.image_type) { - case TGA_CMAP : - case TGA_RGB: - case TGA_MONO : - case TGA_RLECMAP: - case TGA_RLERGB: - case TGA_RLEMONO: - switch(header.is_pixel_depth) { - case 8 : - case 16: - case 24: - case 32: - bResult = TRUE; - break; - default: - bResult = FALSE; - break; + // the color map type should be a 0 or a 1... + if(header.color_map_type != 0 && header.color_map_type != 1) { + return FALSE; + } + // if the color map type is 1 then we validate the map entry information... + if(header.color_map_type > 0) { + // It doesn't make any sense if the first entry is larger than the color map table + if(header.cm_first_entry >= header.cm_length) { + return FALSE; } - break; - default: - bResult = FALSE; - break; + } + // check header.cm_size, don't allow 0 or anything bigger than 32 + if(header.cm_size == 0 || header.cm_size > 32) { + return FALSE; + } + // the width/height shouldn't be 0, right ? + if(header.is_width == 0 || header.is_height == 0) { + return FALSE; + } + // let's now verify all the types that are supported by FreeImage (this is our final verification) + switch(header.image_type) { + case TGA_CMAP : + case TGA_RGB: + case TGA_MONO : + case TGA_RLECMAP: + case TGA_RLERGB: + case TGA_RLEMONO: + switch(header.is_pixel_depth) { + case 8 : + case 16: + case 24: + case 32: + return TRUE; + default: + return FALSE; + } + break; + default: + return FALSE; + } } - - return bResult; + + return FALSE; } static BOOL DLL_CALLCONV @@ -731,8 +749,12 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // calculate the color map size csize = header.cm_length * header.cm_size / 8; + + // read the color map BYTE *cmap = (BYTE*)malloc(csize * sizeof(BYTE)); - + if (cmap == NULL) { + throw FI_MSG_ERROR_DIB_MEMORY; + } io->read_proc(cmap, sizeof(BYTE), csize, handle); // build the palette @@ -740,8 +762,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { switch (header.cm_size) { case 16: { WORD *rgb555 = (WORD*)&cmap[0]; + unsigned start = (unsigned)header.cm_first_entry; + unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length); - for (count = header.cm_first_entry; count < header.cm_length; count++) { + for (count = start; count < stop; count++) { palette[count].rgbRed = (BYTE)((((*rgb555 & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F); palette[count].rgbGreen = (BYTE)((((*rgb555 & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F); palette[count].rgbBlue = (BYTE)((((*rgb555 & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F); @@ -752,8 +776,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { case 24: { FILE_BGR *bgr = (FILE_BGR*)&cmap[0]; + unsigned start = (unsigned)header.cm_first_entry; + unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length); - for (count = header.cm_first_entry; count < header.cm_length; count++) { + for (count = start; count < stop; count++) { palette[count].rgbBlue = bgr->b; palette[count].rgbGreen = bgr->g; palette[count].rgbRed = bgr->r; @@ -769,8 +795,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { memset(trns, 0xFF, 256); FILE_BGRA *bgra = (FILE_BGRA*)&cmap[0]; + unsigned start = (unsigned)header.cm_first_entry; + unsigned stop = MIN((unsigned)256, (unsigned)header.cm_length); - for (count = header.cm_first_entry; count < header.cm_length; count++) { + for (count = start; count < stop; count++) { palette[count].rgbBlue = bgra->b; palette[count].rgbGreen = bgra->g; palette[count].rgbRed = bgra->r; diff --git a/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp b/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp index 72218a2564..4e502c56e8 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp @@ -193,9 +193,6 @@ TIFF * TIFFFdOpen(thandle_t handle, const char *name, const char *mode) { TIFF *tif; - // Set up the callback for extended TIFF directory tag support - // (see XTIFF.cpp) - XTIFFInitialize(); // Open the file; the callback will set everything up tif = TIFFClientOpen(name, mode, handle, @@ -1894,6 +1891,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if(buf == NULL) { throw FI_MSG_ERROR_MEMORY; } + memset(buf, 0, TIFFStripSize(tif) * sizeof(BYTE)); BOOL bThrowMessage = FALSE; @@ -2622,6 +2620,10 @@ void DLL_CALLCONV InitTIFF(Plugin *plugin, int format_id) { s_format_id = format_id; + // Set up the callback for extended TIFF directory tag support (see XTIFF.cpp) + // Must be called before using libtiff + XTIFFInitialize(); + plugin->format_proc = Format; plugin->description_proc = Description; plugin->extension_proc = Extension; diff --git a/plugins/AdvaImg/src/FreeImage/PluginWebP.cpp b/plugins/AdvaImg/src/FreeImage/PluginWebP.cpp new file mode 100644 index 0000000000..9fb0b69447 --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/PluginWebP.cpp @@ -0,0 +1,698 @@ +// ========================================================== +// Google WebP Loader & Writer +// +// Design and implementation by +// - Herve Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" + +#include "../Metadata/FreeImageTag.h" + +#include "../LibWebP/src/webp/decode.h" +#include "../LibWebP/src/webp/encode.h" +#include "../LibWebP/src/enc/vp8enci.h" +#include "../LibWebP/src/webp/mux.h" + +// ========================================================== +// Plugin Interface +// ========================================================== + +static int s_format_id; + +// ---------------------------------------------------------- +// Helpers for the load function +// ---------------------------------------------------------- + +/** +Read the whole file into memory +*/ +static BOOL +ReadFileToWebPData(FreeImageIO *io, fi_handle handle, WebPData * const bitstream) { + uint8_t *raw_data = NULL; + + try { + // Read the input file and put it in memory + long start_pos = io->tell_proc(handle); + io->seek_proc(handle, 0, SEEK_END); + size_t file_length = (size_t)(io->tell_proc(handle) - start_pos); + io->seek_proc(handle, start_pos, SEEK_SET); + raw_data = (uint8_t*)malloc(file_length * sizeof(uint8_t)); + if(!raw_data) { + throw FI_MSG_ERROR_MEMORY; + } + if(io->read_proc(raw_data, 1, (unsigned)file_length, handle) != file_length) { + throw "Error while reading input stream"; + } + + // copy pointers (must be released later using free) + bitstream->bytes = raw_data; + bitstream->size = file_length; + + return TRUE; + + } catch(const char *text) { + if(raw_data) { + free(raw_data); + } + memset(bitstream, 0, sizeof(WebPData)); + if(NULL != text) { + FreeImage_OutputMessageProc(s_format_id, text); + } + return FALSE; + } +} + +// ---------------------------------------------------------- +// Helpers for the save function +// ---------------------------------------------------------- + +/** +Output function. Should return 1 if writing was successful. +data/data_size is the segment of data to write, and 'picture' is for +reference (and so one can make use of picture->custom_ptr). +*/ +static int +WebP_MemoryWriter(const BYTE *data, size_t data_size, const WebPPicture* const picture) { + FIMEMORY *hmem = (FIMEMORY*)picture->custom_ptr; + return data_size ? (FreeImage_WriteMemory(data, 1, (unsigned)data_size, hmem) == data_size) : 0; +} + +// ========================================================== +// Plugin Implementation +// ========================================================== + +static const char * DLL_CALLCONV +Format() { + return "WebP"; +} + +static const char * DLL_CALLCONV +Description() { + return "Google WebP image format"; +} + +static const char * DLL_CALLCONV +Extension() { + return "webp"; +} + +static const char * DLL_CALLCONV +RegExpr() { + return NULL; +} + +static const char * DLL_CALLCONV +MimeType() { + return "image/webp"; +} + +static BOOL DLL_CALLCONV +Validate(FreeImageIO *io, fi_handle handle) { + BYTE riff_signature[4] = { 0x52, 0x49, 0x46, 0x46 }; + BYTE webp_signature[4] = { 0x57, 0x45, 0x42, 0x50 }; + BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + io->read_proc(signature, 1, 12, handle); + + if(memcmp(riff_signature, signature, 4) == 0) { + if(memcmp(webp_signature, signature + 8, 4) == 0) { + return TRUE; + } + } + + return FALSE; +} + +static BOOL DLL_CALLCONV +SupportsExportDepth(int depth) { + return ( + (depth == 24) || + (depth == 32) + ); +} + +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 void * DLL_CALLCONV +Open(FreeImageIO *io, fi_handle handle, BOOL read) { + WebPMux *mux = NULL; + int copy_data = 1; // 1 : copy data into the mux, 0 : keep a link to local data + + if(read) { + // create the MUX object from the input stream + WebPData bitstream; + // read the input file and put it in memory + if(!ReadFileToWebPData(io, handle, &bitstream)) { + return NULL; + } + // create the MUX object + mux = WebPMuxCreate(&bitstream, copy_data); + // no longer needed since copy_data == 1 + free((void*)bitstream.bytes); + if(mux == NULL) { + FreeImage_OutputMessageProc(s_format_id, "Failed to create mux object from file"); + return NULL; + } + } else { + // creates an empty mux object + mux = WebPMuxNew(); + if(mux == NULL) { + FreeImage_OutputMessageProc(s_format_id, "Failed to create empty mux object"); + return NULL; + } + } + + return mux; +} + +static void DLL_CALLCONV +Close(FreeImageIO *io, fi_handle handle, void *data) { + WebPMux *mux = (WebPMux*)data; + if(mux != NULL) { + // free the MUX object + WebPMuxDelete(mux); + } +} + +// ---------------------------------------------------------- + +/** +Decode a WebP image and returns a FIBITMAP image +@param webp_image Raw WebP image +@param flags FreeImage load flags +@return Returns a dib if successfull, returns NULL otherwise +*/ +static FIBITMAP * +DecodeImage(WebPData *webp_image, int flags) { + FIBITMAP *dib = NULL; + + const uint8_t* data = webp_image->bytes; // raw image data + const size_t data_size = webp_image->size; // raw image size + + VP8StatusCode webp_status = VP8_STATUS_OK; + + BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; + + // Main object storing the configuration for advanced decoding + WebPDecoderConfig decoder_config; + // Output buffer + WebPDecBuffer* const output_buffer = &decoder_config.output; + // Features gathered from the bitstream + WebPBitstreamFeatures* const bitstream = &decoder_config.input; + + try { + // Initialize the configuration as empty + // This function must always be called first, unless WebPGetFeatures() is to be called + if(!WebPInitDecoderConfig(&decoder_config)) { + throw "Library version mismatch"; + } + + // Retrieve features from the bitstream + // The bitstream structure is filled with information gathered from the bitstream + webp_status = WebPGetFeatures(data, data_size, bitstream); + if(webp_status != VP8_STATUS_OK) { + throw FI_MSG_ERROR_PARSING; + } + + // Allocate output dib + + unsigned bpp = bitstream->has_alpha ? 32 : 24; + unsigned width = (unsigned)bitstream->width; + unsigned height = (unsigned)bitstream->height; + + dib = FreeImage_AllocateHeader(header_only, width, height, bpp, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if(!dib) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + if(header_only) { + WebPFreeDecBuffer(output_buffer); + return dib; + } + + // --- Set decoding options --- + + // use multi-threaded decoding + decoder_config.options.use_threads = 1; + // set output color space + output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; + + // --- + + // decode the input stream, taking 'config' into account. + + webp_status = WebPDecode(data, data_size, &decoder_config); + if(webp_status != VP8_STATUS_OK) { + throw FI_MSG_ERROR_PARSING; + } + + // fill the dib with the decoded data + + const BYTE *src_bitmap = output_buffer->u.RGBA.rgba; + const unsigned src_pitch = (unsigned)output_buffer->u.RGBA.stride; + + switch(bpp) { + case 24: + for(unsigned y = 0; y < height; y++) { + const BYTE *src_bits = src_bitmap + y * src_pitch; + BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y); + for(unsigned x = 0; x < width; x++) { + dst_bits[FI_RGBA_BLUE] = src_bits[0]; // B + dst_bits[FI_RGBA_GREEN] = src_bits[1]; // G + dst_bits[FI_RGBA_RED] = src_bits[2]; // R + src_bits += 3; + dst_bits += 3; + } + } + break; + case 32: + for(unsigned y = 0; y < height; y++) { + const BYTE *src_bits = src_bitmap + y * src_pitch; + BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, height-1-y); + for(unsigned x = 0; x < width; x++) { + dst_bits[FI_RGBA_BLUE] = src_bits[0]; // B + dst_bits[FI_RGBA_GREEN] = src_bits[1]; // G + dst_bits[FI_RGBA_RED] = src_bits[2]; // R + dst_bits[FI_RGBA_ALPHA] = src_bits[3]; // A + src_bits += 4; + dst_bits += 4; + } + } + break; + } + + // Free the decoder + WebPFreeDecBuffer(output_buffer); + + return dib; + + } catch (const char *text) { + if(dib) { + FreeImage_Unload(dib); + } + WebPFreeDecBuffer(output_buffer); + + if(NULL != text) { + FreeImage_OutputMessageProc(s_format_id, text); + } + + return NULL; + } +} + +static FIBITMAP * DLL_CALLCONV +Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { + WebPMux *mux = NULL; + WebPMuxFrameInfo webp_frame = { 0 }; // raw image + WebPData color_profile; // ICC raw data + WebPData xmp_metadata; // XMP raw data + WebPData exif_metadata; // EXIF raw data + FIBITMAP *dib = NULL; + WebPMuxError error_status; + + if(!handle) { + return NULL; + } + + try { + // get the MUX object + mux = (WebPMux*)data; + if(!mux) { + throw (1); + } + + // gets the feature flags from the mux object + uint32_t webp_flags = 0; + error_status = WebPMuxGetFeatures(mux, &webp_flags); + if(error_status != WEBP_MUX_OK) { + throw (1); + } + + // get image data + error_status = WebPMuxGetFrame(mux, 1, &webp_frame); + + if(error_status == WEBP_MUX_OK) { + // decode the data (can be limited to the header if flags uses FIF_LOAD_NOPIXELS) + dib = DecodeImage(&webp_frame.bitstream, flags); + if(!dib) { + throw (1); + } + + // get ICC profile + if(webp_flags & ICCP_FLAG) { + error_status = WebPMuxGetChunk(mux, "ICCP", &color_profile); + if(error_status == WEBP_MUX_OK) { + FreeImage_CreateICCProfile(dib, (void*)color_profile.bytes, (long)color_profile.size); + } + } + + // get XMP metadata + if(webp_flags & XMP_FLAG) { + error_status = WebPMuxGetChunk(mux, "XMP ", &xmp_metadata); + if(error_status == WEBP_MUX_OK) { + // create a tag + FITAG *tag = FreeImage_CreateTag(); + if(tag) { + FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); + FreeImage_SetTagLength(tag, (DWORD)xmp_metadata.size); + FreeImage_SetTagCount(tag, (DWORD)xmp_metadata.size); + FreeImage_SetTagType(tag, FIDT_ASCII); + FreeImage_SetTagValue(tag, xmp_metadata.bytes); + + // store the tag + FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); + + // destroy the tag + FreeImage_DeleteTag(tag); + } + } + } + + // get Exif metadata + if(webp_flags & EXIF_FLAG) { + error_status = WebPMuxGetChunk(mux, "EXIF", &exif_metadata); + if(error_status == WEBP_MUX_OK) { + // read the Exif raw data as a blob + jpeg_read_exif_profile_raw(dib, exif_metadata.bytes, (unsigned)exif_metadata.size); + // read and decode the Exif data + jpeg_read_exif_profile(dib, exif_metadata.bytes, (unsigned)exif_metadata.size); + } + } + } + + WebPDataClear(&webp_frame.bitstream); + + return dib; + + } catch(int) { + WebPDataClear(&webp_frame.bitstream); + return NULL; + } +} + +// -------------------------------------------------------------------------- + +/** +Encode a FIBITMAP to a WebP image +@param hmem Memory output stream, containing on return the encoded image +@param dib The FIBITMAP to encode +@param flags FreeImage save flags +@return Returns TRUE if successfull, returns FALSE otherwise +*/ +static BOOL +EncodeImage(FIMEMORY *hmem, FIBITMAP *dib, int flags) { + WebPPicture picture; // Input buffer + WebPConfig config; // Coding parameters + + BOOL bIsFlipped = FALSE; + + try { + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned bpp = FreeImage_GetBPP(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + // check image type + FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + + if( !((image_type == FIT_BITMAP) && ((bpp == 24) || (bpp == 32))) ) { + throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; + } + + // check format limits + if(MAX(width, height) > WEBP_MAX_DIMENSION) { + FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height); + return FALSE; + } + + // Initialize output I/O + if(WebPPictureInit(&picture) == 1) { + picture.writer = WebP_MemoryWriter; + picture.custom_ptr = hmem; + picture.width = (int)width; + picture.height = (int)height; + } else { + throw "Couldn't initialize WebPPicture"; + } + + // --- Set encoding parameters --- + + // Initialize encoding parameters to default values + WebPConfigInit(&config); + + // quality/speed trade-off (0=fast, 6=slower-better) + config.method = 6; + + if((flags & WEBP_LOSSLESS) == WEBP_LOSSLESS) { + // lossless encoding + config.lossless = 1; + picture.use_argb = 1; + } else if((flags & 0x7F) > 0) { + // lossy encoding + config.lossless = 0; + // quality is between 1 (smallest file) and 100 (biggest) - default to 75 + config.quality = (float)(flags & 0x7F); + if(config.quality > 100) { + config.quality = 100; + } + } + + // validate encoding parameters + if(WebPValidateConfig(&config) == 0) { + throw "Failed to initialize encoder"; + } + + // --- Perform encoding --- + + // Invert dib scanlines + bIsFlipped = FreeImage_FlipVertical(dib); + + + // convert dib buffer to output stream + + const BYTE *bits = FreeImage_GetBits(dib); + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + switch(bpp) { + case 24: + WebPPictureImportBGR(&picture, bits, pitch); + break; + case 32: + WebPPictureImportBGRA(&picture, bits, pitch); + break; + } +#else + switch(bpp) { + case 24: + WebPPictureImportRGB(&picture, bits, pitch); + break; + case 32: + WebPPictureImportRGBA(&picture, bits, pitch); + break; + } + +#endif // FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + + if(!WebPEncode(&config, &picture)) { + throw "Failed to encode image"; + } + + WebPPictureFree(&picture); + + if(bIsFlipped) { + // invert dib scanlines + FreeImage_FlipVertical(dib); + } + + return TRUE; + + } catch (const char* text) { + + WebPPictureFree(&picture); + + if(bIsFlipped) { + // invert dib scanlines + FreeImage_FlipVertical(dib); + } + + if(NULL != text) { + FreeImage_OutputMessageProc(s_format_id, text); + } + } + + return FALSE; +} + +static BOOL DLL_CALLCONV +Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { + WebPMux *mux = NULL; + FIMEMORY *hmem = NULL; + WebPData webp_image; + WebPData output_data = { 0 }; + WebPMuxError error_status; + + int copy_data = 1; // 1 : copy data into the mux, 0 : keep a link to local data + + if(!dib || !handle || !data) { + return FALSE; + } + + try { + + // get the MUX object + mux = (WebPMux*)data; + if(!mux) { + return FALSE; + } + + // --- prepare image data --- + + // encode image as a WebP blob + hmem = FreeImage_OpenMemory(); + if(!hmem || !EncodeImage(hmem, dib, flags)) { + throw (1); + } + // store the blob into the mux + BYTE *data = NULL; + DWORD data_size = 0; + FreeImage_AcquireMemory(hmem, &data, &data_size); + webp_image.bytes = data; + webp_image.size = data_size; + error_status = WebPMuxSetImage(mux, &webp_image, copy_data); + // no longer needed since copy_data == 1 + FreeImage_CloseMemory(hmem); + hmem = NULL; + if(error_status != WEBP_MUX_OK) { + throw (1); + } + + // --- set metadata --- + + // set ICC color profile + { + FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); + if (iccProfile->size && iccProfile->data) { + WebPData icc_profile; + icc_profile.bytes = (uint8_t*)iccProfile->data; + icc_profile.size = (size_t)iccProfile->size; + error_status = WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data); + if(error_status != WEBP_MUX_OK) { + throw (1); + } + } + } + + // set XMP metadata + { + FITAG *tag = NULL; + if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag)) { + WebPData xmp_profile; + xmp_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag); + xmp_profile.size = (size_t)FreeImage_GetTagLength(tag); + error_status = WebPMuxSetChunk(mux, "XMP ", &xmp_profile, copy_data); + if(error_status != WEBP_MUX_OK) { + throw (1); + } + } + } + + // set Exif metadata + { + FITAG *tag = NULL; + if(FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag)) { + WebPData exif_profile; + exif_profile.bytes = (uint8_t*)FreeImage_GetTagValue(tag); + exif_profile.size = (size_t)FreeImage_GetTagLength(tag); + error_status = WebPMuxSetChunk(mux, "EXIF", &exif_profile, copy_data); + if(error_status != WEBP_MUX_OK) { + throw (1); + } + } + } + + // get data from mux in WebP RIFF format + error_status = WebPMuxAssemble(mux, &output_data); + if(error_status != WEBP_MUX_OK) { + FreeImage_OutputMessageProc(s_format_id, "Failed to create webp output file"); + throw (1); + } + + // write the file to the output stream + if(io->write_proc((void*)output_data.bytes, 1, (unsigned)output_data.size, handle) != output_data.size) { + FreeImage_OutputMessageProc(s_format_id, "Failed to write webp output file"); + throw (1); + } + + // free WebP output file + WebPDataClear(&output_data); + + return TRUE; + + } catch(int) { + if(hmem) { + FreeImage_CloseMemory(hmem); + } + + WebPDataClear(&output_data); + + return FALSE; + } +} + +// ========================================================== +// Init +// ========================================================== + +void DLL_CALLCONV +InitWEBP(Plugin *plugin, int format_id) { + s_format_id = format_id; + + plugin->format_proc = Format; + plugin->description_proc = Description; + plugin->extension_proc = Extension; + plugin->regexpr_proc = RegExpr; + plugin->open_proc = Open; + plugin->close_proc = Close; + plugin->pagecount_proc = NULL; + plugin->pagecapability_proc = NULL; + plugin->load_proc = Load; + plugin->save_proc = Save; + plugin->validate_proc = Validate; + plugin->mime_proc = MimeType; + plugin->supports_export_bpp_proc = SupportsExportDepth; + plugin->supports_export_type_proc = SupportsExportType; + plugin->supports_icc_profiles_proc = SupportsICCProfiles; + plugin->supports_no_pixels_proc = SupportsNoPixels; +} + diff --git a/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp b/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp index 66869b2806..6b103a08c1 100644 --- a/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp +++ b/plugins/AdvaImg/src/FreeImage/tmoColorConvert.cpp @@ -1,479 +1,479 @@ -// ========================================================== -// High Dynamic Range bitmap conversion routines -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// - Mihail Naydenov (mnaydenov@users.sourceforge.net) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" -#include "ToneMapping.h" - -// ---------------------------------------------------------- -// Convert RGB to and from Yxy, same as in Reinhard et al. SIGGRAPH 2002 -// References : -// [1] Radiance Home Page [Online] http://radsite.lbl.gov/radiance/HOME.html -// [2] E. Reinhard, M. Stark, P. Shirley, and J. Ferwerda, -// Photographic Tone Reproduction for Digital Images, ACM Transactions on Graphics, -// 21(3):267-276, 2002 (Proceedings of SIGGRAPH 2002). -// [3] J. Tumblin and H.E. Rushmeier, -// Tone Reproduction for Realistic Images. IEEE Computer Graphics and Applications, -// 13(6):42-48, 1993. -// ---------------------------------------------------------- - -/** -nominal CRT primaries -*/ -/* -static const float CIE_x_r = 0.640F; -static const float CIE_y_r = 0.330F; -static const float CIE_x_g = 0.290F; -static const float CIE_y_g = 0.600F; -static const float CIE_x_b = 0.150F; -static const float CIE_y_b = 0.060F; -static const float CIE_x_w = 0.3333F; // use true white -static const float CIE_y_w = 0.3333F; -*/ -/** -sRGB primaries -*/ -static const float CIE_x_r = 0.640F; -static const float CIE_y_r = 0.330F; -static const float CIE_x_g = 0.300F; -static const float CIE_y_g = 0.600F; -static const float CIE_x_b = 0.150F; -static const float CIE_y_b = 0.060F; -static const float CIE_x_w = 0.3127F; // Illuminant D65 -static const float CIE_y_w = 0.3290F; - -static const float CIE_D = ( CIE_x_r*(CIE_y_g - CIE_y_b) + CIE_x_g*(CIE_y_b - CIE_y_r) + CIE_x_b*(CIE_y_r - CIE_y_g) ); -static const float CIE_C_rD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_g - CIE_y_b) - CIE_y_w*(CIE_x_g - CIE_x_b) + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) ); -static const float CIE_C_gD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_b - CIE_y_r) - CIE_y_w*(CIE_x_b - CIE_x_r) - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) ); -static const float CIE_C_bD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_r - CIE_y_g) - CIE_y_w*(CIE_x_r - CIE_x_g) + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) ); - -/** -RGB to XYZ (no white balance) -*/ -static const float RGB2XYZ[3][3] = { - { CIE_x_r*CIE_C_rD / CIE_D, - CIE_x_g*CIE_C_gD / CIE_D, - CIE_x_b*CIE_C_bD / CIE_D - }, - { CIE_y_r*CIE_C_rD / CIE_D, - CIE_y_g*CIE_C_gD / CIE_D, - CIE_y_b*CIE_C_bD / CIE_D - }, - { (1 - CIE_x_r-CIE_y_r)*CIE_C_rD / CIE_D, - (1 - CIE_x_g-CIE_y_g)*CIE_C_gD / CIE_D, - (1 - CIE_x_b-CIE_y_b)*CIE_C_bD / CIE_D - } -}; - -/** -XYZ to RGB (no white balance) -*/ -static const float XYZ2RGB[3][3] = { - {(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g) / CIE_C_rD, - (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b) / CIE_C_rD, - (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) / CIE_C_rD - }, - {(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b) / CIE_C_gD, - (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) / CIE_C_gD, - (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b) / CIE_C_gD - }, - {(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r) / CIE_C_bD, - (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g) / CIE_C_bD, - (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) / CIE_C_bD - } -}; - -/** -This gives approximately the following matrices : - -static const float RGB2XYZ[3][3] = { - { 0.41239083F, 0.35758433F, 0.18048081F }, - { 0.21263903F, 0.71516865F, 0.072192319F }, - { 0.019330820F, 0.11919473F, 0.95053220F } -}; -static const float XYZ2RGB[3][3] = { - { 3.2409699F, -1.5373832F, -0.49861079F }, - { -0.96924376F, 1.8759676F, 0.041555084F }, - { 0.055630036F, -0.20397687F, 1.0569715F } -}; -*/ - -// ---------------------------------------------------------- - -static const float EPSILON = 1e-06F; -static const float INF = 1e+10F; - -/** -Convert in-place floating point RGB data to Yxy.
-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.
-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.
-On input, pixel->red == Y, pixel->green == x, pixel->blue == y -@param Yxy Source Yxy image to analyze -@param maxLum Maximum luminance -@param minLum Minimum luminance -@param worldLum Average luminance (world adaptation luminance) -@return Returns TRUE if successful, returns FALSE otherwise -*/ -BOOL -LuminanceFromYxy(FIBITMAP *Yxy, float *maxLum, float *minLum, float *worldLum) { - if(FreeImage_GetImageType(Yxy) != FIT_RGBF) - return FALSE; - - const unsigned width = FreeImage_GetWidth(Yxy); - const unsigned height = FreeImage_GetHeight(Yxy); - const unsigned pitch = FreeImage_GetPitch(Yxy); - - float max_lum = 0, min_lum = 0; - double sum = 0; - - BYTE *bits = (BYTE*)FreeImage_GetBits(Yxy); - for(unsigned y = 0; y < height; y++) { - const FIRGBF *pixel = (FIRGBF*)bits; - for(unsigned x = 0; x < width; x++) { - const float Y = pixel[x].red; - max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene - min_lum = (min_lum < Y) ? min_lum : Y; // min Luminance in the scene - sum += log(2.3e-5F + Y); // contrast constant in Tumblin paper - } - // next line - bits += pitch; - } - // maximum luminance - *maxLum = max_lum; - // minimum luminance - *minLum = min_lum; - // average log luminance - double avgLogLum = (sum / (width * height)); - // world adaptation luminance - *worldLum = (float)exp(avgLogLum); - - return TRUE; -} - -/** -Clamp RGBF image highest values to display white, -then convert to 24-bit RGB -*/ -FIBITMAP* -ClampConvertRGBFTo24(FIBITMAP *src) { - if(FreeImage_GetImageType(src) != FIT_RGBF) - return FALSE; - - const unsigned width = FreeImage_GetWidth(src); - const unsigned height = FreeImage_GetHeight(src); - - FIBITMAP *dst = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - if(!dst) return NULL; - - const unsigned src_pitch = FreeImage_GetPitch(src); - const unsigned dst_pitch = FreeImage_GetPitch(dst); - - BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const FIRGBF *src_pixel = (FIRGBF*)src_bits; - BYTE *dst_pixel = (BYTE*)dst_bits; - for(unsigned x = 0; x < width; x++) { - const float red = (src_pixel[x].red > 1) ? 1 : src_pixel[x].red; - const float green = (src_pixel[x].green > 1) ? 1 : src_pixel[x].green; - const float blue = (src_pixel[x].blue > 1) ? 1 : src_pixel[x].blue; - - dst_pixel[FI_RGBA_RED] = (BYTE)(255.0F * red + 0.5F); - dst_pixel[FI_RGBA_GREEN] = (BYTE)(255.0F * green + 0.5F); - dst_pixel[FI_RGBA_BLUE] = (BYTE)(255.0F * blue + 0.5F); - dst_pixel += 3; - } - src_bits += src_pitch; - dst_bits += dst_pitch; - } - - return dst; -} - -/** -Extract the luminance channel L from a RGBF image. -Luminance is calculated from the sRGB model (RGB2XYZ matrix) -using a D65 white point : -L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b ) -Reference : -A Standard Default Color Space for the Internet - sRGB. -[online] http://www.w3.org/Graphics/Color/sRGB -*/ -FIBITMAP* -ConvertRGBFToY(FIBITMAP *src) { - if(FreeImage_GetImageType(src) != FIT_RGBF) - return FALSE; - - const unsigned width = FreeImage_GetWidth(src); - const unsigned height = FreeImage_GetHeight(src); - - FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height); - if(!dst) return NULL; - - const unsigned src_pitch = FreeImage_GetPitch(src); - const unsigned dst_pitch = FreeImage_GetPitch(dst); - - - BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); - BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); - - for(unsigned y = 0; y < height; y++) { - const FIRGBF *src_pixel = (FIRGBF*)src_bits; - float *dst_pixel = (float*)dst_bits; - for(unsigned x = 0; x < width; x++) { - const float L = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue); - dst_pixel[x] = (L > 0) ? L : 0; - } - // next line - src_bits += src_pitch; - dst_bits += dst_pitch; - } - - return dst; -} - -/** -Get the maximum, minimum, average luminance and log average luminance from a Y image -@param dib Source Y image to analyze -@param maxLum Maximum luminance -@param minLum Minimum luminance -@param Lav Average luminance -@param Llav Log average luminance (also known as 'world adaptation luminance') -@return Returns TRUE if successful, returns FALSE otherwise -@see ConvertRGBFToY, FreeImage_TmoReinhard05Ex -*/ -BOOL -LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav) { - if(FreeImage_GetImageType(dib) != FIT_FLOAT) - return FALSE; - - unsigned width = FreeImage_GetWidth(dib); - unsigned height = FreeImage_GetHeight(dib); - unsigned pitch = FreeImage_GetPitch(dib); - - float max_lum = -1e20F, min_lum = 1e20F; - double sumLum = 0, sumLogLum = 0; - - BYTE *bits = (BYTE*)FreeImage_GetBits(dib); - for(unsigned y = 0; y < height; y++) { - const float *pixel = (float*)bits; - for(unsigned x = 0; x < width; x++) { - const float Y = pixel[x]; - max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene - min_lum = ((Y > 0) && (min_lum < Y)) ? min_lum : Y; // min Luminance in the scene - sumLum += Y; // average luminance - sumLogLum += log(2.3e-5F + Y); // contrast constant in Tumblin paper - } - // next line - bits += pitch; - } - - // maximum luminance - *maxLum = max_lum; - // minimum luminance - *minLum = min_lum; - // average luminance - *Lav = (float)(sumLum / (width * height)); - // average log luminance, a.k.a. world adaptation luminance - *Llav = (float)exp(sumLogLum / (width * height)); - - return TRUE; -} -// -------------------------------------------------------------------------- - -static void findMaxMinPercentile(FIBITMAP *Y, float minPrct, float *minLum, float maxPrct, float *maxLum) { - int x, y; - int width = FreeImage_GetWidth(Y); - int height = FreeImage_GetHeight(Y); - int pitch = FreeImage_GetPitch(Y); - - std::vector 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
-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; - } -} +// ========================================================== +// High Dynamic Range bitmap conversion routines +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Mihail Naydenov (mnaydenov@users.sourceforge.net) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "ToneMapping.h" + +// ---------------------------------------------------------- +// Convert RGB to and from Yxy, same as in Reinhard et al. SIGGRAPH 2002 +// References : +// [1] Radiance Home Page [Online] http://radsite.lbl.gov/radiance/HOME.html +// [2] E. Reinhard, M. Stark, P. Shirley, and J. Ferwerda, +// Photographic Tone Reproduction for Digital Images, ACM Transactions on Graphics, +// 21(3):267-276, 2002 (Proceedings of SIGGRAPH 2002). +// [3] J. Tumblin and H.E. Rushmeier, +// Tone Reproduction for Realistic Images. IEEE Computer Graphics and Applications, +// 13(6):42-48, 1993. +// ---------------------------------------------------------- + +/** +nominal CRT primaries +*/ +/* +static const float CIE_x_r = 0.640F; +static const float CIE_y_r = 0.330F; +static const float CIE_x_g = 0.290F; +static const float CIE_y_g = 0.600F; +static const float CIE_x_b = 0.150F; +static const float CIE_y_b = 0.060F; +static const float CIE_x_w = 0.3333F; // use true white +static const float CIE_y_w = 0.3333F; +*/ +/** +sRGB primaries +*/ +static const float CIE_x_r = 0.640F; +static const float CIE_y_r = 0.330F; +static const float CIE_x_g = 0.300F; +static const float CIE_y_g = 0.600F; +static const float CIE_x_b = 0.150F; +static const float CIE_y_b = 0.060F; +static const float CIE_x_w = 0.3127F; // Illuminant D65 +static const float CIE_y_w = 0.3290F; + +static const float CIE_D = ( CIE_x_r*(CIE_y_g - CIE_y_b) + CIE_x_g*(CIE_y_b - CIE_y_r) + CIE_x_b*(CIE_y_r - CIE_y_g) ); +static const float CIE_C_rD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_g - CIE_y_b) - CIE_y_w*(CIE_x_g - CIE_x_b) + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) ); +static const float CIE_C_gD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_b - CIE_y_r) - CIE_y_w*(CIE_x_b - CIE_x_r) - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) ); +static const float CIE_C_bD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_r - CIE_y_g) - CIE_y_w*(CIE_x_r - CIE_x_g) + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) ); + +/** +RGB to XYZ (no white balance) +*/ +static const float RGB2XYZ[3][3] = { + { CIE_x_r*CIE_C_rD / CIE_D, + CIE_x_g*CIE_C_gD / CIE_D, + CIE_x_b*CIE_C_bD / CIE_D + }, + { CIE_y_r*CIE_C_rD / CIE_D, + CIE_y_g*CIE_C_gD / CIE_D, + CIE_y_b*CIE_C_bD / CIE_D + }, + { (1 - CIE_x_r-CIE_y_r)*CIE_C_rD / CIE_D, + (1 - CIE_x_g-CIE_y_g)*CIE_C_gD / CIE_D, + (1 - CIE_x_b-CIE_y_b)*CIE_C_bD / CIE_D + } +}; + +/** +XYZ to RGB (no white balance) +*/ +static const float XYZ2RGB[3][3] = { + {(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g) / CIE_C_rD, + (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b) / CIE_C_rD, + (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) / CIE_C_rD + }, + {(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b) / CIE_C_gD, + (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) / CIE_C_gD, + (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b) / CIE_C_gD + }, + {(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r) / CIE_C_bD, + (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g) / CIE_C_bD, + (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) / CIE_C_bD + } +}; + +/** +This gives approximately the following matrices : + +static const float RGB2XYZ[3][3] = { + { 0.41239083F, 0.35758433F, 0.18048081F }, + { 0.21263903F, 0.71516865F, 0.072192319F }, + { 0.019330820F, 0.11919473F, 0.95053220F } +}; +static const float XYZ2RGB[3][3] = { + { 3.2409699F, -1.5373832F, -0.49861079F }, + { -0.96924376F, 1.8759676F, 0.041555084F }, + { 0.055630036F, -0.20397687F, 1.0569715F } +}; +*/ + +// ---------------------------------------------------------- + +static const float EPSILON = 1e-06F; +static const float INF = 1e+10F; + +/** +Convert in-place floating point RGB data to Yxy.
+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.
+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.
+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 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
+Remove any extremely bright and/or extremely dark pixels +and normalize between 0 and 1. +@param Y Input/Output image +@param minPrct Minimum percentile +@param maxPrct Maximum percentile +*/ +void +NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct) { + int x, y; + float maxLum, minLum; + + if(minPrct > maxPrct) { + // swap values + float t = minPrct; minPrct = maxPrct; maxPrct = t; + } + if(minPrct < 0) minPrct = 0; + if(maxPrct > 1) maxPrct = 1; + + int width = FreeImage_GetWidth(Y); + int height = FreeImage_GetHeight(Y); + int pitch = FreeImage_GetPitch(Y); + + // find max & min luminance values + if((minPrct > 0) || (maxPrct < 1)) { + maxLum = 0, minLum = 0; + findMaxMinPercentile(Y, minPrct, &minLum, maxPrct, &maxLum); + } else { + maxLum = -1e20F, minLum = 1e20F; + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + const float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + const float value = pixel[x]; + maxLum = (maxLum < value) ? value : maxLum; // max Luminance in the scene + minLum = (minLum < value) ? minLum : value; // min Luminance in the scene + } + // next line + bits += pitch; + } + } + if(maxLum == minLum) return; + + // normalize to range 0..1 + const float divider = maxLum - minLum; + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + pixel[x] = (pixel[x] - minLum) / divider; + if(pixel[x] <= 0) pixel[x] = EPSILON; + if(pixel[x] > 1) pixel[x] = 1; + } + // next line + bits += pitch; + } +} diff --git a/plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp b/plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp index a61534474d..d46a8c8426 100644 --- a/plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp +++ b/plugins/AdvaImg/src/FreeImage/tmoDrago03.cpp @@ -1,295 +1,295 @@ -// ========================================================== -// Tone mapping operator (Drago, 2003) -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" -#include "ToneMapping.h" - -// ---------------------------------------------------------- -// Logarithmic mapping operator -// Reference: -// [1] F. Drago, K. Myszkowski, T. Annen, and N. Chiba, -// Adaptive Logarithmic Mapping for Displaying High Contrast Scenes, -// Eurographics 2003. -// ---------------------------------------------------------- - -/** -Bias function -*/ -static inline double -biasFunction(const double b, const double x) { - return pow (x, b); // pow(x, log(bias)/log(0.5) -} - -/** -Padé approximation of log(x + 1) -x(6+x)/(6+4x) good if x < 1 -x*(6 + 0.7662x)/(5.9897 + 3.7658x) between 1 and 2 -See http://www.nezumi.demon.co.uk/consult/logx.htm -*/ -static inline double -pade_log(const double x) { - if(x < 1) { - return (x * (6 + x) / (6 + 4 * x)); - } else if(x < 2) { - return (x * (6 + 0.7662 * x) / (5.9897 + 3.7658 * x)); - } - return log(x + 1); -} - -/** -Log mapping operator -@param dib Input / Output Yxy image -@param maxLum Maximum luminance -@param avgLum Average luminance (world adaptation luminance) -@param biasParam Bias parameter (a zero value default to 0.85) -@param exposure Exposure parameter (default to 0) -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL -ToneMappingDrago03(FIBITMAP *dib, const float maxLum, const float avgLum, float biasParam, const float exposure) { - const float LOG05 = -0.693147F; // log(0.5) - - double Lmax, divider, interpol, biasP; - unsigned x, y; - double L; - - if(FreeImage_GetImageType(dib) != FIT_RGBF) - return FALSE; - - const unsigned width = FreeImage_GetWidth(dib); - const unsigned height = FreeImage_GetHeight(dib); - const unsigned pitch = FreeImage_GetPitch(dib); - - - // arbitrary Bias Parameter - if(biasParam == 0) - biasParam = 0.85F; - - // normalize maximum luminance by average luminance - Lmax = maxLum / avgLum; - - divider = log10(Lmax+1); - biasP = log(biasParam)/LOG05; - -#if !defined(DRAGO03_FAST) - - /** - Normal tone mapping of every pixel - further acceleration is obtained by a Padé approximation of log(x + 1) - */ - BYTE *bits = (BYTE*)FreeImage_GetBits(dib); - for(y = 0; y < height; y++) { - FIRGBF *pixel = (FIRGBF*)bits; - for(x = 0; x < width; x++) { - double Yw = pixel[x].red / avgLum; - Yw *= exposure; - interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8); - L = pade_log(Yw);// log(Yw + 1) - pixel[x].red = (float)((L / interpol) / divider); - } - // next line - bits += pitch; - } - -#else - unsigned index; - int i, j; - - unsigned max_width = width - (width % 3); - unsigned max_height = height - (height % 3); - unsigned fpitch = pitch / sizeof(FIRGBF); - - /** - fast tone mapping - split the image into 3x3 pixel tiles and perform the computation for each group of 9 pixels - further acceleration is obtained by a Padé approximation of log(x + 1) - => produce artifacts and not so faster, so the code has been disabled - */ -#define PIXEL(x, y) image[y*fpitch + x].red - - FIRGBF *image = (FIRGBF*)FreeImage_GetBits(dib); - for(y = 0; y < max_height; y += 3) { - for(x = 0; x < max_width; x += 3) { - double average = 0; - for(i = 0; i < 3; i++) { - for(j = 0; j < 3; j++) { - index = (y + i)*fpitch + (x + j); - image[index].red /= (float)avgLum; - image[index].red *= exposure; - average += image[index].red; - } - } - average = average / 9 - PIXEL(x, y); - if(average > -1 && average < 1) { - interpol = log(2 + pow(PIXEL(x + 1, y + 1) / Lmax, biasP) * 8); - for(i = 0; i < 3; i++) { - for(j = 0; j < 3; j++) { - index = (y + i)*fpitch + (x + j); - L = pade_log(image[index].red);// log(image[index].red + 1) - image[index].red = (float)((L / interpol) / divider); - } - } - } - else { - for(i = 0; i < 3; i++) { - for(j = 0; j < 3; j++) { - index = (y + i)*fpitch + (x + j); - interpol = log(2 + pow(image[index].red / Lmax, biasP) * 8); - L = pade_log(image[index].red);// log(image[index].red + 1) - image[index].red = (float)((L / interpol) / divider); - } - } - } - } //x - } // y - - /** - Normal tone mapping of every pixel for the remaining right and bottom bands - */ - BYTE *bits; - - // right band - bits = (BYTE*)FreeImage_GetBits(dib); - for(y = 0; y < height; y++) { - FIRGBF *pixel = (FIRGBF*)bits; - for(x = max_width; x < width; x++) { - double Yw = pixel[x].red / avgLum; - Yw *= exposure; - interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8); - L = pade_log(Yw);// log(Yw + 1) - pixel[x].red = (float)((L / interpol) / divider); - } - // next line - bits += pitch; - } - // bottom band - bits = (BYTE*)FreeImage_GetBits(dib); - for(y = max_height; y < height; y++) { - FIRGBF *pixel = (FIRGBF*)bits; - for(x = 0; x < max_width; x++) { - double Yw = pixel[x].red / avgLum; - Yw *= exposure; - interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8); - L = pade_log(Yw);// log(Yw + 1) - pixel[x].red = (float)((L / interpol) / divider); - } - // next line - bits += pitch; - } - -#endif // DRAGO03_FAST - - return TRUE; -} - -/** -Custom gamma correction based on the ITU-R BT.709 standard -@param dib RGBF image to be corrected -@param gammaval Gamma value (2.2 is a good default value) -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL -REC709GammaCorrection(FIBITMAP *dib, const float gammaval) { - if(FreeImage_GetImageType(dib) != FIT_RGBF) - return FALSE; - - float slope = 4.5F; - float start = 0.018F; - - const float fgamma = (float)((0.45 / gammaval) * 2); - if(gammaval >= 2.1F) { - start = (float)(0.018 / ((gammaval - 2) * 7.5)); - slope = (float)(4.5 * ((gammaval - 2) * 7.5)); - } else if (gammaval <= 1.9F) { - start = (float)(0.018 * ((2 - gammaval) * 7.5)); - slope = (float)(4.5 / ((2 - gammaval) * 7.5)); - } - - const unsigned width = FreeImage_GetWidth(dib); - const unsigned height = FreeImage_GetHeight(dib); - const unsigned pitch = FreeImage_GetPitch(dib); - - BYTE *bits = (BYTE*)FreeImage_GetBits(dib); - for(unsigned y = 0; y < height; y++) { - float *pixel = (float*)bits; - for(unsigned x = 0; x < width; x++) { - for(int i = 0; i < 3; i++) { - *pixel = (*pixel <= start) ? *pixel * slope : (1.099F * pow(*pixel, fgamma) - 0.099F); - pixel++; - } - } - bits += pitch; - } - - return TRUE; -} - -// ---------------------------------------------------------- -// Main algorithm -// ---------------------------------------------------------- - -/** -Apply the Adaptive Logarithmic Mapping operator to a HDR image and convert to 24-bit RGB -@param src Input RGB16 or RGB[A]F image -@param gamma Gamma correction (gamma > 0). 1 means no correction, 2.2 in the original paper. -@param exposure Exposure parameter (0 means no correction, 0 in the original paper) -@return Returns a 24-bit RGB image if successful, returns NULL otherwise -*/ -FIBITMAP* DLL_CALLCONV -FreeImage_TmoDrago03(FIBITMAP *src, double gamma, double exposure) { - float maxLum, minLum, avgLum; - - if(!FreeImage_HasPixels(src)) return NULL; - - // working RGBF variable - FIBITMAP *dib = NULL; - - dib = FreeImage_ConvertToRGBF(src); - if(!dib) return NULL; - - // default algorithm parameters - const float biasParam = 0.85F; - const float expoParam = (float)pow(2.0, exposure); //default exposure is 1, 2^0 - - // convert to Yxy - ConvertInPlaceRGBFToYxy(dib); - // get the luminance - LuminanceFromYxy(dib, &maxLum, &minLum, &avgLum); - // perform the tone mapping - ToneMappingDrago03(dib, maxLum, avgLum, biasParam, expoParam); - // convert back to RGBF - ConvertInPlaceYxyToRGBF(dib); - if(gamma != 1) { - // perform gamma correction - REC709GammaCorrection(dib, (float)gamma); - } - // clamp image highest values to display white, then convert to 24-bit RGB - FIBITMAP *dst = ClampConvertRGBFTo24(dib); - - // clean-up and return - FreeImage_Unload(dib); - - // copy metadata from src to dst - FreeImage_CloneMetadata(dst, src); - - return dst; -} +// ========================================================== +// Tone mapping operator (Drago, 2003) +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "ToneMapping.h" + +// ---------------------------------------------------------- +// Logarithmic mapping operator +// Reference: +// [1] F. Drago, K. Myszkowski, T. Annen, and N. Chiba, +// Adaptive Logarithmic Mapping for Displaying High Contrast Scenes, +// Eurographics 2003. +// ---------------------------------------------------------- + +/** +Bias function +*/ +static inline double +biasFunction(const double b, const double x) { + return pow (x, b); // pow(x, log(bias)/log(0.5) +} + +/** +Padé approximation of log(x + 1) +x(6+x)/(6+4x) good if x < 1 +x*(6 + 0.7662x)/(5.9897 + 3.7658x) between 1 and 2 +See http://www.nezumi.demon.co.uk/consult/logx.htm +*/ +static inline double +pade_log(const double x) { + if(x < 1) { + return (x * (6 + x) / (6 + 4 * x)); + } else if(x < 2) { + return (x * (6 + 0.7662 * x) / (5.9897 + 3.7658 * x)); + } + return log(x + 1); +} + +/** +Log mapping operator +@param dib Input / Output Yxy image +@param maxLum Maximum luminance +@param avgLum Average luminance (world adaptation luminance) +@param biasParam Bias parameter (a zero value default to 0.85) +@param exposure Exposure parameter (default to 0) +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static BOOL +ToneMappingDrago03(FIBITMAP *dib, const float maxLum, const float avgLum, float biasParam, const float exposure) { + const float LOG05 = -0.693147F; // log(0.5) + + double Lmax, divider, interpol, biasP; + unsigned x, y; + double L; + + if(FreeImage_GetImageType(dib) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + + // arbitrary Bias Parameter + if(biasParam == 0) + biasParam = 0.85F; + + // normalize maximum luminance by average luminance + Lmax = maxLum / avgLum; + + divider = log10(Lmax+1); + biasP = log(biasParam)/LOG05; + +#if !defined(DRAGO03_FAST) + + /** + Normal tone mapping of every pixel + further acceleration is obtained by a Padé approximation of log(x + 1) + */ + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(y = 0; y < height; y++) { + FIRGBF *pixel = (FIRGBF*)bits; + for(x = 0; x < width; x++) { + double Yw = pixel[x].red / avgLum; + Yw *= exposure; + interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8); + L = pade_log(Yw);// log(Yw + 1) + pixel[x].red = (float)((L / interpol) / divider); + } + // next line + bits += pitch; + } + +#else + unsigned index; + int i, j; + + unsigned max_width = width - (width % 3); + unsigned max_height = height - (height % 3); + unsigned fpitch = pitch / sizeof(FIRGBF); + + /** + fast tone mapping + split the image into 3x3 pixel tiles and perform the computation for each group of 9 pixels + further acceleration is obtained by a Padé approximation of log(x + 1) + => produce artifacts and not so faster, so the code has been disabled + */ +#define PIXEL(x, y) image[y*fpitch + x].red + + FIRGBF *image = (FIRGBF*)FreeImage_GetBits(dib); + for(y = 0; y < max_height; y += 3) { + for(x = 0; x < max_width; x += 3) { + double average = 0; + for(i = 0; i < 3; i++) { + for(j = 0; j < 3; j++) { + index = (y + i)*fpitch + (x + j); + image[index].red /= (float)avgLum; + image[index].red *= exposure; + average += image[index].red; + } + } + average = average / 9 - PIXEL(x, y); + if(average > -1 && average < 1) { + interpol = log(2 + pow(PIXEL(x + 1, y + 1) / Lmax, biasP) * 8); + for(i = 0; i < 3; i++) { + for(j = 0; j < 3; j++) { + index = (y + i)*fpitch + (x + j); + L = pade_log(image[index].red);// log(image[index].red + 1) + image[index].red = (float)((L / interpol) / divider); + } + } + } + else { + for(i = 0; i < 3; i++) { + for(j = 0; j < 3; j++) { + index = (y + i)*fpitch + (x + j); + interpol = log(2 + pow(image[index].red / Lmax, biasP) * 8); + L = pade_log(image[index].red);// log(image[index].red + 1) + image[index].red = (float)((L / interpol) / divider); + } + } + } + } //x + } // y + + /** + Normal tone mapping of every pixel for the remaining right and bottom bands + */ + BYTE *bits; + + // right band + bits = (BYTE*)FreeImage_GetBits(dib); + for(y = 0; y < height; y++) { + FIRGBF *pixel = (FIRGBF*)bits; + for(x = max_width; x < width; x++) { + double Yw = pixel[x].red / avgLum; + Yw *= exposure; + interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8); + L = pade_log(Yw);// log(Yw + 1) + pixel[x].red = (float)((L / interpol) / divider); + } + // next line + bits += pitch; + } + // bottom band + bits = (BYTE*)FreeImage_GetBits(dib); + for(y = max_height; y < height; y++) { + FIRGBF *pixel = (FIRGBF*)bits; + for(x = 0; x < max_width; x++) { + double Yw = pixel[x].red / avgLum; + Yw *= exposure; + interpol = log(2 + biasFunction(biasP, Yw / Lmax) * 8); + L = pade_log(Yw);// log(Yw + 1) + pixel[x].red = (float)((L / interpol) / divider); + } + // next line + bits += pitch; + } + +#endif // DRAGO03_FAST + + return TRUE; +} + +/** +Custom gamma correction based on the ITU-R BT.709 standard +@param dib RGBF image to be corrected +@param gammaval Gamma value (2.2 is a good default value) +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static BOOL +REC709GammaCorrection(FIBITMAP *dib, const float gammaval) { + if(FreeImage_GetImageType(dib) != FIT_RGBF) + return FALSE; + + float slope = 4.5F; + float start = 0.018F; + + const float fgamma = (float)((0.45 / gammaval) * 2); + if(gammaval >= 2.1F) { + start = (float)(0.018 / ((gammaval - 2) * 7.5)); + slope = (float)(4.5 * ((gammaval - 2) * 7.5)); + } else if (gammaval <= 1.9F) { + start = (float)(0.018 * ((2 - gammaval) * 7.5)); + slope = (float)(4.5 / ((2 - gammaval) * 7.5)); + } + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(unsigned y = 0; y < height; y++) { + float *pixel = (float*)bits; + for(unsigned x = 0; x < width; x++) { + for(int i = 0; i < 3; i++) { + *pixel = (*pixel <= start) ? *pixel * slope : (1.099F * pow(*pixel, fgamma) - 0.099F); + pixel++; + } + } + bits += pitch; + } + + return TRUE; +} + +// ---------------------------------------------------------- +// Main algorithm +// ---------------------------------------------------------- + +/** +Apply the Adaptive Logarithmic Mapping operator to a HDR image and convert to 24-bit RGB +@param src Input RGB16 or RGB[A]F image +@param gamma Gamma correction (gamma > 0). 1 means no correction, 2.2 in the original paper. +@param exposure Exposure parameter (0 means no correction, 0 in the original paper) +@return Returns a 24-bit RGB image if successful, returns NULL otherwise +*/ +FIBITMAP* DLL_CALLCONV +FreeImage_TmoDrago03(FIBITMAP *src, double gamma, double exposure) { + float maxLum, minLum, avgLum; + + if(!FreeImage_HasPixels(src)) return NULL; + + // working RGBF variable + FIBITMAP *dib = NULL; + + dib = FreeImage_ConvertToRGBF(src); + if(!dib) return NULL; + + // default algorithm parameters + const float biasParam = 0.85F; + const float expoParam = (float)pow(2.0, exposure); //default exposure is 1, 2^0 + + // convert to Yxy + ConvertInPlaceRGBFToYxy(dib); + // get the luminance + LuminanceFromYxy(dib, &maxLum, &minLum, &avgLum); + // perform the tone mapping + ToneMappingDrago03(dib, maxLum, avgLum, biasParam, expoParam); + // convert back to RGBF + ConvertInPlaceYxyToRGBF(dib); + if(gamma != 1) { + // perform gamma correction + REC709GammaCorrection(dib, (float)gamma); + } + // clamp image highest values to display white, then convert to 24-bit RGB + FIBITMAP *dst = ClampConvertRGBFTo24(dib); + + // clean-up and return + FreeImage_Unload(dib); + + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, src); + + return dst; +} diff --git a/plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp b/plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp index 88f054412a..c63f91f762 100644 --- a/plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp +++ b/plugins/AdvaImg/src/FreeImage/tmoFattal02.cpp @@ -1,689 +1,689 @@ -// ========================================================== -// Tone mapping operator (Fattal, 2002) -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" -#include "ToneMapping.h" - -// ---------------------------------------------------------- -// Gradient domain HDR compression -// Reference: -// [1] R. Fattal, D. Lischinski, and M.Werman, -// Gradient domain high dynamic range compression, -// ACM Transactions on Graphics, special issue on Proc. of ACM SIGGRAPH 2002, -// San Antonio, Texas, vol. 21(3), pp. 257-266, 2002. -// ---------------------------------------------------------- - -static const float EPSILON = 1e-4F; - -/** -Performs a 5 by 5 gaussian filtering using two 1D convolutions, -followed by a subsampling by 2. -@param dib Input image -@return Returns a blurred image of size SIZE(dib)/2 -@see GaussianPyramid -*/ -static FIBITMAP* GaussianLevel5x5(FIBITMAP *dib) { - FIBITMAP *h_dib = NULL, *v_dib = NULL, *dst = NULL; - float *src_pixel, *dst_pixel; - - try { - const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); - if(image_type != FIT_FLOAT) throw(1); - - const unsigned width = FreeImage_GetWidth(dib); - const unsigned height = FreeImage_GetHeight(dib); - - h_dib = FreeImage_AllocateT(image_type, width, height); - v_dib = FreeImage_AllocateT(image_type, width, height); - if(!h_dib || !v_dib) throw(1); - - const unsigned pitch = FreeImage_GetPitch(dib) / sizeof(float); - - // horizontal convolution dib -> h_dib - - src_pixel = (float*)FreeImage_GetBits(dib); - dst_pixel = (float*)FreeImage_GetBits(h_dib); - - for(unsigned y = 0; y < height; y++) { - // work on line y - for(unsigned x = 2; x < width - 2; x++) { - dst_pixel[x] = src_pixel[x-2] + src_pixel[x+2] + 4 * (src_pixel[x-1] + src_pixel[x+1]) + 6 * src_pixel[x]; - dst_pixel[x] /= 16; - } - // boundary mirroring - dst_pixel[0] = (2 * src_pixel[2] + 8 * src_pixel[1] + 6 * src_pixel[0]) / 16; - dst_pixel[1] = (src_pixel[3] + 4 * (src_pixel[0] + src_pixel[2]) + 7 * src_pixel[1]) / 16; - dst_pixel[width-2] = (src_pixel[width-4] + 5 * src_pixel[width-1] + 4 * src_pixel[width-3] + 6 * src_pixel[width-2]) / 16; - dst_pixel[width-1] = (src_pixel[width-3] + 5 * src_pixel[width-2] + 10 * src_pixel[width-1]) / 16; - - // next line - src_pixel += pitch; - dst_pixel += pitch; - } - - // vertical convolution h_dib -> v_dib - - src_pixel = (float*)FreeImage_GetBits(h_dib); - dst_pixel = (float*)FreeImage_GetBits(v_dib); - - for(unsigned x = 0; x < width; x++) { - // work on column x - for(unsigned y = 2; y < height - 2; y++) { - const unsigned index = y*pitch + x; - dst_pixel[index] = src_pixel[index-2*pitch] + src_pixel[index+2*pitch] + 4 * (src_pixel[index-pitch] + src_pixel[index+pitch]) + 6 * src_pixel[index]; - dst_pixel[index] /= 16; - } - // boundary mirroring - dst_pixel[x] = (2 * src_pixel[x+2*pitch] + 8 * src_pixel[x+pitch] + 6 * src_pixel[x]) / 16; - dst_pixel[x+pitch] = (src_pixel[x+3*pitch] + 4 * (src_pixel[x] + src_pixel[x+2*pitch]) + 7 * src_pixel[x+pitch]) / 16; - dst_pixel[(height-2)*pitch+x] = (src_pixel[(height-4)*pitch+x] + 5 * src_pixel[(height-1)*pitch+x] + 4 * src_pixel[(height-3)*pitch+x] + 6 * src_pixel[(height-2)*pitch+x]) / 16; - dst_pixel[(height-1)*pitch+x] = (src_pixel[(height-3)*pitch+x] + 5 * src_pixel[(height-2)*pitch+x] + 10 * src_pixel[(height-1)*pitch+x]) / 16; - } - - FreeImage_Unload(h_dib); h_dib = NULL; - - // perform downsampling - - dst = FreeImage_Rescale(v_dib, width/2, height/2, FILTER_BILINEAR); - - FreeImage_Unload(v_dib); - - return dst; - - } catch(int) { - if(h_dib) FreeImage_Unload(h_dib); - if(v_dib) FreeImage_Unload(v_dib); - if(dst) FreeImage_Unload(dst); - return NULL; - } -} - -/** -Compute a Gaussian pyramid using the specified number of levels. -@param H Original bitmap -@param pyramid Resulting pyramid array -@param nlevels Number of resolution levels -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL GaussianPyramid(FIBITMAP *H, FIBITMAP **pyramid, int nlevels) { - try { - // first level is the original image - pyramid[0] = FreeImage_Clone(H); - if(pyramid[0] == NULL) throw(1); - // compute next levels - for(int k = 1; k < nlevels; k++) { - pyramid[k] = GaussianLevel5x5(pyramid[k-1]); - if(pyramid[k] == NULL) throw(1); - } - return TRUE; - } catch(int) { - for(int k = 0; k < nlevels; k++) { - if(pyramid[k] != NULL) { - FreeImage_Unload(pyramid[k]); - pyramid[k] = NULL; - } - } - return FALSE; - } -} - -/** -Compute the gradient magnitude of an input image H using central differences, -and returns the average gradient. -@param H Input image -@param avgGrad [out] Average gradient -@param k Level number -@return Returns the gradient magnitude if successful, returns NULL otherwise -@see GradientPyramid -*/ -static FIBITMAP* GradientLevel(FIBITMAP *H, float *avgGrad, int k) { - FIBITMAP *G = NULL; - - try { - const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H); - if(image_type != FIT_FLOAT) throw(1); - - const unsigned width = FreeImage_GetWidth(H); - const unsigned height = FreeImage_GetHeight(H); - - G = FreeImage_AllocateT(image_type, width, height); - if(!G) throw(1); - - const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float); - - const float divider = (float)(1 << (k + 1)); - float average = 0; - - float *src_pixel = (float*)FreeImage_GetBits(H); - float *dst_pixel = (float*)FreeImage_GetBits(G); - - for(unsigned y = 0; y < height; y++) { - const unsigned n = (y == 0 ? 0 : y-1); - const unsigned s = (y+1 == height ? y : y+1); - for(unsigned x = 0; x < width; x++) { - const unsigned w = (x == 0 ? 0 : x-1); - const unsigned e = (x+1 == width ? x : x+1); - // central difference - const float gx = (src_pixel[y*pitch+e] - src_pixel[y*pitch+w]) / divider; // [Hk(x+1, y) - Hk(x-1, y)] / 2**(k+1) - const float gy = (src_pixel[s*pitch+x] - src_pixel[n*pitch+x]) / divider; // [Hk(x, y+1) - Hk(x, y-1)] / 2**(k+1) - // gradient - dst_pixel[x] = sqrt(gx*gx + gy*gy); - // average gradient - average += dst_pixel[x]; - } - // next line - dst_pixel += pitch; - } - - *avgGrad = average / (width * height); - - return G; - - } catch(int) { - if(G) FreeImage_Unload(G); - return NULL; - } -} - -/** -Calculate gradient magnitude and its average value on each pyramid level -@param pyramid Gaussian pyramid (nlevels levels) -@param nlevels Number of levels -@param gradients [out] Gradient pyramid (nlevels levels) -@param avgGrad [out] Average gradient on each level (array of size nlevels) -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL GradientPyramid(FIBITMAP **pyramid, int nlevels, FIBITMAP **gradients, float *avgGrad) { - try { - for(int k = 0; k < nlevels; k++) { - FIBITMAP *Hk = pyramid[k]; - gradients[k] = GradientLevel(Hk, &avgGrad[k], k); - if(gradients[k] == NULL) throw(1); - } - return TRUE; - } catch(int) { - for(int k = 0; k < nlevels; k++) { - if(gradients[k] != NULL) { - FreeImage_Unload(gradients[k]); - gradients[k] = NULL; - } - } - return FALSE; - } -} - -/** -Compute the gradient attenuation function PHI(x, y) -@param gradients Gradient pyramid (nlevels levels) -@param avgGrad Average gradient on each level (array of size nlevels) -@param nlevels Number of levels -@param alpha Parameter alpha in the paper -@param beta Parameter beta in the paper -@return Returns the attenuation matrix Phi if successful, returns NULL otherwise -*/ -static FIBITMAP* PhiMatrix(FIBITMAP **gradients, float *avgGrad, int nlevels, float alpha, float beta) { - float *src_pixel, *dst_pixel; - FIBITMAP **phi = NULL; - - try { - phi = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*)); - if(!phi) throw(1); - memset(phi, 0, nlevels * sizeof(FIBITMAP*)); - - for(int k = nlevels-1; k >= 0; k--) { - // compute phi(k) - - FIBITMAP *Gk = gradients[k]; - - const unsigned width = FreeImage_GetWidth(Gk); - const unsigned height = FreeImage_GetHeight(Gk); - const unsigned pitch = FreeImage_GetPitch(Gk) / sizeof(float); - - // parameter alpha is 0.1 times the average gradient magnitude - // also, note the factor of 2**k in the denominator; - // that is there to correct for the fact that an average gradient avgGrad(H) over 2**k pixels - // in the original image will appear as a gradient grad(Hk) = 2**k*avgGrad(H) over a single pixel in Hk. - float ALPHA = alpha * avgGrad[k] * (float)((int)1 << k); - if(ALPHA == 0) ALPHA = EPSILON; - - phi[k] = FreeImage_AllocateT(FIT_FLOAT, width, height); - if(!phi[k]) throw(1); - - src_pixel = (float*)FreeImage_GetBits(Gk); - dst_pixel = (float*)FreeImage_GetBits(phi[k]); - for(unsigned y = 0; y < height; y++) { - for(unsigned x = 0; x < width; x++) { - // compute (alpha / grad) * (grad / alpha) ** beta - const float v = src_pixel[x] / ALPHA; - const float value = (float)pow((float)v, (float)(beta-1)); - dst_pixel[x] = (value > 1) ? 1 : value; - } - // next line - src_pixel += pitch; - dst_pixel += pitch; - } - - if(k < nlevels-1) { - // compute PHI(k) = L( PHI(k+1) ) * phi(k) - FIBITMAP *L = FreeImage_Rescale(phi[k+1], width, height, FILTER_BILINEAR); - if(!L) throw(1); - - src_pixel = (float*)FreeImage_GetBits(L); - dst_pixel = (float*)FreeImage_GetBits(phi[k]); - for(unsigned y = 0; y < height; y++) { - for(unsigned x = 0; x < width; x++) { - dst_pixel[x] *= src_pixel[x]; - } - // next line - src_pixel += pitch; - dst_pixel += pitch; - } - - FreeImage_Unload(L); - - // PHI(k+1) is no longer needed - FreeImage_Unload(phi[k+1]); - phi[k+1] = NULL; - } - - // next level - } - - // get the final result and return - FIBITMAP *dst = phi[0]; - - free(phi); - - return dst; - - } catch(int) { - if(phi) { - for(int k = nlevels-1; k >= 0; k--) { - if(phi[k]) FreeImage_Unload(phi[k]); - } - free(phi); - } - return NULL; - } -} - -/** -Compute gradients in x and y directions, attenuate them with the attenuation matrix, -then compute the divergence div G from the attenuated gradient. -@param H Normalized luminance -@param PHI Attenuation matrix -@return Returns the divergence matrix if successful, returns NULL otherwise -*/ -static FIBITMAP* Divergence(FIBITMAP *H, FIBITMAP *PHI) { - FIBITMAP *Gx = NULL, *Gy = NULL, *divG = NULL; - float *phi, *h, *gx, *gy, *divg; - - try { - const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H); - if(image_type != FIT_FLOAT) throw(1); - - const unsigned width = FreeImage_GetWidth(H); - const unsigned height = FreeImage_GetHeight(H); - - Gx = FreeImage_AllocateT(image_type, width, height); - if(!Gx) throw(1); - Gy = FreeImage_AllocateT(image_type, width, height); - if(!Gy) throw(1); - - const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float); - - // perform gradient attenuation - - phi = (float*)FreeImage_GetBits(PHI); - h = (float*)FreeImage_GetBits(H); - gx = (float*)FreeImage_GetBits(Gx); - gy = (float*)FreeImage_GetBits(Gy); - - for(unsigned y = 0; y < height; y++) { - const unsigned s = (y+1 == height ? y : y+1); - for(unsigned x = 0; x < width; x++) { - const unsigned e = (x+1 == width ? x : x+1); - // forward difference - const unsigned index = y*pitch + x; - const float phi_xy = phi[index]; - const float h_xy = h[index]; - gx[x] = (h[y*pitch+e] - h_xy) * phi_xy; // [H(x+1, y) - H(x, y)] * PHI(x, y) - gy[x] = (h[s*pitch+x] - h_xy) * phi_xy; // [H(x, y+1) - H(x, y)] * PHI(x, y) - } - // next line - gx += pitch; - gy += pitch; - } - - // calculate the divergence - - divG = FreeImage_AllocateT(image_type, width, height); - if(!divG) throw(1); - - gx = (float*)FreeImage_GetBits(Gx); - gy = (float*)FreeImage_GetBits(Gy); - divg = (float*)FreeImage_GetBits(divG); - - for(unsigned 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; - } -} +// ========================================================== +// Tone mapping operator (Fattal, 2002) +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "ToneMapping.h" + +// ---------------------------------------------------------- +// Gradient domain HDR compression +// Reference: +// [1] R. Fattal, D. Lischinski, and M.Werman, +// Gradient domain high dynamic range compression, +// ACM Transactions on Graphics, special issue on Proc. of ACM SIGGRAPH 2002, +// San Antonio, Texas, vol. 21(3), pp. 257-266, 2002. +// ---------------------------------------------------------- + +static const float EPSILON = 1e-4F; + +/** +Performs a 5 by 5 gaussian filtering using two 1D convolutions, +followed by a subsampling by 2. +@param dib Input image +@return Returns a blurred image of size SIZE(dib)/2 +@see GaussianPyramid +*/ +static FIBITMAP* GaussianLevel5x5(FIBITMAP *dib) { + FIBITMAP *h_dib = NULL, *v_dib = NULL, *dst = NULL; + float *src_pixel, *dst_pixel; + + try { + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); + if(image_type != FIT_FLOAT) throw(1); + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + + h_dib = FreeImage_AllocateT(image_type, width, height); + v_dib = FreeImage_AllocateT(image_type, width, height); + if(!h_dib || !v_dib) throw(1); + + const unsigned pitch = FreeImage_GetPitch(dib) / sizeof(float); + + // horizontal convolution dib -> h_dib + + src_pixel = (float*)FreeImage_GetBits(dib); + dst_pixel = (float*)FreeImage_GetBits(h_dib); + + for(unsigned y = 0; y < height; y++) { + // work on line y + for(unsigned x = 2; x < width - 2; x++) { + dst_pixel[x] = src_pixel[x-2] + src_pixel[x+2] + 4 * (src_pixel[x-1] + src_pixel[x+1]) + 6 * src_pixel[x]; + dst_pixel[x] /= 16; + } + // boundary mirroring + dst_pixel[0] = (2 * src_pixel[2] + 8 * src_pixel[1] + 6 * src_pixel[0]) / 16; + dst_pixel[1] = (src_pixel[3] + 4 * (src_pixel[0] + src_pixel[2]) + 7 * src_pixel[1]) / 16; + dst_pixel[width-2] = (src_pixel[width-4] + 5 * src_pixel[width-1] + 4 * src_pixel[width-3] + 6 * src_pixel[width-2]) / 16; + dst_pixel[width-1] = (src_pixel[width-3] + 5 * src_pixel[width-2] + 10 * src_pixel[width-1]) / 16; + + // next line + src_pixel += pitch; + dst_pixel += pitch; + } + + // vertical convolution h_dib -> v_dib + + src_pixel = (float*)FreeImage_GetBits(h_dib); + dst_pixel = (float*)FreeImage_GetBits(v_dib); + + for(unsigned x = 0; x < width; x++) { + // work on column x + for(unsigned y = 2; y < height - 2; y++) { + const unsigned index = y*pitch + x; + dst_pixel[index] = src_pixel[index-2*pitch] + src_pixel[index+2*pitch] + 4 * (src_pixel[index-pitch] + src_pixel[index+pitch]) + 6 * src_pixel[index]; + dst_pixel[index] /= 16; + } + // boundary mirroring + dst_pixel[x] = (2 * src_pixel[x+2*pitch] + 8 * src_pixel[x+pitch] + 6 * src_pixel[x]) / 16; + dst_pixel[x+pitch] = (src_pixel[x+3*pitch] + 4 * (src_pixel[x] + src_pixel[x+2*pitch]) + 7 * src_pixel[x+pitch]) / 16; + dst_pixel[(height-2)*pitch+x] = (src_pixel[(height-4)*pitch+x] + 5 * src_pixel[(height-1)*pitch+x] + 4 * src_pixel[(height-3)*pitch+x] + 6 * src_pixel[(height-2)*pitch+x]) / 16; + dst_pixel[(height-1)*pitch+x] = (src_pixel[(height-3)*pitch+x] + 5 * src_pixel[(height-2)*pitch+x] + 10 * src_pixel[(height-1)*pitch+x]) / 16; + } + + FreeImage_Unload(h_dib); h_dib = NULL; + + // perform downsampling + + dst = FreeImage_Rescale(v_dib, width/2, height/2, FILTER_BILINEAR); + + FreeImage_Unload(v_dib); + + return dst; + + } catch(int) { + if(h_dib) FreeImage_Unload(h_dib); + if(v_dib) FreeImage_Unload(v_dib); + if(dst) FreeImage_Unload(dst); + return NULL; + } +} + +/** +Compute a Gaussian pyramid using the specified number of levels. +@param H Original bitmap +@param pyramid Resulting pyramid array +@param nlevels Number of resolution levels +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static BOOL GaussianPyramid(FIBITMAP *H, FIBITMAP **pyramid, int nlevels) { + try { + // first level is the original image + pyramid[0] = FreeImage_Clone(H); + if(pyramid[0] == NULL) throw(1); + // compute next levels + for(int k = 1; k < nlevels; k++) { + pyramid[k] = GaussianLevel5x5(pyramid[k-1]); + if(pyramid[k] == NULL) throw(1); + } + return TRUE; + } catch(int) { + for(int k = 0; k < nlevels; k++) { + if(pyramid[k] != NULL) { + FreeImage_Unload(pyramid[k]); + pyramid[k] = NULL; + } + } + return FALSE; + } +} + +/** +Compute the gradient magnitude of an input image H using central differences, +and returns the average gradient. +@param H Input image +@param avgGrad [out] Average gradient +@param k Level number +@return Returns the gradient magnitude if successful, returns NULL otherwise +@see GradientPyramid +*/ +static FIBITMAP* GradientLevel(FIBITMAP *H, float *avgGrad, int k) { + FIBITMAP *G = NULL; + + try { + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H); + if(image_type != FIT_FLOAT) throw(1); + + const unsigned width = FreeImage_GetWidth(H); + const unsigned height = FreeImage_GetHeight(H); + + G = FreeImage_AllocateT(image_type, width, height); + if(!G) throw(1); + + const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float); + + const float divider = (float)(1 << (k + 1)); + float average = 0; + + float *src_pixel = (float*)FreeImage_GetBits(H); + float *dst_pixel = (float*)FreeImage_GetBits(G); + + for(unsigned y = 0; y < height; y++) { + const unsigned n = (y == 0 ? 0 : y-1); + const unsigned s = (y+1 == height ? y : y+1); + for(unsigned x = 0; x < width; x++) { + const unsigned w = (x == 0 ? 0 : x-1); + const unsigned e = (x+1 == width ? x : x+1); + // central difference + const float gx = (src_pixel[y*pitch+e] - src_pixel[y*pitch+w]) / divider; // [Hk(x+1, y) - Hk(x-1, y)] / 2**(k+1) + const float gy = (src_pixel[s*pitch+x] - src_pixel[n*pitch+x]) / divider; // [Hk(x, y+1) - Hk(x, y-1)] / 2**(k+1) + // gradient + dst_pixel[x] = sqrt(gx*gx + gy*gy); + // average gradient + average += dst_pixel[x]; + } + // next line + dst_pixel += pitch; + } + + *avgGrad = average / (width * height); + + return G; + + } catch(int) { + if(G) FreeImage_Unload(G); + return NULL; + } +} + +/** +Calculate gradient magnitude and its average value on each pyramid level +@param pyramid Gaussian pyramid (nlevels levels) +@param nlevels Number of levels +@param gradients [out] Gradient pyramid (nlevels levels) +@param avgGrad [out] Average gradient on each level (array of size nlevels) +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static BOOL GradientPyramid(FIBITMAP **pyramid, int nlevels, FIBITMAP **gradients, float *avgGrad) { + try { + for(int k = 0; k < nlevels; k++) { + FIBITMAP *Hk = pyramid[k]; + gradients[k] = GradientLevel(Hk, &avgGrad[k], k); + if(gradients[k] == NULL) throw(1); + } + return TRUE; + } catch(int) { + for(int k = 0; k < nlevels; k++) { + if(gradients[k] != NULL) { + FreeImage_Unload(gradients[k]); + gradients[k] = NULL; + } + } + return FALSE; + } +} + +/** +Compute the gradient attenuation function PHI(x, y) +@param gradients Gradient pyramid (nlevels levels) +@param avgGrad Average gradient on each level (array of size nlevels) +@param nlevels Number of levels +@param alpha Parameter alpha in the paper +@param beta Parameter beta in the paper +@return Returns the attenuation matrix Phi if successful, returns NULL otherwise +*/ +static FIBITMAP* PhiMatrix(FIBITMAP **gradients, float *avgGrad, int nlevels, float alpha, float beta) { + float *src_pixel, *dst_pixel; + FIBITMAP **phi = NULL; + + try { + phi = (FIBITMAP**)malloc(nlevels * sizeof(FIBITMAP*)); + if(!phi) throw(1); + memset(phi, 0, nlevels * sizeof(FIBITMAP*)); + + for(int k = nlevels-1; k >= 0; k--) { + // compute phi(k) + + FIBITMAP *Gk = gradients[k]; + + const unsigned width = FreeImage_GetWidth(Gk); + const unsigned height = FreeImage_GetHeight(Gk); + const unsigned pitch = FreeImage_GetPitch(Gk) / sizeof(float); + + // parameter alpha is 0.1 times the average gradient magnitude + // also, note the factor of 2**k in the denominator; + // that is there to correct for the fact that an average gradient avgGrad(H) over 2**k pixels + // in the original image will appear as a gradient grad(Hk) = 2**k*avgGrad(H) over a single pixel in Hk. + float ALPHA = alpha * avgGrad[k] * (float)((int)1 << k); + if(ALPHA == 0) ALPHA = EPSILON; + + phi[k] = FreeImage_AllocateT(FIT_FLOAT, width, height); + if(!phi[k]) throw(1); + + src_pixel = (float*)FreeImage_GetBits(Gk); + dst_pixel = (float*)FreeImage_GetBits(phi[k]); + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + // compute (alpha / grad) * (grad / alpha) ** beta + const float v = src_pixel[x] / ALPHA; + const float value = (float)pow((float)v, (float)(beta-1)); + dst_pixel[x] = (value > 1) ? 1 : value; + } + // next line + src_pixel += pitch; + dst_pixel += pitch; + } + + if(k < nlevels-1) { + // compute PHI(k) = L( PHI(k+1) ) * phi(k) + FIBITMAP *L = FreeImage_Rescale(phi[k+1], width, height, FILTER_BILINEAR); + if(!L) throw(1); + + src_pixel = (float*)FreeImage_GetBits(L); + dst_pixel = (float*)FreeImage_GetBits(phi[k]); + for(unsigned y = 0; y < height; y++) { + for(unsigned x = 0; x < width; x++) { + dst_pixel[x] *= src_pixel[x]; + } + // next line + src_pixel += pitch; + dst_pixel += pitch; + } + + FreeImage_Unload(L); + + // PHI(k+1) is no longer needed + FreeImage_Unload(phi[k+1]); + phi[k+1] = NULL; + } + + // next level + } + + // get the final result and return + FIBITMAP *dst = phi[0]; + + free(phi); + + return dst; + + } catch(int) { + if(phi) { + for(int k = nlevels-1; k >= 0; k--) { + if(phi[k]) FreeImage_Unload(phi[k]); + } + free(phi); + } + return NULL; + } +} + +/** +Compute gradients in x and y directions, attenuate them with the attenuation matrix, +then compute the divergence div G from the attenuated gradient. +@param H Normalized luminance +@param PHI Attenuation matrix +@return Returns the divergence matrix if successful, returns NULL otherwise +*/ +static FIBITMAP* Divergence(FIBITMAP *H, FIBITMAP *PHI) { + FIBITMAP *Gx = NULL, *Gy = NULL, *divG = NULL; + float *phi, *h, *gx, *gy, *divg; + + try { + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(H); + if(image_type != FIT_FLOAT) throw(1); + + const unsigned width = FreeImage_GetWidth(H); + const unsigned height = FreeImage_GetHeight(H); + + Gx = FreeImage_AllocateT(image_type, width, height); + if(!Gx) throw(1); + Gy = FreeImage_AllocateT(image_type, width, height); + if(!Gy) throw(1); + + const unsigned pitch = FreeImage_GetPitch(H) / sizeof(float); + + // perform gradient attenuation + + phi = (float*)FreeImage_GetBits(PHI); + h = (float*)FreeImage_GetBits(H); + gx = (float*)FreeImage_GetBits(Gx); + gy = (float*)FreeImage_GetBits(Gy); + + for(unsigned y = 0; y < height; y++) { + const unsigned s = (y+1 == height ? y : y+1); + for(unsigned x = 0; x < width; x++) { + const unsigned e = (x+1 == width ? x : x+1); + // forward difference + const unsigned index = y*pitch + x; + const float phi_xy = phi[index]; + const float h_xy = h[index]; + gx[x] = (h[y*pitch+e] - h_xy) * phi_xy; // [H(x+1, y) - H(x, y)] * PHI(x, y) + gy[x] = (h[s*pitch+x] - h_xy) * phi_xy; // [H(x, y+1) - H(x, y)] * PHI(x, y) + } + // next line + gx += pitch; + gy += pitch; + } + + // calculate the divergence + + divG = FreeImage_AllocateT(image_type, width, height); + if(!divG) throw(1); + + gx = (float*)FreeImage_GetBits(Gx); + gy = (float*)FreeImage_GetBits(Gy); + divg = (float*)FreeImage_GetBits(divG); + + for(unsigned 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; + } +} -- cgit v1.2.3