diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage')
31 files changed, 9104 insertions, 7021 deletions
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 <stdlib.h>
-#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__)
-#include <malloc.h>
-#endif // _WIN32 || _WIN64 || __MINGW32__
-
-#include "FreeImage.h"
-#include "FreeImageIO.h"
-#include "Utilities.h"
-
-#include "../Metadata/FreeImageTag.h"
-
-/** Constants for the BITMAPINFOHEADER::biCompression field */
-#ifndef _WINGDI_
-#define BI_RGB 0L
-#define BI_BITFIELDS 3L
-#endif // _WINGDI_
-
-// ----------------------------------------------------------
-// Metadata definitions
-// ----------------------------------------------------------
-
-// helper for map<key, value> where value is a pointer to a FreeImage tag
-typedef std::map<std::string, FITAG*> TAGMAP;
-
-// helper for map<FREE_IMAGE_MDMODEL, TAGMAP*>
-typedef std::map<int, TAGMAP*> METADATAMAP;
-
-// helper for metadata iterator
-FI_STRUCT (METADATAHEADER) {
- long pos; // current position when iterating the map
- TAGMAP *tagmap; // pointer to the tag map
-};
-
-// ----------------------------------------------------------
-// FIBITMAP definition
-// ----------------------------------------------------------
-
-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 <stdlib.h> +#if defined(_WIN32) || defined(_WIN64) || defined(__MINGW32__) +#include <malloc.h> +#endif // _WIN32 || _WIN64 || __MINGW32__ + +#include "FreeImage.h" +#include "FreeImageIO.h" +#include "Utilities.h" + +#include "../Metadata/FreeImageTag.h" + +/** Constants for the BITMAPINFOHEADER::biCompression field */ +#ifndef _WINGDI_ +#define BI_RGB 0L +#define BI_BITFIELDS 3L +#endif // _WINGDI_ + +// ---------------------------------------------------------- +// Metadata definitions +// ---------------------------------------------------------- + +// helper for map<key, value> where value is a pointer to a FreeImage tag +typedef std::map<std::string, FITAG*> TAGMAP; + +// helper for map<FREE_IMAGE_MDMODEL, TAGMAP*> +typedef std::map<int, TAGMAP*> METADATAMAP; + +// helper for metadata iterator +FI_STRUCT (METADATAHEADER) { + long pos; // current position when iterating the map + TAGMAP *tagmap; // pointer to the tag map +}; + +// ---------------------------------------------------------- +// FIBITMAP definition +// ---------------------------------------------------------- + +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<Block *> PageCache; - typedef std::list<Block *>::iterator PageCacheIt; - typedef std::map<int, PageCacheIt> PageMap; - typedef std::map<int, PageCacheIt>::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<int> 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<BlockTypeS *> BlockList;
-typedef std::list<BlockTypeS *>::iterator BlockListIterator;
-
-// ----------------------------------------------------------
-
-FI_STRUCT (MULTIBITMAPHEADER) {
- PluginNode *node;
- FREE_IMAGE_FORMAT fif;
- FreeImageIO *io;
- fi_handle handle;
- CacheFile *m_cachefile;
- std::map<FIBITMAP *, int> locked_pages;
- BOOL changed;
- int page_count;
- BlockList m_blocks;
- char *m_filename;
- BOOL read_only;
- FREE_IMAGE_FORMAT cache_fif;
- int load_flags;
-};
-
-// =====================================================================
-// Helper functions
-// =====================================================================
-
-inline void
-ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) {
- size_t lastDot = src_filename.find_last_of('.');
- if (lastDot == std::string::npos) {
- dst_filename = src_filename;
- dst_filename += ".";
- dst_filename += dst_extension;
- }
- else {
- dst_filename = src_filename.substr(0, lastDot + 1);
- dst_filename += dst_extension;
- }
-}
-
-// =====================================================================
-// Internal Multipage functions
-// =====================================================================
-
-inline MULTIBITMAPHEADER *
-FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) {
- return (MULTIBITMAPHEADER *)bitmap->data;
-}
-
-static BlockListIterator DLL_CALLCONV
-FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) {
- assert(NULL != bitmap);
-
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // step 1: find the block that matches the given position
-
- int prev_count = 0;
- int count = 0;
- BlockListIterator i;
- BlockTypeS *current_block = NULL;
-
- for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
- prev_count = count;
-
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
- break;
-
- case BLOCK_REFERENCE :
- count++;
- break;
- }
-
- current_block = *i;
-
- if (count > position)
- break;
- }
-
- // step 2: make sure we found the node. from here it gets a little complicated:
- // * if the block is there, just return it
- // * if the block is a series of blocks, split it in max 3 new blocks
- // and return the splitted block
-
- if ((current_block) && (count > position)) {
- switch(current_block->m_type) {
- case BLOCK_REFERENCE :
- return i;
-
- case BLOCK_CONTINUEUS :
- {
- BlockContinueus *block = (BlockContinueus *)current_block;
-
- if (block->m_start != block->m_end) {
- int item = block->m_start + (position - prev_count);
-
- // left part
-
- if (item != block->m_start) {
- BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1);
- header->m_blocks.insert(i, (BlockTypeS *)block_a);
- }
-
- // middle part
-
- BlockContinueus *block_b = new BlockContinueus(item, item);
- BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b);
-
- // right part
-
- if (item != block->m_end) {
- BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end);
- header->m_blocks.insert(i, (BlockTypeS *)block_c);
- }
-
- // remove the old block that was just splitted
-
- header->m_blocks.remove((BlockTypeS *)block);
- delete block;
-
- // return the splitted block
-
- return block_target;
- }
-
- return i;
- }
- }
- }
- // we should never go here ...
- assert(false);
- return header->m_blocks.end();
-}
-
-int DLL_CALLCONV
-FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) {
- if (bitmap) {
- if (((MULTIBITMAPHEADER *)bitmap->data)->handle) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- header->io->seek_proc(header->handle, 0, SEEK_SET);
-
- void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
-
- int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1;
-
- FreeImage_Close(header->node, header->io, header->handle, data);
-
- return page_count;
- }
- }
-
- return 0;
-}
-
-// =====================================================================
-// Multipage functions
-// =====================================================================
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
-
- FILE *handle = NULL;
- try {
- // sanity check on the parameters
-
- if (create_new) {
- read_only = FALSE;
- }
-
- // retrieve the plugin list to find the node belonging to this plugin
-
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if (node) {
- std::auto_ptr<FreeImageIO> io (new FreeImageIO);
-
- SetDefaultIO(io.get());
-
- if (!create_new) {
- handle = fopen(filename, "rb");
- if (handle == NULL) {
- return NULL;
- }
- }
-
- std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
- std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
- header->m_filename = new char[strlen(filename) + 1];
- strcpy(header->m_filename, filename);
- header->node = node;
- header->fif = fif;
- header->io = io.get ();
- header->handle = handle;
- header->changed = FALSE;
- header->read_only = read_only;
- header->m_cachefile = NULL;
- header->cache_fif = fif;
- header->load_flags = flags;
-
- // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
-
- bitmap->data = header.get();
-
- // cache the page count
-
- header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
-
- // allocate a continueus block to describe the bitmap
-
- if (!create_new) {
- header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
- }
-
- // set up the cache
-
- if (!read_only) {
- std::string cache_name;
- ReplaceExtension(cache_name, filename, "ficache");
-
- std::auto_ptr<CacheFile> cache_file (new CacheFile(cache_name, keep_cache_in_memory));
-
- if (cache_file->open()) {
- // we can use release() as std::bad_alloc won't be thrown from here on
- header->m_cachefile = cache_file.release();
- } else {
- // an error occured ...
- fclose(handle);
- return NULL;
- }
- }
- // return the multibitmap
- // std::bad_alloc won't be thrown from here on
- header.release(); // now owned by bitmap
- io.release(); // now owned by bitmap
- return bitmap.release(); // now owned by caller
- }
- }
- } catch (std::bad_alloc &) {
- /** @todo report error */
- }
- if (handle)
- fclose(handle);
- return NULL;
-}
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_OpenMultiBitmapU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
-
- // convert to single character - no national chars in extensions
- char *extension = (char *)malloc(wcslen(filename)+1);
- unsigned int i=0;
- for (; i < wcslen(filename); i++) // convert 16-bit to 8-bit
- extension[i] = (char)(filename[i] & 0x00FF);
- // set terminating 0
- extension[i]=0;
- FIMULTIBITMAP *fRet = FreeImage_OpenMultiBitmap(fif, extension, create_new, read_only, keep_cache_in_memory, flags);
- free(extension);
-
- return fRet;
-}
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
- try {
- BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
-
- if (io && handle) {
-
- // retrieve the plugin list to find the node belonging to this plugin
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if (node) {
- std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
- std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
- std::auto_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io));
- header->io = tmp_io.get();
- header->m_filename = NULL;
- header->node = node;
- header->fif = fif;
- header->handle = handle;
- header->changed = FALSE;
- header->read_only = read_only;
- header->m_cachefile = NULL;
- header->cache_fif = fif;
- header->load_flags = flags;
-
- // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
-
- bitmap->data = header.get();
-
- // cache the page count
-
- header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
-
- // allocate a continueus block to describe the bitmap
-
- header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
-
- if (!read_only) {
- // set up the cache
- std::auto_ptr<CacheFile> cache_file (new CacheFile("", TRUE));
-
- if (cache_file->open()) {
- header->m_cachefile = cache_file.release();
- }
- }
- tmp_io.release();
- header.release();
- return bitmap.release();
- }
- }
- }
- } catch (std::bad_alloc &) {
- /** @todo report error */
- }
- return NULL;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) {
- if(!bitmap || !bitmap->data || !io || !handle) {
- return FALSE;
- }
-
- BOOL success = TRUE;
-
- // retrieve the plugin list to find the node belonging to this plugin
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if(node) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // dst data
- void *data = FreeImage_Open(node, io, handle, FALSE);
- // src data
- void *data_read = NULL;
-
- if(header->handle) {
- // open src
- header->io->seek_proc(header->handle, 0, SEEK_SET);
- data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE);
- }
-
- // write all the pages to the file using handle and io
-
- int count = 0;
-
- for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) {
- if (success) {
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS:
- {
- BlockContinueus *block = (BlockContinueus *)(*i);
-
- for (int j = block->m_start; j <= block->m_end; j++) {
-
- // load the original source data
- FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read);
-
- // save the data
- success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
- count++;
-
- FreeImage_Unload(dib);
- }
-
- break;
- }
-
- case BLOCK_REFERENCE:
- {
- BlockReference *ref = (BlockReference *)(*i);
-
- // read the compressed data
-
- BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE));
-
- header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size);
-
- // uncompress the data
-
- FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size);
- FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0);
- FreeImage_CloseMemory(hmem);
-
- // get rid of the buffer
- free(compressed_data);
-
- // save the data
-
- success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
- count++;
-
- // unload the dib
-
- FreeImage_Unload(dib);
-
- break;
- }
- }
- } else {
- break;
- }
- }
-
- // close the files
-
- FreeImage_Close(header->node, header->io, header->handle, data_read);
-
- FreeImage_Close(node, io, handle, data);
-
- return success;
- }
- }
-
- return FALSE;
-}
-
-
-BOOL DLL_CALLCONV
-FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) {
- if (bitmap) {
- BOOL success = TRUE;
-
- if (bitmap->data) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // saves changes only of images loaded directly from a file
- if (header->changed && header->m_filename) {
- try {
- // open a temp file
-
- std::string spool_name;
-
- ReplaceExtension(spool_name, header->m_filename, "fispool");
-
- // open the spool file and the source file
-
- FILE *f = fopen(spool_name.c_str(), "w+b");
-
- // saves changes
- if (f == NULL) {
- FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno));
- success = FALSE;
- } else {
- success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags);
-
- // close the files
-
- if (fclose(f) != 0) {
- success = FALSE;
- FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno));
- }
- }
- if (header->handle) {
- fclose((FILE *)header->handle);
- }
-
- // applies changes to the destination file
-
- if (success) {
- remove(header->m_filename);
- success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE;
- if(!success) {
- FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename);
- }
- } else {
- remove(spool_name.c_str());
- }
- } catch (std::bad_alloc &) {
- success = FALSE;
- }
-
- } else {
- if (header->handle && header->m_filename) {
- fclose((FILE *)header->handle);
- }
- }
-
- // clear the blocks list
-
- for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
- delete *i;
- }
-
- // flush and dispose the cache
-
- if (header->m_cachefile) {
- header->m_cachefile->close();
- delete header->m_cachefile;
- }
-
- // delete the last open bitmaps
-
- while (!header->locked_pages.empty()) {
- FreeImage_Unload(header->locked_pages.begin()->first);
-
- header->locked_pages.erase(header->locked_pages.begin()->first);
- }
-
- // get rid of the IO structure
-
- delete header->io;
-
- // delete the filename
-
- if(header->m_filename) {
- delete[] header->m_filename;
- }
-
- // delete the FIMULTIBITMAPHEADER
-
- delete header;
- }
-
- delete bitmap;
-
- return success;
- }
-
- return FALSE;
-}
-
-int DLL_CALLCONV
-FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if (header->page_count == -1) {
- header->page_count = 0;
-
- for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
- break;
-
- case BLOCK_REFERENCE :
- header->page_count++;
- break;
- }
- }
- }
-
- return header->page_count;
- }
-
- return 0;
-}
-
-static BlockReference*
-FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) {
- if (header->read_only || !header->locked_pages.empty())
- return NULL;
-
- DWORD compressed_size = 0;
- BYTE *compressed_data = NULL;
-
- // compress the bitmap data
-
- // open a memory handle
- FIMEMORY *hmem = FreeImage_OpenMemory();
- if(hmem==NULL) return NULL;
- // save the file to memory
- if(!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) {
- FreeImage_CloseMemory(hmem);
- return NULL;
- }
- // get the buffer from the memory stream
- if(!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) {
- FreeImage_CloseMemory(hmem);
- return NULL;
- }
-
- // write the compressed data to the cache
- int ref = header->m_cachefile->writeFile(compressed_data, compressed_size);
- // get rid of the compressed data
- FreeImage_CloseMemory(hmem);
-
- return new(std::nothrow) BlockReference(ref, compressed_size);
-}
-
-void DLL_CALLCONV
-FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) {
- if (!bitmap || !data)
- return;
-
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- BlockReference *block = FreeImage_SavePageToBlock(header, data);
- if(block==NULL) return;
-
- // add the block
- header->m_blocks.push_back((BlockTypeS *)block);
- header->changed = TRUE;
- header->page_count = -1;
-}
-
-void DLL_CALLCONV
-FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) {
- if (!bitmap || !data)
- return;
-
- if (page >= FreeImage_GetPageCount(bitmap))
- return;
-
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- BlockReference *block = FreeImage_SavePageToBlock(header, data);
- if(block==NULL) return;
-
- // add a block
- if (page > 0) {
- BlockListIterator block_source = FreeImage_FindBlock(bitmap, page);
-
- header->m_blocks.insert(block_source, (BlockTypeS *)block);
- } else {
- header->m_blocks.push_front((BlockTypeS *)block);
- }
-
- header->changed = TRUE;
- header->page_count = -1;
-}
-
-void DLL_CALLCONV
-FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if ((!header->read_only) && (header->locked_pages.empty())) {
- if (FreeImage_GetPageCount(bitmap) > 1) {
- BlockListIterator i = FreeImage_FindBlock(bitmap, page);
-
- if (i != header->m_blocks.end()) {
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- delete *i;
- header->m_blocks.erase(i);
- break;
-
- case BLOCK_REFERENCE :
- header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference);
- delete *i;
- header->m_blocks.erase(i);
- break;
- }
-
- header->changed = TRUE;
- header->page_count = -1;
- }
- }
- }
- }
-}
-
-
-FIBITMAP * DLL_CALLCONV
-FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // only lock if the page wasn't locked before...
-
- for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
- if (i->second == page) {
- return NULL;
- }
- }
-
- // open the bitmap
-
- header->io->seek_proc(header->handle, 0, SEEK_SET);
-
- void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
-
- // load the bitmap data
-
- if (data != NULL) {
- FIBITMAP *dib = (header->node->m_plugin->load_proc != NULL) ? header->node->m_plugin->load_proc(header->io, header->handle, page, header->load_flags, data) : NULL;
-
- // close the file
-
- FreeImage_Close(header->node, header->io, header->handle, data);
-
- // if there was still another bitmap open, get rid of it
-
- if (dib) {
- header->locked_pages[dib] = page;
-
- return dib;
- }
-
- return NULL;
- }
- }
-
- return NULL;
-}
-
-void DLL_CALLCONV
-FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) {
- if ((bitmap) && (page)) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // find out if the page we try to unlock is actually locked...
-
- if (header->locked_pages.find(page) != header->locked_pages.end()) {
- // store the bitmap compressed in the cache for later writing
-
- if (changed && !header->read_only) {
- header->changed = TRUE;
-
- // cut loose the block from the rest
-
- BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]);
-
- // compress the data
-
- DWORD compressed_size = 0;
- BYTE *compressed_data = NULL;
-
- // open a memory handle
- FIMEMORY *hmem = FreeImage_OpenMemory();
- // save the page to memory
- FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0);
- // get the buffer from the memory stream
- FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size);
-
- // write the data to the cache
-
- switch ((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- {
- int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
-
- delete (*i);
-
- *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
-
- break;
- }
-
- case BLOCK_REFERENCE :
- {
- BlockReference *reference = (BlockReference *)(*i);
-
- header->m_cachefile->deleteFile(reference->m_reference);
-
- delete (*i);
-
- int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
-
- *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
-
- break;
- }
- }
-
- // get rid of the compressed data
-
- FreeImage_CloseMemory(hmem);
- }
-
- // reset the locked page so that another page can be locked
-
- FreeImage_Unload(page);
-
- header->locked_pages.erase(page);
- }
- }
-}
-
-BOOL DLL_CALLCONV
-FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if ((!header->read_only) && (header->locked_pages.empty())) {
- if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) {
- BlockListIterator block_source = FreeImage_FindBlock(bitmap, target);
- BlockListIterator block_target = FreeImage_FindBlock(bitmap, source);
-
- header->m_blocks.insert(block_target, *block_source);
- header->m_blocks.erase(block_source);
-
- header->changed = TRUE;
-
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) {
- if ((bitmap) && (count)) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if ((pages == NULL) || (*count == 0)) {
- *count = (int)header->locked_pages.size();
- } else {
- int c = 0;
-
- for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
- pages[c] = i->second;
-
- c++;
-
- if (c == *count)
- break;
- }
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-// =====================================================================
-// Memory IO Multipage functions
-// =====================================================================
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
- BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
-
- // retrieve the plugin list to find the node belonging to this plugin
-
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if (node) {
- FreeImageIO *io = new(std::nothrow) FreeImageIO;
-
- if (io) {
- SetMemoryIO(io);
-
- FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP;
-
- if (bitmap) {
- MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER;
-
- if (header) {
- header->m_filename = NULL;
- header->node = node;
- header->fif = fif;
- header->io = io;
- header->handle = (fi_handle)stream;
- header->changed = FALSE;
- header->read_only = read_only;
- header->m_cachefile = NULL;
- header->cache_fif = fif;
- header->load_flags = flags;
-
- // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
-
- bitmap->data = header;
-
- // cache the page count
-
- header->page_count = FreeImage_InternalGetPageCount(bitmap);
-
- // allocate a continueus block to describe the bitmap
-
- header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
-
- if (!read_only) {
- // set up the cache
- CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE);
-
- if (cache_file && cache_file->open()) {
- header->m_cachefile = cache_file;
- }
- }
-
- return bitmap;
- }
-
- delete bitmap;
- }
-
- delete io;
- }
- }
- }
-
- return NULL;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) {
- if (stream && stream->data) {
- FreeImageIO io;
- SetMemoryIO(&io);
-
- return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags);
- }
-
- return FALSE;
-}
+// ========================================================== +// Multi-Page functions +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Laurent Rocher (rocherl@club-internet.fr) +// - Steve Johnson (steve@parisgroup.net) +// - Petr Pytelka (pyta@lightcomp.com) +// - Hervé Drolon (drolon@infonie.fr) +// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net +// - Martin Dyring-Andersen (mda@spamfighter.com) +// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#ifdef _MSC_VER +#pragma warning (disable : 4786) // identifier was truncated to 'number' characters +#endif + +#include "CacheFile.h" +#include "FreeImageIO.h" +#include "Plugin.h" +#include "Utilities.h" +#include "FreeImage.h" + +// ---------------------------------------------------------- + +enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE }; + +// ---------------------------------------------------------- + +struct BlockTypeS { + BlockType m_type; + + BlockTypeS(BlockType type) : m_type(type) { + } + virtual ~BlockTypeS() {} +}; + +struct BlockContinueus : public BlockTypeS { + int m_start; + int m_end; + + BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS), + m_start(s), + m_end(e) { + } +}; + +struct BlockReference : public BlockTypeS { + int m_reference; + int m_size; + + BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE), + m_reference(r), + m_size(size) { + } +}; + +// ---------------------------------------------------------- + +typedef std::list<BlockTypeS *> BlockList; +typedef std::list<BlockTypeS *>::iterator BlockListIterator; + +// ---------------------------------------------------------- + +FI_STRUCT (MULTIBITMAPHEADER) { + PluginNode *node; + FREE_IMAGE_FORMAT fif; + FreeImageIO *io; + fi_handle handle; + CacheFile *m_cachefile; + std::map<FIBITMAP *, int> locked_pages; + BOOL changed; + int page_count; + BlockList m_blocks; + char *m_filename; + BOOL read_only; + FREE_IMAGE_FORMAT cache_fif; + int load_flags; +}; + +// ===================================================================== +// Helper functions +// ===================================================================== + +inline void +ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) { + size_t lastDot = src_filename.find_last_of('.'); + if (lastDot == std::string::npos) { + dst_filename = src_filename; + dst_filename += "."; + dst_filename += dst_extension; + } + else { + dst_filename = src_filename.substr(0, lastDot + 1); + dst_filename += dst_extension; + } +} + +// ===================================================================== +// Internal Multipage functions +// ===================================================================== + +inline MULTIBITMAPHEADER * +FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) { + return (MULTIBITMAPHEADER *)bitmap->data; +} + +static BlockListIterator DLL_CALLCONV +FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) { + assert(NULL != bitmap); + + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // step 1: find the block that matches the given position + + int prev_count = 0; + int count = 0; + BlockListIterator i; + BlockTypeS *current_block = NULL; + + for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) { + prev_count = count; + + switch((*i)->m_type) { + case BLOCK_CONTINUEUS : + count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1; + break; + + case BLOCK_REFERENCE : + count++; + break; + } + + current_block = *i; + + if (count > position) + break; + } + + // step 2: make sure we found the node. from here it gets a little complicated: + // * if the block is there, just return it + // * if the block is a series of blocks, split it in max 3 new blocks + // and return the splitted block + + if ((current_block) && (count > position)) { + switch(current_block->m_type) { + case BLOCK_REFERENCE : + return i; + + case BLOCK_CONTINUEUS : + { + BlockContinueus *block = (BlockContinueus *)current_block; + + if (block->m_start != block->m_end) { + int item = block->m_start + (position - prev_count); + + // left part + + if (item != block->m_start) { + BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1); + header->m_blocks.insert(i, (BlockTypeS *)block_a); + } + + // middle part + + BlockContinueus *block_b = new BlockContinueus(item, item); + BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b); + + // right part + + if (item != block->m_end) { + BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end); + header->m_blocks.insert(i, (BlockTypeS *)block_c); + } + + // remove the old block that was just splitted + + header->m_blocks.remove((BlockTypeS *)block); + delete block; + + // return the splitted block + + return block_target; + } + + return i; + } + } + } + // we should never go here ... + assert(false); + return header->m_blocks.end(); +} + +int DLL_CALLCONV +FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) { + if (bitmap) { + if (((MULTIBITMAPHEADER *)bitmap->data)->handle) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + header->io->seek_proc(header->handle, 0, SEEK_SET); + + void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE); + + int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1; + + FreeImage_Close(header->node, header->io, header->handle, data); + + return page_count; + } + } + + return 0; +} + +// ===================================================================== +// Multipage functions +// ===================================================================== + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) { + + FILE *handle = NULL; + try { + // sanity check on the parameters + + if (create_new) { + read_only = FALSE; + } + + // retrieve the plugin list to find the node belonging to this plugin + + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if (node) { + std::auto_ptr<FreeImageIO> io (new FreeImageIO); + + SetDefaultIO(io.get()); + + if (!create_new) { + handle = fopen(filename, "rb"); + if (handle == NULL) { + return NULL; + } + } + + std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP); + std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER); + header->m_filename = new char[strlen(filename) + 1]; + strcpy(header->m_filename, filename); + header->node = node; + header->fif = fif; + header->io = io.get (); + header->handle = handle; + header->changed = FALSE; + header->read_only = read_only; + header->m_cachefile = NULL; + header->cache_fif = fif; + header->load_flags = flags; + + // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure + + bitmap->data = header.get(); + + // cache the page count + + header->page_count = FreeImage_InternalGetPageCount(bitmap.get()); + + // allocate a continueus block to describe the bitmap + + if (!create_new) { + header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1)); + } + + // set up the cache + + if (!read_only) { + std::string cache_name; + ReplaceExtension(cache_name, filename, "ficache"); + + std::auto_ptr<CacheFile> cache_file (new CacheFile(cache_name, keep_cache_in_memory)); + + if (cache_file->open()) { + // we can use release() as std::bad_alloc won't be thrown from here on + header->m_cachefile = cache_file.release(); + } else { + // an error occured ... + fclose(handle); + return NULL; + } + } + // return the multibitmap + // std::bad_alloc won't be thrown from here on + header.release(); // now owned by bitmap + io.release(); // now owned by bitmap + return bitmap.release(); // now owned by caller + } + } + } catch (std::bad_alloc &) { + /** @todo report error */ + } + if (handle) + fclose(handle); + return NULL; +} + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_OpenMultiBitmapU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) { + + // convert to single character - no national chars in extensions + char *extension = (char *)malloc(wcslen(filename)+1); + unsigned int i=0; + for (; i < wcslen(filename); i++) // convert 16-bit to 8-bit + extension[i] = (char)(filename[i] & 0x00FF); + // set terminating 0 + extension[i]=0; + FIMULTIBITMAP *fRet = FreeImage_OpenMultiBitmap(fif, extension, create_new, read_only, keep_cache_in_memory, flags); + free(extension); + + return fRet; +} + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) { + try { + BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache + + if (io && handle) { + + // retrieve the plugin list to find the node belonging to this plugin + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if (node) { + std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP); + std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER); + std::auto_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io)); + header->io = tmp_io.get(); + header->m_filename = NULL; + header->node = node; + header->fif = fif; + header->handle = handle; + header->changed = FALSE; + header->read_only = read_only; + header->m_cachefile = NULL; + header->cache_fif = fif; + header->load_flags = flags; + + // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure + + bitmap->data = header.get(); + + // cache the page count + + header->page_count = FreeImage_InternalGetPageCount(bitmap.get()); + + // allocate a continueus block to describe the bitmap + + header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1)); + + if (!read_only) { + // set up the cache + std::auto_ptr<CacheFile> cache_file (new CacheFile("", TRUE)); + + if (cache_file->open()) { + header->m_cachefile = cache_file.release(); + } + } + tmp_io.release(); + header.release(); + return bitmap.release(); + } + } + } + } catch (std::bad_alloc &) { + /** @todo report error */ + } + return NULL; +} + +BOOL DLL_CALLCONV +FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) { + if(!bitmap || !bitmap->data || !io || !handle) { + return FALSE; + } + + BOOL success = TRUE; + + // retrieve the plugin list to find the node belonging to this plugin + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if(node) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // dst data + void *data = FreeImage_Open(node, io, handle, FALSE); + // src data + void *data_read = NULL; + + if(header->handle) { + // open src + header->io->seek_proc(header->handle, 0, SEEK_SET); + data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE); + } + + // write all the pages to the file using handle and io + + int count = 0; + + for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) { + if (success) { + switch((*i)->m_type) { + case BLOCK_CONTINUEUS: + { + BlockContinueus *block = (BlockContinueus *)(*i); + + for (int j = block->m_start; j <= block->m_end; j++) { + + // load the original source data + FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read); + + // save the data + success = node->m_plugin->save_proc(io, dib, handle, count, flags, data); + count++; + + FreeImage_Unload(dib); + } + + break; + } + + case BLOCK_REFERENCE: + { + BlockReference *ref = (BlockReference *)(*i); + + // read the compressed data + + BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE)); + + header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size); + + // uncompress the data + + FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size); + FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0); + FreeImage_CloseMemory(hmem); + + // get rid of the buffer + free(compressed_data); + + // save the data + + success = node->m_plugin->save_proc(io, dib, handle, count, flags, data); + count++; + + // unload the dib + + FreeImage_Unload(dib); + + break; + } + } + } else { + break; + } + } + + // close the files + + FreeImage_Close(header->node, header->io, header->handle, data_read); + + FreeImage_Close(node, io, handle, data); + + return success; + } + } + + return FALSE; +} + + +BOOL DLL_CALLCONV +FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) { + if (bitmap) { + BOOL success = TRUE; + + if (bitmap->data) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // saves changes only of images loaded directly from a file + if (header->changed && header->m_filename) { + try { + // open a temp file + + std::string spool_name; + + ReplaceExtension(spool_name, header->m_filename, "fispool"); + + // open the spool file and the source file + + FILE *f = fopen(spool_name.c_str(), "w+b"); + + // saves changes + if (f == NULL) { + FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno)); + success = FALSE; + } else { + success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags); + + // close the files + + if (fclose(f) != 0) { + success = FALSE; + FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno)); + } + } + if (header->handle) { + fclose((FILE *)header->handle); + } + + // applies changes to the destination file + + if (success) { + remove(header->m_filename); + success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE; + if(!success) { + FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename); + } + } else { + remove(spool_name.c_str()); + } + } catch (std::bad_alloc &) { + success = FALSE; + } + + } else { + if (header->handle && header->m_filename) { + fclose((FILE *)header->handle); + } + } + + // clear the blocks list + + for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) { + delete *i; + } + + // flush and dispose the cache + + if (header->m_cachefile) { + header->m_cachefile->close(); + delete header->m_cachefile; + } + + // delete the last open bitmaps + + while (!header->locked_pages.empty()) { + FreeImage_Unload(header->locked_pages.begin()->first); + + header->locked_pages.erase(header->locked_pages.begin()->first); + } + + // get rid of the IO structure + + delete header->io; + + // delete the filename + + if(header->m_filename) { + delete[] header->m_filename; + } + + // delete the FIMULTIBITMAPHEADER + + delete header; + } + + delete bitmap; + + return success; + } + + return FALSE; +} + +int DLL_CALLCONV +FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if (header->page_count == -1) { + header->page_count = 0; + + for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) { + switch((*i)->m_type) { + case BLOCK_CONTINUEUS : + header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1; + break; + + case BLOCK_REFERENCE : + header->page_count++; + break; + } + } + } + + return header->page_count; + } + + return 0; +} + +static BlockReference* +FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) { + if (header->read_only || !header->locked_pages.empty()) + return NULL; + + DWORD compressed_size = 0; + BYTE *compressed_data = NULL; + + // compress the bitmap data + + // open a memory handle + FIMEMORY *hmem = FreeImage_OpenMemory(); + if(hmem==NULL) return NULL; + // save the file to memory + if(!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) { + FreeImage_CloseMemory(hmem); + return NULL; + } + // get the buffer from the memory stream + if(!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) { + FreeImage_CloseMemory(hmem); + return NULL; + } + + // write the compressed data to the cache + int ref = header->m_cachefile->writeFile(compressed_data, compressed_size); + // get rid of the compressed data + FreeImage_CloseMemory(hmem); + + return new(std::nothrow) BlockReference(ref, compressed_size); +} + +void DLL_CALLCONV +FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) { + if (!bitmap || !data) + return; + + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + BlockReference *block = FreeImage_SavePageToBlock(header, data); + if(block==NULL) return; + + // add the block + header->m_blocks.push_back((BlockTypeS *)block); + header->changed = TRUE; + header->page_count = -1; +} + +void DLL_CALLCONV +FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) { + if (!bitmap || !data) + return; + + if (page >= FreeImage_GetPageCount(bitmap)) + return; + + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + BlockReference *block = FreeImage_SavePageToBlock(header, data); + if(block==NULL) return; + + // add a block + if (page > 0) { + BlockListIterator block_source = FreeImage_FindBlock(bitmap, page); + + header->m_blocks.insert(block_source, (BlockTypeS *)block); + } else { + header->m_blocks.push_front((BlockTypeS *)block); + } + + header->changed = TRUE; + header->page_count = -1; +} + +void DLL_CALLCONV +FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if ((!header->read_only) && (header->locked_pages.empty())) { + if (FreeImage_GetPageCount(bitmap) > 1) { + BlockListIterator i = FreeImage_FindBlock(bitmap, page); + + if (i != header->m_blocks.end()) { + switch((*i)->m_type) { + case BLOCK_CONTINUEUS : + delete *i; + header->m_blocks.erase(i); + break; + + case BLOCK_REFERENCE : + header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference); + delete *i; + header->m_blocks.erase(i); + break; + } + + header->changed = TRUE; + header->page_count = -1; + } + } + } + } +} + + +FIBITMAP * DLL_CALLCONV +FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // only lock if the page wasn't locked before... + + for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) { + if (i->second == page) { + return NULL; + } + } + + // open the bitmap + + header->io->seek_proc(header->handle, 0, SEEK_SET); + + void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE); + + // load the bitmap data + + if (data != NULL) { + FIBITMAP *dib = (header->node->m_plugin->load_proc != NULL) ? header->node->m_plugin->load_proc(header->io, header->handle, page, header->load_flags, data) : NULL; + + // close the file + + FreeImage_Close(header->node, header->io, header->handle, data); + + // if there was still another bitmap open, get rid of it + + if (dib) { + header->locked_pages[dib] = page; + + return dib; + } + + return NULL; + } + } + + return NULL; +} + +void DLL_CALLCONV +FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) { + if ((bitmap) && (page)) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // find out if the page we try to unlock is actually locked... + + if (header->locked_pages.find(page) != header->locked_pages.end()) { + // store the bitmap compressed in the cache for later writing + + if (changed && !header->read_only) { + header->changed = TRUE; + + // cut loose the block from the rest + + BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]); + + // compress the data + + DWORD compressed_size = 0; + BYTE *compressed_data = NULL; + + // open a memory handle + FIMEMORY *hmem = FreeImage_OpenMemory(); + // save the page to memory + FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0); + // get the buffer from the memory stream + FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size); + + // write the data to the cache + + switch ((*i)->m_type) { + case BLOCK_CONTINUEUS : + { + int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size); + + delete (*i); + + *i = (BlockTypeS *)new BlockReference(iPage, compressed_size); + + break; + } + + case BLOCK_REFERENCE : + { + BlockReference *reference = (BlockReference *)(*i); + + header->m_cachefile->deleteFile(reference->m_reference); + + delete (*i); + + int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size); + + *i = (BlockTypeS *)new BlockReference(iPage, compressed_size); + + break; + } + } + + // get rid of the compressed data + + FreeImage_CloseMemory(hmem); + } + + // reset the locked page so that another page can be locked + + FreeImage_Unload(page); + + header->locked_pages.erase(page); + } + } +} + +BOOL DLL_CALLCONV +FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if ((!header->read_only) && (header->locked_pages.empty())) { + if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) { + BlockListIterator block_source = FreeImage_FindBlock(bitmap, target); + BlockListIterator block_target = FreeImage_FindBlock(bitmap, source); + + header->m_blocks.insert(block_target, *block_source); + header->m_blocks.erase(block_source); + + header->changed = TRUE; + + return TRUE; + } + } + } + + return FALSE; +} + +BOOL DLL_CALLCONV +FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) { + if ((bitmap) && (count)) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if ((pages == NULL) || (*count == 0)) { + *count = (int)header->locked_pages.size(); + } else { + int c = 0; + + for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) { + pages[c] = i->second; + + c++; + + if (c == *count) + break; + } + } + + return TRUE; + } + + return FALSE; +} + +// ===================================================================== +// Memory IO Multipage functions +// ===================================================================== + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) { + BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache + + // retrieve the plugin list to find the node belonging to this plugin + + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if (node) { + FreeImageIO *io = new(std::nothrow) FreeImageIO; + + if (io) { + SetMemoryIO(io); + + FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP; + + if (bitmap) { + MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER; + + if (header) { + header->m_filename = NULL; + header->node = node; + header->fif = fif; + header->io = io; + header->handle = (fi_handle)stream; + header->changed = FALSE; + header->read_only = read_only; + header->m_cachefile = NULL; + header->cache_fif = fif; + header->load_flags = flags; + + // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure + + bitmap->data = header; + + // cache the page count + + header->page_count = FreeImage_InternalGetPageCount(bitmap); + + // allocate a continueus block to describe the bitmap + + header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1)); + + if (!read_only) { + // set up the cache + CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE); + + if (cache_file && cache_file->open()) { + header->m_cachefile = cache_file; + } + } + + return bitmap; + } + + delete bitmap; + } + + delete io; + } + } + } + + return NULL; +} + +BOOL DLL_CALLCONV +FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) { + if (stream && stream->data) { + FreeImageIO io; + SetMemoryIO(&io); + + return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags); + } + + return FALSE; +} diff --git a/plugins/AdvaImg/src/FreeImage/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 @@ -656,46 +656,6 @@ jpeg_read_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) } /** - Read JPEG_APP1 marker (Exif profile) - @param dib Input FIBITMAP - @param dataptr Pointer to the APP1 marker - @param datalen APP1 marker length - @return Returns TRUE if successful, FALSE otherwise -*/ -static BOOL -jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned int length) { - // marker identifying string for Exif = "Exif\0\0" - BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; - - // verify the identifying string - if(memcmp(exif_signature, profile, sizeof(exif_signature)) != 0) { - // not an Exif profile - return FALSE; - } - - // create a tag - FITAG *tag = FreeImage_CreateTag(); - if(tag) { - FreeImage_SetTagID(tag, EXIF_MARKER); // (JPEG_APP0 + 1) => EXIF marker / Adobe XMP marker - FreeImage_SetTagKey(tag, g_TagLib_ExifRawFieldName); - FreeImage_SetTagLength(tag, (DWORD)length); - FreeImage_SetTagCount(tag, (DWORD)length); - FreeImage_SetTagType(tag, FIDT_BYTE); - FreeImage_SetTagValue(tag, profile); - - // store the tag - FreeImage_SetMetadata(FIMD_EXIF_RAW, dib, FreeImage_GetTagKey(tag), tag); - - // destroy the tag - FreeImage_DeleteTag(tag); - - return TRUE; - } - - return FALSE; -} - -/** Read JFIF "JFXX" extension APP0 marker @param dib Input FIBITMAP @param dataptr Pointer to the APP0 marker @@ -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.<br>
-On output, pixel->red == Y, pixel->green == x, pixel->blue == y
-@param dib Input RGBF / Output Yxy image
-@return Returns TRUE if successful, returns FALSE otherwise
-*/
-BOOL
-ConvertInPlaceRGBFToYxy(FIBITMAP *dib) {
- float result[3];
-
- if(FreeImage_GetImageType(dib) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(dib);
- const unsigned height = FreeImage_GetHeight(dib);
- const unsigned pitch = FreeImage_GetPitch(dib);
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
- for(unsigned y = 0; y < height; y++) {
- FIRGBF *pixel = (FIRGBF*)bits;
- for(unsigned x = 0; x < width; x++) {
- result[0] = result[1] = result[2] = 0;
- for (int i = 0; i < 3; i++) {
- result[i] += RGB2XYZ[i][0] * pixel[x].red;
- result[i] += RGB2XYZ[i][1] * pixel[x].green;
- result[i] += RGB2XYZ[i][2] * pixel[x].blue;
- }
- const float W = result[0] + result[1] + result[2];
- const float Y = result[1];
- if(W > 0) {
- pixel[x].red = Y; // Y
- pixel[x].green = result[0] / W; // x
- pixel[x].blue = result[1] / W; // y
- } else {
- pixel[x].red = pixel[x].green = pixel[x].blue = 0;
- }
- }
- // next line
- bits += pitch;
- }
-
- return TRUE;
-}
-
-/**
-Convert in-place Yxy image to floating point RGB data.<br>
-On input, pixel->red == Y, pixel->green == x, pixel->blue == y
-@param dib Input Yxy / Output RGBF image
-@return Returns TRUE if successful, returns FALSE otherwise
-*/
-BOOL
-ConvertInPlaceYxyToRGBF(FIBITMAP *dib) {
- float result[3];
- float X, Y, Z;
-
- if(FreeImage_GetImageType(dib) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(dib);
- const unsigned height = FreeImage_GetHeight(dib);
- const unsigned pitch = FreeImage_GetPitch(dib);
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
- for(unsigned y = 0; y < height; y++) {
- FIRGBF *pixel = (FIRGBF*)bits;
- for(unsigned x = 0; x < width; x++) {
- Y = pixel[x].red; // Y
- result[1] = pixel[x].green; // x
- result[2] = pixel[x].blue; // y
- if ((Y > EPSILON) && (result[1] > EPSILON) && (result[2] > EPSILON)) {
- X = (result[1] * Y) / result[2];
- Z = (X / result[1]) - X - Y;
- } else {
- X = Z = EPSILON;
- }
- pixel[x].red = X;
- pixel[x].green = Y;
- pixel[x].blue = Z;
- result[0] = result[1] = result[2] = 0;
- for (int i = 0; i < 3; i++) {
- result[i] += XYZ2RGB[i][0] * pixel[x].red;
- result[i] += XYZ2RGB[i][1] * pixel[x].green;
- result[i] += XYZ2RGB[i][2] * pixel[x].blue;
- }
- pixel[x].red = result[0]; // R
- pixel[x].green = result[1]; // G
- pixel[x].blue = result[2]; // B
- }
- // next line
- bits += pitch;
- }
-
- return TRUE;
-}
-
-/**
-Get the maximum, minimum and average luminance.<br>
-On input, pixel->red == Y, pixel->green == x, pixel->blue == y
-@param Yxy Source Yxy image to analyze
-@param maxLum Maximum luminance
-@param minLum Minimum luminance
-@param worldLum Average luminance (world adaptation luminance)
-@return Returns TRUE if successful, returns FALSE otherwise
-*/
-BOOL
-LuminanceFromYxy(FIBITMAP *Yxy, float *maxLum, float *minLum, float *worldLum) {
- if(FreeImage_GetImageType(Yxy) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(Yxy);
- const unsigned height = FreeImage_GetHeight(Yxy);
- const unsigned pitch = FreeImage_GetPitch(Yxy);
-
- float max_lum = 0, min_lum = 0;
- double sum = 0;
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(Yxy);
- for(unsigned y = 0; y < height; y++) {
- const FIRGBF *pixel = (FIRGBF*)bits;
- for(unsigned x = 0; x < width; x++) {
- const float Y = pixel[x].red;
- max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene
- min_lum = (min_lum < Y) ? min_lum : Y; // min Luminance in the scene
- sum += log(2.3e-5F + Y); // contrast constant in Tumblin paper
- }
- // next line
- bits += pitch;
- }
- // maximum luminance
- *maxLum = max_lum;
- // minimum luminance
- *minLum = min_lum;
- // average log luminance
- double avgLogLum = (sum / (width * height));
- // world adaptation luminance
- *worldLum = (float)exp(avgLogLum);
-
- return TRUE;
-}
-
-/**
-Clamp RGBF image highest values to display white,
-then convert to 24-bit RGB
-*/
-FIBITMAP*
-ClampConvertRGBFTo24(FIBITMAP *src) {
- if(FreeImage_GetImageType(src) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(src);
- const unsigned height = FreeImage_GetHeight(src);
-
- FIBITMAP *dst = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
- if(!dst) return NULL;
-
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const unsigned dst_pitch = FreeImage_GetPitch(dst);
-
- BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
- BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
-
- for(unsigned y = 0; y < height; y++) {
- const FIRGBF *src_pixel = (FIRGBF*)src_bits;
- BYTE *dst_pixel = (BYTE*)dst_bits;
- for(unsigned x = 0; x < width; x++) {
- const float red = (src_pixel[x].red > 1) ? 1 : src_pixel[x].red;
- const float green = (src_pixel[x].green > 1) ? 1 : src_pixel[x].green;
- const float blue = (src_pixel[x].blue > 1) ? 1 : src_pixel[x].blue;
-
- dst_pixel[FI_RGBA_RED] = (BYTE)(255.0F * red + 0.5F);
- dst_pixel[FI_RGBA_GREEN] = (BYTE)(255.0F * green + 0.5F);
- dst_pixel[FI_RGBA_BLUE] = (BYTE)(255.0F * blue + 0.5F);
- dst_pixel += 3;
- }
- src_bits += src_pitch;
- dst_bits += dst_pitch;
- }
-
- return dst;
-}
-
-/**
-Extract the luminance channel L from a RGBF image.
-Luminance is calculated from the sRGB model (RGB2XYZ matrix)
-using a D65 white point :
-L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b )
-Reference :
-A Standard Default Color Space for the Internet - sRGB.
-[online] http://www.w3.org/Graphics/Color/sRGB
-*/
-FIBITMAP*
-ConvertRGBFToY(FIBITMAP *src) {
- if(FreeImage_GetImageType(src) != FIT_RGBF)
- return FALSE;
-
- const unsigned width = FreeImage_GetWidth(src);
- const unsigned height = FreeImage_GetHeight(src);
-
- FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height);
- if(!dst) return NULL;
-
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const unsigned dst_pitch = FreeImage_GetPitch(dst);
-
-
- BYTE *src_bits = (BYTE*)FreeImage_GetBits(src);
- BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst);
-
- for(unsigned y = 0; y < height; y++) {
- const FIRGBF *src_pixel = (FIRGBF*)src_bits;
- float *dst_pixel = (float*)dst_bits;
- for(unsigned x = 0; x < width; x++) {
- const float L = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue);
- dst_pixel[x] = (L > 0) ? L : 0;
- }
- // next line
- src_bits += src_pitch;
- dst_bits += dst_pitch;
- }
-
- return dst;
-}
-
-/**
-Get the maximum, minimum, average luminance and log average luminance from a Y image
-@param dib Source Y image to analyze
-@param maxLum Maximum luminance
-@param minLum Minimum luminance
-@param Lav Average luminance
-@param Llav Log average luminance (also known as 'world adaptation luminance')
-@return Returns TRUE if successful, returns FALSE otherwise
-@see ConvertRGBFToY, FreeImage_TmoReinhard05Ex
-*/
-BOOL
-LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav) {
- if(FreeImage_GetImageType(dib) != FIT_FLOAT)
- return FALSE;
-
- unsigned width = FreeImage_GetWidth(dib);
- unsigned height = FreeImage_GetHeight(dib);
- unsigned pitch = FreeImage_GetPitch(dib);
-
- float max_lum = -1e20F, min_lum = 1e20F;
- double sumLum = 0, sumLogLum = 0;
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(dib);
- for(unsigned y = 0; y < height; y++) {
- const float *pixel = (float*)bits;
- for(unsigned x = 0; x < width; x++) {
- const float Y = pixel[x];
- max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene
- min_lum = ((Y > 0) && (min_lum < Y)) ? min_lum : Y; // min Luminance in the scene
- sumLum += Y; // average luminance
- sumLogLum += log(2.3e-5F + Y); // contrast constant in Tumblin paper
- }
- // next line
- bits += pitch;
- }
-
- // maximum luminance
- *maxLum = max_lum;
- // minimum luminance
- *minLum = min_lum;
- // average luminance
- *Lav = (float)(sumLum / (width * height));
- // average log luminance, a.k.a. world adaptation luminance
- *Llav = (float)exp(sumLogLum / (width * height));
-
- return TRUE;
-}
-// --------------------------------------------------------------------------
-
-static void findMaxMinPercentile(FIBITMAP *Y, float minPrct, float *minLum, float maxPrct, float *maxLum) {
- int x, y;
- int width = FreeImage_GetWidth(Y);
- int height = FreeImage_GetHeight(Y);
- int pitch = FreeImage_GetPitch(Y);
-
- std::vector<float> vY(width * height);
-
- BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
- for(y = 0; y < height; y++) {
- float *pixel = (float*)bits;
- for(x = 0; x < width; x++) {
- if(pixel[x] != 0) {
- vY.push_back(pixel[x]);
- }
- }
- bits += pitch;
- }
-
- std::sort(vY.begin(), vY.end());
-
- *minLum = vY.at( int(minPrct * vY.size()) );
- *maxLum = vY.at( int(maxPrct * vY.size()) );
-}
-
-/**
-Clipping function<br>
-Remove any extremely bright and/or extremely dark pixels
-and normalize between 0 and 1.
-@param Y Input/Output image
-@param minPrct Minimum percentile
-@param maxPrct Maximum percentile
-*/
-void
-NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct) {
- int x, y;
- float maxLum, minLum;
-
- if(minPrct > maxPrct) {
- // swap values
- float t = minPrct; minPrct = maxPrct; maxPrct = t;
- }
- if(minPrct < 0) minPrct = 0;
- if(maxPrct > 1) maxPrct = 1;
-
- int width = FreeImage_GetWidth(Y);
- int height = FreeImage_GetHeight(Y);
- int pitch = FreeImage_GetPitch(Y);
-
- // find max & min luminance values
- if((minPrct > 0) || (maxPrct < 1)) {
- maxLum = 0, minLum = 0;
- findMaxMinPercentile(Y, minPrct, &minLum, maxPrct, &maxLum);
- } else {
- maxLum = -1e20F, minLum = 1e20F;
- BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
- for(y = 0; y < height; y++) {
- const float *pixel = (float*)bits;
- for(x = 0; x < width; x++) {
- const float value = pixel[x];
- maxLum = (maxLum < value) ? value : maxLum; // max Luminance in the scene
- minLum = (minLum < value) ? minLum : value; // min Luminance in the scene
- }
- // next line
- bits += pitch;
- }
- }
- if(maxLum == minLum) return;
-
- // normalize to range 0..1
- const float divider = maxLum - minLum;
- BYTE *bits = (BYTE*)FreeImage_GetBits(Y);
- for(y = 0; y < height; y++) {
- float *pixel = (float*)bits;
- for(x = 0; x < width; x++) {
- pixel[x] = (pixel[x] - minLum) / divider;
- if(pixel[x] <= 0) pixel[x] = EPSILON;
- if(pixel[x] > 1) pixel[x] = 1;
- }
- // next line
- bits += pitch;
- }
-}
+// ========================================================== +// High Dynamic Range bitmap conversion routines +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Mihail Naydenov (mnaydenov@users.sourceforge.net) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "FreeImage.h" +#include "Utilities.h" +#include "ToneMapping.h" + +// ---------------------------------------------------------- +// Convert RGB to and from Yxy, same as in Reinhard et al. SIGGRAPH 2002 +// References : +// [1] Radiance Home Page [Online] http://radsite.lbl.gov/radiance/HOME.html +// [2] E. Reinhard, M. Stark, P. Shirley, and J. Ferwerda, +// Photographic Tone Reproduction for Digital Images, ACM Transactions on Graphics, +// 21(3):267-276, 2002 (Proceedings of SIGGRAPH 2002). +// [3] J. Tumblin and H.E. Rushmeier, +// Tone Reproduction for Realistic Images. IEEE Computer Graphics and Applications, +// 13(6):42-48, 1993. +// ---------------------------------------------------------- + +/** +nominal CRT primaries +*/ +/* +static const float CIE_x_r = 0.640F; +static const float CIE_y_r = 0.330F; +static const float CIE_x_g = 0.290F; +static const float CIE_y_g = 0.600F; +static const float CIE_x_b = 0.150F; +static const float CIE_y_b = 0.060F; +static const float CIE_x_w = 0.3333F; // use true white +static const float CIE_y_w = 0.3333F; +*/ +/** +sRGB primaries +*/ +static const float CIE_x_r = 0.640F; +static const float CIE_y_r = 0.330F; +static const float CIE_x_g = 0.300F; +static const float CIE_y_g = 0.600F; +static const float CIE_x_b = 0.150F; +static const float CIE_y_b = 0.060F; +static const float CIE_x_w = 0.3127F; // Illuminant D65 +static const float CIE_y_w = 0.3290F; + +static const float CIE_D = ( CIE_x_r*(CIE_y_g - CIE_y_b) + CIE_x_g*(CIE_y_b - CIE_y_r) + CIE_x_b*(CIE_y_r - CIE_y_g) ); +static const float CIE_C_rD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_g - CIE_y_b) - CIE_y_w*(CIE_x_g - CIE_x_b) + CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) ); +static const float CIE_C_gD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_b - CIE_y_r) - CIE_y_w*(CIE_x_b - CIE_x_r) - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) ); +static const float CIE_C_bD = ( (1/CIE_y_w) * ( CIE_x_w*(CIE_y_r - CIE_y_g) - CIE_y_w*(CIE_x_r - CIE_x_g) + CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) ); + +/** +RGB to XYZ (no white balance) +*/ +static const float RGB2XYZ[3][3] = { + { CIE_x_r*CIE_C_rD / CIE_D, + CIE_x_g*CIE_C_gD / CIE_D, + CIE_x_b*CIE_C_bD / CIE_D + }, + { CIE_y_r*CIE_C_rD / CIE_D, + CIE_y_g*CIE_C_gD / CIE_D, + CIE_y_b*CIE_C_bD / CIE_D + }, + { (1 - CIE_x_r-CIE_y_r)*CIE_C_rD / CIE_D, + (1 - CIE_x_g-CIE_y_g)*CIE_C_gD / CIE_D, + (1 - CIE_x_b-CIE_y_b)*CIE_C_bD / CIE_D + } +}; + +/** +XYZ to RGB (no white balance) +*/ +static const float XYZ2RGB[3][3] = { + {(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g) / CIE_C_rD, + (CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b) / CIE_C_rD, + (CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g) / CIE_C_rD + }, + {(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b) / CIE_C_gD, + (CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r) / CIE_C_gD, + (CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b) / CIE_C_gD + }, + {(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r) / CIE_C_bD, + (CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g) / CIE_C_bD, + (CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r) / CIE_C_bD + } +}; + +/** +This gives approximately the following matrices : + +static const float RGB2XYZ[3][3] = { + { 0.41239083F, 0.35758433F, 0.18048081F }, + { 0.21263903F, 0.71516865F, 0.072192319F }, + { 0.019330820F, 0.11919473F, 0.95053220F } +}; +static const float XYZ2RGB[3][3] = { + { 3.2409699F, -1.5373832F, -0.49861079F }, + { -0.96924376F, 1.8759676F, 0.041555084F }, + { 0.055630036F, -0.20397687F, 1.0569715F } +}; +*/ + +// ---------------------------------------------------------- + +static const float EPSILON = 1e-06F; +static const float INF = 1e+10F; + +/** +Convert in-place floating point RGB data to Yxy.<br> +On output, pixel->red == Y, pixel->green == x, pixel->blue == y +@param dib Input RGBF / Output Yxy image +@return Returns TRUE if successful, returns FALSE otherwise +*/ +BOOL +ConvertInPlaceRGBFToYxy(FIBITMAP *dib) { + float result[3]; + + if(FreeImage_GetImageType(dib) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(unsigned y = 0; y < height; y++) { + FIRGBF *pixel = (FIRGBF*)bits; + for(unsigned x = 0; x < width; x++) { + result[0] = result[1] = result[2] = 0; + for (int i = 0; i < 3; i++) { + result[i] += RGB2XYZ[i][0] * pixel[x].red; + result[i] += RGB2XYZ[i][1] * pixel[x].green; + result[i] += RGB2XYZ[i][2] * pixel[x].blue; + } + const float W = result[0] + result[1] + result[2]; + const float Y = result[1]; + if(W > 0) { + pixel[x].red = Y; // Y + pixel[x].green = result[0] / W; // x + pixel[x].blue = result[1] / W; // y + } else { + pixel[x].red = pixel[x].green = pixel[x].blue = 0; + } + } + // next line + bits += pitch; + } + + return TRUE; +} + +/** +Convert in-place Yxy image to floating point RGB data.<br> +On input, pixel->red == Y, pixel->green == x, pixel->blue == y +@param dib Input Yxy / Output RGBF image +@return Returns TRUE if successful, returns FALSE otherwise +*/ +BOOL +ConvertInPlaceYxyToRGBF(FIBITMAP *dib) { + float result[3]; + float X, Y, Z; + + if(FreeImage_GetImageType(dib) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + const unsigned pitch = FreeImage_GetPitch(dib); + + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(unsigned y = 0; y < height; y++) { + FIRGBF *pixel = (FIRGBF*)bits; + for(unsigned x = 0; x < width; x++) { + Y = pixel[x].red; // Y + result[1] = pixel[x].green; // x + result[2] = pixel[x].blue; // y + if ((Y > EPSILON) && (result[1] > EPSILON) && (result[2] > EPSILON)) { + X = (result[1] * Y) / result[2]; + Z = (X / result[1]) - X - Y; + } else { + X = Z = EPSILON; + } + pixel[x].red = X; + pixel[x].green = Y; + pixel[x].blue = Z; + result[0] = result[1] = result[2] = 0; + for (int i = 0; i < 3; i++) { + result[i] += XYZ2RGB[i][0] * pixel[x].red; + result[i] += XYZ2RGB[i][1] * pixel[x].green; + result[i] += XYZ2RGB[i][2] * pixel[x].blue; + } + pixel[x].red = result[0]; // R + pixel[x].green = result[1]; // G + pixel[x].blue = result[2]; // B + } + // next line + bits += pitch; + } + + return TRUE; +} + +/** +Get the maximum, minimum and average luminance.<br> +On input, pixel->red == Y, pixel->green == x, pixel->blue == y +@param Yxy Source Yxy image to analyze +@param maxLum Maximum luminance +@param minLum Minimum luminance +@param worldLum Average luminance (world adaptation luminance) +@return Returns TRUE if successful, returns FALSE otherwise +*/ +BOOL +LuminanceFromYxy(FIBITMAP *Yxy, float *maxLum, float *minLum, float *worldLum) { + if(FreeImage_GetImageType(Yxy) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(Yxy); + const unsigned height = FreeImage_GetHeight(Yxy); + const unsigned pitch = FreeImage_GetPitch(Yxy); + + float max_lum = 0, min_lum = 0; + double sum = 0; + + BYTE *bits = (BYTE*)FreeImage_GetBits(Yxy); + for(unsigned y = 0; y < height; y++) { + const FIRGBF *pixel = (FIRGBF*)bits; + for(unsigned x = 0; x < width; x++) { + const float Y = MAX(0.0F, pixel[x].red);// avoid negative values + max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene + min_lum = (min_lum < Y) ? min_lum : Y; // min Luminance in the scene + sum += log(2.3e-5F + Y); // contrast constant in Tumblin paper + } + // next line + bits += pitch; + } + // maximum luminance + *maxLum = max_lum; + // minimum luminance + *minLum = min_lum; + // average log luminance + double avgLogLum = (sum / (width * height)); + // world adaptation luminance + *worldLum = (float)exp(avgLogLum); + + return TRUE; +} + +/** +Clamp RGBF image highest values to display white, +then convert to 24-bit RGB +*/ +FIBITMAP* +ClampConvertRGBFTo24(FIBITMAP *src) { + if(FreeImage_GetImageType(src) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + FIBITMAP *dst = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if(!dst) return NULL; + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const FIRGBF *src_pixel = (FIRGBF*)src_bits; + BYTE *dst_pixel = (BYTE*)dst_bits; + for(unsigned x = 0; x < width; x++) { + const float red = (src_pixel[x].red > 1) ? 1 : src_pixel[x].red; + const float green = (src_pixel[x].green > 1) ? 1 : src_pixel[x].green; + const float blue = (src_pixel[x].blue > 1) ? 1 : src_pixel[x].blue; + + dst_pixel[FI_RGBA_RED] = (BYTE)(255.0F * red + 0.5F); + dst_pixel[FI_RGBA_GREEN] = (BYTE)(255.0F * green + 0.5F); + dst_pixel[FI_RGBA_BLUE] = (BYTE)(255.0F * blue + 0.5F); + dst_pixel += 3; + } + src_bits += src_pitch; + dst_bits += dst_pitch; + } + + return dst; +} + +/** +Extract the luminance channel L from a RGBF image. +Luminance is calculated from the sRGB model (RGB2XYZ matrix) +using a D65 white point : +L = ( 0.2126 * r ) + ( 0.7152 * g ) + ( 0.0722 * b ) +Reference : +A Standard Default Color Space for the Internet - sRGB. +[online] http://www.w3.org/Graphics/Color/sRGB +*/ +FIBITMAP* +ConvertRGBFToY(FIBITMAP *src) { + if(FreeImage_GetImageType(src) != FIT_RGBF) + return FALSE; + + const unsigned width = FreeImage_GetWidth(src); + const unsigned height = FreeImage_GetHeight(src); + + FIBITMAP *dst = FreeImage_AllocateT(FIT_FLOAT, width, height); + if(!dst) return NULL; + + const unsigned src_pitch = FreeImage_GetPitch(src); + const unsigned dst_pitch = FreeImage_GetPitch(dst); + + + BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); + BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); + + for(unsigned y = 0; y < height; y++) { + const FIRGBF *src_pixel = (FIRGBF*)src_bits; + float *dst_pixel = (float*)dst_bits; + for(unsigned x = 0; x < width; x++) { + const float L = LUMA_REC709(src_pixel[x].red, src_pixel[x].green, src_pixel[x].blue); + dst_pixel[x] = (L > 0) ? L : 0; + } + // next line + src_bits += src_pitch; + dst_bits += dst_pitch; + } + + return dst; +} + +/** +Get the maximum, minimum, average luminance and log average luminance from a Y image +@param dib Source Y image to analyze +@param maxLum Maximum luminance +@param minLum Minimum luminance +@param Lav Average luminance +@param Llav Log average luminance (also known as 'world adaptation luminance') +@return Returns TRUE if successful, returns FALSE otherwise +@see ConvertRGBFToY, FreeImage_TmoReinhard05Ex +*/ +BOOL +LuminanceFromY(FIBITMAP *dib, float *maxLum, float *minLum, float *Lav, float *Llav) { + if(FreeImage_GetImageType(dib) != FIT_FLOAT) + return FALSE; + + unsigned width = FreeImage_GetWidth(dib); + unsigned height = FreeImage_GetHeight(dib); + unsigned pitch = FreeImage_GetPitch(dib); + + float max_lum = -1e20F, min_lum = 1e20F; + double sumLum = 0, sumLogLum = 0; + + BYTE *bits = (BYTE*)FreeImage_GetBits(dib); + for(unsigned y = 0; y < height; y++) { + const float *pixel = (float*)bits; + for(unsigned x = 0; x < width; x++) { + const float Y = pixel[x]; + max_lum = (max_lum < Y) ? Y : max_lum; // max Luminance in the scene + min_lum = ((Y > 0) && (min_lum < Y)) ? min_lum : Y; // min Luminance in the scene + sumLum += Y; // average luminance + sumLogLum += log(2.3e-5F + Y); // contrast constant in Tumblin paper + } + // next line + bits += pitch; + } + + // maximum luminance + *maxLum = max_lum; + // minimum luminance + *minLum = min_lum; + // average luminance + *Lav = (float)(sumLum / (width * height)); + // average log luminance, a.k.a. world adaptation luminance + *Llav = (float)exp(sumLogLum / (width * height)); + + return TRUE; +} +// -------------------------------------------------------------------------- + +static void findMaxMinPercentile(FIBITMAP *Y, float minPrct, float *minLum, float maxPrct, float *maxLum) { + int x, y; + int width = FreeImage_GetWidth(Y); + int height = FreeImage_GetHeight(Y); + int pitch = FreeImage_GetPitch(Y); + + std::vector<float> vY(width * height); + + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + if(pixel[x] != 0) { + vY.push_back(pixel[x]); + } + } + bits += pitch; + } + + std::sort(vY.begin(), vY.end()); + + *minLum = vY.at( int(minPrct * vY.size()) ); + *maxLum = vY.at( int(maxPrct * vY.size()) ); +} + +/** +Clipping function<br> +Remove any extremely bright and/or extremely dark pixels +and normalize between 0 and 1. +@param Y Input/Output image +@param minPrct Minimum percentile +@param maxPrct Maximum percentile +*/ +void +NormalizeY(FIBITMAP *Y, float minPrct, float maxPrct) { + int x, y; + float maxLum, minLum; + + if(minPrct > maxPrct) { + // swap values + float t = minPrct; minPrct = maxPrct; maxPrct = t; + } + if(minPrct < 0) minPrct = 0; + if(maxPrct > 1) maxPrct = 1; + + int width = FreeImage_GetWidth(Y); + int height = FreeImage_GetHeight(Y); + int pitch = FreeImage_GetPitch(Y); + + // find max & min luminance values + if((minPrct > 0) || (maxPrct < 1)) { + maxLum = 0, minLum = 0; + findMaxMinPercentile(Y, minPrct, &minLum, maxPrct, &maxLum); + } else { + maxLum = -1e20F, minLum = 1e20F; + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + const float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + const float value = pixel[x]; + maxLum = (maxLum < value) ? value : maxLum; // max Luminance in the scene + minLum = (minLum < value) ? minLum : value; // min Luminance in the scene + } + // next line + bits += pitch; + } + } + if(maxLum == minLum) return; + + // normalize to range 0..1 + const float divider = maxLum - minLum; + BYTE *bits = (BYTE*)FreeImage_GetBits(Y); + for(y = 0; y < height; y++) { + float *pixel = (float*)bits; + for(x = 0; x < width; x++) { + pixel[x] = (pixel[x] - minLum) / divider; + if(pixel[x] <= 0) pixel[x] = EPSILON; + if(pixel[x] > 1) pixel[x] = 1; + } + // next line + bits += pitch; + } +} diff --git a/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; + } +} |