diff options
author | Kirill Volinsky <mataes2007@gmail.com> | 2015-05-18 07:31:16 +0000 |
---|---|---|
committer | Kirill Volinsky <mataes2007@gmail.com> | 2015-05-18 07:31:16 +0000 |
commit | 76b86227951fdb5096572c36a256f07aee76def3 (patch) | |
tree | 713dcf30179d88b685605bdc8a03a5068832e4ff /plugins/AdvaImg | |
parent | 4b289716d4cdd6b3ea29aec8d50e0b69afdc8384 (diff) |
git-svn-id: http://svn.miranda-ng.org/main/trunk@13671 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/AdvaImg')
48 files changed, 6139 insertions, 3953 deletions
diff --git a/plugins/AdvaImg/AdvaImg_10.vcxproj b/plugins/AdvaImg/AdvaImg_10.vcxproj index ad8f30c192..37cf1843a9 100644 --- a/plugins/AdvaImg/AdvaImg_10.vcxproj +++ b/plugins/AdvaImg/AdvaImg_10.vcxproj @@ -190,10 +190,13 @@ <ClCompile Include="src\FreeImage\ColorLookup.cpp" />
<ClCompile Include="src\FreeImage\ConversionFloat.cpp" />
<ClCompile Include="src\FreeImage\ConversionRGB16.cpp" />
+ <ClCompile Include="src\FreeImage\ConversionRGBA16.cpp" />
+ <ClCompile Include="src\FreeImage\ConversionRGBAF.cpp" />
<ClCompile Include="src\FreeImage\ConversionUINT16.cpp" />
<ClCompile Include="src\FreeImage\FreeImage.cpp" />
<ClCompile Include="src\FreeImage\FreeImageIO.cpp" />
<ClCompile Include="src\FreeImage\GetType.cpp" />
+ <ClCompile Include="src\FreeImage\LFPQuantizer.cpp" />
<ClCompile Include="src\FreeImage\MemoryIO.cpp" />
<ClCompile Include="src\FreeImage\PixelAccess.cpp" />
<ClCompile Include="src\FreeImage\Plugin.cpp" />
diff --git a/plugins/AdvaImg/AdvaImg_10.vcxproj.filters b/plugins/AdvaImg/AdvaImg_10.vcxproj.filters index c90205fc3b..d99cac1b16 100644 --- a/plugins/AdvaImg/AdvaImg_10.vcxproj.filters +++ b/plugins/AdvaImg/AdvaImg_10.vcxproj.filters @@ -119,6 +119,12 @@ <ClCompile Include="src\FreeImage\ConversionRGBF.cpp">
<Filter>Source Files\Conversion</Filter>
</ClCompile>
+ <ClCompile Include="src\FreeImage\ConversionRGBA16.cpp">
+ <Filter>Source Files\Conversion</Filter>
+ </ClCompile>
+ <ClCompile Include="src\FreeImage\ConversionRGBAF.cpp">
+ <Filter>Source Files\Conversion</Filter>
+ </ClCompile>
<ClCompile Include="src\FreeImage\ConversionType.cpp">
<Filter>Source Files\Conversion</Filter>
</ClCompile>
@@ -143,6 +149,9 @@ <ClCompile Include="src\FreeImage\ToneMapping.cpp">
<Filter>Source Files\Conversion</Filter>
</ClCompile>
+ <ClCompile Include="src\FreeImage\LFPQuantizer.cpp">
+ <Filter>Source Files\Quantizers</Filter>
+ </ClCompile>
<ClCompile Include="src\FreeImage\NNQuantizer.cpp">
<Filter>Source Files\Quantizers</Filter>
</ClCompile>
diff --git a/plugins/AdvaImg/AdvaImg_12.vcxproj b/plugins/AdvaImg/AdvaImg_12.vcxproj index b887d1831f..3f34191495 100644 --- a/plugins/AdvaImg/AdvaImg_12.vcxproj +++ b/plugins/AdvaImg/AdvaImg_12.vcxproj @@ -187,10 +187,13 @@ <ClCompile Include="src\FreeImage\ColorLookup.cpp" />
<ClCompile Include="src\FreeImage\ConversionFloat.cpp" />
<ClCompile Include="src\FreeImage\ConversionRGB16.cpp" />
+ <ClCompile Include="src\FreeImage\ConversionRGBA16.cpp" />
+ <ClCompile Include="src\FreeImage\ConversionRGBAF.cpp" />
<ClCompile Include="src\FreeImage\ConversionUINT16.cpp" />
<ClCompile Include="src\FreeImage\FreeImage.cpp" />
<ClCompile Include="src\FreeImage\FreeImageIO.cpp" />
<ClCompile Include="src\FreeImage\GetType.cpp" />
+ <ClCompile Include="src\FreeImage\LFPQuantizer.cpp" />
<ClCompile Include="src\FreeImage\MemoryIO.cpp" />
<ClCompile Include="src\FreeImage\PixelAccess.cpp" />
<ClCompile Include="src\FreeImage\Plugin.cpp" />
diff --git a/plugins/AdvaImg/AdvaImg_12.vcxproj.filters b/plugins/AdvaImg/AdvaImg_12.vcxproj.filters index c90205fc3b..e92ec756ea 100644 --- a/plugins/AdvaImg/AdvaImg_12.vcxproj.filters +++ b/plugins/AdvaImg/AdvaImg_12.vcxproj.filters @@ -119,6 +119,21 @@ <ClCompile Include="src\FreeImage\ConversionRGBF.cpp">
<Filter>Source Files\Conversion</Filter>
</ClCompile>
+ <ClCompile Include="src\FreeImage\ConversionRGBA16.cpp">
+ <Filter>Source Files\Conversion</Filter>
+ </ClCompile>
+ <ClCompile Include="src\FreeImage\ConversionFloat.cpp">
+ <Filter>Source Files\Conversion</Filter>
+ </ClCompile>
+ <ClCompile Include="src\FreeImage\ConversionRGB16.cpp">
+ <Filter>Source Files\Conversion</Filter>
+ </ClCompile>
+ <ClCompile Include="src\FreeImage\ConversionUINT16.cpp">
+ <Filter>Source Files\Conversion</Filter>
+ </ClCompile>
+ <ClCompile Include="src\FreeImage\ConversionRGBAF.cpp">
+ <Filter>Source Files\Conversion</Filter>
+ </ClCompile>
<ClCompile Include="src\FreeImage\ConversionType.cpp">
<Filter>Source Files\Conversion</Filter>
</ClCompile>
@@ -143,6 +158,9 @@ <ClCompile Include="src\FreeImage\ToneMapping.cpp">
<Filter>Source Files\Conversion</Filter>
</ClCompile>
+ <ClCompile Include="src\FreeImage\LFPQuantizer.cpp">
+ <Filter>Source Files\Quantizers</Filter>
+ </ClCompile>
<ClCompile Include="src\FreeImage\NNQuantizer.cpp">
<Filter>Source Files\Quantizers</Filter>
</ClCompile>
@@ -344,6 +362,18 @@ <ClCompile Include="src\LibJPEG\wrtarga.c">
<Filter>Source Files\LibJPEG\Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\LibJPEG\jaricom.c">
+ <Filter>Source Files\LibJPEG\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\LibJPEG\jcarith.c">
+ <Filter>Source Files\LibJPEG\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\LibJPEG\jdarith.c">
+ <Filter>Source Files\LibJPEG\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\LibJPEG\jdhuff.c">
+ <Filter>Source Files\LibJPEG\Source Files</Filter>
+ </ClCompile>
<ClCompile Include="src\LibPNG\png.c">
<Filter>Source Files\LibPNG\Source Files</Filter>
</ClCompile>
@@ -419,27 +449,6 @@ <ClCompile Include="src\FreeImageToolkit\Resize.cpp">
<Filter>Toolkit Files</Filter>
</ClCompile>
- <ClCompile Include="src\LibJPEG\jaricom.c">
- <Filter>Source Files\LibJPEG\Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\LibJPEG\jcarith.c">
- <Filter>Source Files\LibJPEG\Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\LibJPEG\jdarith.c">
- <Filter>Source Files\LibJPEG\Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\LibJPEG\jdhuff.c">
- <Filter>Source Files\LibJPEG\Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\FreeImage\ConversionFloat.cpp">
- <Filter>Source Files\Conversion</Filter>
- </ClCompile>
- <ClCompile Include="src\FreeImage\ConversionRGB16.cpp">
- <Filter>Source Files\Conversion</Filter>
- </ClCompile>
- <ClCompile Include="src\FreeImage\ConversionUINT16.cpp">
- <Filter>Source Files\Conversion</Filter>
- </ClCompile>
<ClCompile Include="src\FreeImageToolkit\Background.cpp">
<Filter>Toolkit Files</Filter>
</ClCompile>
diff --git a/plugins/AdvaImg/Docs/Whatsnew.txt b/plugins/AdvaImg/Docs/Whatsnew.txt index d761069a64..83515452ac 100644 --- a/plugins/AdvaImg/Docs/Whatsnew.txt +++ b/plugins/AdvaImg/Docs/Whatsnew.txt @@ -5,6 +5,55 @@ What's New for FreeImage ! : changed
+ : added
+March 15th, 2015 - 3.17.0
+! FreeImage now uses LibPNG 1.6.16
+! FreeImage now uses LibWebP 0.4.2 (GIT patch 2015-03-03)
+! FreeImage now uses LibRaw 0.17-Alpha1
+! FreeImage now uses LibTIFF 4.0.4 (CVS patch 2015-01-26)
+! FreeImage now uses OpenEXR 2.2.0
+- [Herve Drolon] removed VS 2003 project files : this IDE is no longer supported because of its outdated C++ compiler
++ [Mihail Naydenov] added FreeImage_ConvertFromRawBitsEx
++ [Herve Drolon] added RAW_UNPROCESSED load flag to the RAW plugin
++ [Herve Drolon] added FreeImage_SetMetadataKeyValue
++ [Herve Drolon] added support for metadata writing to the JPEG-JXR plugin
++ [Herve Drolon] added VS 2013 project files
++ [Herve Drolon] added support for PNG tIME metadata (read/write, handle as Exif-TIFF DateTime)
++ [Carsten Klein] added explicit definition of endianness and color order in compiler options
++ [Carsten Klein] added FIQ_LFPQUANT quantizer algorithm
++ [Carsten Klein] added support for input 32-bit dib in Wu quantizer
++ [Tanner Helland] added FreeImage_ConvertToRGBAF and updated conversions in FreeImage_ConvertToType
++ [Herve Drolon] added FreeImage_ConvertToRGBA16 and updated conversions in FreeImage_ConvertToType
++ [Carsten Klein] added FreeImage_CreateView
++ [Carsten Klein] added FreeImage_RescaleRect
++ [Carsten Klein] added FreeImage_GetMemorySize
+* [Tanner Helland] ICO plugin: improved support for Vista icons
+* [fpgaminer] fixed a rounding error in RGB to greyscale conversion formula
+* [Sven-Hendrik Haase] fixed Makefile.fip so that it installs symlinks
+* [Joachim Reichel] fixed a potential memory access violation in PluginHDR Save function
+* [Christian Schluchter] fixed a bug in FreeImage_LookupSVGColor ("green" color was not found)
+* [Marco Altomonte] fixed TARGA signature validation for TARGA versions < 2.0
+* [Jeremy Reyniers] fixed FreeImage_GetScanLine not working with very large images on x64 platforms
+* [Herve Drolon] improved PluginTIFF compatibility with LibTIFF 4
+* [Aaron Shumate] fixed a segfault occuring on a corrupted animated GIF
+* [Herve Drolon] improved memory allocation in PluginRAW
+* [Herve Drolon] fixed loading/saving of TIFF containing a GPS IFD inside the Exif-TIFF metadata segment (the solution is to ignore the tag)
+* [Mihail Naydenov] fixed a bug in FreeImage_JPEGCrop*/_JPEGTransform* functions occuring when using the same source / destination filename
+* [Herve Drolon] fixed a bug with output image quality in PluginJP2::Save & PluginJ2K::Save functions (regression from FI 3.15.4)
+* [Herve Drolon] improved RAW file format detection
+* [Aaron Shumate] fixed FreeImage_GetFileType behavior with ANI file formats
+* [Herve Drolon] improved Exif reader so as to handle Exif IFD with a suspicious offset (can occur with maker notes)
+* [Herve Drolon] fixed a memory leak in PluginPNG:Save occuring when dealing with invalid PNG files
+* [Tanner Helland] fixed PNG plugin handling of 16-bit grayscale + 16-bit alpha images
+* [Tanner Helland] fixed PNG plugin handling of 16-bit grayscale + tRNS chunk images
+* [Tanner Helland] fixed PNG plugin handling of 24-bit RGB + tRNS chunk images
+* [Tanner Helland] fixed PNG plugin handling of 1-,4-bit greyscale/palettized + tRNS chunk images
+* [ekpyron] fixed invalid directory delimiter in include statement (mingw-w64) in Source/LibJXR/image/sys/strcodec.h
+* [ekpyron] fixed Invalid condition for defining _byteswap_ulong (mingw-w64) in Source/LibJXR/image/sys/strcodec.c
+* [tostercx] fixed FreeImage_Get*Mask not returning 0 for greyscale images
+* [robpats] fixed loading of external plugins when using UNICODE directory names to store plugins
+* [Herve Drolon] fixed loading of JXR files when using memory streams
+* [Carsten Klein] added Dist/ directory creation in Makefiles (in case it is not already present)
+
March 23rd, 2014 - 3.16.0
! FreeImage now uses LibJPEG 9a
! FreeImage now uses LibPNG 1.6.10
diff --git a/plugins/AdvaImg/src/FreeImage.h b/plugins/AdvaImg/src/FreeImage.h index 80a5d07514..fa70ea9f39 100644 --- a/plugins/AdvaImg/src/FreeImage.h +++ b/plugins/AdvaImg/src/FreeImage.h @@ -29,7 +29,7 @@ // Version information ------------------------------------------------------ #define FREEIMAGE_MAJOR_VERSION 3 -#define FREEIMAGE_MINOR_VERSION 16 +#define FREEIMAGE_MINOR_VERSION 17 #define FREEIMAGE_RELEASE_SERIAL 0 // Compiler options --------------------------------------------------------- @@ -69,22 +69,36 @@ #endif // WIN32 / !WIN32 #endif // FREEIMAGE_LIB -// Some versions of gcc may have BYTE_ORDER or __BYTE_ORDER defined +// Endianness: +// Some versions of gcc may have BYTE_ORDER or __BYTE_ORDER defined. // If your big endian system isn't being detected, add an OS specific check -#if (defined(BYTE_ORDER) && BYTE_ORDER==BIG_ENDIAN) || \ - (defined(__BYTE_ORDER) && __BYTE_ORDER==__BIG_ENDIAN) || \ - defined(__BIG_ENDIAN__) -#define FREEIMAGE_BIGENDIAN -#endif // BYTE_ORDER - -// This really only affects 24 and 32 bit formats, the rest are always RGB order. -#define FREEIMAGE_COLORORDER_BGR 0 -#define FREEIMAGE_COLORORDER_RGB 1 -#if defined(FREEIMAGE_BIGENDIAN) -#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_RGB -#else -#define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_BGR -#endif +// or define any of FREEIMAGE_BIGENDIAN and FREEIMAGE_LITTLEENDIAN directly +// to specify the desired endianness. +#if (!defined(FREEIMAGE_BIGENDIAN) && !defined(FREEIMAGE_LITTLEENDIAN)) + #if (defined(BYTE_ORDER) && BYTE_ORDER==BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER==__BIG_ENDIAN) || defined(__BIG_ENDIAN__) + #define FREEIMAGE_BIGENDIAN + #endif // BYTE_ORDER +#endif // !FREEIMAGE_[BIG|LITTLE]ENDIAN + +// Color-Order: +// The specified order of color components red, green and blue affects 24- +// and 32-bit images of type FIT_BITMAP as well as the colors that are part +// of a color palette. All other images always use RGB order. By default, +// color order is coupled to endianness: +// little-endian -> BGR +// big-endian -> RGB +// However, you can always define FREEIMAGE_COLORORDER to any of the known +// orders FREEIMAGE_COLORORDER_BGR (0) and FREEIMAGE_COLORORDER_RGB (1) to +// specify your preferred color order. +#define FREEIMAGE_COLORORDER_BGR 0 +#define FREEIMAGE_COLORORDER_RGB 1 +#if (!defined(FREEIMAGE_COLORORDER)) || ((FREEIMAGE_COLORORDER != FREEIMAGE_COLORORDER_BGR) && (FREEIMAGE_COLORORDER != FREEIMAGE_COLORORDER_RGB)) + #if defined(FREEIMAGE_BIGENDIAN) + #define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_RGB + #else + #define FREEIMAGE_COLORORDER FREEIMAGE_COLORORDER_BGR + #endif // FREEIMAGE_BIGENDIAN +#endif // FREEIMAGE_COLORORDER // Ensure 4-byte enums if we're using Borland C++ compilers #if defined(__BORLANDC__) @@ -357,9 +371,9 @@ typedef struct tagFICOMPLEX { #define FIICC_COLOR_IS_CMYK 0x01 FI_STRUCT (FIICCPROFILE) { - WORD flags; // info flag - DWORD size; // profile's size measured in bytes - void *data; // points to a block of contiguous memory containing the profile + WORD flags; //! info flag + DWORD size; //! profile's size measured in bytes + void *data; //! points to a block of contiguous memory containing the profile }; // Important enums ---------------------------------------------------------- @@ -411,102 +425,103 @@ FI_ENUM(FREE_IMAGE_FORMAT) { /** Image type used in FreeImage. */ FI_ENUM(FREE_IMAGE_TYPE) { - FIT_UNKNOWN = 0, // unknown type - FIT_BITMAP = 1, // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit - FIT_UINT16 = 2, // array of unsigned short : unsigned 16-bit - FIT_INT16 = 3, // array of short : signed 16-bit - FIT_UINT32 = 4, // array of unsigned long : unsigned 32-bit - FIT_INT32 = 5, // array of long : signed 32-bit - FIT_FLOAT = 6, // array of float : 32-bit IEEE floating point - FIT_DOUBLE = 7, // array of double : 64-bit IEEE floating point - FIT_COMPLEX = 8, // array of FICOMPLEX : 2 x 64-bit IEEE floating point - FIT_RGB16 = 9, // 48-bit RGB image : 3 x 16-bit - FIT_RGBA16 = 10, // 64-bit RGBA image : 4 x 16-bit - FIT_RGBF = 11, // 96-bit RGB float image : 3 x 32-bit IEEE floating point - FIT_RGBAF = 12 // 128-bit RGBA float image : 4 x 32-bit IEEE floating point + FIT_UNKNOWN = 0, //! unknown type + FIT_BITMAP = 1, //! standard image : 1-, 4-, 8-, 16-, 24-, 32-bit + FIT_UINT16 = 2, //! array of unsigned short : unsigned 16-bit + FIT_INT16 = 3, //! array of short : signed 16-bit + FIT_UINT32 = 4, //! array of unsigned long : unsigned 32-bit + FIT_INT32 = 5, //! array of long : signed 32-bit + FIT_FLOAT = 6, //! array of float : 32-bit IEEE floating point + FIT_DOUBLE = 7, //! array of double : 64-bit IEEE floating point + FIT_COMPLEX = 8, //! array of FICOMPLEX : 2 x 64-bit IEEE floating point + FIT_RGB16 = 9, //! 48-bit RGB image : 3 x 16-bit + FIT_RGBA16 = 10, //! 64-bit RGBA image : 4 x 16-bit + FIT_RGBF = 11, //! 96-bit RGB float image : 3 x 32-bit IEEE floating point + FIT_RGBAF = 12 //! 128-bit RGBA float image : 4 x 32-bit IEEE floating point }; /** Image color type used in FreeImage. */ FI_ENUM(FREE_IMAGE_COLOR_TYPE) { - FIC_MINISWHITE = 0, // min value is white - FIC_MINISBLACK = 1, // min value is black - FIC_RGB = 2, // RGB color model - FIC_PALETTE = 3, // color map indexed - FIC_RGBALPHA = 4, // RGB color model with alpha channel - FIC_CMYK = 5 // CMYK color model + FIC_MINISWHITE = 0, //! min value is white + FIC_MINISBLACK = 1, //! min value is black + FIC_RGB = 2, //! RGB color model + FIC_PALETTE = 3, //! color map indexed + FIC_RGBALPHA = 4, //! RGB color model with alpha channel + FIC_CMYK = 5 //! CMYK color model }; /** Color quantization algorithms. Constants used in FreeImage_ColorQuantize. */ FI_ENUM(FREE_IMAGE_QUANTIZE) { - FIQ_WUQUANT = 0, // Xiaolin Wu color quantization algorithm - FIQ_NNQUANT = 1 // NeuQuant neural-net quantization algorithm by Anthony Dekker + FIQ_WUQUANT = 0, //! Xiaolin Wu color quantization algorithm + FIQ_NNQUANT = 1, //! NeuQuant neural-net quantization algorithm by Anthony Dekker + FIQ_LFPQUANT = 2 //! Lossless Fast Pseudo-Quantization Algorithm by Carsten Klein }; /** Dithering algorithms. Constants used in FreeImage_Dither. */ FI_ENUM(FREE_IMAGE_DITHER) { - FID_FS = 0, // Floyd & Steinberg error diffusion - FID_BAYER4x4 = 1, // Bayer ordered dispersed dot dithering (order 2 dithering matrix) - FID_BAYER8x8 = 2, // Bayer ordered dispersed dot dithering (order 3 dithering matrix) - FID_CLUSTER6x6 = 3, // Ordered clustered dot dithering (order 3 - 6x6 matrix) - FID_CLUSTER8x8 = 4, // Ordered clustered dot dithering (order 4 - 8x8 matrix) - FID_CLUSTER16x16= 5, // Ordered clustered dot dithering (order 8 - 16x16 matrix) - FID_BAYER16x16 = 6 // Bayer ordered dispersed dot dithering (order 4 dithering matrix) + FID_FS = 0, //! Floyd & Steinberg error diffusion + FID_BAYER4x4 = 1, //! Bayer ordered dispersed dot dithering (order 2 dithering matrix) + FID_BAYER8x8 = 2, //! Bayer ordered dispersed dot dithering (order 3 dithering matrix) + FID_CLUSTER6x6 = 3, //! Ordered clustered dot dithering (order 3 - 6x6 matrix) + FID_CLUSTER8x8 = 4, //! Ordered clustered dot dithering (order 4 - 8x8 matrix) + FID_CLUSTER16x16= 5, //! Ordered clustered dot dithering (order 8 - 16x16 matrix) + FID_BAYER16x16 = 6 //! Bayer ordered dispersed dot dithering (order 4 dithering matrix) }; /** Lossless JPEG transformations Constants used in FreeImage_JPEGTransform */ FI_ENUM(FREE_IMAGE_JPEG_OPERATION) { - FIJPEG_OP_NONE = 0, // no transformation - FIJPEG_OP_FLIP_H = 1, // horizontal flip - FIJPEG_OP_FLIP_V = 2, // vertical flip - FIJPEG_OP_TRANSPOSE = 3, // transpose across UL-to-LR axis - FIJPEG_OP_TRANSVERSE = 4, // transpose across UR-to-LL axis - FIJPEG_OP_ROTATE_90 = 5, // 90-degree clockwise rotation - FIJPEG_OP_ROTATE_180 = 6, // 180-degree rotation - FIJPEG_OP_ROTATE_270 = 7 // 270-degree clockwise (or 90 ccw) + FIJPEG_OP_NONE = 0, //! no transformation + FIJPEG_OP_FLIP_H = 1, //! horizontal flip + FIJPEG_OP_FLIP_V = 2, //! vertical flip + FIJPEG_OP_TRANSPOSE = 3, //! transpose across UL-to-LR axis + FIJPEG_OP_TRANSVERSE = 4, //! transpose across UR-to-LL axis + FIJPEG_OP_ROTATE_90 = 5, //! 90-degree clockwise rotation + FIJPEG_OP_ROTATE_180 = 6, //! 180-degree rotation + FIJPEG_OP_ROTATE_270 = 7 //! 270-degree clockwise (or 90 ccw) }; /** Tone mapping operators. Constants used in FreeImage_ToneMapping. */ FI_ENUM(FREE_IMAGE_TMO) { - FITMO_DRAGO03 = 0, // Adaptive logarithmic mapping (F. Drago, 2003) - FITMO_REINHARD05 = 1, // Dynamic range reduction inspired by photoreceptor physiology (E. Reinhard, 2005) - FITMO_FATTAL02 = 2 // Gradient domain high dynamic range compression (R. Fattal, 2002) + FITMO_DRAGO03 = 0, //! Adaptive logarithmic mapping (F. Drago, 2003) + FITMO_REINHARD05 = 1, //! Dynamic range reduction inspired by photoreceptor physiology (E. Reinhard, 2005) + FITMO_FATTAL02 = 2 //! Gradient domain high dynamic range compression (R. Fattal, 2002) }; /** Upsampling / downsampling filters. Constants used in FreeImage_Rescale. */ FI_ENUM(FREE_IMAGE_FILTER) { - FILTER_BOX = 0, // Box, pulse, Fourier window, 1st order (constant) b-spline - FILTER_BICUBIC = 1, // Mitchell & Netravali's two-param cubic filter - FILTER_BILINEAR = 2, // Bilinear filter - FILTER_BSPLINE = 3, // 4th order (cubic) b-spline - FILTER_CATMULLROM = 4, // Catmull-Rom spline, Overhauser spline - FILTER_LANCZOS3 = 5 // Lanczos3 filter + FILTER_BOX = 0, //! Box, pulse, Fourier window, 1st order (constant) b-spline + FILTER_BICUBIC = 1, //! Mitchell & Netravali's two-param cubic filter + FILTER_BILINEAR = 2, //! Bilinear filter + FILTER_BSPLINE = 3, //! 4th order (cubic) b-spline + FILTER_CATMULLROM = 4, //! Catmull-Rom spline, Overhauser spline + FILTER_LANCZOS3 = 5 //! Lanczos3 filter }; /** Color channels. Constants used in color manipulation routines. */ FI_ENUM(FREE_IMAGE_COLOR_CHANNEL) { - FICC_RGB = 0, // Use red, green and blue channels - FICC_RED = 1, // Use red channel - FICC_GREEN = 2, // Use green channel - FICC_BLUE = 3, // Use blue channel - FICC_ALPHA = 4, // Use alpha channel - FICC_BLACK = 5, // Use black channel - FICC_REAL = 6, // Complex images: use real part - FICC_IMAG = 7, // Complex images: use imaginary part - FICC_MAG = 8, // Complex images: use magnitude - FICC_PHASE = 9 // Complex images: use phase + FICC_RGB = 0, //! Use red, green and blue channels + FICC_RED = 1, //! Use red channel + FICC_GREEN = 2, //! Use green channel + FICC_BLUE = 3, //! Use blue channel + FICC_ALPHA = 4, //! Use alpha channel + FICC_BLACK = 5, //! Use black channel + FICC_REAL = 6, //! Complex images: use real part + FICC_IMAG = 7, //! Complex images: use imaginary part + FICC_MAG = 8, //! Complex images: use magnitude + FICC_PHASE = 9 //! Complex images: use phase }; // Metadata support --------------------------------------------------------- @@ -517,24 +532,24 @@ FI_ENUM(FREE_IMAGE_COLOR_CHANNEL) { Note: RATIONALs are the ratio of two 32-bit integer values. */ FI_ENUM(FREE_IMAGE_MDTYPE) { - FIDT_NOTYPE = 0, // placeholder - FIDT_BYTE = 1, // 8-bit unsigned integer - FIDT_ASCII = 2, // 8-bit bytes w/ last byte null - FIDT_SHORT = 3, // 16-bit unsigned integer - FIDT_LONG = 4, // 32-bit unsigned integer - FIDT_RATIONAL = 5, // 64-bit unsigned fraction - FIDT_SBYTE = 6, // 8-bit signed integer - FIDT_UNDEFINED = 7, // 8-bit untyped data - FIDT_SSHORT = 8, // 16-bit signed integer - FIDT_SLONG = 9, // 32-bit signed integer - FIDT_SRATIONAL = 10, // 64-bit signed fraction - FIDT_FLOAT = 11, // 32-bit IEEE floating point - FIDT_DOUBLE = 12, // 64-bit IEEE floating point - FIDT_IFD = 13, // 32-bit unsigned integer (offset) - FIDT_PALETTE = 14, // 32-bit RGBQUAD - FIDT_LONG8 = 16, // 64-bit unsigned integer - FIDT_SLONG8 = 17, // 64-bit signed integer - FIDT_IFD8 = 18 // 64-bit unsigned integer (offset) + FIDT_NOTYPE = 0, //! placeholder + FIDT_BYTE = 1, //! 8-bit unsigned integer + FIDT_ASCII = 2, //! 8-bit bytes w/ last byte null + FIDT_SHORT = 3, //! 16-bit unsigned integer + FIDT_LONG = 4, //! 32-bit unsigned integer + FIDT_RATIONAL = 5, //! 64-bit unsigned fraction + FIDT_SBYTE = 6, //! 8-bit signed integer + FIDT_UNDEFINED = 7, //! 8-bit untyped data + FIDT_SSHORT = 8, //! 16-bit signed integer + FIDT_SLONG = 9, //! 32-bit signed integer + FIDT_SRATIONAL = 10, //! 64-bit signed fraction + FIDT_FLOAT = 11, //! 32-bit IEEE floating point + FIDT_DOUBLE = 12, //! 64-bit IEEE floating point + FIDT_IFD = 13, //! 32-bit unsigned integer (offset) + FIDT_PALETTE = 14, //! 32-bit RGBQUAD + FIDT_LONG8 = 16, //! 64-bit unsigned integer + FIDT_SLONG8 = 17, //! 64-bit signed integer + FIDT_IFD8 = 18 //! 64-bit unsigned integer (offset) }; /** @@ -542,18 +557,18 @@ FI_ENUM(FREE_IMAGE_MDTYPE) { */ FI_ENUM(FREE_IMAGE_MDMODEL) { FIMD_NODATA = -1, - FIMD_COMMENTS = 0, // single comment or keywords - FIMD_EXIF_MAIN = 1, // Exif-TIFF metadata - FIMD_EXIF_EXIF = 2, // Exif-specific metadata - FIMD_EXIF_GPS = 3, // Exif GPS metadata - FIMD_EXIF_MAKERNOTE = 4, // Exif maker note metadata - FIMD_EXIF_INTEROP = 5, // Exif interoperability metadata - FIMD_IPTC = 6, // IPTC/NAA metadata - FIMD_XMP = 7, // Abobe XMP metadata - FIMD_GEOTIFF = 8, // GeoTIFF metadata - FIMD_ANIMATION = 9, // Animation metadata - FIMD_CUSTOM = 10, // Used to attach other metadata types to a dib - FIMD_EXIF_RAW = 11 // Exif metadata as a raw buffer + FIMD_COMMENTS = 0, //! single comment or keywords + FIMD_EXIF_MAIN = 1, //! Exif-TIFF metadata + FIMD_EXIF_EXIF = 2, //! Exif-specific metadata + FIMD_EXIF_GPS = 3, //! Exif GPS metadata + FIMD_EXIF_MAKERNOTE = 4, //! Exif maker note metadata + FIMD_EXIF_INTEROP = 5, //! Exif interoperability metadata + FIMD_IPTC = 6, //! IPTC/NAA metadata + FIMD_XMP = 7, //! Abobe XMP metadata + FIMD_GEOTIFF = 8, //! GeoTIFF metadata + FIMD_ANIMATION = 9, //! Animation metadata + FIMD_CUSTOM = 10, //! Used to attach other metadata types to a dib + FIMD_EXIF_RAW = 11 //! Exif metadata as a raw buffer }; /** @@ -584,10 +599,10 @@ typedef long (DLL_CALLCONV *FI_TellProc) (fi_handle handle); #endif // WIN32 FI_STRUCT(FreeImageIO) { - FI_ReadProc read_proc; // pointer to the function used to read data - FI_WriteProc write_proc; // pointer to the function used to write data - FI_SeekProc seek_proc; // pointer to the function used to seek - FI_TellProc tell_proc; // pointer to the function used to aquire the current position + FI_ReadProc read_proc; //! pointer to the function used to read data + FI_WriteProc write_proc; //! pointer to the function used to write data + FI_SeekProc seek_proc; //! pointer to the function used to seek + FI_TellProc tell_proc; //! pointer to the function used to aquire the current position }; #if (defined(_WIN32) || defined(__WIN32__)) @@ -721,6 +736,7 @@ typedef void (DLL_CALLCONV *FI_InitProc)(Plugin *plugin, int format_id); #define RAW_PREVIEW 1 //! try to load the embedded JPEG preview with included Exif Data or default to RGB 24-bit #define RAW_DISPLAY 2 //! load the file as RGB 24-bit #define RAW_HALFSIZE 4 //! output a half-size color image +#define RAW_UNPROCESSED 8 //! output a FIT_UINT16 raw Bayer image #define SGI_DEFAULT 0 #define TARGA_DEFAULT 0 #define TARGA_LOAD_RGB888 1 //! if set the loader converts RGB555 and ARGB8888 -> RGB888. @@ -748,12 +764,19 @@ typedef void (DLL_CALLCONV *FI_InitProc)(Plugin *plugin, int format_id); // Background filling options --------------------------------------------------------- // Constants used in FreeImage_FillBackground and FreeImage_EnlargeCanvas -#define FI_COLOR_IS_RGB_COLOR 0x00 // RGBQUAD color is a RGB color (contains no valid alpha channel) -#define FI_COLOR_IS_RGBA_COLOR 0x01 // RGBQUAD color is a RGBA color (contains a valid alpha channel) -#define FI_COLOR_FIND_EQUAL_COLOR 0x02 // For palettized images: lookup equal RGB color from palette -#define FI_COLOR_ALPHA_IS_INDEX 0x04 // The color's rgbReserved member (alpha) contains the palette index to be used +#define FI_COLOR_IS_RGB_COLOR 0x00 //! RGBQUAD color is a RGB color (contains no valid alpha channel) +#define FI_COLOR_IS_RGBA_COLOR 0x01 //! RGBQUAD color is a RGBA color (contains a valid alpha channel) +#define FI_COLOR_FIND_EQUAL_COLOR 0x02 //! For palettized images: lookup equal RGB color from palette +#define FI_COLOR_ALPHA_IS_INDEX 0x04 //! The color's rgbReserved member (alpha) contains the palette index to be used #define FI_COLOR_PALETTE_SEARCH_MASK (FI_COLOR_FIND_EQUAL_COLOR | FI_COLOR_ALPHA_IS_INDEX) // No color lookup is performed +// RescaleEx options --------------------------------------------------------- +// Constants used in FreeImage_RescaleEx + +#define FI_RESCALE_DEFAULT 0x00 //! default options; none of the following other options apply +#define FI_RESCALE_TRUE_COLOR 0x01 //! for non-transparent greyscale images, convert to 24-bit if src bitdepth <= 8 (default is a 8-bit greyscale image). +#define FI_RESCALE_OMIT_METADATA 0x02 //! do not copy metadata to the rescaled image + #ifdef __cplusplus extern "C" { @@ -887,6 +910,7 @@ DLL_API unsigned DLL_CALLCONV FreeImage_GetHeight(FIBITMAP *dib); DLL_API unsigned DLL_CALLCONV FreeImage_GetLine(FIBITMAP *dib); DLL_API unsigned DLL_CALLCONV FreeImage_GetPitch(FIBITMAP *dib); DLL_API unsigned DLL_CALLCONV FreeImage_GetDIBSize(FIBITMAP *dib); +DLL_API unsigned DLL_CALLCONV FreeImage_GetMemorySize(FIBITMAP *dib); DLL_API RGBQUAD *DLL_CALLCONV FreeImage_GetPalette(FIBITMAP *dib); DLL_API unsigned DLL_CALLCONV FreeImage_GetDotsPerMeterX(FIBITMAP *dib); @@ -977,12 +1001,15 @@ DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Threshold(FIBITMAP *dib, BYTE T); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Dither(FIBITMAP *dib, FREE_IMAGE_DITHER algorithm); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE)); +DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertFromRawBitsEx(BOOL copySource, BYTE *bits, FREE_IMAGE_TYPE type, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE)); DLL_API void DLL_CALLCONV FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown FI_DEFAULT(FALSE)); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToFloat(FIBITMAP *dib); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBF(FIBITMAP *dib); +DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBAF(FIBITMAP *dib); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToUINT16(FIBITMAP *dib); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGB16(FIBITMAP *dib); +DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToRGBA16(FIBITMAP *dib); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToStandardType(FIBITMAP *src, BOOL scale_linear FI_DEFAULT(TRUE)); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_linear FI_DEFAULT(TRUE)); @@ -1038,6 +1065,7 @@ DLL_API void DLL_CALLCONV FreeImage_FindCloseMetadata(FIMETADATA *mdhandle); // metadata setter and getter DLL_API BOOL DLL_CALLCONV FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag); DLL_API BOOL DLL_CALLCONV FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag); +DLL_API BOOL DLL_CALLCONV FreeImage_SetMetadataKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value); // helpers DLL_API unsigned DLL_CALLCONV FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib); @@ -1054,11 +1082,11 @@ DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransform(const char *src_file, const ch DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect FI_DEFAULT(TRUE)); DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom); DLL_API BOOL DLL_CALLCONV FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom); -DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
-DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
-DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
-DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE));
-
+DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE)); +DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE)); +DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE)); +DLL_API BOOL DLL_CALLCONV FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect FI_DEFAULT(TRUE)); + // -------------------------------------------------------------------------- // Image manipulation toolkit @@ -1075,6 +1103,7 @@ DLL_API BOOL DLL_CALLCONV FreeImage_FlipVertical(FIBITMAP *dib); // upsampling / downsampling DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Rescale(FIBITMAP *dib, int dst_width, int dst_height, FREE_IMAGE_FILTER filter FI_DEFAULT(FILTER_CATMULLROM)); DLL_API FIBITMAP *DLL_CALLCONV FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert FI_DEFAULT(TRUE)); +DLL_API FIBITMAP *DLL_CALLCONV FreeImage_RescaleRect(FIBITMAP *dib, int dst_width, int dst_height, int left, int top, int right, int bottom, FREE_IMAGE_FILTER filter FI_DEFAULT(FILTER_CATMULLROM), unsigned flags FI_DEFAULT(0)); // color manipulation routines (point operations) DLL_API BOOL DLL_CALLCONV FreeImage_AdjustCurve(FIBITMAP *dib, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel); @@ -1099,6 +1128,8 @@ DLL_API BOOL DLL_CALLCONV FreeImage_SetComplexChannel(FIBITMAP *dst, FIBITMAP *s // copy / paste / composite routines DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Copy(FIBITMAP *dib, int left, int top, int right, int bottom); DLL_API BOOL DLL_CALLCONV FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha); +DLL_API FIBITMAP *DLL_CALLCONV FreeImage_CreateView(FIBITMAP *dib, unsigned left, unsigned top, unsigned right, unsigned bottom); + DLL_API FIBITMAP *DLL_CALLCONV FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg FI_DEFAULT(FALSE), RGBQUAD *appBkColor FI_DEFAULT(NULL), FIBITMAP *bg FI_DEFAULT(NULL)); DLL_API BOOL DLL_CALLCONV FreeImage_PreMultiplyWithAlpha(FIBITMAP *dib); diff --git a/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp b/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp index 2ba5539d4c..347ad1f3e5 100644 --- a/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp +++ b/plugins/AdvaImg/src/FreeImage/BitmapAccess.cpp @@ -36,10 +36,18 @@ #include "FreeImage.h" #include "FreeImageIO.h" #include "Utilities.h" +#include "MapIntrospector.h" #include "../Metadata/FreeImageTag.h" -/** Constants for the BITMAPINFOHEADER::biCompression field */ +/** +Constants for the BITMAPINFOHEADER::biCompression field +BI_RGB: +The bitmap is in uncompressed red green blue (RGB) format that is not compressed and does not use color masks. +BI_BITFIELDS: +The bitmap is not compressed and the color table consists of three DWORD color masks that specify the red, green, and blue components, +respectively, of each pixel. This is valid when used with 16 and 32-bits per pixel bitmaps. +*/ #ifndef _WINGDI_ #define BI_RGB 0L #define BI_BITFIELDS 3L @@ -49,38 +57,66 @@ // Metadata definitions // ---------------------------------------------------------- -// helper for map<key, value> where value is a pointer to a FreeImage tag +/** 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*> +/** helper for map<FREE_IMAGE_MDMODEL, TAGMAP*> */ typedef std::map<int, TAGMAP*> METADATAMAP; -// helper for metadata iterator +/** helper for metadata iterator */ FI_STRUCT (METADATAHEADER) { - long pos; // current position when iterating the map - TAGMAP *tagmap; // pointer to the tag map + long pos; //! current position when iterating the map + TAGMAP *tagmap; //! pointer to the tag map }; // ---------------------------------------------------------- // FIBITMAP definition // ---------------------------------------------------------- +/** +FreeImage header structure +*/ FI_STRUCT (FREEIMAGEHEADER) { - FREE_IMAGE_TYPE type; // data type - bitmap, array of long, double, complex, etc + /** data type - bitmap, array of long, double, complex, etc */ + FREE_IMAGE_TYPE type; + + /** background color used for RGB transparency */ + RGBQUAD bkgnd_color; + + /**@name transparency management */ + //@{ + /** + why another table ? for easy transparency table retrieval ! + transparency could be stored in the palette, which is better + overall, but it requires quite some changes and it will render + FreeImage_GetTransparencyTable obsolete in its current form; + */ + BYTE transparent_table[256]; + /** number of transparent colors */ + int transparency_count; + /** TRUE if the image is transparent */ + BOOL transparent; + //@} - RGBQUAD bkgnd_color; // background color used for RGB transparency + /** space to hold ICC profile */ + FIICCPROFILE iccProfile; - 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 + /** contains a list of metadata models attached to the bitmap */ + METADATAMAP *metadata; - METADATAMAP *metadata; // contains a list of metadata models attached to the bitmap + /** FALSE if the FIBITMAP only contains the header and no pixel data */ + BOOL has_pixels; - BOOL has_pixels; // FALSE if the FIBITMAP only contains the header and no pixel data + /** optionally contains a thumbnail attached to the bitmap */ + FIBITMAP *thumbnail; - FIBITMAP *thumbnail; // optionally contains a thumbnail attached to the bitmap + /**@name external pixel buffer management */ + //@{ + /** pointer to user provided pixels, NULL otherwise */ + BYTE *external_bits; + /** user provided pitch, 0 otherwise */ + unsigned external_pitch; + //@} //BYTE filler[1]; // fill to 32-bit alignment }; @@ -89,10 +125,13 @@ FI_STRUCT (FREEIMAGEHEADER) { // FREEIMAGERGBMASKS definition // ---------------------------------------------------------- +/** +RGB mask structure - mainly used for 16-bit RGB555 / RGB 565 FIBITMAP +*/ FI_STRUCT (FREEIMAGERGBMASKS) { - unsigned red_mask; // bit layout of the red components - unsigned green_mask; // bit layout of the green components - unsigned blue_mask; // bit layout of the blue components + 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 }; // ---------------------------------------------------------- @@ -155,40 +194,50 @@ void FreeImage_Aligned_Free(void* mem) { #endif // _WIN32 || _WIN64 // ---------------------------------------------------------- -// DIB information functions +// FIBITMAP memory management // ---------------------------------------------------------- /** Calculate the size of a FreeImage image. Align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary. +This function includes a protection against malicious images, based on a KISS integer overflow detection mechanism. @param header_only If TRUE, calculate a 'header only' FIBITMAP size, otherwise calculate a full FIBITMAP size -@param width -@param height -@param bpp -@param need_masks -@see FreeImage_AllocateHeaderT +@param width Image width +@param height Image height +@param bpp Number of bits-per-pixel +@param need_masks We only store the masks (and allocate memory for them) for 16-bit images of type FIT_BITMAP +@return Returns a size in BYTE units +@see FreeImage_AllocateBitmap */ static size_t -FreeImage_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); +FreeImage_GetInternalImageSize(BOOL header_only, unsigned width, unsigned height, unsigned bpp, BOOL need_masks) { + size_t dib_size = sizeof(FREEIMAGEHEADER); + dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0); + dib_size += FIBITMAP_ALIGNMENT - sizeof(BITMAPINFOHEADER) % FIBITMAP_ALIGNMENT; + dib_size += sizeof(BITMAPINFOHEADER); // palette is aligned on a 16 bytes boundary dib_size += sizeof(RGBQUAD) * CalculateUsedPaletteEntries(bpp); // we both add palette size and masks size if need_masks is true, since CalculateUsedPaletteEntries // always returns 0 if need_masks is true (which is only true for 16 bit images). dib_size += need_masks ? sizeof(DWORD) * 3 : 0; - dib_size += (dib_size % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - dib_size % FIBITMAP_ALIGNMENT : 0); + 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; + dib_size += (size_t)CalculatePitch(CalculateLine(width, bpp)) * (size_t)height; // check for possible malloc overflow using a KISS integer overflow detection mechanism { + const double dPitch = floor( ((double)bpp * width + 31.0) / 32.0 ) * 4.0; + const double dImageSize = (double)header_size + dPitch * height; + if(dImageSize != (double)dib_size) { + // here, we are sure to encounter a malloc overflow: try to avoid it ... + return 0; + } + /* The following constant take into account the additionnal memory used by aligned malloc functions as well as debug malloc functions. @@ -196,12 +245,7 @@ FreeImage_GetImageSizeHeader(BOOL header_only, unsigned width, unsigned height, 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; @@ -224,8 +268,33 @@ 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) { +/** +Internal FIBITMAP allocation. + +This function accepts (ext_bits, ext_pitch) arguments. If these are provided the FIBITMAP +will be allocated as "header only", but bits and pitch will be stored within the FREEIMAGEHEADER +and the resulting FIBITMAP will have pixels, i.e. HasPixels() will return TRUE. +- GetBits() and GetPitch return the correct values - either offsets or the stored values (user-provided bits and pitch). +- Clone() creates a new FIBITMAP with copy of the user pixel data. +- Unload's implementation does not need to change - it just release a "header only" dib. +Note that when using external data, the data does not need to have the same alignment as the default 4-byte alignment. +This enables the possibility to access buffers with, for instance, stricter alignment, +like the ones used in low-level APIs like OpenCL or intrinsics. + +@param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP +@param ext_bits Pointer to external user's pixel buffer if using wrapped buffer, NULL otherwise +@param ext_pitch Pointer to external user's pixel buffer pitch if using wrapped buffer, 0 otherwise +@param type Image type +@param width Image width +@param height Image height +@param bpp Number of bits per pixel +@param red_mask Image red mask +@param green_mask Image green mask +@param blue_mask Image blue mask +@return Returns the allocated FIBITMAP if successful, returns NULL otherwise +*/ +static FIBITMAP * +FreeImage_AllocateBitmap(BOOL header_only, BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { // check input variables width = abs(width); @@ -233,6 +302,12 @@ FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int if(!((width > 0) && (height > 0))) { return NULL; } + if(ext_bits) { + if(ext_pitch == 0) { + return NULL; + } + assert(header_only == FALSE); + } // we only store the masks (and allocate memory for them) for 16-bit images of type FIT_BITMAP BOOL need_masks = FALSE; @@ -302,7 +377,9 @@ FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int // 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); + // when using a user provided pixel buffer, force a 'header only' allocation + + size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks); if(dib_size == 0) { // memory allocation will fail (probably a malloc overflow) @@ -317,12 +394,13 @@ FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int // write out the FREEIMAGEHEADER - FREEIMAGEHEADER *fih = (FREEIMAGEHEADER *)bitmap->data; - fih->type = type; + FREEIMAGEHEADER *fih = (FREEIMAGEHEADER *)bitmap->data; + + fih->type = type; memset(&fih->bkgnd_color, 0, sizeof(RGBQUAD)); - fih->transparent = FALSE; + fih->transparent = FALSE; fih->transparency_count = 0; memset(fih->transparent_table, 0xff, 256); @@ -331,9 +409,9 @@ FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int // initialize FIICCPROFILE link FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(bitmap); - iccProfile->size = 0; - iccProfile->data = 0; - iccProfile->flags = 0; + iccProfile->size = 0; + iccProfile->data = 0; + iccProfile->flags = 0; // initialize metadata models list @@ -343,6 +421,11 @@ FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int fih->thumbnail = NULL; + // store a pointer to user provided pixel buffer (if any) + + fih->external_bits = ext_bits; + fih->external_pitch = ext_pitch; + // write out the BITMAPINFOHEADER BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(bitmap); @@ -385,18 +468,28 @@ FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int } FIBITMAP * DLL_CALLCONV +FreeImage_AllocateHeaderForBits(BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { + return FreeImage_AllocateBitmap(FALSE, ext_bits, ext_pitch, type, width, height, bpp, red_mask, green_mask, blue_mask); +} + +FIBITMAP * DLL_CALLCONV +FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { + return FreeImage_AllocateBitmap(header_only, NULL, 0, type, width, height, bpp, red_mask, green_mask, blue_mask); +} + +FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { - return FreeImage_AllocateHeaderT(header_only, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); + return FreeImage_AllocateBitmap(header_only, NULL, 0, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); } FIBITMAP * DLL_CALLCONV FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { - return FreeImage_AllocateHeaderT(FALSE, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); + return FreeImage_AllocateBitmap(FALSE, NULL, 0, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); } FIBITMAP * DLL_CALLCONV FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { - return FreeImage_AllocateHeaderT(FALSE, type, width, height, bpp, red_mask, green_mask, blue_mask); + return FreeImage_AllocateBitmap(FALSE, NULL, 0, type, width, height, bpp, red_mask, green_mask, blue_mask); } void DLL_CALLCONV @@ -404,8 +497,9 @@ FreeImage_Unload(FIBITMAP *dib) { if (NULL != dib) { if (NULL != dib->data) { // delete possible icc profile ... - if (FreeImage_GetICCProfile(dib)->data) + if (FreeImage_GetICCProfile(dib)->data) { free(FreeImage_GetICCProfile(dib)->data); + } // delete metadata models METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; @@ -431,6 +525,7 @@ FreeImage_Unload(FIBITMAP *dib) { // delete bitmap ... FreeImage_Aligned_Free(dib->data); } + free(dib); // ... and the wrapper } } @@ -439,17 +534,22 @@ FreeImage_Unload(FIBITMAP *dib) { FIBITMAP * DLL_CALLCONV FreeImage_Clone(FIBITMAP *dib) { - if(!dib) return NULL; + 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); + unsigned width = FreeImage_GetWidth(dib); + unsigned height = FreeImage_GetHeight(dib); + unsigned bpp = FreeImage_GetBPP(dib); + + const BYTE *ext_bits = ((FREEIMAGEHEADER *)dib->data)->external_bits; // check for pixel availability ... BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE; + // check whether this image has masks defined ... - BOOL need_masks = (bpp == 16 && type == FIT_BITMAP) ? TRUE : FALSE; + 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, @@ -464,12 +564,14 @@ FreeImage_Clone(FIBITMAP *dib) { METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)new_dib->data)->metadata; - // calculate the size of a FreeImage image + // calculate the size of the src image // align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary // palette is aligned on a 16 bytes boundary // pixels are aligned on a 16 bytes boundary + + // when using a user provided pixel buffer, force a 'header only' calculation - size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp, need_masks); + size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks); // copy the bitmap + internal pointers (remember to restore new_dib internal pointers later) memcpy(new_dib->data, dib->data, dib_size); @@ -515,6 +617,16 @@ FreeImage_Clone(FIBITMAP *dib) { // copy the thumbnail FreeImage_SetThumbnail(new_dib, FreeImage_GetThumbnail(dib)); + // copy user provided pixel buffer (if any) + if(ext_bits) { + const unsigned pitch = FreeImage_GetPitch(dib); + const unsigned linesize = FreeImage_GetLine(dib); + for(unsigned y = 0; y < height; y++) { + memcpy(FreeImage_GetScanLine(new_dib, y), ext_bits, linesize); + ext_bits += pitch; + } + } + return new_dib; } @@ -523,6 +635,28 @@ FreeImage_Clone(FIBITMAP *dib) { // ---------------------------------------------------------- +BYTE * DLL_CALLCONV +FreeImage_GetBits(FIBITMAP *dib) { + if(!FreeImage_HasPixels(dib)) { + return NULL; + } + + if(((FREEIMAGEHEADER *)dib->data)->external_bits) { + return ((FREEIMAGEHEADER *)dib->data)->external_bits; + } + + // returns the pixels aligned on a FIBITMAP_ALIGNMENT bytes alignment boundary + size_t lp = (size_t)FreeImage_GetInfoHeader(dib); + lp += sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * FreeImage_GetColorsUsed(dib); + lp += FreeImage_HasRGBMasks(dib) ? sizeof(DWORD) * 3 : 0; + lp += (lp % FIBITMAP_ALIGNMENT ? FIBITMAP_ALIGNMENT - lp % FIBITMAP_ALIGNMENT : 0); + return (BYTE *)lp; +} + +// ---------------------------------------------------------- +// DIB information functions +// ---------------------------------------------------------- + FIBITMAP* DLL_CALLCONV FreeImage_GetThumbnail(FIBITMAP *dib) { return (dib != NULL) ? ((FREEIMAGEHEADER *)dib->data)->thumbnail : NULL; @@ -589,15 +723,17 @@ FreeImage_GetColorType(FIBITMAP *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)) { + 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; + if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) { + return FIC_MINISWHITE; + } } return FIC_PALETTE; @@ -611,16 +747,18 @@ FreeImage_GetColorType(FIBITMAP *dib) { rgb = FreeImage_GetPalette(dib); for (int i = 0; i < ncolors; i++) { - if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) + 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) + if ((ncolors-i-1) != rgb->rgbRed) { return FIC_PALETTE; - else + } else { minisblack = 0; + } } rgb++; @@ -635,17 +773,20 @@ FreeImage_GetColorType(FIBITMAP *dib) { case 32: { - if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK) + 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; + for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { + if (rgb[x].rgbReserved != 0xFF) { + return FIC_RGBALPHA; + } + } } return FIC_RGB; } @@ -687,7 +828,10 @@ FreeImage_GetRedMask(FIBITMAP *dib) { case FIT_BITMAP: // check for 16-bit RGB (565 or 555) masks = FreeImage_GetRGBMasks(dib); - return masks ? masks->red_mask : FI_RGBA_RED_MASK; + if (masks) { + return masks->red_mask; + } + return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_RED_MASK : 0; default: return 0; } @@ -701,7 +845,10 @@ FreeImage_GetGreenMask(FIBITMAP *dib) { case FIT_BITMAP: // check for 16-bit RGB (565 or 555) masks = FreeImage_GetRGBMasks(dib); - return masks ? masks->green_mask : FI_RGBA_GREEN_MASK; + if (masks) { + return masks->green_mask; + } + return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_GREEN_MASK : 0; default: return 0; } @@ -715,7 +862,10 @@ FreeImage_GetBlueMask(FIBITMAP *dib) { case FIT_BITMAP: // check for 16-bit RGB (565 or 555) masks = FreeImage_GetRGBMasks(dib); - return masks ? masks->blue_mask : FI_RGBA_BLUE_MASK; + if (masks) { + return masks->blue_mask; + } + return FreeImage_GetBPP(dib) >= 24 ? FI_RGBA_BLUE_MASK : 0; default: return 0; } @@ -967,7 +1117,11 @@ FreeImage_GetLine(FIBITMAP *dib) { unsigned DLL_CALLCONV FreeImage_GetPitch(FIBITMAP *dib) { - return dib ? FreeImage_GetLine(dib) + 3 & ~3 : 0; + if(dib) { + FREEIMAGEHEADER *fih = (FREEIMAGEHEADER *)dib->data; + return fih->external_bits ? fih->external_pitch : (FreeImage_GetLine(dib) + 3 & ~3); + } + return 0; } unsigned DLL_CALLCONV @@ -1011,7 +1165,9 @@ FreeImage_SetDotsPerMeterY(FIBITMAP *dib, unsigned res) { BITMAPINFOHEADER * DLL_CALLCONV FreeImage_GetInfoHeader(FIBITMAP *dib) { - if(!dib) return NULL; + 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; @@ -1029,8 +1185,9 @@ FreeImage_GetInfo(FIBITMAP *dib) { FIMETADATA * DLL_CALLCONV FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag) { - if(!dib) + if(!dib) { return NULL; + } // get the metadata model METADATAMAP *metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; @@ -1072,8 +1229,9 @@ FreeImage_FindFirstMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, FITAG **tag BOOL DLL_CALLCONV FreeImage_FindNextMetadata(FIMETADATA *mdhandle, FITAG **tag) { - if(!mdhandle) + if(!mdhandle) { return FALSE; + } METADATAHEADER *mdh = (METADATAHEADER *)mdhandle->data; TAGMAP *tagmap = mdh->tagmap; @@ -1115,7 +1273,9 @@ FreeImage_FindCloseMetadata(FIMETADATA *mdhandle) { BOOL DLL_CALLCONV FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) { - if(!src || !dst) return FALSE; + if(!src || !dst) { + return FALSE; + } // get metadata links METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)src->data)->metadata; @@ -1165,8 +1325,9 @@ FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) { BOOL DLL_CALLCONV FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG *tag) { - if(!dib) + if(!dib) { return FALSE; + } TAGMAP *tagmap = NULL; @@ -1254,8 +1415,9 @@ FreeImage_SetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, BOOL DLL_CALLCONV FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, FITAG **tag) { - if(!dib || !key || !tag) + if(!dib || !key || !tag) { return FALSE; + } TAGMAP *tagmap = NULL; *tag = NULL; @@ -1278,12 +1440,50 @@ FreeImage_GetMetadata(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, return (*tag != NULL) ? TRUE : FALSE; } +/** +Build and set a FITAG whose type is FIDT_ASCII. +@param model Metadata model to be filled +@param dib Image to be filled +@param key Tag key +@param value Tag value as a ASCII string +@return Returns TRUE if successful, returns FALSE otherwise +*/ +BOOL DLL_CALLCONV +FreeImage_SetMetadataKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) { + if(!dib || !key || !value) { + return FALSE; + } + // create a tag + FITAG *tag = FreeImage_CreateTag(); + if(tag) { + BOOL bSuccess = TRUE; + // fill the tag + DWORD tag_length = (DWORD)(strlen(value) + 1); + bSuccess &= FreeImage_SetTagKey(tag, key); + bSuccess &= FreeImage_SetTagLength(tag, tag_length); + bSuccess &= FreeImage_SetTagCount(tag, tag_length); + bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII); + bSuccess &= FreeImage_SetTagValue(tag, value); + if(bSuccess) { + // set the tag + bSuccess &= FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag); + } + // delete the tag + FreeImage_DeleteTag(tag); + + return bSuccess; + } + + return FALSE; +} + // ---------------------------------------------------------- unsigned DLL_CALLCONV FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib) { - if(!dib) + if(!dib) { return FALSE; + } TAGMAP *tagmap = NULL; @@ -1303,4 +1503,71 @@ FreeImage_GetMetadataCount(FREE_IMAGE_MDMODEL model, FIBITMAP *dib) { // ---------------------------------------------------------- +unsigned DLL_CALLCONV +FreeImage_GetMemorySize(FIBITMAP *dib) { + if (!dib) { + return 0; + } + FREEIMAGEHEADER *header = (FREEIMAGEHEADER *)dib->data; + BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(dib); + + BOOL header_only = !header->has_pixels || header->external_bits != NULL; + BOOL need_masks = bih->biCompression == BI_BITFIELDS; + unsigned width = bih->biWidth; + unsigned height = bih->biHeight; + unsigned bpp = bih->biBitCount; + + // start off with the size of the FIBITMAP structure + size_t size = sizeof(FIBITMAP); + + // add sizes of FREEIMAGEHEADER, BITMAPINFOHEADER, palette and DIB data + size += FreeImage_GetInternalImageSize(header_only, width, height, bpp, need_masks); + + // add ICC profile size + size += header->iccProfile.size; + + // add thumbnail image size + if (header->thumbnail) { + // we assume a thumbnail not having a thumbnail as well, + // so this recursive call should not create an infinite loop + size += FreeImage_GetMemorySize(header->thumbnail); + } + + // add metadata size + METADATAMAP *md = header->metadata; + if (!md) { + return (unsigned)size; + } + + // add size of METADATAMAP + size += sizeof(METADATAMAP); + + const size_t models = md->size(); + if (models == 0) { + return (unsigned)size; + } + + unsigned tags = 0; + + for (METADATAMAP::iterator i = md->begin(); i != md->end(); i++) { + TAGMAP *tm = i->second; + if (tm) { + for (TAGMAP::iterator j = tm->begin(); j != tm->end(); j++) { + ++tags; + const std::string & key = j->first; + size += key.capacity(); + size += FreeImage_GetTagMemorySize(j->second); + } + } + } + + // add size of all TAGMAP instances + size += models * sizeof(TAGMAP); + // add size of tree nodes in METADATAMAP + size += MapIntrospector<METADATAMAP>::GetNodesMemorySize(models); + // add size of tree nodes in TAGMAP + size += MapIntrospector<TAGMAP>::GetNodesMemorySize(tags); + + return (unsigned)size; +} diff --git a/plugins/AdvaImg/src/FreeImage/ColorLookup.cpp b/plugins/AdvaImg/src/FreeImage/ColorLookup.cpp index 5f677eee5c..0f4435a725 100644 --- a/plugins/AdvaImg/src/FreeImage/ColorLookup.cpp +++ b/plugins/AdvaImg/src/FreeImage/ColorLookup.cpp @@ -26,10 +26,10 @@ // RGB color names --------------------------------------------------------- typedef struct tagNamedColor { - const char *name; // color name - BYTE r; // red value - BYTE g; // green value - BYTE b; // blue value + const char *name; //! color name + BYTE r; //! red value + BYTE g; //! green value + BYTE b; //! blue value } NamedColor; // -------------------------------------------------------------------------- @@ -43,20 +43,21 @@ Helper function : perform a binary search on a color array */ static int binsearch(const char *name, const NamedColor *color_array, int n) { - int cond, low, mid, high; + int cond, low, mid, high; low = 0; high = n - 1; while (low <= high) { - mid = (low + high) / 2; - if ((cond = strcmp(name, color_array[mid].name)) < 0) - high = mid - 1; - else if (cond > 0) - low = mid + 1; - else - return mid; - } - return -1; + mid = (low + high) / 2; + if ((cond = strcmp(name, color_array[mid].name)) < 0) { + high = mid - 1; + } else if (cond > 0) { + low = mid + 1; + } else { + return mid; + } + } + return -1; } /** @@ -68,22 +69,24 @@ Perform a binary search on a color array */ static int FreeImage_LookupNamedColor(const char *szColor, const NamedColor *color_array, int ncolors) { - int i; + int i; char color[64]; // make lower case name, squezze white space for (i = 0; szColor[i] && i < sizeof(color) - 1; i++) { - if (isspace(szColor[i])) + if (isspace(szColor[i])) { continue; - if (isupper(szColor[i])) - color[i] = (char)tolower(szColor[i]); - else + } + if (isupper(szColor[i])) { + color[i] = (char)tolower(szColor[i]); + } else { color[i] = szColor[i]; + } } color[i] = 0; - return (binsearch(color, color_array, ncolors)); + return binsearch(color, color_array, ncolors); } // ========================================================== @@ -595,153 +598,153 @@ FreeImage_LookupX11Color(const char *szColor, BYTE *nRed, BYTE *nGreen, BYTE *nB the final recommendation for changes) */ static NamedColor SVGColorMap[] = { - { "aliceblue", 240, 248, 255 }, - { "antiquewhite", 250, 235, 215 }, - { "aqua", 0, 255, 255 }, - { "aquamarine", 127, 255, 212 }, - { "azure", 240, 255, 255 }, - { "beige", 245, 245, 220 }, - { "bisque", 255, 228, 196 }, - { "black", 0, 0, 0 }, - { "blanchedalmond", 255, 235, 205 }, - { "blue", 0, 0, 255 }, - { "blueviolet", 138, 43, 226 }, - { "brown", 165, 42, 42 }, - { "burlywood", 222, 184, 135 }, - { "cadetblue", 95, 158, 160 }, - { "chartreuse", 127, 255, 0 }, - { "chocolate", 210, 105, 30 }, - { "coral", 255, 127, 80 }, - { "cornflowerblue", 100, 149, 237 }, - { "cornsilk", 255, 248, 220 }, - { "crimson", 220, 20, 60 }, - { "cyan", 0, 255, 255 }, - { "darkblue", 0, 0, 139 }, - { "darkcyan", 0, 139, 139 }, - { "darkgoldenrod", 184, 134, 11 }, - { "darkgray", 169, 169, 169 }, - { "darkgreen", 0, 100, 0 }, - { "darkgrey", 169, 169, 169 }, - { "darkkhaki", 189, 183, 107 }, - { "darkmagenta", 139, 0, 139 }, - { "darkolivegreen", 85, 107, 47 }, - { "darkorange", 255, 140, 0 }, - { "darkorchid", 153, 50, 204 }, - { "darkred", 139, 0, 0 }, - { "darksalmon", 233, 150, 122 }, - { "darkseagreen", 143, 188, 143 }, - { "darkslateblue", 72, 61, 139 }, - { "darkslategray", 47, 79, 79 }, - { "darkslategrey", 47, 79, 79 }, - { "darkturquoise", 0, 206, 209 }, - { "darkviolet", 148, 0, 211 }, - { "deeppink", 255, 20, 147 }, - { "deepskyblue", 0, 191, 255 }, - { "dimgray", 105, 105, 105 }, - { "dimgrey", 105, 105, 105 }, - { "dodgerblue", 30, 144, 255 }, - { "firebrick", 178, 34, 34 }, - { "floralwhite", 255, 250, 240 }, - { "forestgreen", 34, 139, 34 }, - { "fuchsia", 255, 0, 255 }, - { "gainsboro", 220, 220, 220 }, - { "ghostwhite", 248, 248, 255 }, - { "gold", 255, 215, 0 }, - { "goldenrod", 218, 165, 32 }, - { "gray", 128, 128, 128 }, - { "grey", 128, 128, 128 }, - { "green", 0, 128, 0 }, - { "greenyellow", 173, 255, 47 }, - { "honeydew", 240, 255, 240 }, - { "hotpink", 255, 105, 180 }, - { "indianred", 205, 92, 92 }, - { "indigo", 75, 0, 130 }, - { "ivory", 255, 255, 240 }, - { "khaki", 240, 230, 140 }, - { "lavender", 230, 230, 250 }, - { "lavenderblush", 255, 240, 245 }, - { "lawngreen", 124, 252, 0 }, - { "lemonchiffon", 255, 250, 205 }, - { "lightblue", 173, 216, 230 }, - { "lightcoral", 240, 128, 128 }, - { "lightcyan", 224, 255, 255 }, - { "lightgoldenrodyellow", 250, 250, 210 }, - { "lightgray", 211, 211, 211 }, - { "lightgreen", 144, 238, 144 }, - { "lightgrey", 211, 211, 211 }, - { "lightpink", 255, 182, 193 }, - { "lightsalmon", 255, 160, 122 }, - { "lightseagreen", 32, 178, 170 }, - { "lightskyblue", 135, 206, 250 }, - { "lightslategray", 119, 136, 153 }, - { "lightslategrey", 119, 136, 153 }, - { "lightsteelblue", 176, 196, 222 }, - { "lightyellow", 255, 255, 224 }, - { "lime", 0, 255, 0 }, - { "limegreen", 50, 205, 50 }, - { "linen", 250, 240, 230 }, - { "magenta", 255, 0, 255 }, - { "maroon", 128, 0, 0 }, - { "mediumaquamarine", 102, 205, 170 }, - { "mediumblue", 0, 0, 205 }, - { "mediumorchid", 186, 85, 211 }, - { "mediumpurple", 147, 112, 219 }, - { "mediumseagreen", 60, 179, 113 }, - { "mediumslateblue", 123, 104, 238 }, - { "mediumspringgreen", 0, 250, 154 }, - { "mediumturquoise", 72, 209, 204 }, - { "mediumvioletred", 199, 21, 133 }, - { "midnightblue", 25, 25, 112 }, - { "mintcream", 245, 255, 250 }, - { "mistyrose", 255, 228, 225 }, - { "moccasin", 255, 228, 181 }, - { "navajowhite", 255, 222, 173 }, - { "navy", 0, 0, 128 }, - { "oldlace", 253, 245, 230 }, - { "olive", 128, 128, 0 }, - { "olivedrab", 107, 142, 35 }, - { "orange", 255, 165, 0 }, - { "orangered", 255, 69, 0 }, - { "orchid", 218, 112, 214 }, - { "palegoldenrod", 238, 232, 170 }, - { "palegreen", 152, 251, 152 }, - { "paleturquoise", 175, 238, 238 }, - { "palevioletred", 219, 112, 147 }, - { "papayawhip", 255, 239, 213 }, - { "peachpuff", 255, 218, 185 }, - { "peru", 205, 133, 63 }, - { "pink", 255, 192, 203 }, - { "plum", 221, 160, 221 }, - { "powderblue", 176, 224, 230 }, - { "purple", 128, 0, 128 }, - { "red", 255, 0, 0 }, - { "rosybrown", 188, 143, 143 }, - { "royalblue", 65, 105, 225 }, - { "saddlebrown", 139, 69, 19 }, - { "salmon", 250, 128, 114 }, - { "sandybrown", 244, 164, 96 }, - { "seagreen", 46, 139, 87 }, - { "seashell", 255, 245, 238 }, - { "sienna", 160, 82, 45 }, - { "silver", 192, 192, 192 }, - { "skyblue", 135, 206, 235 }, - { "slateblue", 106, 90, 205 }, - { "slategray", 112, 128, 144 }, - { "slategrey", 112, 128, 144 }, - { "snow", 255, 250, 250 }, - { "springgreen", 0, 255, 127 }, - { "steelblue", 70, 130, 180 }, - { "tan", 210, 180, 140 }, - { "teal", 0, 128, 128 }, - { "thistle", 216, 191, 216 }, - { "tomato", 255, 99, 71 }, - { "turquoise", 64, 224, 208 }, - { "violet", 238, 130, 238 }, - { "wheat", 245, 222, 179 }, - { "white", 255, 255, 255 }, - { "whitesmoke", 245, 245, 245 }, - { "yellow", 255, 255, 0 }, - { "yellowgreen", 154, 205, 50 } + { "aliceblue", 240, 248, 255 }, + { "antiquewhite", 250, 235, 215 }, + { "aqua", 0, 255, 255 }, + { "aquamarine", 127, 255, 212 }, + { "azure", 240, 255, 255 }, + { "beige", 245, 245, 220 }, + { "bisque", 255, 228, 196 }, + { "black", 0, 0, 0 }, + { "blanchedalmond", 255, 235, 205 }, + { "blue", 0, 0, 255 }, + { "blueviolet", 138, 43, 226 }, + { "brown", 165, 42, 42 }, + { "burlywood", 222, 184, 135 }, + { "cadetblue", 95, 158, 160 }, + { "chartreuse", 127, 255, 0 }, + { "chocolate", 210, 105, 30 }, + { "coral", 255, 127, 80 }, + { "cornflowerblue", 100, 149, 237 }, + { "cornsilk", 255, 248, 220 }, + { "crimson", 220, 20, 60 }, + { "cyan", 0, 255, 255 }, + { "darkblue", 0, 0, 139 }, + { "darkcyan", 0, 139, 139 }, + { "darkgoldenrod", 184, 134, 11 }, + { "darkgray", 169, 169, 169 }, + { "darkgreen", 0, 100, 0 }, + { "darkgrey", 169, 169, 169 }, + { "darkkhaki", 189, 183, 107 }, + { "darkmagenta", 139, 0, 139 }, + { "darkolivegreen", 85, 107, 47 }, + { "darkorange", 255, 140, 0 }, + { "darkorchid", 153, 50, 204 }, + { "darkred", 139, 0, 0 }, + { "darksalmon", 233, 150, 122 }, + { "darkseagreen", 143, 188, 143 }, + { "darkslateblue", 72, 61, 139 }, + { "darkslategray", 47, 79, 79 }, + { "darkslategrey", 47, 79, 79 }, + { "darkturquoise", 0, 206, 209 }, + { "darkviolet", 148, 0, 211 }, + { "deeppink", 255, 20, 147 }, + { "deepskyblue", 0, 191, 255 }, + { "dimgray", 105, 105, 105 }, + { "dimgrey", 105, 105, 105 }, + { "dodgerblue", 30, 144, 255 }, + { "firebrick", 178, 34, 34 }, + { "floralwhite", 255, 250, 240 }, + { "forestgreen", 34, 139, 34 }, + { "fuchsia", 255, 0, 255 }, + { "gainsboro", 220, 220, 220 }, + { "ghostwhite", 248, 248, 255 }, + { "gold", 255, 215, 0 }, + { "goldenrod", 218, 165, 32 }, + { "gray", 128, 128, 128 }, + { "green", 0, 128, 0 }, + { "greenyellow", 173, 255, 47 }, + { "grey", 128, 128, 128 }, + { "honeydew", 240, 255, 240 }, + { "hotpink", 255, 105, 180 }, + { "indianred", 205, 92, 92 }, + { "indigo", 75, 0, 130 }, + { "ivory", 255, 255, 240 }, + { "khaki", 240, 230, 140 }, + { "lavender", 230, 230, 250 }, + { "lavenderblush", 255, 240, 245 }, + { "lawngreen", 124, 252, 0 }, + { "lemonchiffon", 255, 250, 205 }, + { "lightblue", 173, 216, 230 }, + { "lightcoral", 240, 128, 128 }, + { "lightcyan", 224, 255, 255 }, + { "lightgoldenrodyellow", 250, 250, 210 }, + { "lightgray", 211, 211, 211 }, + { "lightgreen", 144, 238, 144 }, + { "lightgrey", 211, 211, 211 }, + { "lightpink", 255, 182, 193 }, + { "lightsalmon", 255, 160, 122 }, + { "lightseagreen", 32, 178, 170 }, + { "lightskyblue", 135, 206, 250 }, + { "lightslategray", 119, 136, 153 }, + { "lightslategrey", 119, 136, 153 }, + { "lightsteelblue", 176, 196, 222 }, + { "lightyellow", 255, 255, 224 }, + { "lime", 0, 255, 0 }, + { "limegreen", 50, 205, 50 }, + { "linen", 250, 240, 230 }, + { "magenta", 255, 0, 255 }, + { "maroon", 128, 0, 0 }, + { "mediumaquamarine", 102, 205, 170 }, + { "mediumblue", 0, 0, 205 }, + { "mediumorchid", 186, 85, 211 }, + { "mediumpurple", 147, 112, 219 }, + { "mediumseagreen", 60, 179, 113 }, + { "mediumslateblue", 123, 104, 238 }, + { "mediumspringgreen", 0, 250, 154 }, + { "mediumturquoise", 72, 209, 204 }, + { "mediumvioletred", 199, 21, 133 }, + { "midnightblue", 25, 25, 112 }, + { "mintcream", 245, 255, 250 }, + { "mistyrose", 255, 228, 225 }, + { "moccasin", 255, 228, 181 }, + { "navajowhite", 255, 222, 173 }, + { "navy", 0, 0, 128 }, + { "oldlace", 253, 245, 230 }, + { "olive", 128, 128, 0 }, + { "olivedrab", 107, 142, 35 }, + { "orange", 255, 165, 0 }, + { "orangered", 255, 69, 0 }, + { "orchid", 218, 112, 214 }, + { "palegoldenrod", 238, 232, 170 }, + { "palegreen", 152, 251, 152 }, + { "paleturquoise", 175, 238, 238 }, + { "palevioletred", 219, 112, 147 }, + { "papayawhip", 255, 239, 213 }, + { "peachpuff", 255, 218, 185 }, + { "peru", 205, 133, 63 }, + { "pink", 255, 192, 203 }, + { "plum", 221, 160, 221 }, + { "powderblue", 176, 224, 230 }, + { "purple", 128, 0, 128 }, + { "red", 255, 0, 0 }, + { "rosybrown", 188, 143, 143 }, + { "royalblue", 65, 105, 225 }, + { "saddlebrown", 139, 69, 19 }, + { "salmon", 250, 128, 114 }, + { "sandybrown", 244, 164, 96 }, + { "seagreen", 46, 139, 87 }, + { "seashell", 255, 245, 238 }, + { "sienna", 160, 82, 45 }, + { "silver", 192, 192, 192 }, + { "skyblue", 135, 206, 235 }, + { "slateblue", 106, 90, 205 }, + { "slategray", 112, 128, 144 }, + { "slategrey", 112, 128, 144 }, + { "snow", 255, 250, 250 }, + { "springgreen", 0, 255, 127 }, + { "steelblue", 70, 130, 180 }, + { "tan", 210, 180, 140 }, + { "teal", 0, 128, 128 }, + { "thistle", 216, 191, 216 }, + { "tomato", 255, 99, 71 }, + { "turquoise", 64, 224, 208 }, + { "violet", 238, 130, 238 }, + { "wheat", 245, 222, 179 }, + { "white", 255, 255, 255 }, + { "whitesmoke", 245, 245, 245 }, + { "yellow", 255, 255, 0 }, + { "yellowgreen", 154, 205, 50 } }; diff --git a/plugins/AdvaImg/src/FreeImage/Conversion.cpp b/plugins/AdvaImg/src/FreeImage/Conversion.cpp index 04cec65ab5..815057ad08 100644 --- a/plugins/AdvaImg/src/FreeImage/Conversion.cpp +++ b/plugins/AdvaImg/src/FreeImage/Conversion.cpp @@ -6,6 +6,7 @@ // - HervĂ© Drolon (drolon@infonie.fr) // - Jani Kajala (janik@remedy.fi) // - Mihail Naydenov (mnaydenov@users.sourceforge.net) +// - Carsten Klein (cklein05@users.sourceforge.net) // // This file is part of FreeImage 3 // @@ -372,7 +373,8 @@ FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int Palet if( ReserveSize < 0 ) ReserveSize = 0; if( ReserveSize > PaletteSize ) ReserveSize = PaletteSize; if (FreeImage_HasPixels(dib)) { - if (FreeImage_GetBPP(dib) == 24) { + const unsigned bpp = FreeImage_GetBPP(dib); + if((FreeImage_GetImageType(dib) == FIT_BITMAP) && (bpp == 24 || bpp == 32)) { switch(quantize) { case FIQ_WUQUANT : { @@ -387,9 +389,14 @@ FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int Palet } catch (const char *) { return NULL; } + break; } case FIQ_NNQUANT : { + if (bpp == 32) { + // 32-bit images not supported by NNQUANT + return NULL; + } // sampling factor in range 1..30. // 1 => slower (but better), 30 => faster. Default value is 1 const int sampling = 1; @@ -402,6 +409,16 @@ FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int Palet } return dst; } + case FIQ_LFPQUANT : + { + LFPQuantizer Q(PaletteSize); + FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette); + if(dst) { + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, dib); + } + return dst; + } } } } @@ -412,26 +429,47 @@ FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int Palet // ========================================================== FIBITMAP * DLL_CALLCONV -FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) { - FIBITMAP *dib = FreeImage_Allocate(width, height, bpp, red_mask, green_mask, blue_mask); +FreeImage_ConvertFromRawBitsEx(BOOL copySource, BYTE *bits, FREE_IMAGE_TYPE type, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) { + FIBITMAP *dib = NULL; - if (dib != NULL) { - if (topdown) { - for (int i = height - 1; i >= 0; --i) { - memcpy(FreeImage_GetScanLine(dib, i), bits, FreeImage_GetLine(dib)); - bits += pitch; - } - } else { - for (int i = 0; i < height; ++i) { - memcpy(FreeImage_GetScanLine(dib, i), bits, FreeImage_GetLine(dib)); - bits += pitch; - } + if(copySource) { + // allocate a FIBITMAP with internally managed pixel buffer + dib = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask); + if(!dib) { + return NULL; + } + // copy user provided pixel buffer into the dib + const unsigned linesize = FreeImage_GetLine(dib); + for(int y = 0; y < height; y++) { + memcpy(FreeImage_GetScanLine(dib, y), bits, linesize); + // next line in user's buffer + bits += pitch; + } + // flip pixels vertically if needed + if(topdown) { + FreeImage_FlipVertical(dib); + } + } + else { + // allocate a FIBITMAP using a wrapper to user provided pixel buffer + dib = FreeImage_AllocateHeaderForBits(bits, pitch, type, width, height, bpp, red_mask, green_mask, blue_mask); + if(!dib) { + return NULL; + } + // flip pixels vertically if needed + if(topdown) { + FreeImage_FlipVertical(dib); } } return dib; } +FIBITMAP * DLL_CALLCONV +FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) { + return FreeImage_ConvertFromRawBitsEx(TRUE /* copySource */, bits, FIT_BITMAP, width, height, pitch, bpp, red_mask, green_mask, blue_mask, topdown); +} + void DLL_CALLCONV FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) { if (FreeImage_HasPixels(dib) && (bits != NULL)) { diff --git a/plugins/AdvaImg/src/FreeImage/ConversionType.cpp b/plugins/AdvaImg/src/FreeImage/ConversionType.cpp index b537f72814..a2ca90ff9d 100644 --- a/plugins/AdvaImg/src/FreeImage/ConversionType.cpp +++ b/plugins/AdvaImg/src/FreeImage/ConversionType.cpp @@ -3,6 +3,7 @@ // // Design and implementation by // - Hervé Drolon (drolon@infonie.fr) +// - Tanner Helland (tannerhelland@users.sf.net) // // This file is part of FreeImage 3 // @@ -346,11 +347,13 @@ FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_line dst = FreeImage_ConvertToRGB16(src); break; case FIT_RGBA16: + dst = FreeImage_ConvertToRGBA16(src); break; case FIT_RGBF: dst = FreeImage_ConvertToRGBF(src); break; case FIT_RGBAF: + dst = FreeImage_ConvertToRGBAF(src); break; } break; @@ -378,11 +381,13 @@ FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_line dst = FreeImage_ConvertToRGB16(src); break; case FIT_RGBA16: + dst = FreeImage_ConvertToRGBA16(src); break; case FIT_RGBF: dst = FreeImage_ConvertToRGBF(src); break; case FIT_RGBAF: + dst = FreeImage_ConvertToRGBAF(src); break; } break; @@ -503,6 +508,7 @@ FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_line dst = FreeImage_ConvertToRGBF(src); break; case FIT_RGBAF: + dst = FreeImage_ConvertToRGBAF(src); break; } break; @@ -582,11 +588,13 @@ FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_line case FIT_COMPLEX: break; case FIT_RGBA16: + dst = FreeImage_ConvertToRGBA16(src); break; case FIT_RGBF: dst = FreeImage_ConvertToRGBF(src); break; case FIT_RGBAF: + dst = FreeImage_ConvertToRGBAF(src); break; } break; @@ -618,6 +626,7 @@ FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_line dst = FreeImage_ConvertToRGBF(src); break; case FIT_RGBAF: + dst = FreeImage_ConvertToRGBAF(src); break; } break; @@ -645,6 +654,7 @@ FreeImage_ConvertToType(FIBITMAP *src, FREE_IMAGE_TYPE dst_type, BOOL scale_line case FIT_RGBA16: break; case FIT_RGBAF: + dst = FreeImage_ConvertToRGBAF(src); break; } break; diff --git a/plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp b/plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp index f8cf7604eb..83394f049c 100644 --- a/plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp +++ b/plugins/AdvaImg/src/FreeImage/FreeImageIO.cpp @@ -68,14 +68,18 @@ _MemoryReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data); for(x = 0; x < count; x++) { - //if there isnt size bytes left to read, set pos to eof and return a short count - if( (mem_header->filelen - mem_header->curpos) < (long)size ) { - mem_header->curpos = mem_header->filelen; + long remaining_bytes = mem_header->file_length - mem_header->current_position; + //if there isn't size bytes left to read, set pos to eof and return a short count + if( remaining_bytes < (long)size ) { + if(remaining_bytes > 0) { + memcpy( buffer, (char *)mem_header->data + mem_header->current_position, remaining_bytes ); + } + mem_header->current_position = mem_header->file_length; break; } //copy size bytes count times - memcpy( buffer, (char *)mem_header->data + mem_header->curpos, size ); - mem_header->curpos += size; + memcpy( buffer, (char *)mem_header->data + mem_header->current_position, size ); + mem_header->current_position += size; buffer = (char *)buffer + size; } return x; @@ -89,32 +93,32 @@ _MemoryWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data); //double the data block size if we need to - while( (mem_header->curpos + (long)(size*count)) >= mem_header->datalen ) { + while( (mem_header->current_position + (long)(size * count)) >= mem_header->data_length ) { //if we are at or above 1G, we cant double without going negative - if( mem_header->datalen & 0x40000000 ) { + if( mem_header->data_length & 0x40000000 ) { //max 2G - if( mem_header->datalen == 0x7FFFFFFF ) { + if( mem_header->data_length == 0x7FFFFFFF ) { return 0; } newdatalen = 0x7FFFFFFF; - } else if( mem_header->datalen == 0 ) { + } else if( mem_header->data_length == 0 ) { //default to 4K if nothing yet newdatalen = 4096; } else { //double size - newdatalen = mem_header->datalen << 1; + newdatalen = mem_header->data_length << 1; } newdata = realloc( mem_header->data, newdatalen ); if( !newdata ) { return 0; } mem_header->data = newdata; - mem_header->datalen = newdatalen; + mem_header->data_length = newdatalen; } - memcpy( (char *)mem_header->data + mem_header->curpos, buffer, size*count ); - mem_header->curpos += size*count; - if( mem_header->curpos > mem_header->filelen ) { - mem_header->filelen = mem_header->curpos; + memcpy( (char *)mem_header->data + mem_header->current_position, buffer, size * count ); + mem_header->current_position += size * count; + if( mem_header->current_position > mem_header->file_length ) { + mem_header->file_length = mem_header->current_position; } return count; } @@ -123,25 +127,28 @@ int DLL_CALLCONV _MemorySeekProc(fi_handle handle, long offset, int origin) { FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data); + // you can use _MemorySeekProc to reposition the pointer anywhere in a file + // the pointer can also be positioned beyond the end of the file + switch(origin) { //0 to filelen-1 are 'inside' the file default: case SEEK_SET: //can fseek() to 0-7FFFFFFF always if( offset >= 0 ) { - mem_header->curpos = offset; + mem_header->current_position = offset; return 0; } break; case SEEK_CUR: - if( mem_header->curpos + offset >= 0 ) { - mem_header->curpos += offset; + if( mem_header->current_position + offset >= 0 ) { + mem_header->current_position += offset; return 0; } break; case SEEK_END: - if( mem_header->filelen + offset >= 0 ) { - mem_header->curpos = mem_header->filelen + offset; + if( mem_header->file_length + offset >= 0 ) { + mem_header->current_position = mem_header->file_length + offset; return 0; } break; @@ -154,7 +161,7 @@ long DLL_CALLCONV _MemoryTellProc(fi_handle handle) { FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(((FIMEMORY*)handle)->data); - return mem_header->curpos; + return mem_header->current_position; } // ---------------------------------------------------------- diff --git a/plugins/AdvaImg/src/FreeImage/LFPQuantizer.cpp b/plugins/AdvaImg/src/FreeImage/LFPQuantizer.cpp new file mode 100644 index 0000000000..8b592c30f1 --- /dev/null +++ b/plugins/AdvaImg/src/FreeImage/LFPQuantizer.cpp @@ -0,0 +1,208 @@ +// ========================================================== +// LFPQuantizer class implementation +// +// Design and implementation by +// - Carsten Klein (cklein05@users.sourceforge.net) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#include "Quantizers.h" +#include "FreeImage.h" +#include "Utilities.h" + +LFPQuantizer::LFPQuantizer(unsigned PaletteSize) : + m_size(0), m_limit(PaletteSize), m_index(0) { + m_map = new MapEntry[MAP_SIZE]; + memset(m_map, 0xFF, MAP_SIZE * sizeof(MapEntry)); +} + +LFPQuantizer::~LFPQuantizer() { + delete[] m_map; +} + +FIBITMAP* LFPQuantizer::Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette) { + + if (ReserveSize > 0 && ReservePalette != NULL) { + AddReservePalette(ReservePalette, ReserveSize); + } + + const unsigned width = FreeImage_GetWidth(dib); + const unsigned height = FreeImage_GetHeight(dib); + + FIBITMAP *dib8 = FreeImage_Allocate(width, height, 8); + if (dib8 == NULL) { + return NULL; + } + + const unsigned src_pitch = FreeImage_GetPitch(dib); + const unsigned dst_pitch = FreeImage_GetPitch(dib8); + + const BYTE * const src_bits = FreeImage_GetBits(dib); + BYTE * const dst_bits = FreeImage_GetBits(dib8); + + unsigned last_color = -1; + int last_index = 0; + + if (FreeImage_GetBPP(dib) == 24) { + + // Getting the source pixel as an unsigned int is much faster than + // working with FI_RGBA_xxx and shifting. However, this may fail + // for the very last pixel, since its rgbReserved member (alpha) + // may actually point to an address beyond the bitmap's memory. So, + // we do not process the last scanline in the first loop. + + // Process all but the last scanline. + for (unsigned y = 0; y < height - 1; ++y) { + BYTE *dst_line = dst_bits + y * dst_pitch; + const BYTE *src_line = src_bits + y * src_pitch; + for (unsigned x = 0; x < width; ++x) { + const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF; + if (color != last_color) { + last_color = color; + last_index = GetIndexForColor(color); + if (last_index == -1) { + FreeImage_Unload(dib8); + return NULL; + } + } + dst_line[x] = last_index; + src_line += 3; + } + } + + // Process all but the last pixel of the last scanline. + BYTE *dst_line = dst_bits + (height - 1) * dst_pitch; + const BYTE *src_line = src_bits + (height - 1) * src_pitch; + for (unsigned x = 0; x < width - 1; ++x) { + const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF; + if (color != last_color) { + last_color = color; + last_index = GetIndexForColor(color); + if (last_index == -1) { + FreeImage_Unload(dib8); + return NULL; + } + } + dst_line[x] = last_index; + src_line += 3; + } + + // Process the last pixel (src_line should already point to it). + const unsigned color = 0 | src_line[FI_RGBA_BLUE] << FI_RGBA_BLUE_SHIFT + | src_line[FI_RGBA_GREEN] << FI_RGBA_GREEN_SHIFT + | src_line[FI_RGBA_RED] << FI_RGBA_RED_SHIFT; + if (color != last_color) { + last_color = color; + last_index = GetIndexForColor(color); + if (last_index == -1) { + FreeImage_Unload(dib8); + return NULL; + } + } + dst_line[width - 1] = last_index; + + } else { + for (unsigned y = 0; y < height; ++y) { + BYTE *dst_line = dst_bits + y * dst_pitch; + const BYTE *src_line = src_bits + y * src_pitch; + for (unsigned x = 0; x < width; ++x) { + const unsigned color = *((unsigned *) src_line) & 0x00FFFFFF; + if (color != last_color) { + last_color = color; + last_index = GetIndexForColor(color); + if (last_index == -1) { + FreeImage_Unload(dib8); + return NULL; + } + } + dst_line[x] = last_index; + src_line += 4; + } + } + } + + WritePalette(FreeImage_GetPalette(dib8)); + return dib8; +} + +/** + * Returns the palette index of the specified color. Tries to put the + * color into the map, if it's not already present in the map. In that + * case, a new index is used for the color. Returns -1, if adding the + * color would exceed the desired maximum number of colors in the + * palette. + * @param color the color to get the index from + * @return the palette index of the specified color or -1, if there + * is no space left in the palette + */ +inline int LFPQuantizer::GetIndexForColor(unsigned color) { + unsigned bucket = hash(color) & (MAP_SIZE - 1); + while (m_map[bucket].color != color) { + if (m_map[bucket].color == EMPTY_BUCKET) { + if (m_size == m_limit) { + return -1; + } + m_map[bucket].color = color; + m_map[bucket].index = m_index++; + ++m_size; + break; + } + bucket = (bucket + 1) % MAP_SIZE; + } + return m_map[bucket].index; +} + +/** + * Adds the specified number of entries of the specified reserve + * palette to the newly created palette. + * @param *palette a pointer to the reserve palette to copy from + * @param size the number of entries to copy + */ +void LFPQuantizer::AddReservePalette(const void *palette, unsigned size) { + if (size > MAX_SIZE) { + size = MAX_SIZE; + } + unsigned *ppal = (unsigned *) palette; + const unsigned offset = m_limit - size; + for (unsigned i = 0; i < size; ++i) { + const unsigned color = *ppal++; + const unsigned index = i + offset; + unsigned bucket = hash(color) & (MAP_SIZE - 1); + while((m_map[bucket].color != EMPTY_BUCKET) && (m_map[bucket].color != color)) { + bucket = (bucket + 1) % MAP_SIZE; + } + if(m_map[bucket].color != color) { + m_map[bucket].color = color; + m_map[bucket].index = index; + } + } + m_size += size; +} + +/** + * Copies the newly created palette into the specified destination + * palette. Although unused palette entries are not overwritten in + * the destination palette, it is assumed to have space for at + * least 256 entries. + * @param palette a pointer to the destination palette + */ +void LFPQuantizer::WritePalette(void *palette) { + for (unsigned i = 0; i < MAP_SIZE; ++i) { + if (m_map[i].color != EMPTY_BUCKET) { + ((unsigned *) palette)[m_map[i].index] = m_map[i].color; + } + } +} diff --git a/plugins/AdvaImg/src/FreeImage/MNGHelper.cpp b/plugins/AdvaImg/src/FreeImage/MNGHelper.cpp index a4c67a2abe..ed3664cf77 100644 --- a/plugins/AdvaImg/src/FreeImage/MNGHelper.cpp +++ b/plugins/AdvaImg/src/FreeImage/MNGHelper.cpp @@ -357,7 +357,6 @@ Retrieve the position of a chunk in a PNG stream */ static BOOL mng_FindChunk(FIMEMORY *hPngMemory, BYTE *chunk_name, long offset, DWORD *start_pos, DWORD *next_pos) { - BOOL mEnd = FALSE; DWORD mLength = 0; BYTE *data = NULL; @@ -513,10 +512,14 @@ mng_RemoveChunk(FIMEMORY *hPngMemory, BYTE *chunk_name) { DWORD next_pos = 0; bResult = mng_FindChunk(hPngMemory, chunk_name, 8, &start_pos, &next_pos); - if(!bResult) return FALSE; + if(!bResult) { + return FALSE; + } bResult = mng_CopyRemoveChunks(hPngMemory, start_pos, next_pos); - if(!bResult) return FALSE; + if(!bResult) { + return FALSE; + } return TRUE; } @@ -529,10 +532,14 @@ mng_InsertChunk(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk DWORD next_pos = 0; bResult = mng_FindChunk(hPngMemory, inNextChunkName, 8, &start_pos, &next_pos); - if(!bResult) return FALSE; + if(!bResult) { + return FALSE; + } bResult = mng_CopyInsertChunks(hPngMemory, inNextChunkName, inInsertChunk, chunk_length, start_pos, next_pos); - if(!bResult) return FALSE; + if(!bResult) { + return FALSE; + } return TRUE; } @@ -962,7 +969,7 @@ mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, in jng_color_type = mChunk[8]; jng_image_sample_depth = mChunk[9]; jng_image_compression_method = mChunk[10]; - BYTE jng_image_interlace_method = mChunk[11]; + //BYTE jng_image_interlace_method = mChunk[11]; // for debug only jng_alpha_sample_depth = mChunk[12]; jng_alpha_compression_method = mChunk[13]; @@ -1000,7 +1007,9 @@ mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, in break; } // load the JPEG - if(dib) FreeImage_Unload(dib); + if(dib) { + FreeImage_Unload(dib); + } dib = mng_LoadFromMemoryHandle(hJpegMemory, flags); // load the PNG alpha layer @@ -1017,7 +1026,9 @@ mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, in } mng_WritePNGStream(jng_width, jng_height, jng_alpha_sample_depth, data, size_in_bytes, hPngMemory); // load the PNG - if(dib_alpha) FreeImage_Unload(dib_alpha); + if(dib_alpha) { + FreeImage_Unload(dib_alpha); + } dib_alpha = mng_LoadFromMemoryHandle(hPngMemory, flags); } } diff --git a/plugins/AdvaImg/src/FreeImage/MemoryIO.cpp b/plugins/AdvaImg/src/FreeImage/MemoryIO.cpp index 6ae3fb2e11..e0997856a9 100644 --- a/plugins/AdvaImg/src/FreeImage/MemoryIO.cpp +++ b/plugins/AdvaImg/src/FreeImage/MemoryIO.cpp @@ -48,7 +48,7 @@ FreeImage_OpenMemory(BYTE *data, DWORD size_in_bytes) { // wrap a user buffer mem_header->delete_me = FALSE; mem_header->data = (BYTE*)data; - mem_header->datalen = mem_header->filelen = size_in_bytes; + mem_header->data_length = mem_header->file_length = size_in_bytes; } else { mem_header->delete_me = TRUE; } @@ -120,7 +120,7 @@ FreeImage_AcquireMemory(FIMEMORY *stream, BYTE **data, DWORD *size_in_bytes) { FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(stream->data); *data = (BYTE*)mem_header->data; - *size_in_bytes = mem_header->filelen; + *size_in_bytes = mem_header->file_length; return TRUE; } diff --git a/plugins/AdvaImg/src/FreeImage/MultiPage.cpp b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp index d48e11be94..4fe76adb16 100644 --- a/plugins/AdvaImg/src/FreeImage/MultiPage.cpp +++ b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp @@ -269,8 +269,8 @@ FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL crea header->node = node; header->fif = fif; header->io = io.get (); - header->handle = handle; - header->changed = FALSE; + header->handle = handle; + header->changed = FALSE; header->read_only = read_only; header->m_cachefile = NULL; header->cache_fif = fif; @@ -344,13 +344,13 @@ FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_h 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); @@ -359,13 +359,13 @@ FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_h header->m_filename = NULL; header->node = node; header->fif = fif; - header->handle = handle; - header->changed = FALSE; - header->read_only = read_only; + 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(); @@ -381,7 +381,7 @@ FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_h 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(); } @@ -408,7 +408,7 @@ FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, // retrieve the plugin list to find the node belonging to this plugin PluginList *list = FreeImage_GetPluginList(); - + if (list) { PluginNode *node = list->FindNodeFromFIF(fif); diff --git a/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp b/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp index c69463c316..b5714b2929 100644 --- a/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp +++ b/plugins/AdvaImg/src/FreeImage/PixelAccess.cpp @@ -28,19 +28,6 @@ // ---------------------------------------------------------- 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; diff --git a/plugins/AdvaImg/src/FreeImage/Plugin.cpp b/plugins/AdvaImg/src/FreeImage/Plugin.cpp index 6f88e47e68..13da67434e 100644 --- a/plugins/AdvaImg/src/FreeImage/Plugin.cpp +++ b/plugins/AdvaImg/src/FreeImage/Plugin.cpp @@ -218,7 +218,7 @@ 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 @@ -261,8 +261,8 @@ FreeImage_Initialise(BOOL load_local_plugins_only) { //s_plugins->AddNode(InitXBM); //s_plugins->AddNode(InitXPM); //s_plugins->AddNode(InitDDS); - s_plugins->AddNode(InitGIF); - //s_plugins->AddNode(InitHDR); + s_plugins->AddNode(InitGIF); + //s_plugins->AddNode(InitHDR); //s_plugins->AddNode(InitG3); //s_plugins->AddNode(InitSGI); //s_plugins->AddNode(InitEXR); @@ -275,26 +275,26 @@ FreeImage_Initialise(BOOL load_local_plugins_only) { //#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]; + wchar_t current_dir[2 * _MAX_PATH], module[2 * _MAX_PATH]; BOOL bOk = FALSE; // store the current directory. then set the directory to the application location - if (GetCurrentDirectoryA(2 * _MAX_PATH, current_dir) != 0) { - if (GetModuleFileNameA(NULL, module, 2 * _MAX_PATH) != 0) { - char *last_point = strrchr(module, '\\'); + if (GetCurrentDirectoryW(2 * _MAX_PATH, current_dir) != 0) { + if (GetModuleFileNameW(NULL, module, 2 * _MAX_PATH) != 0) { + wchar_t *last_point = wcsrchr(module, L'\\'); if (last_point) { - *last_point = '\0'; + *last_point = L'\0'; - bOk = SetCurrentDirectoryA(module); + bOk = SetCurrentDirectoryW(module); } } } @@ -335,7 +335,7 @@ FreeImage_Initialise(BOOL load_local_plugins_only) { // restore the current directory if (bOk) { - SetCurrentDirectoryA(current_dir); + SetCurrentDirectoryW(current_dir); } } #endif // _WIN32 diff --git a/plugins/AdvaImg/src/FreeImage/PluginEXR.cpp b/plugins/AdvaImg/src/FreeImage/PluginEXR.cpp index 4a19b8b56f..b286430380 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginEXR.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginEXR.cpp @@ -22,6 +22,12 @@ #include "FreeImage.h" #include "Utilities.h" + +#ifdef _MSC_VER +// OpenEXR has many problems with MSVC warnings (why not just correct them ?), just ignore one of them +#pragma warning (disable : 4800) // ImfVersion.h - 'const int' : forcing value to bool 'true' or 'false' (performance warning) +#endif + #include "../OpenEXR/IlmImf/ImfIO.h" #include "../OpenEXR/Iex/Iex.h" #include "../OpenEXR/IlmImf/ImfOutputFile.h" @@ -44,72 +50,64 @@ static int s_format_id; /** FreeImage input stream wrapper +@see Imf_2_2::IStream */ -class C_IStream: public Imf::IStream { -public: - C_IStream (FreeImageIO *io, fi_handle handle): - IStream(""), _io (io), _handle(handle) {} - - virtual bool read (char c[/*n*/], int n); - virtual Imf::Int64 tellg (); - virtual void seekg (Imf::Int64 pos); - virtual void clear () {}; - +class C_IStream : public Imf::IStream { private: FreeImageIO *_io; fi_handle _handle; + +public: + C_IStream (FreeImageIO *io, fi_handle handle) : + Imf::IStream(""), _io (io), _handle(handle) { + } + + virtual bool read (char c[/*n*/], int n) { + return ((unsigned)n != _io->read_proc(c, 1, n, _handle)); + } + + virtual Imath::Int64 tellg() { + return _io->tell_proc(_handle); + } + + virtual void seekg(Imath::Int64 pos) { + _io->seek_proc(_handle, (unsigned)pos, SEEK_SET); + } + + virtual void clear() { + } }; +// ---------------------------------------------------------- /** FreeImage output stream wrapper +@see Imf_2_2::OStream */ -class C_OStream: public Imf::OStream { -public: - C_OStream (FreeImageIO *io, fi_handle handle): - OStream(""), _io (io), _handle(handle) {} - - virtual void write (const char c[/*n*/], int n); - virtual Imf::Int64 tellp (); - virtual void seekp (Imf::Int64 pos); - +class C_OStream : public Imf::OStream { private: FreeImageIO *_io; fi_handle _handle; -}; - - -bool -C_IStream::read (char c[/*n*/], int n) { - return ((unsigned)n != _io->read_proc(c, 1, n, _handle)); -} - -Imf::Int64 -C_IStream::tellg () { - return _io->tell_proc(_handle); -} -void -C_IStream::seekg (Imf::Int64 pos) { - _io->seek_proc(_handle, (unsigned)pos, SEEK_SET); -} +public: + C_OStream (FreeImageIO *io, fi_handle handle) : + Imf::OStream(""), _io (io), _handle(handle) { + } -void -C_OStream::write (const char c[/*n*/], int n) { - if((unsigned)n != _io->write_proc((void*)&c[0], 1, n, _handle)) { - Iex::throwErrnoExc(); + virtual void write(const char c[/*n*/], int n) { + if((unsigned)n != _io->write_proc((void*)&c[0], 1, n, _handle)) { + Iex::throwErrnoExc(); + } } -} -Imf::Int64 -C_OStream::tellp () { - return _io->tell_proc(_handle); -} + virtual Imath::Int64 tellp() { + return _io->tell_proc(_handle); + } -void -C_OStream::seekp (Imf::Int64 pos) { - _io->seek_proc(_handle, (unsigned)pos, SEEK_SET); -} + virtual void seekp(Imath::Int64 pos) { + _io->seek_proc(_handle, (unsigned)pos, SEEK_SET); + } +}; // ---------------------------------------------------------- @@ -667,7 +665,9 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void if(pixelType == Imf::HALF) { // convert from float to half halfData = new(std::nothrow) half[width * height * components]; - if(!halfData) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); + if(!halfData) { + THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); + } for(int y = 0; y < height; y++) { float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); @@ -716,7 +716,9 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void file.setFrameBuffer (frameBuffer); file.writePixels (height); - if(halfData != NULL) delete[] halfData; + if(halfData != NULL) { + delete[] halfData; + } if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); @@ -725,7 +727,9 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void return TRUE; } catch(Iex::BaseExc & e) { - if(halfData != NULL) delete[] halfData; + if(halfData != NULL) { + delete[] halfData; + } if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); @@ -745,6 +749,11 @@ void DLL_CALLCONV InitEXR(Plugin *plugin, int format_id) { s_format_id = format_id; + // initialize the OpenEXR library + // note that this OpenEXR function produce so called "false memory leaks" + // see http://lists.nongnu.org/archive/html/openexr-devel/2013-11/msg00000.html + Imf::staticInitialize(); + plugin->format_proc = Format; plugin->description_proc = Description; plugin->extension_proc = Extension; diff --git a/plugins/AdvaImg/src/FreeImage/PluginG3.cpp b/plugins/AdvaImg/src/FreeImage/PluginG3.cpp index d5c08b36e6..0a083b459b 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginG3.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginG3.cpp @@ -92,7 +92,7 @@ G3GetFileSize(FreeImageIO *io, fi_handle handle) { static BOOL G3ReadFile(FreeImageIO *io, fi_handle handle, uint8 *tif_rawdata, tmsize_t tif_rawdatasize) { - return ((tmsize_t)(io->read_proc(tif_rawdata, tif_rawdatasize, 1, handle) * tif_rawdatasize) == tif_rawdatasize); + return ((tmsize_t)(io->read_proc(tif_rawdata, (unsigned)tif_rawdatasize, 1, handle) * tif_rawdatasize) == tif_rawdatasize); } // ========================================================== diff --git a/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp b/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp index 87c0185865..0153959ab8 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginGIF.cpp @@ -4,6 +4,7 @@ // Design and implementation by // - Ryan Rubley <ryan@lostreality.org> // - Raphaël Gaquer <raphael.gaquer@alcer.com> +// - Aaron Shumate <aaron@shumate.us> // // This file is part of FreeImage 3 // @@ -773,7 +774,11 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { } if( info.disposal_method == GIF_DISPOSAL_BACKGROUND ) { for( y = 0; y < info.height; y++ ) { - scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left; + const int scanidx = logicalheight - (y + info.top) - 1; + if ( scanidx < 0 ) { + break; // If data is corrupt, don't calculate in invalid scanline + } + scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left; for( x = 0; x < info.width; x++ ) { *scanline++ = background; } @@ -800,7 +805,11 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { } //copy page data into logical buffer, with full alpha opaqueness for( y = 0; y < info.height; y++ ) { - scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left; + const int scanidx = logicalheight - (y + info.top) - 1; + if ( scanidx < 0 ) { + break; // If data is corrupt, don't calculate in invalid scanline + } + scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, scanidx) + info.left; BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1); for( x = 0; x < info.width; x++ ) { if( !have_transparent || *pageline != transparent_color ) { diff --git a/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp b/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp index 0cde6139db..28ce1a5768 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginHDR.cpp @@ -459,8 +459,9 @@ rgbe_WriteBytes_RLE(FreeImageIO *io, fi_handle handle, BYTE *data, int numbytes) beg_run += run_count; old_run_count = run_count; run_count = 1; - while((data[beg_run] == data[beg_run + run_count]) && (beg_run + run_count < numbytes) && (run_count < 127)) + while((beg_run + run_count < numbytes) && (run_count < 127) && (data[beg_run] == data[beg_run + run_count])) { run_count++; + } } // if data before next big run is a short run then write it as such if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) { diff --git a/plugins/AdvaImg/src/FreeImage/PluginICO.cpp b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp index df5ecee91d..c818379f78 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginICO.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginICO.cpp @@ -110,6 +110,23 @@ CalculateImageOffset(std::vector<FIBITMAP*>& vPages, int nIndex ) { return dwSize; } +/** +Vista icon support +@return Returns TRUE if the bitmap data is stored in PNG format +*/ +static BOOL +IsPNG(FreeImageIO *io, fi_handle handle) { + BYTE png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; + BYTE signature[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + long tell = io->tell_proc(handle); + io->read_proc(&signature, 1, 8, handle); + BOOL bIsPNG = (memcmp(png_signature, signature, 8) == 0); + io->seek_proc(handle, tell, SEEK_SET); + + return bIsPNG; +} + #ifdef FREEIMAGE_BIGENDIAN static void SwapInfoHeader(BITMAPINFOHEADER *header) { @@ -407,16 +424,17 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // load the requested icon if (page < icon_header->idCount) { // seek to the start of the bitmap data for the icon - io->seek_proc(handle, 0, SEEK_SET); - io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_CUR); + io->seek_proc(handle, icon_list[page].dwImageOffset, SEEK_SET); - if((icon_list[page].bWidth == 0) && (icon_list[page].bHeight == 0)) { + if( IsPNG(io, handle) ) { // Vista icon support + // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx dib = FreeImage_LoadFromHandle(FIF_PNG, io, handle, header_only ? FIF_LOAD_NOPIXELS : PNG_DEFAULT); } else { // standard icon support // see http://msdn.microsoft.com/en-us/library/ms997538.aspx + // see http://blogs.msdn.com/b/oldnewthing/archive/2010/10/18/10077133.aspx dib = LoadStandardIcon(io, handle, flags, header_only); } diff --git a/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp b/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp index 5c23a7c6e1..b8bcfc8b58 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginJ2K.cpp @@ -175,7 +175,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (header_only) { // create output image dib = J2KImageToFIBITMAP(s_format_id, image, header_only); - if(!dib) throw "Failed to import JPEG2000 image"; + if(!dib) { + throw "Failed to import JPEG2000 image"; + } // clean-up and return header data opj_destroy_codec(d_codec); opj_image_destroy(image); @@ -193,7 +195,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // create output image dib = J2KImageToFIBITMAP(s_format_id, image, header_only); - if(!dib) throw "Failed to import JPEG2000 image"; + if(!dib) { + throw "Failed to import JPEG2000 image"; + } // free image data structure opj_image_destroy(image); @@ -201,7 +205,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { return dib; } catch (const char *text) { - if(dib) FreeImage_Unload(dib); + if(dib) { + FreeImage_Unload(dib); + } // free remaining structures opj_destroy_codec(d_codec); opj_image_destroy(image); @@ -231,27 +237,22 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void 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; + 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_rates[0] = (float)(flags & 0x3FF); } 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; + if(!image) { + return FALSE; + } // decide if MCT should be used parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0; diff --git a/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp b/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp index edf5b396c3..742fe2c038 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginJP2.cpp @@ -175,7 +175,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (header_only) { // create output image dib = J2KImageToFIBITMAP(s_format_id, image, header_only); - if(!dib) throw "Failed to import JPEG2000 image"; + if(!dib) { + throw "Failed to import JPEG2000 image"; + } // clean-up and return header data opj_destroy_codec(d_codec); opj_image_destroy(image); @@ -193,7 +195,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // create output image dib = J2KImageToFIBITMAP(s_format_id, image, header_only); - if(!dib) throw "Failed to import JPEG2000 image"; + if(!dib) { + throw "Failed to import JPEG2000 image"; + } // free image data structure opj_image_destroy(image); @@ -201,7 +205,9 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { return dib; } catch (const char *text) { - if(dib) FreeImage_Unload(dib); + if(dib) { + FreeImage_Unload(dib); + } // free remaining structures opj_destroy_codec(d_codec); opj_image_destroy(image); @@ -231,27 +237,22 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void 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; + parameters.tcp_numlayers = 0; // if no rate entered, apply a 16:1 rate by default - if(flags == J2K_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_rates[0] = (float)(flags & 0x3FF); } 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; + if(!image) { + return FALSE; + } // decide if MCT should be used parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0; diff --git a/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp b/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp index c1b45e6347..573989c5df 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp @@ -919,9 +919,6 @@ jpeg_write_exif_profile_raw(j_compress_ptr cinfo, FIBITMAP *dib) { if(tag_exif) { const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_exif); - - if (NULL == tag_value) - return FALSE; // verify the identifying string if(memcmp(exif_signature, tag_value, sizeof(exif_signature)) != 0) { @@ -929,21 +926,23 @@ jpeg_write_exif_profile_raw(j_compress_ptr cinfo, FIBITMAP *dib) { return FALSE; } - DWORD tag_length = FreeImage_GetTagLength(tag_exif); - - BYTE *profile = (BYTE*)malloc(tag_length * sizeof(BYTE)); - if(profile == NULL) return FALSE; + if(NULL != tag_value) { + DWORD tag_length = FreeImage_GetTagLength(tag_exif); - for(DWORD i = 0; i < tag_length; i += 65504L) { - unsigned length = MIN((long)(tag_length - i), 65504L); + BYTE *profile = (BYTE*)malloc(tag_length * sizeof(BYTE)); + if(profile == NULL) return FALSE; - memcpy(profile, tag_value + i, length); - jpeg_write_marker(cinfo, EXIF_MARKER, profile, length); - } + 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); + free(profile); - return TRUE; + return TRUE; + } } return FALSE; diff --git a/plugins/AdvaImg/src/FreeImage/PluginJXR.cpp b/plugins/AdvaImg/src/FreeImage/PluginJXR.cpp index f5e4878c1d..0e14e09ac9 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginJXR.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginJXR.cpp @@ -163,8 +163,6 @@ JXR_ErrorMessage(const int error) { default: return "Invalid instruction - please contact the FreeImage team"; } - - return NULL; } // ========================================================== @@ -410,11 +408,12 @@ GetOutputPixelFormat(FIBITMAP *dib, PKPixelFormatGUID *guid_format, BOOL *bHasAl } // ========================================================== -// Metadata loading & saving +// Metadata loading // ========================================================== /** Read a JPEG-XR IFD as a buffer +@see ReadMetadata */ static ERR ReadProfile(WMPStream* pStream, unsigned cbByteCount, unsigned uOffset, BYTE **ppbProfile) { @@ -436,6 +435,7 @@ ReadProfile(WMPStream* pStream, unsigned cbByteCount, unsigned uOffset, BYTE **p /** Convert a DPKPROPVARIANT to a FITAG, then store the tag as FIMD_EXIF_MAIN +@see ReadDescriptiveMetadata */ static BOOL ReadPropVariant(WORD tag_id, const DPKPROPVARIANT & varSrc, FIBITMAP *dib) { @@ -470,7 +470,7 @@ ReadPropVariant(WORD tag_id, const DPKPROPVARIANT & varSrc, FIBITMAP *dib) { 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_SetTagCount(tag, dwSize); FreeImage_SetTagLength(tag, dwSize); FreeImage_SetTagValue(tag, varSrc.VT.pwszVal); break; @@ -533,6 +533,7 @@ ReadDescriptiveMetadata(PKImageDecode *pID, FIBITMAP *dib) { /** Read ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata +@see ReadProfile, ReadDescriptiveMetadata */ static ERR ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) { @@ -593,7 +594,7 @@ ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) { error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); JXR_CHECK(error_code); // decode the Exif profile - jpegxr_read_exif_profile(dib, pbProfile, cbByteCount); + jpegxr_read_exif_profile(dib, pbProfile, cbByteCount, uOffset); } // Exif-GPS metadata @@ -603,7 +604,7 @@ ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) { error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile); JXR_CHECK(error_code); // decode the Exif-GPS profile - jpegxr_read_exif_gps_profile(dib, pbProfile, cbByteCount); + jpegxr_read_exif_gps_profile(dib, pbProfile, cbByteCount, uOffset); } // free profile buffer @@ -630,6 +631,168 @@ ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) { } // ========================================================== +// Metadata saving +// ========================================================== + +/** +Convert a FITAG (coming from FIMD_EXIF_MAIN) to a DPKPROPVARIANT. +No allocation is needed here, the function just copy pointers when needed. +@see WriteDescriptiveMetadata +*/ +static BOOL +WritePropVariant(FIBITMAP *dib, WORD tag_id, DPKPROPVARIANT & varDst) { + FITAG *tag = NULL; + + TagLib& s = TagLib::instance(); + + // clear output DPKPROPVARIANT + varDst.vt = DPKVT_EMPTY; + + // given the tag id, get the tag key + const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL); + // then, get the tag info + if(!FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) { + return FALSE; + } + + // set the tag value + switch(FreeImage_GetTagType(tag)) { + case FIDT_ASCII: + varDst.vt = DPKVT_LPSTR; + varDst.VT.pszVal = (char*)FreeImage_GetTagValue(tag); + break; + case FIDT_BYTE: + case FIDT_UNDEFINED: + varDst.vt = DPKVT_LPWSTR; + varDst.VT.pwszVal = (U16*)FreeImage_GetTagValue(tag); + break; + case FIDT_SHORT: + varDst.vt = DPKVT_UI2; + varDst.VT.uiVal = *((U16*)FreeImage_GetTagValue(tag)); + break; + case FIDT_LONG: + varDst.vt = DPKVT_UI4; + varDst.VT.ulVal = *((U32*)FreeImage_GetTagValue(tag)); + break; + default: + break; + } + + return TRUE; +} + +/** +Write EXIF_MAIN metadata to JPEG-XR descriptive metadata +@see WritePropVariant +*/ +static ERR +WriteDescriptiveMetadata(PKImageEncode *pIE, FIBITMAP *dib) { + ERR error_code = 0; // error code as returned by the interface + DESCRIPTIVEMETADATA DescMetadata; + + // fill the DESCRIPTIVEMETADATA structure (use pointers to arrays when needed) + WritePropVariant(dib, WMP_tagImageDescription, DescMetadata.pvarImageDescription); + WritePropVariant(dib, WMP_tagCameraMake, DescMetadata.pvarCameraMake); + WritePropVariant(dib, WMP_tagCameraModel, DescMetadata.pvarCameraModel); + WritePropVariant(dib, WMP_tagSoftware, DescMetadata.pvarSoftware); + WritePropVariant(dib, WMP_tagDateTime, DescMetadata.pvarDateTime); + WritePropVariant(dib, WMP_tagArtist, DescMetadata.pvarArtist); + WritePropVariant(dib, WMP_tagCopyright, DescMetadata.pvarCopyright); + WritePropVariant(dib, WMP_tagRatingStars, DescMetadata.pvarRatingStars); + WritePropVariant(dib, WMP_tagRatingValue, DescMetadata.pvarRatingValue); + WritePropVariant(dib, WMP_tagCaption, DescMetadata.pvarCaption); + WritePropVariant(dib, WMP_tagDocumentName, DescMetadata.pvarDocumentName); + WritePropVariant(dib, WMP_tagPageName, DescMetadata.pvarPageName); + WritePropVariant(dib, WMP_tagPageNumber, DescMetadata.pvarPageNumber); + WritePropVariant(dib, WMP_tagHostComputer, DescMetadata.pvarHostComputer); + + // copy the structure to the encoder + error_code = pIE->SetDescriptiveMetadata(pIE, &DescMetadata); + + // no need to free anything here + return error_code; +} + +/** +Write ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata +*/ +static ERR +WriteMetadata(PKImageEncode *pIE, FIBITMAP *dib) { + ERR error_code = 0; // error code as returned by the interface + BYTE *profile = NULL; + unsigned profile_size = 0; + + try { + // write ICC profile + { + FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); + if(iccProfile->data) { + error_code = pIE->SetColorContext(pIE, (U8*)iccProfile->data, iccProfile->size); + JXR_CHECK(error_code); + } + } + + // write descriptive metadata + if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib)) { + error_code = WriteDescriptiveMetadata(pIE, dib); + JXR_CHECK(error_code); + } + + // write IPTC metadata + if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) { + // create a binary profile + if(write_iptc_profile(dib, &profile, &profile_size)) { + // write the profile + error_code = PKImageEncode_SetIPTCNAAMetadata_WMP(pIE, profile, profile_size); + JXR_CHECK(error_code); + // release profile + free(profile); + profile = NULL; + } + } + + // write XMP metadata + { + FITAG *tag_xmp = NULL; + if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp)) { + error_code = PKImageEncode_SetXMPMetadata_WMP(pIE, (BYTE*)FreeImage_GetTagValue(tag_xmp), FreeImage_GetTagLength(tag_xmp)); + JXR_CHECK(error_code); + } + } + + // write Exif metadata + { + if(tiff_get_ifd_profile(dib, FIMD_EXIF_EXIF, &profile, &profile_size)) { + error_code = PKImageEncode_SetEXIFMetadata_WMP(pIE, profile, profile_size); + JXR_CHECK(error_code); + // release profile + free(profile); + profile = NULL; + } + } + + // write Exif GPS metadata + { + if(tiff_get_ifd_profile(dib, FIMD_EXIF_GPS, &profile, &profile_size)) { + error_code = PKImageEncode_SetGPSInfoMetadata_WMP(pIE, profile, profile_size); + JXR_CHECK(error_code); + // release profile + free(profile); + profile = NULL; + } + } + + return WMP_errSuccess; + + } catch(...) { + free(profile); + return error_code; + } +} + + + +// ========================================================== // Quantization tables (Y, U, V, YHP, UHP, VHP), // optimized for PSNR // ========================================================== @@ -1238,8 +1401,11 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void float resY = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterY(dib)); pEncoder->SetResolution(pEncoder, resX, resY); - // write pixels - // -------------- + // set metadata + WriteMetadata(pEncoder, dib); + + // write metadata & pixels + // ----------------------- // dib coordinates are upside-down relative to usual conventions bIsFlipped = FreeImage_FlipVertical(dib); @@ -1250,7 +1416,7 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void // get dst pitch (count of BYTE for stride) const unsigned cbStride = FreeImage_GetPitch(dib); - // write pixels on output + // write metadata + pixels on output error_code = pEncoder->WritePixels(pEncoder, height, dib_bits, cbStride); JXR_CHECK(error_code); diff --git a/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp b/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp index 231e8baa22..ea3c46b14e 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginPFM.cpp @@ -63,11 +63,13 @@ Get an integer value from the actual position pointed by handle static int pfm_get_int(FreeImageIO *io, fi_handle handle) { char c = 0; - BOOL firstchar; + BOOL bFirstChar; // skip forward to start of next number - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } while (1) { // eat comments @@ -75,15 +77,16 @@ pfm_get_int(FreeImageIO *io, fi_handle handle) { if (c == '#') { // if we're at a comment, read to end of line - firstchar = TRUE; + bFirstChar = TRUE; while (1) { - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } - if (firstchar && c == ' ') { + if (bFirstChar && c == ' ') { // loop off 1 sp after # - - firstchar = FALSE; + bFirstChar = FALSE; } else if (c == '\n') { break; } @@ -92,11 +95,12 @@ pfm_get_int(FreeImageIO *io, fi_handle handle) { if (c >= '0' && c <='9') { // we've found what we were looking for - break; } - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } } // we're at the start of a number, continue until we hit a non-number @@ -106,10 +110,13 @@ pfm_get_int(FreeImageIO *io, fi_handle handle) { while (1) { i = (i * 10) + (c - '0'); - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } - if (c < '0' || c > '9') - break; + if (c < '0' || c > '9') { + break; + } } return i; diff --git a/plugins/AdvaImg/src/FreeImage/PluginPICT.cpp b/plugins/AdvaImg/src/FreeImage/PluginPICT.cpp index 99958a489c..371056d20b 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginPICT.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginPICT.cpp @@ -82,21 +82,21 @@ static const int outputMessageSize = 256; // Internal functions // ========================================================== -static unsigned +static BYTE Read8(FreeImageIO *io, fi_handle handle) { - unsigned char i = 0; + BYTE i = 0; io->read_proc(&i, 1, 1, handle); return i; } -static unsigned +static WORD Read16(FreeImageIO *io, fi_handle handle) { // reads a two-byte big-endian integer from the given file and returns its value. // assumes unsigned. unsigned hi = Read8(io, handle); unsigned lo = Read8(io, handle); - return lo + (hi << 8); + return (WORD)(lo + (hi << 8)); } static unsigned @@ -388,7 +388,7 @@ ReadColorTable( FreeImageIO *io, fi_handle handle, WORD* pNumColors, RGBQUAD* pP // The indicies in a device colour table are bogus and // usually == 0, so I assume we allocate up the list of // colours in order. - val = i; + val = (WORD)i; } if (val >= numColors) { throw "pixel value greater than color table size."; @@ -416,7 +416,7 @@ SkipBits( FreeImageIO *io, fi_handle handle, MacRect* bounds, WORD rowBytes, int if (pixelSize <= 8) { rowBytes &= 0x7fff; } - pixwidth = width; + pixwidth = (WORD)width; if (pixelSize == 16) { pixwidth *= 2; @@ -541,6 +541,7 @@ expandBuf8( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst ) static BYTE* UnpackPictRow( FreeImageIO *io, fi_handle handle, BYTE* pLineBuf, int width, int rowBytes, int srcBytes ) { + if (rowBytes < 8) { // Ah-ha! The bits aren't actually packed. This will be easy. io->read_proc( pLineBuf, rowBytes, 1, handle ); } @@ -589,7 +590,7 @@ Unpack32Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, int width = bounds->right - bounds->left; if (rowBytes == 0) { - rowBytes = width*4; + rowBytes = (WORD)( width * 4 ); } BYTE* pLineBuf = (BYTE*)malloc( rowBytes ); // Let's allocate enough for 4 bit planes @@ -656,7 +657,7 @@ Unpack8Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, rowBytes &= 0x7fff; if (rowBytes == 0) { - rowBytes = width; + rowBytes = (WORD)width; } for ( int i = 0; i < height; i++ ) { @@ -694,7 +695,7 @@ UnpackBits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, W rowBytes &= 0x7fff; } - pixwidth = width; + pixwidth = (WORD)width; pkpixsize = 1; // RLE unit: one byte for everything... if (pixelSize == 16) { // ...except 16 bpp. pkpixsize = 2; diff --git a/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp b/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp index f6b59e299b..fe80a2b533 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginPNG.cpp @@ -6,6 +6,7 @@ // - Herve Drolon (drolon@infonie.fr) // - Detlev Vendt (detlev.vendt@brillit.de) // - Aaron Shumate (trek@startreker.com) +// - Tanner Helland (tannerhelland@users.sf.net) // // This file is part of FreeImage 3 // @@ -49,9 +50,9 @@ typedef struct { fi_handle s_handle; } fi_ioStructure, *pfi_ioStructure; -///////////////////////////////////////////////////////////////////////////// -// libpng interface -// +// ========================================================== +// libpng interface +// ========================================================== static void _ReadProc(png_structp png_ptr, unsigned char *data, png_size_t size) { @@ -99,6 +100,7 @@ ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { FITAG *tag = NULL; png_textp text_ptr = NULL; + png_timep mod_time = NULL; int num_text = 0; // iTXt/tEXt/zTXt chuncks @@ -130,6 +132,31 @@ ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { } } + // timestamp chunk + if(png_get_tIME(png_ptr, info_ptr, &mod_time)) { + char timestamp[32]; + // create a tag + tag = FreeImage_CreateTag(); + if(!tag) return FALSE; + + // convert as 'yyyy:MM:dd hh:mm:ss' + sprintf(timestamp, "%4d:%02d:%02d %2d:%02d:%02d", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second); + + DWORD tag_length = (DWORD)strlen(timestamp) + 1; + FreeImage_SetTagLength(tag, tag_length); + FreeImage_SetTagCount(tag, tag_length); + FreeImage_SetTagType(tag, FIDT_ASCII); + FreeImage_SetTagID(tag, TAG_DATETIME); + FreeImage_SetTagValue(tag, timestamp); + + // store the tag as Exif-TIFF + FreeImage_SetTagKey(tag, "DateTime"); + FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, FreeImage_GetTagKey(tag), tag); + + // destroy the tag + FreeImage_DeleteTag(tag); + } + return TRUE; } @@ -143,6 +170,7 @@ WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { BOOL bResult = TRUE; png_text text_metadata; + png_time mod_time; // set the 'Comments' metadata as iTXt chuncks @@ -174,7 +202,7 @@ WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { if(tag && FreeImage_GetTagLength(tag)) { memset(&text_metadata, 0, sizeof(png_text)); text_metadata.compression = 1; // iTXt, none - text_metadata.key = (char*)g_png_xmp_keyword; // keyword, 1-79 character description of "text" + text_metadata.key = (char*)g_png_xmp_keyword; // keyword, 1-79 character description of "text" text_metadata.text = (char*)FreeImage_GetTagValue(tag); // comment, may be an empty string (ie "") text_metadata.text_length = FreeImage_GetTagLength(tag);// length of the text string text_metadata.itxt_length = FreeImage_GetTagLength(tag);// length of the itxt string @@ -186,6 +214,23 @@ WriteMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { bResult &= TRUE; } + // set the Exif-TIFF 'DateTime' metadata as a tIME chunk + tag = NULL; + FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "DateTime", &tag); + if(tag && FreeImage_GetTagLength(tag)) { + int year, month, day, hour, minute, second; + const char *value = (char*)FreeImage_GetTagValue(tag); + if(sscanf(value, "%4d:%02d:%02d %2d:%02d:%02d", &year, &month, &day, &hour, &minute, &second) == 6) { + mod_time.year = (png_uint_16)year; + mod_time.month = (png_byte)month; + mod_time.day = (png_byte)day; + mod_time.hour = (png_byte)hour; + mod_time.minute = (png_byte)minute; + mod_time.second = (png_byte)second; + png_set_tIME (png_ptr, info_ptr, &mod_time); + } + } + return bResult; } @@ -265,21 +310,207 @@ SupportsNoPixels() { return TRUE; } -// ---------------------------------------------------------- +// -------------------------------------------------------------------------- + +/** +Configure the decoder so that decoded pixels are compatible with a FREE_IMAGE_TYPE format. +Set conversion instructions as needed. +@param png_ptr PNG handle +@param info_ptr PNG info handle +@param flags Decoder flags +@param output_image_type Returned FreeImage converted image type +@return Returns TRUE if successful, returns FALSE otherwise +@see png_read_update_info +*/ +static BOOL +ConfigureDecoder(png_structp png_ptr, png_infop info_ptr, int flags, FREE_IMAGE_TYPE *output_image_type) { + // get original image info + const int color_type = png_get_color_type(png_ptr, info_ptr); + const int bit_depth = png_get_bit_depth(png_ptr, info_ptr); + const int pixel_depth = bit_depth * png_get_channels(png_ptr, info_ptr); + + FREE_IMAGE_TYPE image_type = FIT_BITMAP; // assume standard image type + + // check for transparency table or single transparent color + BOOL bIsTransparent = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) == PNG_INFO_tRNS ? TRUE : FALSE; + + // check allowed combinations of colour type and bit depth + // then get converted FreeImage type + + switch(color_type) { + case PNG_COLOR_TYPE_GRAY: // color type '0', bitdepth = 1, 2, 4, 8, 16 + switch(bit_depth) { + case 1: + case 2: + case 4: + case 8: + // expand grayscale images to the full 8-bit from 2-bit/pixel + if (pixel_depth == 2) { + png_set_expand_gray_1_2_4_to_8(png_ptr); + } + + // if a tRNS chunk is provided, we must also expand the grayscale data to 8-bits, + // this allows us to make use of the transparency table with existing FreeImage methods + if (bIsTransparent && (pixel_depth < 8)) { + png_set_expand_gray_1_2_4_to_8(png_ptr); + } + break; + + case 16: + image_type = (pixel_depth == 16) ? FIT_UINT16 : FIT_UNKNOWN; + + // 16-bit grayscale images can contain a transparent value (shade) + // if found, expand the transparent value to a full alpha channel + if (bIsTransparent && (image_type != FIT_UNKNOWN)) { + // expand tRNS to a full alpha channel + png_set_tRNS_to_alpha(png_ptr); + + // expand new 16-bit gray + 16-bit alpha to full 64-bit RGBA + png_set_gray_to_rgb(png_ptr); + + image_type = FIT_RGBA16; + } + break; + + default: + image_type = FIT_UNKNOWN; + break; + } + break; + + case PNG_COLOR_TYPE_RGB: // color type '2', bitdepth = 8, 16 + switch(bit_depth) { + case 8: + image_type = (pixel_depth == 24) ? FIT_BITMAP : FIT_UNKNOWN; + break; + case 16: + image_type = (pixel_depth == 48) ? FIT_RGB16 : FIT_UNKNOWN; + break; + default: + image_type = FIT_UNKNOWN; + break; + } + // sometimes, 24- or 48-bit images may contain transparency information + // check for this use case and convert to an alpha-compatible format + if (bIsTransparent && (image_type != FIT_UNKNOWN)) { + // if the image is 24-bit RGB, mark it as 32-bit; if it is 48-bit, mark it as 64-bit + image_type = (pixel_depth == 24) ? FIT_BITMAP : (pixel_depth == 48) ? FIT_RGBA16 : FIT_UNKNOWN; + // expand tRNS chunk to alpha channel + png_set_tRNS_to_alpha(png_ptr); + } + break; + + case PNG_COLOR_TYPE_PALETTE: // color type '3', bitdepth = 1, 2, 4, 8 + switch(bit_depth) { + case 1: + case 2: + case 4: + case 8: + // expand palette images to the full 8 bits from 2 bits/pixel + if (pixel_depth == 2) { + png_set_packing(png_ptr); + } + + // if a tRNS chunk is provided, we must also expand the palletized data to 8-bits, + // this allows us to make use of the transparency table with existing FreeImage methods + if (bIsTransparent && (pixel_depth < 8)) { + png_set_packing(png_ptr); + } + break; + + default: + image_type = FIT_UNKNOWN; + break; + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: // color type '4', bitdepth = 8, 16 + switch(bit_depth) { + case 8: + // 8-bit grayscale + 8-bit alpha => convert to 32-bit RGBA + image_type = (pixel_depth == 16) ? FIT_BITMAP : FIT_UNKNOWN; + break; + case 16: + // 16-bit grayscale + 16-bit alpha => convert to 64-bit RGBA + image_type = (pixel_depth == 32) ? FIT_RGBA16 : FIT_UNKNOWN; + break; + default: + image_type = FIT_UNKNOWN; + break; + } + // expand 8-bit greyscale + 8-bit alpha to 32-bit + // expand 16-bit greyscale + 16-bit alpha to 64-bit + png_set_gray_to_rgb(png_ptr); + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: // color type '6', bitdepth = 8, 16 + switch(bit_depth) { + case 8: + break; + case 16: + image_type = (pixel_depth == 64) ? FIT_RGBA16 : FIT_UNKNOWN; + break; + default: + image_type = FIT_UNKNOWN; + break; + } + break; + } + + // check for unknown or invalid formats + if(image_type == FIT_UNKNOWN) { + *output_image_type = image_type; + return FALSE; + } + +#ifndef FREEIMAGE_BIGENDIAN + if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) { + // turn on 16-bit byte swapping + png_set_swap(png_ptr); + } +#endif + +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + if((image_type == FIT_BITMAP) && ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA))) { + // flip the RGB pixels to BGR (or RGBA to BGRA) + png_set_bgr(png_ptr); + } +#endif + + // gamma correction + // unlike the example in the libpng documentation, we have *no* idea where + // this file may have come from--so if it doesn't have a file gamma, don't + // do any correction ("do no harm") + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { + double gamma = 0; + double screen_gamma = 2.2; + + if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) { + png_set_gamma(png_ptr, screen_gamma, gamma); + } + } + + // all transformations have been registered; now update info_ptr data + png_read_update_info(png_ptr, info_ptr); + + // return the output image type + *output_image_type = image_type; + + return TRUE; +} static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr = NULL; - png_infop info_ptr; + png_infop info_ptr = NULL; png_uint_32 width, height; - png_colorp png_palette = NULL; - int color_type, palette_entries = 0; - int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels + int color_type; + int bit_depth; + int pixel_depth = 0; // pixel_depth = bit_depth * channels FIBITMAP *dib = NULL; - RGBQUAD *palette = NULL; // pointer to dib palette - png_bytepp row_pointers = NULL; - int i; + png_bytepp row_pointers = NULL; fi_ioStructure fio; fio.s_handle = handle; @@ -334,154 +565,58 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); - pixel_depth = png_get_bit_depth(png_ptr, info_ptr) * png_get_channels(png_ptr, info_ptr); - - // get image data type (assume standard image type) + // configure the decoder FREE_IMAGE_TYPE image_type = FIT_BITMAP; - if (bit_depth == 16) { - if ((pixel_depth == 16) && (color_type == PNG_COLOR_TYPE_GRAY)) { - image_type = FIT_UINT16; - } - else if ((pixel_depth == 48) && (color_type == PNG_COLOR_TYPE_RGB)) { - image_type = FIT_RGB16; - } - else if ((pixel_depth == 64) && (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { - image_type = FIT_RGBA16; - } else { - // tell libpng to strip 16 bit/color files down to 8 bits/color - png_set_strip_16(png_ptr); - bit_depth = 8; - } - } - -#ifndef FREEIMAGE_BIGENDIAN - if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) { - // turn on 16 bit byte swapping - png_set_swap(png_ptr); - } -#endif - - // set some additional flags - - switch(color_type) { - case PNG_COLOR_TYPE_RGB: - case PNG_COLOR_TYPE_RGB_ALPHA: -#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR - // flip the RGB pixels to BGR (or RGBA to BGRA) - - if(image_type == FIT_BITMAP) { - png_set_bgr(png_ptr); - } -#endif - break; - - case PNG_COLOR_TYPE_PALETTE: - // expand palette images to the full 8 bits from 2 bits/pixel - - if (pixel_depth == 2) { - png_set_packing(png_ptr); - pixel_depth = 8; - } - - break; - - case PNG_COLOR_TYPE_GRAY: - // expand grayscale images to the full 8 bits from 2 bits/pixel - // but *do not* expand fully transparent palette entries to a full alpha channel - - if (pixel_depth == 2) { - png_set_expand_gray_1_2_4_to_8(png_ptr); - pixel_depth = 8; - } - - break; - - case PNG_COLOR_TYPE_GRAY_ALPHA: - // expand 8-bit greyscale + 8-bit alpha to 32-bit - png_set_gray_to_rgb(png_ptr); -#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR - // flip the RGBA pixels to BGRA - - png_set_bgr(png_ptr); -#endif - pixel_depth = 32; - - break; - - default: - throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; + if(!ConfigureDecoder(png_ptr, info_ptr, flags, &image_type)) { + throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } - // unlike the example in the libpng documentation, we have *no* idea where - // this file may have come from--so if it doesn't have a file gamma, don't - // do any correction ("do no harm") - - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { - double gamma = 0; - double screen_gamma = 2.2; - - if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) { - png_set_gamma(png_ptr, screen_gamma, gamma); - } - } - - // all transformations have been registered; now update info_ptr data - - png_read_update_info(png_ptr, info_ptr); + // update image info - // color type may have changed, due to our transformations + color_type = png_get_color_type(png_ptr, info_ptr); + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + pixel_depth = bit_depth * png_get_channels(png_ptr, info_ptr); - color_type = png_get_color_type(png_ptr,info_ptr); - - // create a DIB and write the bitmap header - // set up the DIB palette, if needed + // create a dib and write the bitmap header + // set up the dib palette, if needed switch (color_type) { case PNG_COLOR_TYPE_RGB: - png_set_invert_alpha(png_ptr); - - if(image_type == FIT_BITMAP) { - dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - } else { - dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); - } - break; - case PNG_COLOR_TYPE_RGB_ALPHA: - if(image_type == FIT_BITMAP) { - dib = FreeImage_AllocateHeader(header_only, width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - } else { - dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); - } + dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; case PNG_COLOR_TYPE_PALETTE: - dib = FreeImage_AllocateHeader(header_only, width, height, pixel_depth); + dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + if(dib) { + png_colorp png_palette = NULL; + int palette_entries = 0; - png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries); + png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries); - palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib)); - palette = FreeImage_GetPalette(dib); + palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib)); - // store the palette + // store the palette - for (i = 0; i < palette_entries; i++) { - palette[i].rgbRed = png_palette[i].red; - palette[i].rgbGreen = png_palette[i].green; - palette[i].rgbBlue = png_palette[i].blue; + RGBQUAD *palette = FreeImage_GetPalette(dib); + for(int i = 0; i < palette_entries; i++) { + palette[i].rgbRed = png_palette[i].red; + palette[i].rgbGreen = png_palette[i].green; + palette[i].rgbBlue = png_palette[i].blue; + } } break; case PNG_COLOR_TYPE_GRAY: - dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); + dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); - if(pixel_depth <= 8) { - palette = FreeImage_GetPalette(dib); - palette_entries = 1 << pixel_depth; + if(dib && (pixel_depth <= 8)) { + RGBQUAD *palette = FreeImage_GetPalette(dib); + const int palette_entries = 1 << pixel_depth; - for (i = 0; i < palette_entries; i++) { + for(int i = 0; i < palette_entries; i++) { palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1)); @@ -493,6 +628,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } + if(!dib) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + // store the transparency table if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { @@ -507,21 +646,26 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) { // single transparent color - if (trans_color->gray < palette_entries) { + if (trans_color->gray < 256) { BYTE table[256]; - memset(table, 0xFF, palette_entries); + memset(table, 0xFF, 256); table[trans_color->gray] = 0; - FreeImage_SetTransparencyTable(dib, table, palette_entries); + FreeImage_SetTransparencyTable(dib, table, 256); + } + // check for a full transparency table, too + else if ((trans_alpha) && (pixel_depth <= 8)) { + FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } + } else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) { // transparency table FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } } - // store the background color + // store the background color (only supported for FIT_BITMAP types) - if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { + if ((image_type == FIT_BITMAP) && png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { // Get the background color to draw transparent and alpha images over. // Note that even if the PNG file supplies a background, you are not required to // use it - you should use the (solid) application background if it has one. @@ -598,7 +742,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // allow loading of PNG with minor errors (such as images with several IDAT chunks) for (png_uint_32 k = 0; k < height; k++) { - row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k); + row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k); } png_set_benign_errors(png_ptr, 1); @@ -644,7 +788,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { free(row_pointers); } if (dib) { - FreeImage_Unload(dib); + FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, text); @@ -655,6 +799,8 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { return NULL; } +// -------------------------------------------------------------------------- + static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr; @@ -903,7 +1049,7 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { - FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width); + FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width); png_write_row(png_ptr, buffer); } } @@ -911,8 +1057,8 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void } else { // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { - for (png_uint_32 k = 0; k < height; k++) { - png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1)); + for (png_uint_32 k = 0; k < height; k++) { + png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1)); } } } @@ -930,7 +1076,11 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void png_destroy_write_struct(&png_ptr, &info_ptr); return TRUE; + } catch (const char *text) { + if(png_ptr) { + png_destroy_write_struct(&png_ptr, &info_ptr); + } FreeImage_OutputMessageProc(s_format_id, text); } } diff --git a/plugins/AdvaImg/src/FreeImage/PluginPNM.cpp b/plugins/AdvaImg/src/FreeImage/PluginPNM.cpp index 3155315559..3b4d0de554 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginPNM.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginPNM.cpp @@ -33,11 +33,13 @@ Get an integer value from the actual position pointed by handle static int GetInt(FreeImageIO *io, fi_handle handle) { char c = 0; - BOOL firstchar; + BOOL bFirstChar; // skip forward to start of next number - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } while (1) { // eat comments @@ -45,15 +47,16 @@ GetInt(FreeImageIO *io, fi_handle handle) { if (c == '#') { // if we're at a comment, read to end of line - firstchar = TRUE; + bFirstChar = TRUE; while (1) { - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } - if (firstchar && c == ' ') { + if (bFirstChar && c == ' ') { // loop off 1 sp after # - - firstchar = FALSE; + bFirstChar = FALSE; } else if (c == '\n') { break; } @@ -62,11 +65,12 @@ GetInt(FreeImageIO *io, fi_handle handle) { if (c >= '0' && c <='9') { // we've found what we were looking for - break; } - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } } // we're at the start of a number, continue until we hit a non-number @@ -76,10 +80,13 @@ GetInt(FreeImageIO *io, fi_handle handle) { while (1) { i = (i * 10) + (c - '0'); - if(!io->read_proc(&c, 1, 1, handle)) throw FI_MSG_ERROR_PARSING; + if(!io->read_proc(&c, 1, 1, handle)) { + throw FI_MSG_ERROR_PARSING; + } - if (c < '0' || c > '9') - break; + if (c < '0' || c > '9') { + break; + } } return i; diff --git a/plugins/AdvaImg/src/FreeImage/PluginRAW.cpp b/plugins/AdvaImg/src/FreeImage/PluginRAW.cpp index bf5d82169c..e9bd5bfa77 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginRAW.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginRAW.cpp @@ -140,12 +140,72 @@ public: /** Convert a processed raw data array to a FIBITMAP +@param RawProcessor LibRaw handle containing the processed raw image +@return Returns the converted dib if successfull, returns NULL otherwise +*/ +static FIBITMAP * +libraw_ConvertProcessedRawToDib(LibRaw *RawProcessor) { + FIBITMAP *dib = NULL; + int width, height, colors, bpp; + + try { + int bgr = 0; // pixel copy order: RGB if (bgr == 0) and BGR otherwise + + // get image info + RawProcessor->get_mem_image_format(&width, &height, &colors, &bpp); + + // only 3-color images supported... + if(colors != 3) { + throw "LibRaw : only 3-color images supported"; + } + + if(bpp == 16) { + // allocate output dib + dib = FreeImage_AllocateT(FIT_RGB16, width, height); + if(!dib) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + } else if(bpp == 8) { +#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR + bgr = 1; // only useful for FIT_BITMAP types +#endif + + // allocate output dib + dib = FreeImage_AllocateT(FIT_BITMAP, width, height, 24); + if(!dib) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + } + + // copy post-processed bitmap data into FIBITMAP buffer + if(RawProcessor->copy_mem_image(FreeImage_GetBits(dib), FreeImage_GetPitch(dib), bgr) != LIBRAW_SUCCESS) { + throw "LibRaw : failed to copy data into dib"; + } + + // flip vertically + FreeImage_FlipVertical(dib); + + return dib; + + } catch(const char *text) { + FreeImage_Unload(dib); + FreeImage_OutputMessageProc(s_format_id, text); + return NULL; + } +} + + +/** +Convert a processed raw image to a FIBITMAP @param image Processed raw image @return Returns the converted dib if successfull, returns NULL otherwise +@see libraw_LoadEmbeddedPreview */ static FIBITMAP * -libraw_ConvertToDib(libraw_processed_image_t *image) { +libraw_ConvertProcessedImageToDib(libraw_processed_image_t *image) { FIBITMAP *dib = NULL; + try { unsigned width = image->width; unsigned height = image->height; @@ -185,12 +245,14 @@ libraw_ConvertToDib(libraw_processed_image_t *image) { } } } + + return dib; } catch(const char *text) { + FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, text); + return NULL; } - - return dib; } /** @@ -228,9 +290,9 @@ libraw_LoadEmbeddedPreview(LibRaw *RawProcessor, int flags) { dib = FreeImage_LoadFromMemory(fif, hmem, flags); // close the stream FreeImage_CloseMemory(hmem); - } else { + } else if((flags & FIF_LOAD_NOPIXELS) != FIF_LOAD_NOPIXELS) { // convert processed data to output dib - dib = libraw_ConvertToDib(thumb_image); + dib = libraw_ConvertProcessedImageToDib(thumb_image); } } else { throw "LibRaw : failed to run dcraw_make_mem_thumb"; @@ -262,7 +324,6 @@ Load raw data and convert to FIBITMAP static FIBITMAP * libraw_LoadRawData(LibRaw *RawProcessor, int bitspersample) { FIBITMAP *dib = NULL; - libraw_processed_image_t *processed_image = NULL; try { // set decoding parameters @@ -300,38 +361,119 @@ libraw_LoadRawData(LibRaw *RawProcessor, int bitspersample) { } // retrieve processed image - int error_code = 0; - processed_image = RawProcessor->dcraw_make_mem_image(&error_code); - if(processed_image) { - // type SHOULD be LIBRAW_IMAGE_BITMAP, but we'll check - if(processed_image->type != LIBRAW_IMAGE_BITMAP) { - throw "invalid image type"; + dib = libraw_ConvertProcessedRawToDib(RawProcessor); + + return dib; + + } catch(const char *text) { + FreeImage_OutputMessageProc(s_format_id, text); + return NULL; + } +} + +/** +Load the Bayer matrix (unprocessed raw data) as a FIT_UINT16 image. +Note that some formats don't have a Bayer matrix (e.g. Foveon, Canon sRAW, demosaiced DNG files). +@param RawProcessor Libraw handle +@return Returns the loaded dib if successfull, returns NULL otherwise +*/ +static FIBITMAP * +libraw_LoadUnprocessedData(LibRaw *RawProcessor) { + FIBITMAP *dib = NULL; + + try { + // unpack data + if(RawProcessor->unpack() != LIBRAW_SUCCESS) { + throw "LibRaw : failed to unpack data"; + } + + // check for a supported Bayer format + if(!(RawProcessor->imgdata.idata.filters || RawProcessor->imgdata.idata.colors == 1)) { + throw "LibRaw : only Bayer-pattern RAW files are supported"; + } + + // allocate output dib + const unsigned width = RawProcessor->imgdata.sizes.raw_width; + const unsigned height = RawProcessor->imgdata.sizes.raw_height; + const size_t line_size = width * sizeof(WORD); + const WORD *src_bits = (WORD*)RawProcessor->imgdata.rawdata.raw_image; + + if(src_bits) { + dib = FreeImage_AllocateT(FIT_UINT16, width, height); + } + if(!dib) { + throw FI_MSG_ERROR_DIB_MEMORY; + } + + // retrieve the raw image + for(unsigned y = 0; y < height; y++) { + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dib, height - 1 - y); + memcpy(dst_bits, src_bits, line_size); + src_bits += width; + } + + // store metadata needed for post-processing + { + char value[512]; + + const libraw_image_sizes_t *sizes = &RawProcessor->imgdata.sizes; + + // image output width & height + { + sprintf(value, "%d", sizes->iwidth); + FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Output.Width", value); + + sprintf(value, "%d", sizes->iheight); + FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Output.Height", value); } - // only 3-color images supported... - if(processed_image->colors != 3) { - throw "only 3-color images supported"; + + // image output frame + { + const unsigned f_left = sizes->left_margin; + const unsigned f_top = sizes->top_margin; + const unsigned f_width = sizes->width; + const unsigned f_height = sizes->height; + + sprintf(value, "%d", f_left); + FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Left", value); + + sprintf(value, "%d", f_top); + FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Top", value); + + sprintf(value, "%d", f_width); + FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Width", value); + + sprintf(value, "%d", f_height); + FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.Frame.Height", value); } - } else { - throw "LibRaw : failed to run dcraw_make_mem_image"; - } - // convert processed data to output dib - dib = libraw_ConvertToDib(processed_image); - - // clean-up and return - RawProcessor->dcraw_clear_mem(processed_image); + // Bayer pattern + // Mask describing the order of color pixels in the matrix. + // This field describe 16 pixels (8 rows with two pixels in each, from left to right and from top to bottom). + + if(RawProcessor->imgdata.idata.filters) { + // description of colors numbered from 0 to 3 (RGBG,RGBE,GMCY, or GBTG) + char *cdesc = RawProcessor->imgdata.idata.cdesc; + if(!cdesc[3]) { + cdesc[3] = 'G'; + } + char *pattern = &value[0]; + for(int i = 0; i < 16; i++) { + pattern[i] = cdesc[ RawProcessor->fcol(i >> 1, i & 1) ]; + } + pattern[16] = 0; + FreeImage_SetMetadataKeyValue(FIMD_COMMENTS, dib, "Raw.BayerPattern", value); + } + } + return dib; } catch(const char *text) { - // clean-up and return - if(processed_image) { - RawProcessor->dcraw_clear_mem(processed_image); - } + FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, text); + return NULL; } - - return NULL; } // ========================================================== @@ -397,8 +539,8 @@ Extension() { "sr2," // Sony Digital Camera Raw Image Format. "srf," // Sony Digital Camera Raw Image Format for DSC-F828 8 megapixel digital camera or Sony DSC-R1. "srw," // Samsung Raw Image Format. - "sti"; // Sinar Capture Shop Raw Image File. -// "x3f" // Sigma Digital Camera Raw Image Format for devices based on Foveon X3 direct image sensor. + "sti," // Sinar Capture Shop Raw Image File. + "x3f"; // Sigma Digital Camera Raw Image Format for devices based on Foveon X3 direct image sensor. return raw_extensions; } @@ -416,33 +558,37 @@ static BOOL HasMagicHeader(FreeImageIO *io, fi_handle handle) { const unsigned signature_size = 32; BYTE signature[signature_size] = { 0 }; - - // Canon (CR2), Intel byte order + /* + note: classic TIFF signature is + { 0x49, 0x49, 0x2A, 0x00 } Classic TIFF, little-endian + { 0x4D, 0x4D, 0x00, 0x2A } Classic TIFF, big-endian + */ + // Canon (CR2), little-endian byte order const BYTE CR2_II[] = { 0x49, 0x49, 0x2A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x52, 0x02, 0x00 }; - // Canon (CR2), Motorola byte order - const BYTE CR2_MM[] = { 0x4D, 0x4D, 0x2A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x52, 0x02, 0x00 }; - // Canon (CRW), Intel byte order - const BYTE CRW_II[] = { 0x49, 0x49, 0x1A, 0x00, 0x00, 0x00, 0x48, 0x45, 0x41, 0x50, 0x43, 0x43, 0x44, 0x52, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + // Canon (CRW), little-endian byte order + const BYTE CRW_II[] = { 0x49, 0x49, 0x1A, 0x00, 0x00, 0x00, 0x48, 0x45, 0x41, 0x50, 0x43, 0x43, 0x44, 0x52, 0x02, 0x00 }; // Minolta (MRW) const BYTE MRW[] = { 0x00, 0x4D, 0x52, 0x4D, 0x00 }; - // Olympus (ORF), Intel byte order + // Olympus (ORF), little-endian byte order const BYTE ORF_IIRS[] = { 0x49, 0x49, 0x52, 0x53, 0x08, 0x00, 0x00, 0x00 }; const BYTE ORF_IIRO[] = { 0x49, 0x49, 0x52, 0x4F, 0x08, 0x00, 0x00, 0x00 }; - // Olympus (ORF), Motorola byte order + // Olympus (ORF), big-endian byte order const BYTE ORF_MMOR[] = { 0x4D, 0x4D, 0x4F, 0x52, 0x00, 0x00, 0x00, 0x08 }; // Fujifilm (RAF) - const BYTE RAF[] = { 0x46, 0x55, 0x4A, 0x49, 0x46, 0x49, 0x4C, 0x4D, 0x43, 0x43, 0x44, 0x2D, 0x52, 0x41, 0x57, 0x20, 0x30, 0x32, 0x30, 0x31 }; - // Panasonic (RW2) or Leica (RWL) - const BYTE RW2_II[] = { 0x49, 0x49, 0x55, 0x00, 0x18, 0x00, 0x00, 0x00, 0x88, 0xE7, 0x74, 0xD8, 0xF8, 0x25, 0x1D, 0x4D, 0x94, 0x7A, 0x6E, 0x77, 0x82, 0x2B, 0x5D, 0x6A }; + const BYTE RAF[] = { 0x46, 0x55, 0x4A, 0x49, 0x46, 0x49, 0x4C, 0x4D, 0x43, 0x43, 0x44, 0x2D, 0x52, 0x41, 0x57, 0x20 }; + // Panasonic (RW2) or Leica (RWL), little-endian byte order + const BYTE RWx_II[] = { 0x49, 0x49, 0x55, 0x00, 0x18, 0x00, 0x00, 0x00, 0x88, 0xE7, 0x74, 0xD8, 0xF8, 0x25, 0x1D, 0x4D, 0x94, 0x7A, 0x6E, 0x77, 0x82, 0x2B, 0x5D, 0x6A }; + // Panasonic (RAW) or Leica (RAW), little-endian byte order + const BYTE RAW_II[] = { 0x49, 0x49, 0x55, 0x00, 0x08, 0x00, 0x00, 0x00, 0x22, 0x00, 0x01, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00 }; + // Foveon (X3F) + const BYTE X3F[] = { 0x46, 0x4F, 0x56, 0x62 }; if(io->read_proc(signature, 1, signature_size, handle) != signature_size) { return FALSE; } if(memcmp(CR2_II, signature, 12) == 0) return TRUE; - if(memcmp(CR2_MM, signature, 12) == 0) - return TRUE; - if(memcmp(CRW_II, signature, 26) == 0) + if(memcmp(CRW_II, signature, 16) == 0) return TRUE; if(memcmp(MRW, signature, 5) == 0) return TRUE; @@ -452,9 +598,13 @@ HasMagicHeader(FreeImageIO *io, fi_handle handle) { return TRUE; if(memcmp(ORF_MMOR, signature, 8) == 0) return TRUE; - if(memcmp(RAF, signature, 20) == 0) + if(memcmp(RAF, signature, 16) == 0) return TRUE; - if(memcmp(RW2_II, signature, 24) == 0) + if(memcmp(RWx_II, signature, 24) == 0) + return TRUE; + if(memcmp(RAW_II, signature, 18) == 0) + return TRUE; + if(memcmp(X3F, signature, 4) == 0) return TRUE; return FALSE; @@ -547,6 +697,8 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { RawProcessor->imgdata.params.shot_select = 0; // (-w) Use camera white balance, if possible (otherwise, fallback to auto_wb) RawProcessor->imgdata.params.use_camera_wb = 1; + // (-M) Use any color matrix from the camera metadata. This option only affects Olympus, Leaf, and Phase One cameras. + RawProcessor->imgdata.params.use_camera_matrix = 1; // (-h) outputs the image in 50% size RawProcessor->imgdata.params.half_size = ((flags & RAW_HALFSIZE) == RAW_HALFSIZE) ? 1 : 0; @@ -559,6 +711,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // header only mode dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, RawProcessor->imgdata.sizes.width, RawProcessor->imgdata.sizes.height); } + else if((flags & RAW_UNPROCESSED) == RAW_UNPROCESSED) { + // load raw data without post-processing (i.e. as a Bayer matrix) + dib = libraw_LoadUnprocessedData(RawProcessor); + } else if((flags & RAW_PREVIEW) == RAW_PREVIEW) { // try to get the embedded JPEG dib = libraw_LoadEmbeddedPreview(RawProcessor, 0); diff --git a/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp b/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp index f12d7286ce..84864308e0 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginTARGA.cpp @@ -39,20 +39,20 @@ #endif typedef struct tagTGAHEADER { - BYTE id_length; // ID length - BYTE color_map_type; // color map type - BYTE image_type; // image type - - WORD cm_first_entry; // first entry index - WORD cm_length; // color map length - BYTE cm_size; // color map entry size, in bits - - WORD is_xorigin; // X-origin of image - WORD is_yorigin; // Y-origin of image - WORD is_width; // image width - WORD is_height; // image height - BYTE is_pixel_depth; // pixel depth - BYTE is_image_descriptor; // image descriptor + BYTE id_length; //! length of the image ID field + BYTE color_map_type; //! whether a color map is included + BYTE image_type; //! compression and color types + + WORD cm_first_entry; //! first entry index (offset into the color map table) + WORD cm_length; //! color map length (number of entries) + BYTE cm_size; //! color map entry size, in bits (number of bits per pixel) + + WORD is_xorigin; //! X-origin of image (absolute coordinate of lower-left corner for displays where origin is at the lower left) + WORD is_yorigin; //! Y-origin of image (as for X-origin) + WORD is_width; //! image width + WORD is_height; //! image height + BYTE is_pixel_depth; //! bits per pixel + BYTE is_image_descriptor; //! image descriptor, bits 3-0 give the alpha channel depth, bits 5-4 give direction } TGAHEADER; typedef struct tagTGAEXTENSIONAREA { @@ -400,14 +400,14 @@ Validate(FreeImageIO *io, fi_handle handle) { } // 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 + // 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; } - } - // check header.cm_size, don't allow 0 or anything bigger than 32 - if(header.cm_size == 0 || header.cm_size > 32) { - return FALSE; + // 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) { @@ -415,9 +415,9 @@ Validate(FreeImageIO *io, fi_handle handle) { } // 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_CMAP: case TGA_RGB: - case TGA_MONO : + case TGA_MONO: case TGA_RLECMAP: case TGA_RLERGB: case TGA_RLEMONO: @@ -435,8 +435,6 @@ Validate(FreeImageIO *io, fi_handle handle) { return FALSE; } } - - return FALSE; } static BOOL DLL_CALLCONV @@ -1339,7 +1337,7 @@ Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void header.is_width = (WORD)FreeImage_GetWidth(dib); header.is_height = (WORD)FreeImage_GetHeight(dib); header.is_pixel_depth = (BYTE)bpp; - header.is_image_descriptor = 0; + header.is_image_descriptor = (bpp == 32 ? 8 : 0); if (palette) { header.color_map_type = 1; diff --git a/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp b/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp index 4e502c56e8..1b454531c1 100644 --- a/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp +++ b/plugins/AdvaImg/src/FreeImage/PluginTIFF.cpp @@ -44,29 +44,22 @@ #include "FreeImageIO.h" #include "PSDParser.h" -// ---------------------------------------------------------- -// geotiff interface (see XTIFF.cpp) -// ---------------------------------------------------------- - -// Extended TIFF Directory GEO Tag Support +// -------------------------------------------------------------------------- +// GeoTIFF profile (see XTIFF.cpp) +// -------------------------------------------------------------------------- void XTIFFInitialize(); +BOOL tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib); +BOOL tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib); -// GeoTIFF profile -void tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib); -void tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib); - -// ---------------------------------------------------------- -// exif interface (see XTIFF.cpp) +// -------------------------------------------------------------------------- +// TIFF Exif profile (see XTIFF.cpp) // ---------------------------------------------------------- - -// TIFF Exif profile BOOL tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib); BOOL tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib); -// ---------------------------------------------------------- +// -------------------------------------------------------------------------- // LogLuv conversion functions interface (see TIFFLogLuv.cpp) -// ---------------------------------------------------------- - +// -------------------------------------------------------------------------- void tiff_ConvertLineXYZToRGB(BYTE *target, BYTE *source, double stonits, int width_in_pixels); void tiff_ConvertLineRGBToXYZ(BYTE *target, BYTE *source, int width_in_pixels); @@ -141,13 +134,13 @@ typedef struct { static tmsize_t _tiffReadProc(thandle_t handle, void *buf, tmsize_t size) { fi_TIFFIO *fio = (fi_TIFFIO*)handle; - return fio->io->read_proc(buf, size, 1, fio->handle) * size; + return fio->io->read_proc(buf, (unsigned)size, 1, fio->handle) * size; } static tmsize_t _tiffWriteProc(thandle_t handle, void *buf, tmsize_t size) { fi_TIFFIO *fio = (fi_TIFFIO*)handle; - return fio->io->write_proc(buf, size, 1, fio->handle) * size; + return fio->io->write_proc(buf, (unsigned)size, 1, fio->handle) * size; } static toff_t @@ -192,21 +185,12 @@ Open a TIFF file descriptor for reading or writing TIFF * TIFFFdOpen(thandle_t handle, const char *name, const char *mode) { TIFF *tif; - // Open the file; the callback will set everything up tif = TIFFClientOpen(name, mode, handle, _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc, _tiffSizeProc, _tiffMapProc, _tiffUnmapProc); - // Warning: tif_fd is declared as 'int' currently (see libTIFF), - // may result in incorrect file pointers inside libTIFF on - // 64bit machines (sizeof(int) != sizeof(long)). - // Needs to be fixed within libTIFF. - if (tif) { - tif->tif_fd = (long)handle; - } - return tif; } @@ -933,7 +917,6 @@ tiff_write_xmp_profile(TIFF *tiff, FIBITMAP *dib) { static BOOL tiff_write_exif_profile(TIFF *tiff, FIBITMAP *dib) { BOOL bResult = FALSE; - uint32 exif_offset = 0; // write EXIF_MAIN tags, EXIF_EXIF not supported yet bResult = tiff_write_exif_tags(tiff, TagLib::EXIF_MAIN, dib); @@ -1062,6 +1045,8 @@ Open(FreeImageIO *io, fi_handle handle, BOOL read) { if (read) { fio->tif = TIFFFdOpen((thandle_t)fio, "", "r"); } else { + // mode = "w" : write Classic TIFF + // mode = "w8" : write Big TIFF fio->tif = TIFFFdOpen((thandle_t)fio, "", "w"); } if(fio->tif == NULL) { @@ -1106,11 +1091,10 @@ PageCount(FreeImageIO *io, fi_handle handle, void *data) { check for uncommon bitspersample values (e.g. 10, 12, ...) @param photometric TIFFTAG_PHOTOMETRIC tiff tag @param bitspersample TIFFTAG_BITSPERSAMPLE tiff tag -@param samplesperpixel TIFFTAG_SAMPLESPERPIXEL tiff tag @return Returns FALSE if a uncommon bit-depth is encountered, returns TRUE otherwise */ static BOOL -IsValidBitsPerSample(uint16 photometric, uint16 bitspersample, uint16 samplesperpixel) { +IsValidBitsPerSample(uint16 photometric, uint16 bitspersample) { switch(bitspersample) { case 1: @@ -1131,7 +1115,12 @@ IsValidBitsPerSample(uint16 photometric, uint16 bitspersample, uint16 samplesper } break; case 32: - return TRUE; + if((photometric == PHOTOMETRIC_MINISWHITE) || (photometric == PHOTOMETRIC_MINISBLACK) || (photometric == PHOTOMETRIC_LOGLUV)) { + return TRUE; + } else { + return FALSE; + } + break; case 64: case 128: if(photometric == PHOTOMETRIC_MINISBLACK) { @@ -1376,7 +1365,7 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // check for unsupported formats // --------------------------------------------------------------------------------- - if(IsValidBitsPerSample(photometric, bitspersample, samplesperpixel) == FALSE) { + if(IsValidBitsPerSample(photometric, bitspersample) == FALSE) { FreeImage_OutputMessageProc(s_format_id, "Unable to handle this format: bitspersample = %d, samplesperpixel = %d, photometric = %d", (int)bitspersample, (int)samplesperpixel, (int)photometric); @@ -2029,8 +2018,8 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { // calculate src line and dst pitch int dst_pitch = FreeImage_GetPitch(dib); - int tileRowSize = TIFFTileRowSize(tif); - int imageRowSize = TIFFScanlineSize(tif); + uint32 tileRowSize = (uint32)TIFFTileRowSize(tif); + uint32 imageRowSize = (uint32)TIFFScanlineSize(tif); // In the tiff file the lines are saved from up to down @@ -2038,11 +2027,10 @@ Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { BYTE *bits = FreeImage_GetScanLine(dib, height - 1); - uint32 x, y, rowSize; - for (y = 0; y < height; y += tileHeight) { + for (uint32 y = 0; y < height; y += tileHeight) { int32 nrows = (y + tileHeight > height ? height - y : tileHeight); - for (x = 0, rowSize = 0; x < width; x += tileWidth, rowSize += tileRowSize) { + for (uint32 x = 0, rowSize = 0; x < width; x += tileWidth, rowSize += tileRowSize) { memset(tileBuffer, 0, tileSize); // read one tile diff --git a/plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp b/plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp index 041eae368b..66d37066bf 100644 --- a/plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp +++ b/plugins/AdvaImg/src/FreeImage/WuQuantizer.cpp @@ -108,22 +108,43 @@ WuQuantizer::Hist3D(LONG *vwt, LONG *vmr, LONG *vmg, LONG *vmb, float *m2, int R for(i = 0; i < 256; i++) table[i] = i * i; - for(y = 0; y < height; y++) { - BYTE *bits = FreeImage_GetScanLine(m_dib, y); - - for(x = 0; x < width; x++) { - inr = (bits[FI_RGBA_RED] >> 3) + 1; - ing = (bits[FI_RGBA_GREEN] >> 3) + 1; - inb = (bits[FI_RGBA_BLUE] >> 3) + 1; - ind = INDEX(inr, ing, inb); - Qadd[y*width + x] = (WORD)ind; - // [inr][ing][inb] - vwt[ind]++; - vmr[ind] += bits[FI_RGBA_RED]; - vmg[ind] += bits[FI_RGBA_GREEN]; - vmb[ind] += bits[FI_RGBA_BLUE]; - m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]); - bits += 3; + if (FreeImage_GetBPP(m_dib) == 24) { + for(y = 0; y < height; y++) { + BYTE *bits = FreeImage_GetScanLine(m_dib, y); + + for(x = 0; x < width; x++) { + inr = (bits[FI_RGBA_RED] >> 3) + 1; + ing = (bits[FI_RGBA_GREEN] >> 3) + 1; + inb = (bits[FI_RGBA_BLUE] >> 3) + 1; + ind = INDEX(inr, ing, inb); + Qadd[y*width + x] = (WORD)ind; + // [inr][ing][inb] + vwt[ind]++; + vmr[ind] += bits[FI_RGBA_RED]; + vmg[ind] += bits[FI_RGBA_GREEN]; + vmb[ind] += bits[FI_RGBA_BLUE]; + m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]); + bits += 3; + } + } + } else { + for(y = 0; y < height; y++) { + BYTE *bits = FreeImage_GetScanLine(m_dib, y); + + for(x = 0; x < width; x++) { + inr = (bits[FI_RGBA_RED] >> 3) + 1; + ing = (bits[FI_RGBA_GREEN] >> 3) + 1; + inb = (bits[FI_RGBA_BLUE] >> 3) + 1; + ind = INDEX(inr, ing, inb); + Qadd[y*width + x] = (WORD)ind; + // [inr][ing][inb] + vwt[ind]++; + vmr[ind] += bits[FI_RGBA_RED]; + vmg[ind] += bits[FI_RGBA_GREEN]; + vmb[ind] += bits[FI_RGBA_BLUE]; + m2[ind] += (float)(table[bits[FI_RGBA_RED]] + table[bits[FI_RGBA_GREEN]] + table[bits[FI_RGBA_BLUE]]); + bits += 4; + } } } diff --git a/plugins/AdvaImg/src/FreeImageIO.h b/plugins/AdvaImg/src/FreeImageIO.h index b251d474f6..c846b5bf0a 100644 --- a/plugins/AdvaImg/src/FreeImageIO.h +++ b/plugins/AdvaImg/src/FreeImageIO.h @@ -29,16 +29,31 @@ // ---------------------------------------------------------- FI_STRUCT (FIMEMORYHEADER) { - /// remember to delete the buffer + /** + Flag used to remember to delete the 'data' buffer. + When the buffer is a wrapped buffer, it is read-only, no need to delete it. + When the buffer is a read/write buffer, it is allocated dynamically and must be deleted when no longer needed. + */ BOOL delete_me; - /// file length - long filelen; - /// buffer size - long datalen; - /// current position - long curpos; - /// start buffer address + /** + file_length is equal to the input buffer size when the buffer is a wrapped buffer, i.e. file_length == data_length. + file_length is the amount of the written bytes when the buffer is a read/write buffer. + */ + long file_length; + /** + When using read-only input buffers, data_length is equal to the input buffer size, i.e. the file_length. + When using read/write buffers, data_length is the size of the allocated buffer, + whose size is greater than or equal to file_length. + */ + long data_length; + /** + start buffer address + */ void *data; + /** + Current position into the memory stream + */ + long current_position; }; void SetDefaultIO(FreeImageIO *io); diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp index 08cdd4473b..06b31aa332 100644 --- a/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp +++ b/plugins/AdvaImg/src/FreeImageToolkit/Background.cpp @@ -215,14 +215,13 @@ static BOOL FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) { if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) { - return FALSE; + return FALSE;; } if (!color) { return FALSE; } - RGBQUAD blend; const RGBQUAD *color_intl = color; unsigned bpp = FreeImage_GetBPP(dib); unsigned width = FreeImage_GetWidth(dib); @@ -251,7 +250,7 @@ FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) { // release, just assume to have an unicolor background and fill // all with an 'alpha-blended' color. if (color->rgbReserved < 255) { - + // If we will draw on an unicolor background, it's // faster to draw opaque with an alpha blended color. // So, first get the color from the first pixel in the @@ -259,24 +258,25 @@ FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) { RGBQUAD bgcolor; if (bpp == 8) { bgcolor = FreeImage_GetPalette(dib)[*src_bits]; - } else { + } else { bgcolor.rgbBlue = src_bits[FI_RGBA_BLUE]; bgcolor.rgbGreen = src_bits[FI_RGBA_GREEN]; bgcolor.rgbRed = src_bits[FI_RGBA_RED]; bgcolor.rgbReserved = 0xFF; } + RGBQUAD blend; GetAlphaBlendedColor(&bgcolor, color_intl, &blend); color_intl = &blend; } } - + int index = (bpp <= 8) ? GetPaletteIndex(dib, color_intl, options, &color_type) : 0; if (index == -1) { // No palette index found for a palletized // image. This should never happen... return FALSE; } - + // first, build the first scanline (line 0) switch (bpp) { case 1: { diff --git a/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp b/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp index e4b8155739..d05a5dfdc8 100644 --- a/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp +++ b/plugins/AdvaImg/src/FreeImageToolkit/CopyPaste.cpp @@ -6,6 +6,7 @@ // - Hervé Drolon (drolon@infonie.fr) // - Manfred Tausch (manfred.tausch@t-online.de) // - Riley McNiff (rmcniff@marexgroup.com) +// - Carsten Klein (cklein05@users.sourceforge.net) // // This file is part of FreeImage 3 // @@ -92,7 +93,6 @@ Combine1(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned static BOOL Combine4(FIBITMAP *dst_dib, FIBITMAP *src_dib, unsigned x, unsigned y, unsigned alpha) { - int swapTable[16]; BOOL bOddStart, bOddEnd; @@ -744,4 +744,118 @@ FreeImage_Paste(FIBITMAP *dst, FIBITMAP *src, int left, int top, int alpha) { return bResult; } +// ---------------------------------------------------------- + +/** @brief Creates a dynamic read/write view into a FreeImage bitmap. + + A dynamic view is a FreeImage bitmap with its own width and height, that, + however, shares its bits with another FreeImage bitmap. Typically, views + are used to define one or more rectangular sub-images of an existing + bitmap. All FreeImage operations, like saving, displaying and all the + toolkit functions, when applied to the view, only affect the view's + rectangular area. + + Although the view's backing image's bits not need to be copied around, + which makes the view much faster than similar solutions using + FreeImage_Copy, a view uses some private memory that needs to be freed by + calling FreeImage_Unload on the view's handle to prevent memory leaks. + + Only the backing image's pixels are shared by the view. For all other image + data, notably for the resolution, background color, color palette, + transparency table and for the ICC profile, the view gets a private copy + of the data. By default, the backing image's metadata is NOT copied to + the view. + + As with all FreeImage functions that take a rectangle region, top and left + positions are included, whereas right and bottom positions are excluded + from the rectangle area. + + Since the memory block shared by the backing image and the view must start + at a byte boundary, the value of parameter left must be a multiple of 8 + for 1-bit images and a multiple of 2 for 4-bit images. + + @param dib The FreeImage bitmap on which to create the view. + @param left The left position of the view's area. + @param top The top position of the view's area. + @param right The right position of the view's area. + @param bottom The bottom position of the view's area. + @return Returns a handle to the newly created view or NULL if the view + was not created. + */ +FIBITMAP * DLL_CALLCONV +FreeImage_CreateView(FIBITMAP *dib, unsigned left, unsigned top, unsigned right, unsigned bottom) { + if (!FreeImage_HasPixels(dib)) { + return NULL; + } + + // normalize the rectangle + if (right < left) { + INPLACESWAP(left, right); + } + if (bottom < top) { + INPLACESWAP(top, bottom); + } + + // check the size of the sub image + unsigned width = FreeImage_GetWidth(dib); + unsigned height = FreeImage_GetHeight(dib); + if (left < 0 || right > width || top < 0 || bottom > height) { + return NULL; + } + + unsigned bpp = FreeImage_GetBPP(dib); + BYTE *bits = FreeImage_GetScanLine(dib, height - bottom); + switch (bpp) { + case 1: + if (left % 8 != 0) { + // view can only start at a byte boundary + return NULL; + } + bits += (left / 8); + break; + case 4: + if (left % 2 != 0) { + // view can only start at a byte boundary + return NULL; + } + bits += (left / 2); + break; + default: + bits += left * (bpp / 8); + break; + } + + FIBITMAP *dst = FreeImage_AllocateHeaderForBits(bits, FreeImage_GetPitch(dib), FreeImage_GetImageType(dib), + right - left, bottom - top, + bpp, + FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib)); + + if (dst == NULL) { + return NULL; + } + // copy some basic image properties needed for displaying and saving + + // resolution + FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(dib)); + FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(dib)); + + // background color + RGBQUAD bkcolor; + if (FreeImage_GetBackgroundColor(dib, &bkcolor)) { + FreeImage_SetBackgroundColor(dst, &bkcolor); + } + + // palette + memcpy(FreeImage_GetPalette(dst), FreeImage_GetPalette(dib), FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD)); + + // transparency table + FreeImage_SetTransparencyTable(dst, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); + + // ICC profile + FIICCPROFILE *src_profile = FreeImage_GetICCProfile(dib); + FIICCPROFILE *dst_profile = FreeImage_CreateICCProfile(dst, src_profile->data, src_profile->size); + dst_profile->flags = src_profile->flags; + + return dst; +} diff --git a/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp b/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp index 132fef7e85..6f9ba8e1f2 100644 --- a/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp +++ b/plugins/AdvaImg/src/FreeImageToolkit/JPEGTransform.cpp @@ -1,623 +1,623 @@ -// ==========================================================
-// JPEG lossless transformations
-//
-// Design and implementation by
-// - Petr Pytelka (pyta@lightcomp.com)
-// - Hervé Drolon (drolon@infonie.fr)
+// ========================================================== +// JPEG lossless transformations +// +// Design and implementation by +// - Petr Pytelka (pyta@lightcomp.com) +// - 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!
-// ==========================================================
-
-extern "C" {
-#define XMD_H
-#undef FAR
-#include <setjmp.h>
-
-#include "../LibJPEG/jinclude.h"
-#include "../LibJPEG/jpeglib.h"
-#include "../LibJPEG/jerror.h"
-#include "../LibJPEG/transupp.h"
-}
-
-#include "FreeImage.h"
-#include "Utilities.h"
-#include "FreeImageIO.h"
-
+// +// 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! +// ========================================================== + +extern "C" { +#define XMD_H +#undef FAR +#include <setjmp.h> + +#include "../LibJPEG/jinclude.h" +#include "../LibJPEG/jpeglib.h" +#include "../LibJPEG/jerror.h" +#include "../LibJPEG/transupp.h" +} + +#include "FreeImage.h" +#include "Utilities.h" +#include "FreeImageIO.h" + // ---------------------------------------------------------- // Source manager & Destination manager setup // (see PluginJPEG.cpp) // ---------------------------------------------------------- -
+ void jpeg_freeimage_src(j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io); void jpeg_freeimage_dst(j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io); -
-// ----------------------------------------------------------
-// Error handling
+ +// ---------------------------------------------------------- +// Error handling // (see also PluginJPEG.cpp) -// ----------------------------------------------------------
-
-/**
- Receives control for a fatal error. Information sufficient to
- generate the error message has been stored in cinfo->err; call
- output_message to display it. Control must NOT return to the caller;
- generally this routine will exit() or longjmp() somewhere.
-*/
-METHODDEF(void)
-ls_jpeg_error_exit (j_common_ptr cinfo) {
- // always display the message
- (*cinfo->err->output_message)(cinfo);
-
- // allow JPEG with a premature end of file
- if((cinfo)->err->msg_parm.i[0] != 13) {
-
- // let the memory manager delete any temp files before we die
- jpeg_destroy(cinfo);
-
- throw FIF_JPEG;
- }
-}
-
-/**
- Actual output of any JPEG message. Note that this method does not know
- how to generate a message, only where to send it.
-*/
-METHODDEF(void)
-ls_jpeg_output_message (j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
-
- // create the message
- (*cinfo->err->format_message)(cinfo, buffer);
- // send it to user's message proc
- FreeImage_OutputMessageProc(FIF_JPEG, buffer);
-}
-
-// ----------------------------------------------------------
-// Main program
-// ----------------------------------------------------------
-
-/**
-Build a crop string.
-
-@param crop Output crop string
-@param left Specifies the left position of the cropped rectangle
-@param top Specifies the top position of the cropped rectangle
-@param right Specifies the right position of the cropped rectangle
-@param bottom Specifies the bottom position of the cropped rectangle
-@param width Image width
-@param height Image height
-@return Returns TRUE if successful, returns FALSE otherwise
-*/
-static BOOL
-getCropString(char* crop, int* left, int* top, int* right, int* bottom, int width, int height) {
- if(!left || !top || !right || !bottom) {
- return FALSE;
- }
-
- *left = CLAMP(*left, 0, width);
- *top = CLAMP(*top, 0, height);
-
- // negative/zero right and bottom count from the edges inwards
-
- if(*right <= 0) {
- *right = width + *right;
- }
- if(*bottom <= 0) {
- *bottom = height + *bottom;
- }
-
- *right = CLAMP(*right, 0, width);
- *bottom = CLAMP(*bottom, 0, height);
-
- // test for empty rect
-
- if(((*left - *right) == 0) || ((*top - *bottom) == 0)) {
- return FALSE;
- }
-
- // normalize the rectangle
-
- if(*right < *left) {
- INPLACESWAP(*left, *right);
- }
- if(*bottom < *top) {
- INPLACESWAP(*top, *bottom);
- }
-
- // test for "noop" rect
-
- if(*left == 0 && *right == width && *top == 0 && *bottom == height) {
- return FALSE;
- }
-
- // build the crop option
- sprintf(crop, "%dx%d+%d+%d", *right - *left, *bottom - *top, *left, *top);
-
- return TRUE;
-}
-
-static BOOL
-JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL);
- const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle);
- BOOL swappedDim = FALSE;
- BOOL trimH = FALSE;
- BOOL trimV = FALSE;
-
- // Set up the jpeglib structures
- jpeg_decompress_struct srcinfo;
- jpeg_compress_struct dstinfo;
- jpeg_error_mgr jsrcerr, jdsterr;
- jvirt_barray_ptr *src_coef_arrays = NULL;
- jvirt_barray_ptr *dst_coef_arrays = NULL;
- // Support for copying optional markers from source to destination file
- JCOPY_OPTION copyoption;
- // Image transformation options
- jpeg_transform_info transfoptions;
-
- // Initialize structures
- memset(&srcinfo, 0, sizeof(srcinfo));
- memset(&jsrcerr, 0, sizeof(jsrcerr));
- memset(&jdsterr, 0, sizeof(jdsterr));
- memset(&dstinfo, 0, sizeof(dstinfo));
- memset(&transfoptions, 0, sizeof(transfoptions));
-
- // Copy all extra markers from source file
- copyoption = JCOPYOPT_ALL;
-
- // Set up default JPEG parameters
- transfoptions.force_grayscale = FALSE;
- transfoptions.crop = FALSE;
-
- // Select the transform option
- switch(operation) {
- case FIJPEG_OP_FLIP_H: // horizontal flip
- transfoptions.transform = JXFORM_FLIP_H;
- trimH = TRUE;
- break;
- case FIJPEG_OP_FLIP_V: // vertical flip
- transfoptions.transform = JXFORM_FLIP_V;
- trimV = TRUE;
- break;
- case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis
- transfoptions.transform = JXFORM_TRANSPOSE;
- swappedDim = TRUE;
- break;
- case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis
- transfoptions.transform = JXFORM_TRANSVERSE;
- trimH = TRUE;
- trimV = TRUE;
- swappedDim = TRUE;
- break;
- case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation
- transfoptions.transform = JXFORM_ROT_90;
- trimH = TRUE;
- swappedDim = TRUE;
- break;
- case FIJPEG_OP_ROTATE_180: // 180-degree rotation
- trimH = TRUE;
- trimV = TRUE;
- transfoptions.transform = JXFORM_ROT_180;
- break;
- case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw)
- transfoptions.transform = JXFORM_ROT_270;
- trimV = TRUE;
- swappedDim = TRUE;
- break;
- default:
- case FIJPEG_OP_NONE: // no transformation
- transfoptions.transform = JXFORM_NONE;
- break;
- }
- // (perfect == TRUE) ==> fail if there is non-transformable edge blocks
- transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE;
- // Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle.
- transfoptions.trim = TRUE;
-
- try {
-
- // Initialize the JPEG decompression object with default error handling
- srcinfo.err = jpeg_std_error(&jsrcerr);
- srcinfo.err->error_exit = ls_jpeg_error_exit;
- srcinfo.err->output_message = ls_jpeg_output_message;
- jpeg_create_decompress(&srcinfo);
-
- // Initialize the JPEG compression object with default error handling
- dstinfo.err = jpeg_std_error(&jdsterr);
- dstinfo.err->error_exit = ls_jpeg_error_exit;
- dstinfo.err->output_message = ls_jpeg_output_message;
- jpeg_create_compress(&dstinfo);
-
- // Specify data source for decompression
- jpeg_freeimage_src(&srcinfo, src_handle, src_io);
-
- // Enable saving of extra markers that we want to copy
- jcopy_markers_setup(&srcinfo, copyoption);
-
- // Read the file header
- jpeg_read_header(&srcinfo, TRUE);
-
- // crop option
- char crop[64];
- const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height);
-
- if(hasCrop) {
- if(!jtransform_parse_crop_spec(&transfoptions, crop)) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop);
- throw(1);
- }
- }
-
- // Any space needed by a transform option must be requested before
- // jpeg_read_coefficients so that memory allocation will be done right
-
- // Prepare transformation workspace
- // Fails right away if perfect flag is TRUE and transformation is not perfect
- if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect");
- throw(1);
- }
-
- if(left || top) {
- // compute left and top offsets, it's a bit tricky, taking into account both
- // transform, which might have trimed the image,
- // and crop itself, which is adjusted to lie on a iMCU boundary
-
- const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width;
- const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height;
-
- int transformedFullWidth = fullWidth;
- int transformedFullHeight = fullHeight;
-
- if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) {
- transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width;
- }
- if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) {
- transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height;
- }
-
- const int trimmedWidth = fullWidth - transformedFullWidth;
- const int trimmedHeight = fullHeight - transformedFullHeight;
-
- if(left) {
- *left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width;
- }
- if(top) {
- *top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height;
- }
- }
-
- if(right) {
- *right = (left ? *left : 0) + transfoptions.output_width;
- }
- if(bottom) {
- *bottom = (top ? *top : 0) + transfoptions.output_height;
- }
-
- // if only the crop rect is requested, we are done
-
- if(onlyReturnCropRect) {
- jpeg_destroy_compress(&dstinfo);
- jpeg_destroy_decompress(&srcinfo);
- return TRUE;
- }
-
- // Read source file as DCT coefficients
- src_coef_arrays = jpeg_read_coefficients(&srcinfo);
-
- // Initialize destination compression parameters from source values
- jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
-
- // Adjust destination parameters if required by transform options;
- // also find out which set of coefficient arrays will hold the output
- dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
-
- // Note: we assume that jpeg_read_coefficients consumed all input
- // until JPEG_REACHED_EOI, and that jpeg_finish_decompress will
- // only consume more while (! cinfo->inputctl->eoi_reached).
- // We cannot call jpeg_finish_decompress here since we still need the
- // virtual arrays allocated from the source object for processing.
-
- if(src_handle == dst_handle) {
- dst_io->seek_proc(dst_handle, stream_start, SEEK_SET);
- }
-
- // Specify data destination for compression
- jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io);
-
- // Start compressor (note no image data is actually written here)
- jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
-
- // Copy to the output file any extra markers that we want to preserve
- jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
-
- // Execute image transformation, if any
- jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions);
-
- // Finish compression and release memory
- jpeg_finish_compress(&dstinfo);
- jpeg_destroy_compress(&dstinfo);
- jpeg_finish_decompress(&srcinfo);
- jpeg_destroy_decompress(&srcinfo);
-
- }
- catch(...) {
- jpeg_destroy_compress(&dstinfo);
- jpeg_destroy_decompress(&srcinfo);
- return FALSE;
- }
-
- return TRUE;
-}
-
-// ----------------------------------------------------------
-// FreeImage interface
-// ----------------------------------------------------------
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- return JPEGTransformFromHandle(src_io, src_handle, dst_io, dst_handle, operation, left, top, right, bottom, perfect);
-}
-
-static void
-closeStdIO(fi_handle src_handle, fi_handle dst_handle) {
- if(src_handle) {
- fclose((FILE*)src_handle);
- }
- if(dst_handle) {
- fclose((FILE*)dst_handle);
- }
-}
-
-static BOOL
-openStdIO(const char* src_file, const char* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
- *src_handle = NULL;
- *dst_handle = NULL;
-
- FreeImageIO io;
- SetDefaultIO (&io);
-
- const BOOL isSameFile = (dst_file && (strcmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
-
- FILE* srcp = NULL;
- FILE* dstp = NULL;
-
- if(isSameFile) {
- srcp = fopen(src_file, "r+b");
- dstp = srcp;
- }
- else {
- srcp = fopen(src_file, "rb");
- if(dst_file) {
- dstp = fopen(dst_file, "wb");
- }
- }
-
- if(!srcp || (dst_file && !dstp)) {
- if(!srcp) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for reading", src_file);
- } else {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for writing", dst_file);
- }
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
- FreeImage_OutputMessageProc(FIF_JPEG, " Source file \"%s\" is not jpeg", src_file);
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- *dst_io = io;
- *src_handle = srcp;
- *dst_handle = dstp;
-
- return TRUE;
-}
-
-static BOOL
-openStdIOU(const wchar_t* src_file, const wchar_t* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
-#ifdef _WIN32
-
- *src_handle = NULL;
- *dst_handle = NULL;
-
- FreeImageIO io;
- SetDefaultIO (&io);
-
- const BOOL isSameFile = (dst_file && (wcscmp(src_file, dst_file) == 0)) ? TRUE : FALSE;
-
- FILE* srcp = NULL;
- FILE* dstp = NULL;
-
- if(isSameFile) {
- srcp = _wfopen(src_file, L"r+b");
- dstp = srcp;
- } else {
- srcp = _wfopen(src_file, L"rb");
- if(dst_file) {
- dstp = _wfopen(dst_file, L"wb");
- }
- }
-
- if(!srcp || (dst_file && !dstp)) {
- if(!srcp) {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open source file for reading");
- } else {
- FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open destination file for writing");
- }
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) {
- FreeImage_OutputMessageProc(FIF_JPEG, " Source file is not jpeg");
- closeStdIO(srcp, dstp);
- return FALSE;
- }
-
- *dst_io = io;
- *src_handle = srcp;
- *dst_handle = dstp;
-
- return TRUE;
-
-#else
- return FALSE;
-#endif // _WIN32
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIO(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) {
- return FALSE;
- }
-
- BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
-
- closeStdIO(src, dst);
-
- return ret;
-}
-
-// --------------------------------------------------------------------------
-
-static BOOL
-getMemIO(FIMEMORY* src_stream, FIMEMORY* dst_stream, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) {
- *src_handle = NULL;
- *dst_handle = NULL;
-
- FreeImageIO io;
- SetMemoryIO (&io);
-
- if(dst_stream) {
- FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(dst_stream->data);
- if(mem_header->delete_me != TRUE) {
- // do not save in a user buffer
- FreeImage_OutputMessageProc(FIF_JPEG, "Destination memory buffer is read only");
- return FALSE;
- }
- }
-
- *dst_io = io;
- *src_handle = src_stream;
- *dst_handle = dst_stream;
-
- return TRUE;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) {
- FreeImageIO io;
- fi_handle src;
- fi_handle dst;
-
- if(!getMemIO(src_stream, dst_stream, &io, &src, &dst)) {
- return FALSE;
- }
-
- return FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect);
-}
-
+// ---------------------------------------------------------- + +/** + Receives control for a fatal error. Information sufficient to + generate the error message has been stored in cinfo->err; call + output_message to display it. Control must NOT return to the caller; + generally this routine will exit() or longjmp() somewhere. +*/ +METHODDEF(void) +ls_jpeg_error_exit (j_common_ptr cinfo) { + // always display the message + (*cinfo->err->output_message)(cinfo); + + // allow JPEG with a premature end of file + if((cinfo)->err->msg_parm.i[0] != 13) { + + // let the memory manager delete any temp files before we die + jpeg_destroy(cinfo); + + throw FIF_JPEG; + } +} + +/** + Actual output of any JPEG message. Note that this method does not know + how to generate a message, only where to send it. +*/ +METHODDEF(void) +ls_jpeg_output_message (j_common_ptr cinfo) { + char buffer[JMSG_LENGTH_MAX]; + + // create the message + (*cinfo->err->format_message)(cinfo, buffer); + // send it to user's message proc + FreeImage_OutputMessageProc(FIF_JPEG, buffer); +} + +// ---------------------------------------------------------- +// Main program +// ---------------------------------------------------------- + +/** +Build a crop string. + +@param crop Output crop string +@param left Specifies the left position of the cropped rectangle +@param top Specifies the top position of the cropped rectangle +@param right Specifies the right position of the cropped rectangle +@param bottom Specifies the bottom position of the cropped rectangle +@param width Image width +@param height Image height +@return Returns TRUE if successful, returns FALSE otherwise +*/ +static BOOL +getCropString(char* crop, int* left, int* top, int* right, int* bottom, int width, int height) { + if(!left || !top || !right || !bottom) { + return FALSE; + } + + *left = CLAMP(*left, 0, width); + *top = CLAMP(*top, 0, height); + + // negative/zero right and bottom count from the edges inwards + + if(*right <= 0) { + *right = width + *right; + } + if(*bottom <= 0) { + *bottom = height + *bottom; + } + + *right = CLAMP(*right, 0, width); + *bottom = CLAMP(*bottom, 0, height); + + // test for empty rect + + if(((*left - *right) == 0) || ((*top - *bottom) == 0)) { + return FALSE; + } + + // normalize the rectangle + + if(*right < *left) { + INPLACESWAP(*left, *right); + } + if(*bottom < *top) { + INPLACESWAP(*top, *bottom); + } + + // test for "noop" rect + + if(*left == 0 && *right == width && *top == 0 && *bottom == height) { + return FALSE; + } + + // build the crop option + sprintf(crop, "%dx%d+%d+%d", *right - *left, *bottom - *top, *left, *top); + + return TRUE; +} + +static BOOL +JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) { + const BOOL onlyReturnCropRect = (dst_io == NULL) || (dst_handle == NULL); + const long stream_start = onlyReturnCropRect ? 0 : dst_io->tell_proc(dst_handle); + BOOL swappedDim = FALSE; + BOOL trimH = FALSE; + BOOL trimV = FALSE; + + // Set up the jpeglib structures + jpeg_decompress_struct srcinfo; + jpeg_compress_struct dstinfo; + jpeg_error_mgr jsrcerr, jdsterr; + jvirt_barray_ptr *src_coef_arrays = NULL; + jvirt_barray_ptr *dst_coef_arrays = NULL; + // Support for copying optional markers from source to destination file + JCOPY_OPTION copyoption; + // Image transformation options + jpeg_transform_info transfoptions; + + // Initialize structures + memset(&srcinfo, 0, sizeof(srcinfo)); + memset(&jsrcerr, 0, sizeof(jsrcerr)); + memset(&jdsterr, 0, sizeof(jdsterr)); + memset(&dstinfo, 0, sizeof(dstinfo)); + memset(&transfoptions, 0, sizeof(transfoptions)); + + // Copy all extra markers from source file + copyoption = JCOPYOPT_ALL; + + // Set up default JPEG parameters + transfoptions.force_grayscale = FALSE; + transfoptions.crop = FALSE; + + // Select the transform option + switch(operation) { + case FIJPEG_OP_FLIP_H: // horizontal flip + transfoptions.transform = JXFORM_FLIP_H; + trimH = TRUE; + break; + case FIJPEG_OP_FLIP_V: // vertical flip + transfoptions.transform = JXFORM_FLIP_V; + trimV = TRUE; + break; + case FIJPEG_OP_TRANSPOSE: // transpose across UL-to-LR axis + transfoptions.transform = JXFORM_TRANSPOSE; + swappedDim = TRUE; + break; + case FIJPEG_OP_TRANSVERSE: // transpose across UR-to-LL axis + transfoptions.transform = JXFORM_TRANSVERSE; + trimH = TRUE; + trimV = TRUE; + swappedDim = TRUE; + break; + case FIJPEG_OP_ROTATE_90: // 90-degree clockwise rotation + transfoptions.transform = JXFORM_ROT_90; + trimH = TRUE; + swappedDim = TRUE; + break; + case FIJPEG_OP_ROTATE_180: // 180-degree rotation + trimH = TRUE; + trimV = TRUE; + transfoptions.transform = JXFORM_ROT_180; + break; + case FIJPEG_OP_ROTATE_270: // 270-degree clockwise (or 90 ccw) + transfoptions.transform = JXFORM_ROT_270; + trimV = TRUE; + swappedDim = TRUE; + break; + default: + case FIJPEG_OP_NONE: // no transformation + transfoptions.transform = JXFORM_NONE; + break; + } + // (perfect == TRUE) ==> fail if there is non-transformable edge blocks + transfoptions.perfect = (perfect == TRUE) ? TRUE : FALSE; + // Drop non-transformable edge blocks: trim off any partial edge MCUs that the transform can't handle. + transfoptions.trim = TRUE; + + try { + + // Initialize the JPEG decompression object with default error handling + srcinfo.err = jpeg_std_error(&jsrcerr); + srcinfo.err->error_exit = ls_jpeg_error_exit; + srcinfo.err->output_message = ls_jpeg_output_message; + jpeg_create_decompress(&srcinfo); + + // Initialize the JPEG compression object with default error handling + dstinfo.err = jpeg_std_error(&jdsterr); + dstinfo.err->error_exit = ls_jpeg_error_exit; + dstinfo.err->output_message = ls_jpeg_output_message; + jpeg_create_compress(&dstinfo); + + // Specify data source for decompression + jpeg_freeimage_src(&srcinfo, src_handle, src_io); + + // Enable saving of extra markers that we want to copy + jcopy_markers_setup(&srcinfo, copyoption); + + // Read the file header + jpeg_read_header(&srcinfo, TRUE); + + // crop option + char crop[64]; + const BOOL hasCrop = getCropString(crop, left, top, right, bottom, swappedDim ? srcinfo.image_height : srcinfo.image_width, swappedDim ? srcinfo.image_width : srcinfo.image_height); + + if(hasCrop) { + if(!jtransform_parse_crop_spec(&transfoptions, crop)) { + FreeImage_OutputMessageProc(FIF_JPEG, "Bogus crop argument %s", crop); + throw(1); + } + } + + // Any space needed by a transform option must be requested before + // jpeg_read_coefficients so that memory allocation will be done right + + // Prepare transformation workspace + // Fails right away if perfect flag is TRUE and transformation is not perfect + if( !jtransform_request_workspace(&srcinfo, &transfoptions) ) { + FreeImage_OutputMessageProc(FIF_JPEG, "Transformation is not perfect"); + throw(1); + } + + if(left || top) { + // compute left and top offsets, it's a bit tricky, taking into account both + // transform, which might have trimed the image, + // and crop itself, which is adjusted to lie on a iMCU boundary + + const int fullWidth = swappedDim ? srcinfo.image_height : srcinfo.image_width; + const int fullHeight = swappedDim ? srcinfo.image_width : srcinfo.image_height; + + int transformedFullWidth = fullWidth; + int transformedFullHeight = fullHeight; + + if(trimH && transformedFullWidth/transfoptions.iMCU_sample_width > 0) { + transformedFullWidth = (transformedFullWidth/transfoptions.iMCU_sample_width) * transfoptions.iMCU_sample_width; + } + if(trimV && transformedFullHeight/transfoptions.iMCU_sample_height > 0) { + transformedFullHeight = (transformedFullHeight/transfoptions.iMCU_sample_height) * transfoptions.iMCU_sample_height; + } + + const int trimmedWidth = fullWidth - transformedFullWidth; + const int trimmedHeight = fullHeight - transformedFullHeight; + + if(left) { + *left = trimmedWidth + transfoptions.x_crop_offset * transfoptions.iMCU_sample_width; + } + if(top) { + *top = trimmedHeight + transfoptions.y_crop_offset * transfoptions.iMCU_sample_height; + } + } + + if(right) { + *right = (left ? *left : 0) + transfoptions.output_width; + } + if(bottom) { + *bottom = (top ? *top : 0) + transfoptions.output_height; + } + + // if only the crop rect is requested, we are done + + if(onlyReturnCropRect) { + jpeg_destroy_compress(&dstinfo); + jpeg_destroy_decompress(&srcinfo); + return TRUE; + } + + // Read source file as DCT coefficients + src_coef_arrays = jpeg_read_coefficients(&srcinfo); + + // Initialize destination compression parameters from source values + jpeg_copy_critical_parameters(&srcinfo, &dstinfo); + + // Adjust destination parameters if required by transform options; + // also find out which set of coefficient arrays will hold the output + dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions); + + // Note: we assume that jpeg_read_coefficients consumed all input + // until JPEG_REACHED_EOI, and that jpeg_finish_decompress will + // only consume more while (! cinfo->inputctl->eoi_reached). + // We cannot call jpeg_finish_decompress here since we still need the + // virtual arrays allocated from the source object for processing. + + if(src_handle == dst_handle) { + dst_io->seek_proc(dst_handle, stream_start, SEEK_SET); + } + + // Specify data destination for compression + jpeg_freeimage_dst(&dstinfo, dst_handle, dst_io); + + // Start compressor (note no image data is actually written here) + jpeg_write_coefficients(&dstinfo, dst_coef_arrays); + + // Copy to the output file any extra markers that we want to preserve + jcopy_markers_execute(&srcinfo, &dstinfo, copyoption); + + // Execute image transformation, if any + jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transfoptions); + + // Finish compression and release memory + jpeg_finish_compress(&dstinfo); + jpeg_destroy_compress(&dstinfo); + jpeg_finish_decompress(&srcinfo); + jpeg_destroy_decompress(&srcinfo); + + } + catch(...) { + jpeg_destroy_compress(&dstinfo); + jpeg_destroy_decompress(&srcinfo); + return FALSE; + } + + return TRUE; +} + +// ---------------------------------------------------------- +// FreeImage interface +// ---------------------------------------------------------- + +BOOL DLL_CALLCONV +FreeImage_JPEGTransformFromHandle(FreeImageIO* src_io, fi_handle src_handle, FreeImageIO* dst_io, fi_handle dst_handle, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) { + return JPEGTransformFromHandle(src_io, src_handle, dst_io, dst_handle, operation, left, top, right, bottom, perfect); +} + +static void +closeStdIO(fi_handle src_handle, fi_handle dst_handle) { + if(src_handle) { + fclose((FILE*)src_handle); + } + if(dst_handle && (dst_handle != src_handle)) { + fclose((FILE*)dst_handle); + } +} + +static BOOL +openStdIO(const char* src_file, const char* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) { + *src_handle = NULL; + *dst_handle = NULL; + + FreeImageIO io; + SetDefaultIO (&io); + + const BOOL isSameFile = (dst_file && (strcmp(src_file, dst_file) == 0)) ? TRUE : FALSE; + + FILE* srcp = NULL; + FILE* dstp = NULL; + + if(isSameFile) { + srcp = fopen(src_file, "r+b"); + dstp = srcp; + } + else { + srcp = fopen(src_file, "rb"); + if(dst_file) { + dstp = fopen(dst_file, "wb"); + } + } + + if(!srcp || (dst_file && !dstp)) { + if(!srcp) { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for reading", src_file); + } else { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open \"%s\" for writing", dst_file); + } + closeStdIO(srcp, dstp); + return FALSE; + } + + if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) { + FreeImage_OutputMessageProc(FIF_JPEG, " Source file \"%s\" is not jpeg", src_file); + closeStdIO(srcp, dstp); + return FALSE; + } + + *dst_io = io; + *src_handle = srcp; + *dst_handle = dstp; + + return TRUE; +} + +static BOOL +openStdIOU(const wchar_t* src_file, const wchar_t* dst_file, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) { +#ifdef _WIN32 + + *src_handle = NULL; + *dst_handle = NULL; + + FreeImageIO io; + SetDefaultIO (&io); + + const BOOL isSameFile = (dst_file && (wcscmp(src_file, dst_file) == 0)) ? TRUE : FALSE; + + FILE* srcp = NULL; + FILE* dstp = NULL; + + if(isSameFile) { + srcp = _wfopen(src_file, L"r+b"); + dstp = srcp; + } else { + srcp = _wfopen(src_file, L"rb"); + if(dst_file) { + dstp = _wfopen(dst_file, L"wb"); + } + } + + if(!srcp || (dst_file && !dstp)) { + if(!srcp) { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open source file for reading"); + } else { + FreeImage_OutputMessageProc(FIF_JPEG, "Cannot open destination file for writing"); + } + closeStdIO(srcp, dstp); + return FALSE; + } + + if(FreeImage_GetFileTypeFromHandle(&io, srcp) != FIF_JPEG) { + FreeImage_OutputMessageProc(FIF_JPEG, " Source file is not jpeg"); + closeStdIO(srcp, dstp); + return FALSE; + } + + *dst_io = io; + *src_handle = srcp; + *dst_handle = dstp; + + return TRUE; + +#else + return FALSE; +#endif // _WIN32 +} + +BOOL DLL_CALLCONV +FreeImage_JPEGTransform(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) { + FreeImageIO io; + fi_handle src; + fi_handle dst; + + if(!openStdIO(src_file, dst_file, &io, &src, &dst)) { + return FALSE; + } + + BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect); + + closeStdIO(src, dst); + + return ret; +} + +BOOL DLL_CALLCONV +FreeImage_JPEGCrop(const char *src_file, const char *dst_file, int left, int top, int right, int bottom) { + FreeImageIO io; + fi_handle src; + fi_handle dst; + + if(!openStdIO(src_file, dst_file, &io, &src, &dst)) { + return FALSE; + } + + BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE); + + closeStdIO(src, dst); + + return ret; +} + +BOOL DLL_CALLCONV +FreeImage_JPEGTransformU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, BOOL perfect) { + FreeImageIO io; + fi_handle src; + fi_handle dst; + + if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) { + return FALSE; + } + + BOOL ret = JPEGTransformFromHandle(&io, src, &io, dst, operation, NULL, NULL, NULL, NULL, perfect); + + closeStdIO(src, dst); + + return ret; +} + +BOOL DLL_CALLCONV +FreeImage_JPEGCropU(const wchar_t *src_file, const wchar_t *dst_file, int left, int top, int right, int bottom) { + FreeImageIO io; + fi_handle src; + fi_handle dst; + + if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) { + return FALSE; + } + + BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, FIJPEG_OP_NONE, &left, &top, &right, &bottom, FALSE); + + closeStdIO(src, dst); + + return ret; +} + +BOOL DLL_CALLCONV +FreeImage_JPEGTransformCombined(const char *src_file, const char *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) { + FreeImageIO io; + fi_handle src; + fi_handle dst; + + if(!openStdIO(src_file, dst_file, &io, &src, &dst)) { + return FALSE; + } + + BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect); + + closeStdIO(src, dst); + + return ret; +} + +BOOL DLL_CALLCONV +FreeImage_JPEGTransformCombinedU(const wchar_t *src_file, const wchar_t *dst_file, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) { + FreeImageIO io; + fi_handle src; + fi_handle dst; + + if(!openStdIOU(src_file, dst_file, &io, &src, &dst)) { + return FALSE; + } + + BOOL ret = FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect); + + closeStdIO(src, dst); + + return ret; +} + +// -------------------------------------------------------------------------- + +static BOOL +getMemIO(FIMEMORY* src_stream, FIMEMORY* dst_stream, FreeImageIO* dst_io, fi_handle* src_handle, fi_handle* dst_handle) { + *src_handle = NULL; + *dst_handle = NULL; + + FreeImageIO io; + SetMemoryIO (&io); + + if(dst_stream) { + FIMEMORYHEADER *mem_header = (FIMEMORYHEADER*)(dst_stream->data); + if(mem_header->delete_me != TRUE) { + // do not save in a user buffer + FreeImage_OutputMessageProc(FIF_JPEG, "Destination memory buffer is read only"); + return FALSE; + } + } + + *dst_io = io; + *src_handle = src_stream; + *dst_handle = dst_stream; + + return TRUE; +} + +BOOL DLL_CALLCONV +FreeImage_JPEGTransformCombinedFromMemory(FIMEMORY* src_stream, FIMEMORY* dst_stream, FREE_IMAGE_JPEG_OPERATION operation, int* left, int* top, int* right, int* bottom, BOOL perfect) { + FreeImageIO io; + fi_handle src; + fi_handle dst; + + if(!getMemIO(src_stream, dst_stream, &io, &src, &dst)) { + return FALSE; + } + + return FreeImage_JPEGTransformFromHandle(&io, src, &io, dst, operation, left, top, right, bottom, perfect); +} + diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp index 0c8bbc2787..4f885c29a5 100644 --- a/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp +++ b/plugins/AdvaImg/src/FreeImageToolkit/Rescale.cpp @@ -22,11 +22,27 @@ #include "Resize.h" -FIBITMAP * DLL_CALLCONV -FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) { +FIBITMAP * DLL_CALLCONV +FreeImage_RescaleRect(FIBITMAP *src, int dst_width, int dst_height, int src_left, int src_top, int src_right, int src_bottom, FREE_IMAGE_FILTER filter, unsigned flags) { FIBITMAP *dst = NULL; - if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (FreeImage_GetWidth(src) <= 0) || (FreeImage_GetHeight(src) <= 0)) { + const int src_width = FreeImage_GetWidth(src); + const int src_height = FreeImage_GetHeight(src); + + if (!FreeImage_HasPixels(src) || (dst_width <= 0) || (dst_height <= 0) || (src_width <= 0) || (src_height <= 0)) { + return NULL; + } + + // normalize the rectangle + if (src_right < src_left) { + INPLACESWAP(src_left, src_right); + } + if (src_bottom < src_top) { + INPLACESWAP(src_top, src_bottom); + } + + // check the size of the sub image + if((src_left < 0) || (src_right > src_width) || (src_top < 0) || (src_bottom > src_height)) { return NULL; } @@ -59,18 +75,25 @@ FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTE CResizeEngine Engine(pFilter); - dst = Engine.scale(src, dst_width, dst_height, 0, 0, - FreeImage_GetWidth(src), FreeImage_GetHeight(src)); + dst = Engine.scale(src, dst_width, dst_height, src_left, src_top, + src_right - src_left, src_bottom - src_top, flags); delete pFilter; - // copy metadata from src to dst - FreeImage_CloneMetadata(dst, src); - + if ((flags & FI_RESCALE_OMIT_METADATA) != FI_RESCALE_OMIT_METADATA) { + // copy metadata from src to dst + FreeImage_CloneMetadata(dst, src); + } + return dst; } FIBITMAP * DLL_CALLCONV +FreeImage_Rescale(FIBITMAP *src, int dst_width, int dst_height, FREE_IMAGE_FILTER filter) { + return FreeImage_RescaleRect(src, dst_width, dst_height, 0, 0, FreeImage_GetWidth(src), FreeImage_GetHeight(src), filter, FI_RESCALE_DEFAULT); +} + +FIBITMAP * DLL_CALLCONV FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert) { FIBITMAP *thumbnail = NULL; int new_width, new_height; @@ -164,6 +187,6 @@ FreeImage_MakeThumbnail(FIBITMAP *dib, int max_pixel_size, BOOL convert) { // copy metadata from src to dst FreeImage_CloneMetadata(thumbnail, dib); - + return thumbnail; } diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp index 283a91e830..dbc738ffd9 100644 --- a/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp +++ b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp @@ -1,1998 +1,2116 @@ -// ==========================================================
-// Upsampling / downsampling classes
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Detlev Vendt (detlev.vendt@brillit.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 "Resize.h"
-
-/**
-Returns the color type of a bitmap. In contrast to FreeImage_GetColorType,
-this function optionally supports a boolean OUT parameter, that receives TRUE,
-if the specified bitmap is greyscale, that is, it consists of grey colors only.
-Although it returns the same value as returned by FreeImage_GetColorType for all
-image types, this extended function primarily is intended for palletized images,
-since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F)
-images. However, the outgoing boolean is properly maintained for palletized images,
-as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example.
-@param dib A pointer to a FreeImage bitmap to calculate the extended color type for
-@param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap
-is greyscale, that is, it consists of grey colors only. This parameter can be NULL.
-@return the color type of the specified bitmap
-*/
-static FREE_IMAGE_COLOR_TYPE
-GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) {
- const unsigned bpp = FreeImage_GetBPP(dib);
- const unsigned size = CalculateUsedPaletteEntries(bpp);
- const RGBQUAD * const pal = FreeImage_GetPalette(dib);
- FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK;
- BOOL bIsGrey = TRUE;
-
- switch (bpp) {
- case 1:
- {
- for (unsigned i = 0; i < size; i++) {
- if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
- color_type = FIC_PALETTE;
- bIsGrey = FALSE;
- break;
- }
- }
- if (bIsGrey) {
- if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) {
- color_type = FIC_MINISWHITE;
- } else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) {
- color_type = FIC_PALETTE;
- }
- }
- break;
- }
-
- case 4:
- case 8:
- {
- for (unsigned i = 0; i < size; i++) {
- if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
- color_type = FIC_PALETTE;
- bIsGrey = FALSE;
- break;
- }
- if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) {
- if ((size - i - 1) != pal[i].rgbBlue) {
- color_type = FIC_PALETTE;
- if (!bIsGreyscale) {
- // exit loop if we're not setting
- // bIsGreyscale parameter
- break;
- }
- } else {
- color_type = FIC_MINISWHITE;
- }
- }
- }
- break;
- }
-
- default:
- {
- color_type = FreeImage_GetColorType(dib);
- bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE;
- break;
- }
-
- }
- if (bIsGreyscale) {
- *bIsGreyscale = bIsGrey;
- }
-
- return color_type;
-}
-
-/**
-Returns a pointer to an RGBA palette, created from the specified bitmap.
-The RGBA palette is a copy of the specified bitmap's palette, that, additionally
-contains the bitmap's transparency information in the rgbReserved member
-of the palette's RGBQUAD elements.
-@param dib A pointer to a FreeImage bitmap to create the RGBA palette from.
-@param buffer A pointer to the buffer to store the RGBA palette.
-@return A pointer to the newly created RGBA palette or NULL, if the specified
-bitmap is no palletized standard bitmap. If non-NULL, the returned value is
-actually the pointer passed in parameter 'buffer'.
-*/
-static inline RGBQUAD *
-GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) {
- // clone the palette
- const unsigned ncolors = FreeImage_GetColorsUsed(dib);
- if (ncolors == 0) {
- return NULL;
- }
- memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD));
- // merge the transparency table
- const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib));
- const BYTE * const tt = FreeImage_GetTransparencyTable(dib);
- for (unsigned i = 0; i < ntransp; i++) {
- buffer[i].rgbReserved = tt[i];
- }
- for (unsigned i = ntransp; i < ncolors; i++) {
- buffer[i].rgbReserved = 255;
- }
- return buffer;
-}
-
-// --------------------------------------------------------------------------
-
-CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
- double dWidth;
- double dFScale;
- const double dFilterWidth = pFilter->GetWidth();
-
- // scale factor
- const double dScale = double(uDstSize) / double(uSrcSize);
-
- if(dScale < 1.0) {
- // minification
- dWidth = dFilterWidth / dScale;
- dFScale = dScale;
- } else {
- // magnification
- dWidth = dFilterWidth;
- dFScale = 1.0;
- }
-
- // allocate a new line contributions structure
- //
- // window size is the number of sampled pixels
- m_WindowSize = 2 * (int)ceil(dWidth) + 1;
- // length of dst line (no. of rows / cols)
- m_LineLength = uDstSize;
-
- // allocate list of contributions
- m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
- for(unsigned u = 0; u < m_LineLength; u++) {
- // allocate contributions for every pixel
- m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
- }
-
- // offset for discrete to continuous coordinate conversion
- const double dOffset = (0.5 / dScale);
-
- for(unsigned u = 0; u < m_LineLength; u++) {
- // scan through line of contributions
-
- // inverse mapping (discrete dst 'u' to continous src 'dCenter')
- const double dCenter = (double)u / dScale + dOffset;
-
- // find the significant edge points that affect the pixel
- const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5));
- const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize));
-
- m_WeightTable[u].Left = iLeft;
- m_WeightTable[u].Right = iRight;
-
- double dTotalWeight = 0; // sum of weights (initialized to zero)
- for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
- // calculate weights
- const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
- // assert((iSrc-iLeft) < m_WindowSize);
- m_WeightTable[u].Weights[iSrc-iLeft] = weight;
- dTotalWeight += weight;
- }
- if((dTotalWeight > 0) && (dTotalWeight != 1)) {
- // normalize weight of neighbouring points
- for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
- // normalize point
- m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
- }
- }
-
- // simplify the filter, discarding null weights at the right
- {
- int iTrailing = iRight - iLeft - 1;
- while(m_WeightTable[u].Weights[iTrailing] == 0) {
- m_WeightTable[u].Right--;
- iTrailing--;
- if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
- break;
- }
- }
-
- }
-
- } // next dst pixel
-}
-
-CWeightsTable::~CWeightsTable() {
- for(unsigned u = 0; u < m_LineLength; u++) {
- // free contributions for every pixel
- free(m_WeightTable[u].Weights);
- }
- // free list of pixels contributions
- free(m_WeightTable);
-}
-
-// --------------------------------------------------------------------------
-
-FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height) {
-
- const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
- const unsigned src_bpp = FreeImage_GetBPP(src);
-
- // determine the image's color type
- BOOL bIsGreyscale = FALSE;
- FREE_IMAGE_COLOR_TYPE color_type;
- if (src_bpp <= 8) {
- color_type = GetExtendedColorType(src, &bIsGreyscale);
- } else {
- color_type = FIC_RGB;
- }
-
- // determine the required bit depth of the destination image
- unsigned dst_bpp;
- if (color_type == FIC_PALETTE && !bIsGreyscale) {
- // non greyscale FIC_PALETTE images require a high-color destination
- // image (24- or 32-bits depending on the image's transparent state)
- dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
- } else if (src_bpp <= 8) {
- // greyscale images require an 8-bit destination image
- // (or a 32-bit image if the image is transparent)
- dst_bpp = FreeImage_IsTransparent(src) ? 32 : 8;
- if (dst_bpp == 32) {
- // additionally, for transparent images we always need a
- // palette including transparency information (an RGBA palette)
- // so, set color_type accordingly.
- color_type = FIC_PALETTE;
- }
- } else if (src_bpp == 16 && image_type == FIT_BITMAP) {
- // 16-bit 555 and 565 RGB images require a high-color destination image
- // (fixed to 24 bits, since 16-bit RGBs don't support transparency in FreeImage)
- dst_bpp = 24;
- } else {
- // bit depth remains unchanged for all other images
- dst_bpp = src_bpp;
- }
-
- // early exit if destination size is equal to source size
- if ((src_width == dst_width) && (src_height == dst_height)) {
- FIBITMAP *out = src;
- FIBITMAP *tmp = src;
- if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
- out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
- tmp = out;
- }
- if (src_bpp != dst_bpp) {
- switch (dst_bpp) {
- case 8:
- out = FreeImage_ConvertToGreyscale(tmp);
- if (tmp != src) {
- FreeImage_Unload(tmp);
- }
- break;
-
- case 24:
- out = FreeImage_ConvertTo24Bits(tmp);
- if (tmp != src) {
- FreeImage_Unload(tmp);
- }
- break;
-
- case 32:
- out = FreeImage_ConvertTo32Bits(tmp);
- if (tmp != src) {
- FreeImage_Unload(tmp);
- }
- break;
- }
- }
-
- return (out != src) ? out : FreeImage_Clone(src);
- }
-
- RGBQUAD pal_buffer[256];
- RGBQUAD *src_pal = NULL;
-
- // provide the source image's palette to the rescaler for
- // FIC_PALETTE type images (this includes palletized greyscale
- // images with an unordered palette as well as transparent images)
- if (color_type == FIC_PALETTE) {
- if (dst_bpp == 32) {
- // a 32 bit destination image signals transparency, so
- // create an RGBA palette from the source palette
- src_pal = GetRGBAPalette(src, pal_buffer);
- } else {
- src_pal = FreeImage_GetPalette(src);
- }
- }
-
- // allocate the dst image
- FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
- if (!dst) {
- return NULL;
- }
-
- if (dst_bpp == 8) {
- RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
- if (color_type == FIC_MINISWHITE) {
- // build an inverted greyscale palette
- CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
- }
- /*
- else {
- // build a default greyscale palette
- // Currently, FreeImage_AllocateT already creates a default
- // greyscale palette for 8 bpp images, so we can skip this here.
- CREATE_GREYSCALE_PALETTE(dst_pal, 256);
- }
- */
- }
-
- // calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
- // value of src_offset_y is measured from the bottom of the image
- unsigned src_offset_x = src_left;
- unsigned src_offset_y;
- if (src_top > 0) {
- src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;
- } else {
- src_offset_y = 0;
- }
-
- /*
- Decide which filtering order (xy or yx) is faster for this mapping.
- --- The theory ---
- Try to minimize calculations by counting the number of convolution multiplies
- if(dst_width*src_height <= src_width*dst_height) {
- // xy filtering
- } else {
- // yx filtering
- }
- --- The practice ---
- Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
- if(dst_width*dst_height <= src_width*dst_height) {
- // xy filtering
- } else {
- // yx filtering
- }
- */
-
- if (dst_width <= src_width) {
- // xy filtering
- // -------------
-
- FIBITMAP *tmp = NULL;
-
- if (src_width != dst_width) {
- // source and destination widths are different so, we must
- // filter horizontally
- if (src_height != dst_height) {
- // source and destination heights are also different so, we need
- // a temporary image
- tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp, 0, 0, 0);
- if (!tmp) {
- FreeImage_Unload(dst);
- return NULL;
- }
- } else {
- // source and destination heights are equal so, we can directly
- // scale into destination image (second filter method will not
- // be invoked)
- tmp = dst;
- }
-
- // scale source image horizontally into temporary (or destination) image
- horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width);
-
- // set x and y offsets to zero for the second filter method
- // invocation (the temporary image only contains the portion of
- // the image to be rescaled with no offsets)
- src_offset_x = 0;
- src_offset_y = 0;
-
- // also ensure, that the second filter method gets no source
- // palette (the temporary image is palletized only, if it is
- // greyscale; in that case, it is an 8-bit image with a linear
- // palette so, the source palette is not needed or will even be
- // mismatching, if the source palette is unordered)
- src_pal = NULL;
- } else {
- // source and destination widths are equal so, just copy the
- // image pointer
- tmp = src;
- }
-
- if (src_height != dst_height) {
- // source and destination heights are different so, scale
- // temporary (or source) image vertically into destination image
- verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height);
- }
-
- // free temporary image, if not pointing to either src or dst
- if (tmp != src && tmp != dst) {
- FreeImage_Unload(tmp);
- }
-
- } else {
- // yx filtering
- // -------------
-
- // Remark:
- // The yx filtering branch could be more optimized by taking into,
- // account that (src_width != dst_width) is always true, which
- // follows from the above condition, which selects filtering order.
- // Since (dst_width <= src_width) == TRUE selects xy filtering,
- // both widths must be different when performing yx filtering.
- // However, to make the code more robust, not depending on that
- // condition and more symmetric to the xy filtering case, these
- // (src_width != dst_width) conditions are still in place.
-
- FIBITMAP *tmp = NULL;
-
- if (src_height != dst_height) {
- // source and destination heights are different so, we must
- // filter vertically
- if (src_width != dst_width) {
- // source and destination widths are also different so, we need
- // a temporary image
- tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp, 0, 0, 0);
- if (!tmp) {
- FreeImage_Unload(dst);
- return NULL;
- }
- } else {
- // source and destination widths are equal so, we can directly
- // scale into destination image (second filter method will not
- // be invoked)
- tmp = dst;
- }
-
- // scale source image vertically into temporary (or destination) image
- verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);
-
- // set x and y offsets to zero for the second filter method
- // invocation (the temporary image only contains the portion of
- // the image to be rescaled with no offsets)
- src_offset_x = 0;
- src_offset_y = 0;
-
- // also ensure, that the second filter method gets no source
- // palette (the temporary image is palletized only, if it is
- // greyscale; in that case, it is an 8-bit image with a linear
- // palette so, the source palette is not needed or will even be
- // mismatching, if the source palette is unordered)
- src_pal = NULL;
-
- } else {
- // source and destination heights are equal so, just copy the
- // image pointer
- tmp = src;
- }
-
- if (src_width != dst_width) {
- // source and destination heights are different so, scale
- // temporary (or source) image horizontally into destination image
- horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
- }
-
- // free temporary image, if not pointing to either src or dst
- if (tmp != src && tmp != dst) {
- FreeImage_Unload(tmp);
- }
- }
-
- return dst;
-}
-
-void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) {
-
- // allocate and calculate the contributions
- CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
-
- // step through rows
- switch(FreeImage_GetImageType(src)) {
- case FIT_BITMAP:
- {
- switch(FreeImage_GetBPP(src)) {
- case 1:
- {
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the 1-bit non-transparent greyscale
- // image to 8 bpp
- src_offset_x >>= 3;
- if (src_pal) {
- // we have got a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- } else {
- // we do not have a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
- }
- value *= 0xFF;
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 1-bit image
- // to 24 bpp; we always have got a palette here
- src_offset_x >>= 3;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 1-bit image
- // to 32 bpp; we always have got a palette here
- src_offset_x >>= 3;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 4:
- {
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the non-transparent 4-bit greyscale image
- // to 8 bpp; we always have got a palette for 4-bit images
- src_offset_x >>= 1;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
- value += (weightsTable.getWeight(x, i - iLeft)
- * (double)*(BYTE *)&src_pal[pixel]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 4-bit image
- // to 24 bpp; we always have got a palette for 4-bit images
- src_offset_x >>= 1;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 4-bit image
- // to 32 bpp; we always have got a palette for 4-bit images
- src_offset_x >>= 1;
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i - iLeft);
- const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 8:
- {
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // scale the 8-bit non-transparent greyscale image
- // into an 8 bpp destination image
- if (src_pal) {
- // we have got a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double value = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(x, i)
- * (double)*(BYTE *)&src_pal[pixel[i]]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- } else {
- // we do not have a palette
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double value = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 8-bit image
- // to 24 bpp; we always have got a palette here
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 8-bit image
- // to 32 bpp; we always have got a palette here
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * const pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0, a = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 16:
- {
- // transparently convert the 16-bit non-transparent image
- // to 24 bpp
- if (IS_FORMAT_RGB565(src)) {
- // image has 565 format
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
- g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
- b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
- pixel++;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- } else {
- // image has 555 format
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
- g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
- b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
- pixel++;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // scale the 24-bit non-transparent image
- // into a 24 bpp destination image
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE * pixel = src_bits + iLeft * 3;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[FI_RGBA_RED]);
- g += (weight * (double)pixel[FI_RGBA_GREEN]);
- b += (weight * (double)pixel[FI_RGBA_BLUE]);
- pixel += 3;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += 3;
- }
- }
- }
- break;
-
- case 32:
- {
- // scale the 32-bit transparent image
- // into a 32 bpp destination image
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
- BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const BYTE *pixel = src_bits + iLeft * 4;
- double r = 0, g = 0, b = 0, a = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[FI_RGBA_RED]);
- g += (weight * (double)pixel[FI_RGBA_GREEN]);
- b += (weight * (double)pixel[FI_RGBA_BLUE]);
- a += (weight * (double)pixel[FI_RGBA_ALPHA]);
- pixel += 4;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += 4;
- }
- }
- }
- break;
- }
- }
- break;
-
- case FIT_UINT16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft * wordspp;
- double value = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- value += (weight * (double)pixel[0]);
- pixel++;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
- dst_bits += wordspp;
- }
- }
- }
- break;
-
- case FIT_RGB16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft * wordspp;
- double r = 0, g = 0, b = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[0]);
- g += (weight * (double)pixel[1]);
- b += (weight * (double)pixel[2]);
- pixel += wordspp;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
- dst_bits += wordspp;
- }
- }
- }
- break;
-
- case FIT_RGBA16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
-
- for (unsigned y = 0; y < height; y++) {
- // scale each row
- const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
- WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
-
- for (unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
- const WORD *pixel = src_bits + iLeft * wordspp;
- double r = 0, g = 0, b = 0, a = 0;
-
- // for(i = iLeft to iRight)
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i);
- r += (weight * (double)pixel[0]);
- g += (weight * (double)pixel[1]);
- b += (weight * (double)pixel[2]);
- a += (weight * (double)pixel[3]);
- pixel += wordspp;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
- dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
- dst_bits += wordspp;
- }
- }
- }
- break;
-
- case FIT_FLOAT:
- case FIT_RGBF:
- case FIT_RGBAF:
- {
- // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
- const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);
-
- for(unsigned y = 0; y < height; y++) {
- // scale each row
- const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
- float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
-
- for(unsigned x = 0; x < dst_width; x++) {
- // loop through row
- const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
- double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
-
- for(unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(x, i-iLeft);
-
- unsigned index = i * floatspp; // pixel index
- for (unsigned j = 0; j < floatspp; j++) {
- value[j] += (weight * (double)src_bits[index++]);
- }
- }
-
- // place result in destination pixel
- for (unsigned j = 0; j < floatspp; j++) {
- dst_bits[j] = (float)value[j];
- }
-
- dst_bits += floatspp;
- }
- }
- }
- break;
- }
-}
-
-/// Performs vertical image filtering
-void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) {
-
- // allocate and calculate the contributions
- CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
-
- // step through columns
- switch(FreeImage_GetImageType(src)) {
- case FIT_BITMAP:
- {
- const unsigned dst_pitch = FreeImage_GetPitch(dst);
- BYTE * const dst_base = FreeImage_GetBits(dst);
-
- switch(FreeImage_GetBPP(src)) {
- case 1:
- {
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE * const src_base = FreeImage_GetBits(src)
- + src_offset_y * src_pitch + (src_offset_x >> 3);
-
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the 1-bit non-transparent greyscale
- // image to 8 bpp
- if (src_pal) {
- // we have got a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = (*src_bits & mask) != 0;
- value += (weightsTable.getWeight(y, i)
- * (double)*(BYTE *)&src_pal[pixel]);
- src_bits += src_pitch;
- }
- value *= 0xFF;
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- } else {
- // we do not have a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(y, i)
- * (double)((*src_bits & mask) != 0));
- src_bits += src_pitch;
- }
- value *= 0xFF;
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 1-bit image
- // to 24 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = (*src_bits & mask) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 1-bit image
- // to 32 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 4;
- const unsigned index = x >> 3;
- const unsigned mask = 0x80 >> (x & 0x07);
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = (*src_bits & mask) != 0;
- const BYTE * const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 4:
- {
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);
-
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // transparently convert the non-transparent 4-bit greyscale image
- // to 8 bpp; we always have got a palette for 4-bit images
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
- const unsigned index = x >> 1;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
- value += (weightsTable.getWeight(y, i)
- * (double)*(BYTE *)&src_pal[pixel]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 4-bit image
- // to 24 bpp; we always have got a palette for 4-bit images
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
- const unsigned index = x >> 1;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
- const BYTE *const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 4-bit image
- // to 32 bpp; we always have got a palette for 4-bit images
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 4;
- const unsigned index = x >> 1;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
- const BYTE *const entry = (BYTE *)&src_pal[pixel];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 8:
- {
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
-
- switch(FreeImage_GetBPP(dst)) {
- case 8:
- {
- // scale the 8-bit non-transparent greyscale image
- // into an 8 bpp destination image
- if (src_pal) {
- // we have got a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(y, i)
- * (double)*(BYTE *)&src_pal[*src_bits]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- } else {
- // we do not have a palette
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- value += (weightsTable.getWeight(y, i)
- * (double)*src_bits);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // transparently convert the non-transparent 8-bit image
- // to 24 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // transparently convert the transparent 8-bit image
- // to 32 bpp; we always have got a palette here
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 4;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
- r += (weight * (double)entry[FI_RGBA_RED]);
- g += (weight * (double)entry[FI_RGBA_GREEN]);
- b += (weight * (double)entry[FI_RGBA_BLUE]);
- a += (weight * (double)entry[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case 16:
- {
- // transparently convert the 16-bit non-transparent image
- // to 24 bpp
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
-
- if (IS_FORMAT_RGB565(src)) {
- // image has 565 format
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
- g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
- b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- } else {
- // image has 555 format
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- BYTE *dst_bits = dst_base + x * 3;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + x;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
- g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
- b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- }
- break;
-
- case 24:
- {
- // scale the 24-bit transparent image
- // into a 24 bpp destination image
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * 3;
- BYTE *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[FI_RGBA_RED]);
- g += (weight * (double)src_bits[FI_RGBA_GREEN]);
- b += (weight * (double)src_bits[FI_RGBA_BLUE]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case 32:
- {
- // scale the 32-bit transparent image
- // into a 32 bpp destination image
- const unsigned src_pitch = FreeImage_GetPitch(src);
- const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * 4;
- BYTE *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const BYTE *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[FI_RGBA_RED]);
- g += (weight * (double)src_bits[FI_RGBA_GREEN]);
- b += (weight * (double)src_bits[FI_RGBA_BLUE]);
- a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
- dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
- }
- break;
-
- case FIT_UINT16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
- WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * wordspp; // pixel index
- WORD *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + index;
- double value = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- value += (weight * (double)src_bits[0]);
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case FIT_RGB16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
- WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * wordspp; // pixel index
- WORD *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[0]);
- g += (weight * (double)src_bits[1]);
- b += (weight * (double)src_bits[2]);
-
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case FIT_RGBA16:
- {
- // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
- const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
- WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
- const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * wordspp; // pixel index
- WORD *dst_bits = dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
- const WORD *src_bits = src_base + iLeft * src_pitch + index;
- double r = 0, g = 0, b = 0, a = 0;
-
- for (unsigned i = 0; i < iLimit; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i);
- r += (weight * (double)src_bits[0]);
- g += (weight * (double)src_bits[1]);
- b += (weight * (double)src_bits[2]);
- a += (weight * (double)src_bits[3]);
-
- src_bits += src_pitch;
- }
-
- // clamp and place result in destination pixel
- dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
- dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
- dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
- dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
-
- dst_bits += dst_pitch;
- }
- }
- }
- break;
-
- case FIT_FLOAT:
- case FIT_RGBF:
- case FIT_RGBAF:
- {
- // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
- const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float);
-
- const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
- float *const dst_base = (float *)FreeImage_GetBits(dst);
-
- const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
- const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp;
-
- for (unsigned x = 0; x < width; x++) {
- // work on column x in dst
- const unsigned index = x * floatspp; // pixel index
- float *dst_bits = (float *)dst_base + index;
-
- // scale each column
- for (unsigned y = 0; y < dst_height; y++) {
- // loop through column
- const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
- const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
- const float *src_bits = src_base + iLeft * src_pitch + index;
- double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
-
- for (unsigned i = iLeft; i < iRight; i++) {
- // scan between boundaries
- // accumulate weighted effect of each neighboring pixel
- const double weight = weightsTable.getWeight(y, i - iLeft);
- for (unsigned j = 0; j < floatspp; j++) {
- value[j] += (weight * (double)src_bits[j]);
- }
- src_bits += src_pitch;
- }
-
- // place result in destination pixel
- for (unsigned j = 0; j < floatspp; j++) {
- dst_bits[j] = (float)value[j];
- }
- dst_bits += dst_pitch;
- }
- }
- }
- break;
- }
-}
+// ========================================================== +// Upsampling / downsampling classes +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Detlev Vendt (detlev.vendt@brillit.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 "Resize.h" + +/** +Returns the color type of a bitmap. In contrast to FreeImage_GetColorType, +this function optionally supports a boolean OUT parameter, that receives TRUE, +if the specified bitmap is greyscale, that is, it consists of grey colors only. +Although it returns the same value as returned by FreeImage_GetColorType for all +image types, this extended function primarily is intended for palletized images, +since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F) +images. However, the outgoing boolean is properly maintained for palletized images, +as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example. +@param dib A pointer to a FreeImage bitmap to calculate the extended color type for +@param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap +is greyscale, that is, it consists of grey colors only. This parameter can be NULL. +@return the color type of the specified bitmap +*/ +static FREE_IMAGE_COLOR_TYPE +GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) { + const unsigned bpp = FreeImage_GetBPP(dib); + const unsigned size = CalculateUsedPaletteEntries(bpp); + const RGBQUAD * const pal = FreeImage_GetPalette(dib); + FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK; + BOOL bIsGrey = TRUE; + + switch (bpp) { + case 1: + { + for (unsigned i = 0; i < size; i++) { + if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) { + color_type = FIC_PALETTE; + bIsGrey = FALSE; + break; + } + } + if (bIsGrey) { + if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) { + color_type = FIC_MINISWHITE; + } else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) { + color_type = FIC_PALETTE; + } + } + break; + } + + case 4: + case 8: + { + for (unsigned i = 0; i < size; i++) { + if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) { + color_type = FIC_PALETTE; + bIsGrey = FALSE; + break; + } + if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) { + if ((size - i - 1) != pal[i].rgbBlue) { + color_type = FIC_PALETTE; + if (!bIsGreyscale) { + // exit loop if we're not setting + // bIsGreyscale parameter + break; + } + } else { + color_type = FIC_MINISWHITE; + } + } + } + break; + } + + default: + { + color_type = FreeImage_GetColorType(dib); + bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE; + break; + } + + } + if (bIsGreyscale) { + *bIsGreyscale = bIsGrey; + } + + return color_type; +} + +/** +Returns a pointer to an RGBA palette, created from the specified bitmap. +The RGBA palette is a copy of the specified bitmap's palette, that, additionally +contains the bitmap's transparency information in the rgbReserved member +of the palette's RGBQUAD elements. +@param dib A pointer to a FreeImage bitmap to create the RGBA palette from. +@param buffer A pointer to the buffer to store the RGBA palette. +@return A pointer to the newly created RGBA palette or NULL, if the specified +bitmap is no palletized standard bitmap. If non-NULL, the returned value is +actually the pointer passed in parameter 'buffer'. +*/ +static inline RGBQUAD * +GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) { + // clone the palette + const unsigned ncolors = FreeImage_GetColorsUsed(dib); + if (ncolors == 0) { + return NULL; + } + memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD)); + // merge the transparency table + const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib)); + const BYTE * const tt = FreeImage_GetTransparencyTable(dib); + for (unsigned i = 0; i < ntransp; i++) { + buffer[i].rgbReserved = tt[i]; + } + for (unsigned i = ntransp; i < ncolors; i++) { + buffer[i].rgbReserved = 255; + } + return buffer; +} + +// -------------------------------------------------------------------------- + +CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) { + double dWidth; + double dFScale; + const double dFilterWidth = pFilter->GetWidth(); + + // scale factor + const double dScale = double(uDstSize) / double(uSrcSize); + + if(dScale < 1.0) { + // minification + dWidth = dFilterWidth / dScale; + dFScale = dScale; + } else { + // magnification + dWidth = dFilterWidth; + dFScale = 1.0; + } + + // allocate a new line contributions structure + // + // window size is the number of sampled pixels + m_WindowSize = 2 * (int)ceil(dWidth) + 1; + // length of dst line (no. of rows / cols) + m_LineLength = uDstSize; + + // allocate list of contributions + m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution)); + for(unsigned u = 0; u < m_LineLength; u++) { + // allocate contributions for every pixel + m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double)); + } + + // offset for discrete to continuous coordinate conversion + const double dOffset = (0.5 / dScale); + + for(unsigned u = 0; u < m_LineLength; u++) { + // scan through line of contributions + + // inverse mapping (discrete dst 'u' to continous src 'dCenter') + const double dCenter = (double)u / dScale + dOffset; + + // find the significant edge points that affect the pixel + const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5)); + const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize)); + + m_WeightTable[u].Left = iLeft; + m_WeightTable[u].Right = iRight; + + double dTotalWeight = 0; // sum of weights (initialized to zero) + for(int iSrc = iLeft; iSrc < iRight; iSrc++) { + // calculate weights + const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter)); + // assert((iSrc-iLeft) < m_WindowSize); + m_WeightTable[u].Weights[iSrc-iLeft] = weight; + dTotalWeight += weight; + } + if((dTotalWeight > 0) && (dTotalWeight != 1)) { + // normalize weight of neighbouring points + for(int iSrc = iLeft; iSrc < iRight; iSrc++) { + // normalize point + m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight; + } + } + + // simplify the filter, discarding null weights at the right + { + int iTrailing = iRight - iLeft - 1; + while(m_WeightTable[u].Weights[iTrailing] == 0) { + m_WeightTable[u].Right--; + iTrailing--; + if(m_WeightTable[u].Right == m_WeightTable[u].Left) { + break; + } + } + + } + + } // next dst pixel +} + +CWeightsTable::~CWeightsTable() { + for(unsigned u = 0; u < m_LineLength; u++) { + // free contributions for every pixel + free(m_WeightTable[u].Weights); + } + // free list of pixels contributions + free(m_WeightTable); +} + +// -------------------------------------------------------------------------- + +FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags) { + + const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src); + const unsigned src_bpp = FreeImage_GetBPP(src); + + // determine the image's color type + BOOL bIsGreyscale = FALSE; + FREE_IMAGE_COLOR_TYPE color_type; + if (src_bpp <= 8) { + color_type = GetExtendedColorType(src, &bIsGreyscale); + } else { + color_type = FIC_RGB; + } + + // determine the required bit depth of the destination image + unsigned dst_bpp; + unsigned dst_bpp_s1 = 0; + if (color_type == FIC_PALETTE && !bIsGreyscale) { + // non greyscale FIC_PALETTE images require a high-color destination + // image (24- or 32-bits depending on the image's transparent state) + dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24; + } else if (src_bpp <= 8) { + // greyscale images require an 8-bit destination image + // (or a 32-bit image if the image is transparent); + // however, if flag FI_RESCALE_TRUE_COLOR is set, we will return + // a true color (24 bpp) image + if (FreeImage_IsTransparent(src)) { + dst_bpp = 32; + // additionally, for transparent images we always need a + // palette including transparency information (an RGBA palette) + // so, set color_type accordingly + color_type = FIC_PALETTE; + } else { + dst_bpp = ((flags & FI_RESCALE_TRUE_COLOR) == FI_RESCALE_TRUE_COLOR) ? 24 : 8; + // in any case, we use a fast 8-bit temporary image for the + // first filter operation (stage 1, either horizontal or + // vertical) and implicitly convert to 24 bpp (if requested + // by flag FI_RESCALE_TRUE_COLOR) during the second filter + // operation + dst_bpp_s1 = 8; + } + } else if (src_bpp == 16 && image_type == FIT_BITMAP) { + // 16-bit 555 and 565 RGB images require a high-color destination + // image (fixed to 24 bits, since 16-bit RGBs don't support + // transparency in FreeImage) + dst_bpp = 24; + } else { + // bit depth remains unchanged for all other images + dst_bpp = src_bpp; + } + + // make 'stage 1' bpp a copy of the destination bpp if it + // was not explicitly set + if (dst_bpp_s1 == 0) { + dst_bpp_s1 = dst_bpp; + } + + // early exit if destination size is equal to source size + if ((src_width == dst_width) && (src_height == dst_height)) { + FIBITMAP *out = src; + FIBITMAP *tmp = src; + if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) { + out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height); + tmp = out; + } + if (src_bpp != dst_bpp) { + switch (dst_bpp) { + case 8: + out = FreeImage_ConvertToGreyscale(tmp); + break; + case 24: + out = FreeImage_ConvertTo24Bits(tmp); + break; + case 32: + out = FreeImage_ConvertTo32Bits(tmp); + break; + default: + break; + } + if (tmp != src) { + FreeImage_Unload(tmp); + tmp = NULL; + } + } + + return (out != src) ? out : FreeImage_Clone(src); + } + + RGBQUAD pal_buffer[256]; + RGBQUAD *src_pal = NULL; + + // provide the source image's palette to the rescaler for + // FIC_PALETTE type images (this includes palletized greyscale + // images with an unordered palette as well as transparent images) + if (color_type == FIC_PALETTE) { + if (dst_bpp == 32) { + // a 32-bit destination image signals transparency, so + // create an RGBA palette from the source palette + src_pal = GetRGBAPalette(src, pal_buffer); + } else { + src_pal = FreeImage_GetPalette(src); + } + } + + // allocate the dst image + FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0); + if (!dst) { + return NULL; + } + + if (dst_bpp == 8) { + RGBQUAD * const dst_pal = FreeImage_GetPalette(dst); + if (color_type == FIC_MINISWHITE) { + // build an inverted greyscale palette + CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256); + } + /* + else { + // build a default greyscale palette + // Currently, FreeImage_AllocateT already creates a default + // greyscale palette for 8 bpp images, so we can skip this here. + CREATE_GREYSCALE_PALETTE(dst_pal, 256); + } + */ + } + + // calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the + // value of src_offset_y is measured from the bottom of the image + unsigned src_offset_x = src_left; + unsigned src_offset_y = FreeImage_GetHeight(src) - src_height - src_top; + + /* + Decide which filtering order (xy or yx) is faster for this mapping. + --- The theory --- + Try to minimize calculations by counting the number of convolution multiplies + if(dst_width*src_height <= src_width*dst_height) { + // xy filtering + } else { + // yx filtering + } + --- The practice --- + Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task) + if(dst_width*dst_height <= src_width*dst_height) { + // xy filtering + } else { + // yx filtering + } + */ + + if (dst_width <= src_width) { + // xy filtering + // ------------- + + FIBITMAP *tmp = NULL; + + if (src_width != dst_width) { + // source and destination widths are different so, we must + // filter horizontally + if (src_height != dst_height) { + // source and destination heights are also different so, we need + // a temporary image + tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp_s1, 0, 0, 0); + if (!tmp) { + FreeImage_Unload(dst); + return NULL; + } + } else { + // source and destination heights are equal so, we can directly + // scale into destination image (second filter method will not + // be invoked) + tmp = dst; + } + + // scale source image horizontally into temporary (or destination) image + horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width); + + // set x and y offsets to zero for the second filter method + // invocation (the temporary image only contains the portion of + // the image to be rescaled with no offsets) + src_offset_x = 0; + src_offset_y = 0; + + // also ensure, that the second filter method gets no source + // palette (the temporary image is palletized only, if it is + // greyscale; in that case, it is an 8-bit image with a linear + // palette so, the source palette is not needed or will even be + // mismatching, if the source palette is unordered) + src_pal = NULL; + } else { + // source and destination widths are equal so, just copy the + // image pointer + tmp = src; + } + + if (src_height != dst_height) { + // source and destination heights are different so, scale + // temporary (or source) image vertically into destination image + verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height); + } + + // free temporary image, if not pointing to either src or dst + if (tmp != src && tmp != dst) { + FreeImage_Unload(tmp); + } + + } else { + // yx filtering + // ------------- + + // Remark: + // The yx filtering branch could be more optimized by taking into, + // account that (src_width != dst_width) is always true, which + // follows from the above condition, which selects filtering order. + // Since (dst_width <= src_width) == TRUE selects xy filtering, + // both widths must be different when performing yx filtering. + // However, to make the code more robust, not depending on that + // condition and more symmetric to the xy filtering case, these + // (src_width != dst_width) conditions are still in place. + + FIBITMAP *tmp = NULL; + + if (src_height != dst_height) { + // source and destination heights are different so, we must + // filter vertically + if (src_width != dst_width) { + // source and destination widths are also different so, we need + // a temporary image + tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp_s1, 0, 0, 0); + if (!tmp) { + FreeImage_Unload(dst); + return NULL; + } + } else { + // source and destination widths are equal so, we can directly + // scale into destination image (second filter method will not + // be invoked) + tmp = dst; + } + + // scale source image vertically into temporary (or destination) image + verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height); + + // set x and y offsets to zero for the second filter method + // invocation (the temporary image only contains the portion of + // the image to be rescaled with no offsets) + src_offset_x = 0; + src_offset_y = 0; + + // also ensure, that the second filter method gets no source + // palette (the temporary image is palletized only, if it is + // greyscale; in that case, it is an 8-bit image with a linear + // palette so, the source palette is not needed or will even be + // mismatching, if the source palette is unordered) + src_pal = NULL; + + } else { + // source and destination heights are equal so, just copy the + // image pointer + tmp = src; + } + + if (src_width != dst_width) { + // source and destination heights are different so, scale + // temporary (or source) image horizontally into destination image + horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width); + } + + // free temporary image, if not pointing to either src or dst + if (tmp != src && tmp != dst) { + FreeImage_Unload(tmp); + } + } + + return dst; +} + +void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) { + + // allocate and calculate the contributions + CWeightsTable weightsTable(m_pFilter, dst_width, src_width); + + // step through rows + switch(FreeImage_GetImageType(src)) { + case FIT_BITMAP: + { + switch(FreeImage_GetBPP(src)) { + case 1: + { + switch(FreeImage_GetBPP(dst)) { + case 8: + { + // transparently convert the 1-bit non-transparent greyscale image to 8 bpp + src_offset_x >>= 3; + if (src_pal) { + // we have got a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE * const dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double value = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0; + value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]); + } + + // clamp and place result in destination pixel + dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + } + } + } else { + // we do not have a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE * const dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double value = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0; + value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel); + } + value *= 0xFF; + + // clamp and place result in destination pixel + dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + } + } + } + } + break; + + case 24: + { + // transparently convert the non-transparent 1-bit image to 24 bpp + src_offset_x >>= 3; + if (src_pal) { + // we have got a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double r = 0, g = 0, b = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i - iLeft); + const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0; + const BYTE * const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits += 3; + } + } + } else { + // we do not have a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double value = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0; + value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel); + } + value *= 0xFF; + + // clamp and place result in destination pixel + const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_RED] = bval; + dst_bits[FI_RGBA_GREEN] = bval; + dst_bits[FI_RGBA_BLUE] = bval; + dst_bits += 3; + } + } + } + } + break; + + case 32: + { + // transparently convert the transparent 1-bit image to 32 bpp; + // we always have got a palette here + src_offset_x >>= 3; + + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double r = 0, g = 0, b = 0, a = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i - iLeft); + const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0; + const BYTE * const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + a += (weight * (double)entry[FI_RGBA_ALPHA]); + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF); + dst_bits += 4; + } + } + } + break; + } + } + break; + + case 4: + { + switch(FreeImage_GetBPP(dst)) { + case 8: + { + // transparently convert the non-transparent 4-bit greyscale image to 8 bpp; + // we always have got a palette for 4-bit images + src_offset_x >>= 1; + + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE * const dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double value = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4; + value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]); + } + + // clamp and place result in destination pixel + dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + } + } + } + break; + + case 24: + { + // transparently convert the non-transparent 4-bit image to 24 bpp; + // we always have got a palette for 4-bit images + src_offset_x >>= 1; + + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double r = 0, g = 0, b = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i - iLeft); + const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4; + const BYTE * const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits += 3; + } + } + } + break; + + case 32: + { + // transparently convert the transparent 4-bit image to 32 bpp; + // we always have got a palette for 4-bit images + src_offset_x >>= 1; + + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double r = 0, g = 0, b = 0, a = 0; + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i - iLeft); + const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4; + const BYTE * const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + a += (weight * (double)entry[FI_RGBA_ALPHA]); + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF); + dst_bits += 4; + } + } + } + break; + } + } + break; + + case 8: + { + switch(FreeImage_GetBPP(dst)) { + case 8: + { + // scale the 8-bit non-transparent greyscale image + // into an 8 bpp destination image + if (src_pal) { + // we have got a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE * const dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const BYTE * const pixel = src_bits + iLeft; + double value = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + value += (weightsTable.getWeight(x, i) * (double)*(BYTE *)&src_pal[pixel[i]]); + } + + // clamp and place result in destination pixel + dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + } + } + } else { + // we do not have a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE * const dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const BYTE * const pixel = src_bits + iLeft; + double value = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + value += (weightsTable.getWeight(x, i) * (double)pixel[i]); + } + + // clamp and place result in destination pixel + dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + } + } + } + } + break; + + case 24: + { + // transparently convert the non-transparent 8-bit image to 24 bpp + if (src_pal) { + // we have got a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const BYTE * const pixel = src_bits + iLeft; + double r = 0, g = 0, b = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + const BYTE *const entry = (BYTE *)&src_pal[pixel[i]]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits += 3; + } + } + } else { + // we do not have a palette + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const BYTE * const pixel = src_bits + iLeft; + double value = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + value += (weight * (double)pixel[i]); + } + + // clamp and place result in destination pixel + const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_RED] = bval; + dst_bits[FI_RGBA_GREEN] = bval; + dst_bits[FI_RGBA_BLUE] = bval; + dst_bits += 3; + } + } + } + } + break; + + case 32: + { + // transparently convert the transparent 8-bit image to 32 bpp; + // we always have got a palette here + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const BYTE * const pixel = src_bits + iLeft; + double r = 0, g = 0, b = 0, a = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + const BYTE * const entry = (BYTE *)&src_pal[pixel[i]]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + a += (weight * (double)entry[FI_RGBA_ALPHA]); + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF); + dst_bits += 4; + } + } + } + break; + } + } + break; + + case 16: + { + // transparently convert the 16-bit non-transparent image to 24 bpp + if (IS_FORMAT_RGB565(src)) { + // image has 565 format + for (unsigned y = 0; y < height; y++) { + // scale each row + const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD); + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const WORD *pixel = src_bits + iLeft; + double r = 0, g = 0, b = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT)); + g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT)); + b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT)); + pixel++; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits += 3; + } + } + } else { + // image has 555 format + for (unsigned y = 0; y < height; y++) { + // scale each row + const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const WORD *pixel = src_bits + iLeft; + double r = 0, g = 0, b = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT)); + g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT)); + b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT)); + pixel++; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits += 3; + } + } + } + } + break; + + case 24: + { + // scale the 24-bit non-transparent image into a 24 bpp destination image + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const BYTE * pixel = src_bits + iLeft * 3; + double r = 0, g = 0, b = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + r += (weight * (double)pixel[FI_RGBA_RED]); + g += (weight * (double)pixel[FI_RGBA_GREEN]); + b += (weight * (double)pixel[FI_RGBA_BLUE]); + pixel += 3; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits += 3; + } + } + } + break; + + case 32: + { + // scale the 32-bit transparent image into a 32 bpp destination image + for (unsigned y = 0; y < height; y++) { + // scale each row + const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4; + BYTE *dst_bits = FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const BYTE *pixel = src_bits + iLeft * 4; + double r = 0, g = 0, b = 0, a = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + r += (weight * (double)pixel[FI_RGBA_RED]); + g += (weight * (double)pixel[FI_RGBA_GREEN]); + b += (weight * (double)pixel[FI_RGBA_BLUE]); + a += (weight * (double)pixel[FI_RGBA_ALPHA]); + pixel += 4; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF); + dst_bits += 4; + } + } + } + break; + } + } + break; + + case FIT_UINT16: + { + // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit) + const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD); + + for (unsigned y = 0; y < height; y++) { + // scale each row + const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD); + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const WORD *pixel = src_bits + iLeft * wordspp; + double value = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + value += (weight * (double)pixel[0]); + pixel++; + } + + // clamp and place result in destination pixel + dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF); + dst_bits += wordspp; + } + } + } + break; + + case FIT_RGB16: + { + // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit) + const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD); + + for (unsigned y = 0; y < height; y++) { + // scale each row + const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD); + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const WORD *pixel = src_bits + iLeft * wordspp; + double r = 0, g = 0, b = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + r += (weight * (double)pixel[0]); + g += (weight * (double)pixel[1]); + b += (weight * (double)pixel[2]); + pixel += wordspp; + } + + // clamp and place result in destination pixel + dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF); + dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF); + dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF); + dst_bits += wordspp; + } + } + } + break; + + case FIT_RGBA16: + { + // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit) + const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD); + + for (unsigned y = 0; y < height; y++) { + // scale each row + const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD); + WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); + + for (unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary + const WORD *pixel = src_bits + iLeft * wordspp; + double r = 0, g = 0, b = 0, a = 0; + + // for(i = iLeft to iRight) + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i); + r += (weight * (double)pixel[0]); + g += (weight * (double)pixel[1]); + b += (weight * (double)pixel[2]); + a += (weight * (double)pixel[3]); + pixel += wordspp; + } + + // clamp and place result in destination pixel + dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF); + dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF); + dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF); + dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF); + dst_bits += wordspp; + } + } + } + break; + + case FIT_FLOAT: + case FIT_RGBF: + case FIT_RGBAF: + { + // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit) + const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float); + + for(unsigned y = 0; y < height; y++) { + // scale each row + const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float); + float *dst_bits = (float*)FreeImage_GetScanLine(dst, y); + + for(unsigned x = 0; x < dst_width; x++) { + // loop through row + const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary + double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max + + for(unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(x, i-iLeft); + + unsigned index = i * floatspp; // pixel index + for (unsigned j = 0; j < floatspp; j++) { + value[j] += (weight * (double)src_bits[index++]); + } + } + + // place result in destination pixel + for (unsigned j = 0; j < floatspp; j++) { + dst_bits[j] = (float)value[j]; + } + + dst_bits += floatspp; + } + } + } + break; + } +} + +/// Performs vertical image filtering +void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) { + + // allocate and calculate the contributions + CWeightsTable weightsTable(m_pFilter, dst_height, src_height); + + // step through columns + switch(FreeImage_GetImageType(src)) { + case FIT_BITMAP: + { + const unsigned dst_pitch = FreeImage_GetPitch(dst); + BYTE * const dst_base = FreeImage_GetBits(dst); + + switch(FreeImage_GetBPP(src)) { + case 1: + { + const unsigned src_pitch = FreeImage_GetPitch(src); + const BYTE * const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 3); + + switch(FreeImage_GetBPP(dst)) { + case 8: + { + // transparently convert the 1-bit non-transparent greyscale image to 8 bpp + if (src_pal) { + // we have got a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x; + const unsigned index = x >> 3; + const unsigned mask = 0x80 >> (x & 0x07); + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const unsigned pixel = (*src_bits & mask) != 0; + value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]); + src_bits += src_pitch; + } + value *= 0xFF; + + // clamp and place result in destination pixel + *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } else { + // we do not have a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x; + const unsigned index = x >> 3; + const unsigned mask = 0x80 >> (x & 0x07); + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0)); + src_bits += src_pitch; + } + value *= 0xFF; + + // clamp and place result in destination pixel + *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + } + break; + + case 24: + { + // transparently convert the non-transparent 1-bit image to 24 bpp + if (src_pal) { + // we have got a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 3; + const unsigned index = x >> 3; + const unsigned mask = 0x80 >> (x & 0x07); + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + const unsigned pixel = (*src_bits & mask) != 0; + const BYTE * const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } else { + // we do not have a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 3; + const unsigned index = x >> 3; + const unsigned mask = 0x80 >> (x & 0x07); + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0)); + src_bits += src_pitch; + } + value *= 0xFF; + + // clamp and place result in destination pixel + const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_RED] = bval; + dst_bits[FI_RGBA_GREEN] = bval; + dst_bits[FI_RGBA_BLUE] = bval; + dst_bits += dst_pitch; + } + } + } + } + break; + + case 32: + { + // transparently convert the transparent 1-bit image to 32 bpp; + // we always have got a palette here + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 4; + const unsigned index = x >> 3; + const unsigned mask = 0x80 >> (x & 0x07); + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0, a = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + const unsigned pixel = (*src_bits & mask) != 0; + const BYTE * const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + a += (weight * (double)entry[FI_RGBA_ALPHA]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + break; + } + } + break; + + case 4: + { + const unsigned src_pitch = FreeImage_GetPitch(src); + const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1); + + switch(FreeImage_GetBPP(dst)) { + case 8: + { + // transparently convert the non-transparent 4-bit greyscale image to 8 bpp; + // we always have got a palette for 4-bit images + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x; + const unsigned index = x >> 1; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4; + value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + break; + + case 24: + { + // transparently convert the non-transparent 4-bit image to 24 bpp; + // we always have got a palette for 4-bit images + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 3; + const unsigned index = x >> 1; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4; + const BYTE *const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + break; + + case 32: + { + // transparently convert the transparent 4-bit image to 32 bpp; + // we always have got a palette for 4-bit images + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 4; + const unsigned index = x >> 1; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0, a = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4; + const BYTE *const entry = (BYTE *)&src_pal[pixel]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + a += (weight * (double)entry[FI_RGBA_ALPHA]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + break; + } + } + break; + + case 8: + { + const unsigned src_pitch = FreeImage_GetPitch(src); + const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x; + + switch(FreeImage_GetBPP(dst)) { + case 8: + { + // scale the 8-bit non-transparent greyscale image into an 8 bpp destination image + if (src_pal) { + // we have got a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + x; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[*src_bits]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } else { + // we do not have a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + x; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + value += (weightsTable.getWeight(y, i) * (double)*src_bits); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + } + break; + + case 24: + { + // transparently convert the non-transparent 8-bit image to 24 bpp + if (src_pal) { + // we have got a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 3; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + x; + double r = 0, g = 0, b = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + const BYTE * const entry = (BYTE *)&src_pal[*src_bits]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } else { + // we do not have a palette + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 3; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + x; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + value += (weightsTable.getWeight(y, i) * (double)*src_bits); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_RED] = bval; + dst_bits[FI_RGBA_GREEN] = bval; + dst_bits[FI_RGBA_BLUE] = bval; + dst_bits += dst_pitch; + } + } + } + } + break; + + case 32: + { + // transparently convert the transparent 8-bit image to 32 bpp; + // we always have got a palette here + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 4; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + x; + double r = 0, g = 0, b = 0, a = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + const BYTE * const entry = (BYTE *)&src_pal[*src_bits]; + r += (weight * (double)entry[FI_RGBA_RED]); + g += (weight * (double)entry[FI_RGBA_GREEN]); + b += (weight * (double)entry[FI_RGBA_BLUE]); + a += (weight * (double)entry[FI_RGBA_ALPHA]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + break; + } + } + break; + + case 16: + { + // transparently convert the 16-bit non-transparent image to 24 bpp + const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD); + const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x; + + if (IS_FORMAT_RGB565(src)) { + // image has 565 format + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 3; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const WORD *src_bits = src_base + iLeft * src_pitch + x; + double r = 0, g = 0, b = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT)); + g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT)); + b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT)); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } else { + // image has 555 format + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + BYTE *dst_bits = dst_base + x * 3; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const WORD *src_bits = src_base + iLeft * src_pitch + x; + double r = 0, g = 0, b = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT)); + g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT)); + b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT)); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + } + break; + + case 24: + { + // scale the 24-bit transparent image into a 24 bpp destination image + const unsigned src_pitch = FreeImage_GetPitch(src); + const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3; + + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + const unsigned index = x * 3; + BYTE *dst_bits = dst_base + index; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + r += (weight * (double)src_bits[FI_RGBA_RED]); + g += (weight * (double)src_bits[FI_RGBA_GREEN]); + b += (weight * (double)src_bits[FI_RGBA_BLUE]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + break; + + case 32: + { + // scale the 32-bit transparent image into a 32 bpp destination image + const unsigned src_pitch = FreeImage_GetPitch(src); + const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4; + + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + const unsigned index = x * 4; + BYTE *dst_bits = dst_base + index; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const BYTE *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0, a = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + r += (weight * (double)src_bits[FI_RGBA_RED]); + g += (weight * (double)src_bits[FI_RGBA_GREEN]); + b += (weight * (double)src_bits[FI_RGBA_BLUE]); + a += (weight * (double)src_bits[FI_RGBA_ALPHA]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF); + dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF); + dst_bits += dst_pitch; + } + } + } + break; + } + } + break; + + case FIT_UINT16: + { + // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit) + const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD); + + const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD); + WORD *const dst_base = (WORD *)FreeImage_GetBits(dst); + + const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD); + const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp; + + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + const unsigned index = x * wordspp; // pixel index + WORD *dst_bits = dst_base + index; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const WORD *src_bits = src_base + iLeft * src_pitch + index; + double value = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + value += (weight * (double)src_bits[0]); + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF); + + dst_bits += dst_pitch; + } + } + } + break; + + case FIT_RGB16: + { + // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit) + const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD); + + const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD); + WORD *const dst_base = (WORD *)FreeImage_GetBits(dst); + + const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD); + const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp; + + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + const unsigned index = x * wordspp; // pixel index + WORD *dst_bits = dst_base + index; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const WORD *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + r += (weight * (double)src_bits[0]); + g += (weight * (double)src_bits[1]); + b += (weight * (double)src_bits[2]); + + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF); + dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF); + dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF); + + dst_bits += dst_pitch; + } + } + } + break; + + case FIT_RGBA16: + { + // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit) + const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD); + + const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD); + WORD *const dst_base = (WORD *)FreeImage_GetBits(dst); + + const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD); + const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp; + + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + const unsigned index = x * wordspp; // pixel index + WORD *dst_bits = dst_base + index; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary + const WORD *src_bits = src_base + iLeft * src_pitch + index; + double r = 0, g = 0, b = 0, a = 0; + + for (unsigned i = 0; i < iLimit; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i); + r += (weight * (double)src_bits[0]); + g += (weight * (double)src_bits[1]); + b += (weight * (double)src_bits[2]); + a += (weight * (double)src_bits[3]); + + src_bits += src_pitch; + } + + // clamp and place result in destination pixel + dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF); + dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF); + dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF); + dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF); + + dst_bits += dst_pitch; + } + } + } + break; + + case FIT_FLOAT: + case FIT_RGBF: + case FIT_RGBAF: + { + // Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit) + const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float); + + const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float); + float *const dst_base = (float *)FreeImage_GetBits(dst); + + const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float); + const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp; + + for (unsigned x = 0; x < width; x++) { + // work on column x in dst + const unsigned index = x * floatspp; // pixel index + float *dst_bits = (float *)dst_base + index; + + // scale each column + for (unsigned y = 0; y < dst_height; y++) { + // loop through column + const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary + const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary + const float *src_bits = src_base + iLeft * src_pitch + index; + double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max + + for (unsigned i = iLeft; i < iRight; i++) { + // scan between boundaries + // accumulate weighted effect of each neighboring pixel + const double weight = weightsTable.getWeight(y, i - iLeft); + for (unsigned j = 0; j < floatspp; j++) { + value[j] += (weight * (double)src_bits[j]); + } + src_bits += src_pitch; + } + + // place result in destination pixel + for (unsigned j = 0; j < floatspp; j++) { + dst_bits[j] = (float)value[j]; + } + dst_bits += dst_pitch; + } + } + } + break; + } +} diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Resize.h b/plugins/AdvaImg/src/FreeImageToolkit/Resize.h index ca382efa60..ce1d7328d7 100644 --- a/plugins/AdvaImg/src/FreeImageToolkit/Resize.h +++ b/plugins/AdvaImg/src/FreeImageToolkit/Resize.h @@ -1,196 +1,196 @@ -// ==========================================================
-// Upsampling / downsampling classes
-//
-// Design and implementation by
-// - Hervé Drolon (drolon@infonie.fr)
-// - Detlev Vendt (detlev.vendt@brillit.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!
-// ==========================================================
-
-#ifndef _RESIZE_H_
-#define _RESIZE_H_
-
-#include "FreeImage.h"
-#include "Utilities.h"
-#include "Filters.h"
-
-/**
- Filter weights table.<br>
- This class stores contribution information for an entire line (row or column).
-*/
-class CWeightsTable
-{
-/**
- Sampled filter weight table.<br>
- Contribution information for a single pixel
-*/
-typedef struct {
- /// Normalized weights of neighboring pixels
- double *Weights;
- /// Bounds of source pixels window
- unsigned Left, Right;
-} Contribution;
-
-private:
- /// Row (or column) of contribution weights
- Contribution *m_WeightTable;
- /// Filter window size (of affecting source pixels)
- unsigned m_WindowSize;
- /// Length of line (no. of rows / cols)
- unsigned m_LineLength;
-
-public:
- /**
- Constructor<br>
- Allocate and compute the weights table
- @param pFilter Filter used for upsampling or downsampling
- @param uDstSize Length (in pixels) of the destination line buffer
- @param uSrcSize Length (in pixels) of the source line buffer
- */
- CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize);
-
- /**
- Destructor<br>
- Destroy the weights table
- */
- ~CWeightsTable();
-
- /** Retrieve a filter weight, given source and destination positions
- @param dst_pos Pixel position in destination line buffer
- @param src_pos Pixel position in source line buffer
- @return Returns the filter weight
- */
- double getWeight(unsigned dst_pos, unsigned src_pos) {
- return m_WeightTable[dst_pos].Weights[src_pos];
- }
-
- /** Retrieve left boundary of source line buffer
- @param dst_pos Pixel position in destination line buffer
- @return Returns the left boundary of source line buffer
- */
- unsigned getLeftBoundary(unsigned dst_pos) {
- return m_WeightTable[dst_pos].Left;
- }
-
- /** Retrieve right boundary of source line buffer
- @param dst_pos Pixel position in destination line buffer
- @return Returns the right boundary of source line buffer
- */
- unsigned getRightBoundary(unsigned dst_pos) {
- return m_WeightTable[dst_pos].Right;
- }
-};
-
-// ---------------------------------------------
-
-/**
- CResizeEngine<br>
- This class performs filtered zoom. It scales an image to the desired dimensions with
- any of the CGenericFilter derived filter class.<br>
- It works with FIT_BITMAP buffers, WORD buffers (FIT_UINT16, FIT_RGB16, FIT_RGBA16)
- and float buffers (FIT_FLOAT, FIT_RGBF, FIT_RGBAF).<br><br>
-
- <b>References</b> : <br>
- [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering.
- UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html
- [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999.
- [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp
-
-*/
-class CResizeEngine
-{
-private:
- /// Pointer to the FIR / IIR filter
- CGenericFilter* m_pFilter;
-
-public:
-
- /**
- Constructor
- @param filter FIR /IIR filter to be used
- */
- CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {}
-
- /// Destructor
- virtual ~CResizeEngine() {}
-
- /** Scale an image to the desired dimensions.
-
- Method CResizeEngine::scale, as well as the two filtering methods
- CResizeEngine::horizontalFilter and CResizeEngine::verticalFilter take
- four additional parameters, that define a rectangle in the source
- image to be rescaled.
-
- These are src_left, src_top, src_width and src_height and should work
- like these of function FreeImage_Copy. However, src_left and src_top are
- actually named src_offset_x and src_offset_y in the filtering methods.
-
- Additionally, since src_height and dst_height are always the same for
- method horizontalFilter as src_width and dst_width are always the same
- for verticalFilter, these have been stripped down to a single parameter
- height and width for horizontalFilter and verticalFilter respectively.
-
- Currently, method scale is called with the actual size of the source
- image. However, in a future version, we could provide a new function
- called FreeImage_RescaleRect that rescales only part of an image.
-
- @param src Pointer to the source image
- @param dst_width Destination image width
- @param dst_height Destination image height
- @param src_left Left boundary of the source rectangle to be scaled
- @param src_top Top boundary of the source rectangle to be scaled
- @param src_width Width of the source rectangle to be scaled
- @param src_height Height of the source rectangle to be scaled
- @return Returns the scaled image if successful, returns NULL otherwise
- */
- FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height);
-
-private:
-
- /**
- Performs horizontal image filtering
-
- @param src Source image
- @param height Source / Destination image height
- @param src_width Source image width
- @param src_offset_x
- @param src_offset_y
- @param src_pal
- @param dst Destination image
- @param dst_width Destination image width
- */
- void horizontalFilter(FIBITMAP * const src, const unsigned height, const unsigned src_width,
- const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
- FIBITMAP * const dst, const unsigned dst_width);
-
- /**
- Performs vertical image filtering
- @param src Source image
- @param width Source / Destination image width
- @param src_height Source image height
- @param src_offset_x
- @param src_offset_y
- @param src_pal
- @param dst Destination image
- @param dst_height Destination image height
- */
- void verticalFilter(FIBITMAP * const src, const unsigned width, const unsigned src_height,
- const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal,
- FIBITMAP * const dst, const unsigned dst_height);
-};
-
-#endif // _RESIZE_H_
+// ========================================================== +// Upsampling / downsampling classes +// +// Design and implementation by +// - Hervé Drolon (drolon@infonie.fr) +// - Detlev Vendt (detlev.vendt@brillit.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! +// ========================================================== + +#ifndef _RESIZE_H_ +#define _RESIZE_H_ + +#include "FreeImage.h" +#include "Utilities.h" +#include "Filters.h" + +/** + Filter weights table.<br> + This class stores contribution information for an entire line (row or column). +*/ +class CWeightsTable +{ +/** + Sampled filter weight table.<br> + Contribution information for a single pixel +*/ +typedef struct { + /// Normalized weights of neighboring pixels + double *Weights; + /// Bounds of source pixels window + unsigned Left, Right; +} Contribution; + +private: + /// Row (or column) of contribution weights + Contribution *m_WeightTable; + /// Filter window size (of affecting source pixels) + unsigned m_WindowSize; + /// Length of line (no. of rows / cols) + unsigned m_LineLength; + +public: + /** + Constructor<br> + Allocate and compute the weights table + @param pFilter Filter used for upsampling or downsampling + @param uDstSize Length (in pixels) of the destination line buffer + @param uSrcSize Length (in pixels) of the source line buffer + */ + CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize); + + /** + Destructor<br> + Destroy the weights table + */ + ~CWeightsTable(); + + /** Retrieve a filter weight, given source and destination positions + @param dst_pos Pixel position in destination line buffer + @param src_pos Pixel position in source line buffer + @return Returns the filter weight + */ + double getWeight(unsigned dst_pos, unsigned src_pos) { + return m_WeightTable[dst_pos].Weights[src_pos]; + } + + /** Retrieve left boundary of source line buffer + @param dst_pos Pixel position in destination line buffer + @return Returns the left boundary of source line buffer + */ + unsigned getLeftBoundary(unsigned dst_pos) { + return m_WeightTable[dst_pos].Left; + } + + /** Retrieve right boundary of source line buffer + @param dst_pos Pixel position in destination line buffer + @return Returns the right boundary of source line buffer + */ + unsigned getRightBoundary(unsigned dst_pos) { + return m_WeightTable[dst_pos].Right; + } +}; + +// --------------------------------------------- + +/** + CResizeEngine<br> + This class performs filtered zoom. It scales an image to the desired dimensions with + any of the CGenericFilter derived filter class.<br> + It works with FIT_BITMAP buffers, WORD buffers (FIT_UINT16, FIT_RGB16, FIT_RGBA16) + and float buffers (FIT_FLOAT, FIT_RGBF, FIT_RGBAF).<br><br> + + <b>References</b> : <br> + [1] Paul Heckbert, C code to zoom raster images up or down, with nice filtering. + UC Berkeley, August 1989. [online] http://www-2.cs.cmu.edu/afs/cs.cmu.edu/Web/People/ph/heckbert.html + [2] Eran Yariv, Two Pass Scaling using Filters. The Code Project, December 1999. + [online] http://www.codeproject.com/bitmap/2_pass_scaling.asp + +*/ +class CResizeEngine +{ +private: + /// Pointer to the FIR / IIR filter + CGenericFilter* m_pFilter; + +public: + + /** + Constructor + @param filter FIR /IIR filter to be used + */ + CResizeEngine(CGenericFilter* filter):m_pFilter(filter) {} + + /// Destructor + virtual ~CResizeEngine() {} + + /** Scale an image to the desired dimensions. + + Method CResizeEngine::scale, as well as the two filtering methods + CResizeEngine::horizontalFilter and CResizeEngine::verticalFilter take + four additional parameters, that define a rectangle in the source + image to be rescaled. + + These are src_left, src_top, src_width and src_height and should work + like these of function FreeImage_Copy. However, src_left and src_top are + actually named src_offset_x and src_offset_y in the filtering methods. + + Additionally, since src_height and dst_height are always the same for + method horizontalFilter as src_width and dst_width are always the same + for verticalFilter, these have been stripped down to a single parameter + height and width for horizontalFilter and verticalFilter respectively. + + Currently, method scale is called with the actual size of the source + image. However, in a future version, we could provide a new function + called FreeImage_RescaleRect that rescales only part of an image. + + @param src Pointer to the source image + @param dst_width Destination image width + @param dst_height Destination image height + @param src_left Left boundary of the source rectangle to be scaled + @param src_top Top boundary of the source rectangle to be scaled + @param src_width Width of the source rectangle to be scaled + @param src_height Height of the source rectangle to be scaled + @return Returns the scaled image if successful, returns NULL otherwise + */ + FIBITMAP* scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags); + +private: + + /** + Performs horizontal image filtering + + @param src Source image + @param height Source / Destination image height + @param src_width Source image width + @param src_offset_x + @param src_offset_y + @param src_pal + @param dst Destination image + @param dst_width Destination image width + */ + void horizontalFilter(FIBITMAP * const src, const unsigned height, const unsigned src_width, + const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal, + FIBITMAP * const dst, const unsigned dst_width); + + /** + Performs vertical image filtering + @param src Source image + @param width Source / Destination image width + @param src_height Source image height + @param src_offset_x + @param src_offset_y + @param src_pal + @param dst Destination image + @param dst_height Destination image height + */ + void verticalFilter(FIBITMAP * const src, const unsigned width, const unsigned src_height, + const unsigned src_offset_x, const unsigned src_offset_y, const RGBQUAD * const src_pal, + FIBITMAP * const dst, const unsigned dst_height); +}; + +#endif // _RESIZE_H_ diff --git a/plugins/AdvaImg/src/MapIntrospector.h b/plugins/AdvaImg/src/MapIntrospector.h new file mode 100644 index 0000000000..091ba68b26 --- /dev/null +++ b/plugins/AdvaImg/src/MapIntrospector.h @@ -0,0 +1,212 @@ +// ========================================================== +// STL MapIntrospector class +// +// Design and implementation by +// - Carsten Klein (cklein05@users.sourceforge.net) +// +// This file is part of FreeImage 3 +// +// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES +// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE +// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED +// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT +// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY +// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL +// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER +// THIS DISCLAIMER. +// +// Use at your own risk! +// ========================================================== + +#ifndef MAPINTROSPECTOR_H_ +#define MAPINTROSPECTOR_H_ + +// we need at least one C++ header included, +// that defines the C++ Standard Library's version macro, +// that is used below to identify the library. +#include <cstdlib> + +/** +Class MapIntrospector - STL std::map Introspector + +The MapIntrospector is a helper class used to calculate or estimate part +of the internal memory footprint of a std::map, that is the memory used +by N entries, where N is provided as an argument. This class is used by +function FreeImage_GetMemorySize, which aims to get the total memory +usage for a FreeImage bitmap. + +The type argument _Maptype must take the type of the std::map to be +introspected. + +This class accounts for 'internal' memory per entry only, that is, the +size returned does neither include the actual size of the std::map class +itself, nor does it include the size of referenced keys and values (also +the actual bytes required for std::string type keys or values are not +counted). For example, the total memory usage should be something like: + +typedef std::map<std::string, double> DBLMAP +DBLMAP MyMap; + +int total_size = sizeof(DBLMAP) + MapIntrospector<DBLMAP>::GetNodesMemorySize(MyMap.size()) +for (DBLMAP::iterator i = MyMap->begin(); i != MyMap->end(); i++) { + std::string key = i->first; + total_size += key.capacity(); +} + +So, basically, this class' task is to get the (constant) number of bytes +per entry, which is multiplied by N (parameter node_count) and returned +in method GetNodesMemorySize. Since this heavily depends on the actually +used C++ Standard Library, this class must be implemented specifically +for each C++ Standard Library. + +At current, there is an implementation available for these C++ Standard +Libraries: + +- Microsoft C++ Standard Library +- GNU Standard C++ Library v3, libstdc++-v3 +- LLVM "libc++" C++ Standard Library (untested) +- Unknown C++ Standard Library + +Volunteers for testing as well as for providing support for other/new +libraries are welcome. + +The 'Unknown C++ Standard Library' case is used if no other known C++ +Standard Library was detected. It uses a typical _Node structure to +declare an estimated memory consumption for a node. +*/ + +#if defined(_CPPLIB_VER) // Microsoft C++ Standard Library +/** + The Microsoft C++ Standard Library uses the protected structure _Node + of class std::_Tree_nod to represent a node. This class is used by + std::_Tree, the base class of std::map. So, since std::map is derived + from std::_Tree (and _Node is protected), we can get access to this + structure by deriving from std::map. + + Additionally, the Microsoft C++ Standard Library uses a separately + allocated end node for its balanced red-black tree so, actually, there + are size() + 1 nodes present in memory. + + With all that in place, the total memory for all nodes in std::map + is simply (node_count + 1) * sizeof(_Node). +*/ +template<class _Maptype> +class MapIntrospector: private _Maptype { +public: + static size_t GetNodesMemorySize(size_t node_count) { + return (node_count + 1) * sizeof(_Node); + } +}; + +#elif defined(__GLIBCXX__) // GNU Standard C++ Library v3, libstdc++-v3 +/** + The GNU Standard C++ Library v3 uses structure std::_Rb_tree_node<_Val>, + which is publicly declared in the standard namespace. Its value type + _Val is actually the map's value_type std::map::value_type. + + So, the total memory for all nodes in std::map is simply + node_count * sizeof(std::_Rb_tree_node<_Val>), _Val being the map's + value_type. +*/ +template<class _Maptype> +class MapIntrospector { +private: + typedef typename _Maptype::value_type _Val; + +public: + static size_t GetNodesMemorySize(size_t node_count) { + return node_count * sizeof(std::_Rb_tree_node<_Val>); + } +}; + +#elif defined(_LIBCPP_VERSION) // "libc++" C++ Standard Library (LLVM) +/* + The "libc++" C++ Standard Library uses structure + std::__tree_node<_Val, void*> for regular nodes and one instance of + structure std::__tree_end_node<void*> for end nodes, which both are + publicly declared in the standard namespace. Its value type _Val is + actually the map's value_type std::map::value_type. + + So, the total memory for all nodes in std::map is simply + node_count * sizeof(std::__tree_node<_Val, void*>) + + sizeof(std::__tree_end_node<void*>). + + REMARK: this implementation is not yet tested! +*/ +template<class _Maptype> +class MapIntrospector { +private: + typedef typename _Maptype::value_type _Val; + +public: + static size_t GetNodesMemorySize(size_t node_count) { + return node_count * sizeof(std::__tree_node<_Val, void*>) + sizeof(std::__tree_end_node<void*>); + } +}; + +//#elif defined(_ADD_YOUR_CPP_STD_LIBRARY_HERE_) + +#else // Unknown C++ Standard Library +/** + If we do not know the actual C++ Standard Library and so, have no + access to any internal types, we can just make some assumptions about + the implementation and memory usage. + + However, all implementations will probably be based on a balanced + red-black tree, will also store the map's value_type in each node and + need some extra information like the node's color. For a binary tree, + at least two pointers, one for left and one for right are required. + Since it is handy, many implementations also have a parent pointer. + + We let the compiler calculate the size of the above mentioned items by + using a fake structure. By using a real structure (in contrast to just + adding numbers/bytes) we'll get correct pointer sizes as well as any + padding applied for free. +*/ +template<class _Maptype> +class MapIntrospector { +private: + /* Define some handy typedefs to build up the structure. */ + + /** + Each node will likely store the value_type of the mapping, + that is a std::pair<_Key, _Value>. + */ + typedef typename _Maptype::value_type _Val; + + /** + We will need some pointers, since std::map is likely implemented + as a balanced red-black tree. + */ + typedef void* _Ptr; + + /** + Space for some extra information (like the node's color). + An int should be sufficient. + */ + typedef int _Ext; + + /* The memory required for each node will likely look like this + structure. We will just multiply sizeof(_Node) by the number + of nodes to get the total memory of all nodes. By using the + size of the structure, we will also take care of the compiler's + default padding. + */ + typedef struct { + _Ptr _parent_node; + _Ptr _left_node; + _Ptr _right_node; + _Val _value; + _Ext _extra_info; + } _Node; + +public: + static size_t GetNodesMemorySize(size_t node_count) { + return node_count * sizeof(_Node); + } +}; + +#endif // Standard C++ Library + +#endif // MAPINTROSPECTOR_H_ diff --git a/plugins/AdvaImg/src/Metadata/Exif.cpp b/plugins/AdvaImg/src/Metadata/Exif.cpp index abc840607b..35265cb2ab 100644 --- a/plugins/AdvaImg/src/Metadata/Exif.cpp +++ b/plugins/AdvaImg/src/Metadata/Exif.cpp @@ -120,15 +120,15 @@ ReadInt32(BOOL msb_order, const void *buffer) { return value; } -static unsigned short +static WORD ReadUint16(BOOL msb_order, const void *buffer) { - unsigned short value; + WORD value; if(msb_order) { - value = (unsigned short) ((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]); + value = (WORD) ((((BYTE*) buffer)[0] << 8) | ((BYTE*) buffer)[1]); return value; } - value = (unsigned short) ((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]); + value = (WORD) ((((BYTE*) buffer)[1] << 8) | ((BYTE*) buffer)[0]); return value; } @@ -146,9 +146,9 @@ Process a IFD offset Returns the offset and the metadata model for this tag */ static void -processIFDOffset(FITAG *tag, char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) { +processIFDOffset(FITAG *tag, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) { // get the IFD offset - *subdirOffset = (DWORD) ReadUint32(msb_order, pval); + *subdirOffset = ReadUint32(msb_order, pval); // select a tag info table switch(FreeImage_GetTagID(tag)) { @@ -162,7 +162,6 @@ processIFDOffset(FITAG *tag, char *pval, BOOL msb_order, DWORD *subdirOffset, Ta *md_model = TagLib::EXIF_INTEROP; break; } - } /** @@ -170,7 +169,7 @@ Process a maker note IFD offset Returns the offset and the metadata model for this tag */ static void -processMakerNote(FIBITMAP *dib, char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) { +processMakerNote(FIBITMAP *dib, const char *pval, BOOL msb_order, DWORD *subdirOffset, TagLib::MDMODEL *md_model) { FITAG *tagMake = NULL; *subdirOffset = 0; @@ -238,12 +237,12 @@ processMakerNote(FIBITMAP *dib, char *pval, BOOL msb_order, DWORD *subdirOffset, } } else if ((memcmp("FUJIFILM", pval, 8) == 0) || (Maker && (FreeImage_strnicmp("Fujifilm", Maker, 8) == 0))) { // Fujifile Makernote - // Fujifilm's Makernote always use Intel order altough the Exif section maybe in Intel order or in Motorola order. + // Fujifilm's Makernote always use little-endian order altough the Exif section maybe in little-endian order or in big-endian order. // If msb_order == TRUE, the Makernote won't be read: // the value of ifdStart will be 0x0c000000 instead of 0x0000000c and the MakerNote section will be discarded later // in jpeg_read_exif_dir because the IFD is too high *md_model = TagLib::EXIF_MAKERNOTE_FUJIFILM; - DWORD ifdStart = (DWORD) ReadUint32(msb_order, pval + 8); + DWORD ifdStart = ReadUint32(msb_order, pval + 8); *subdirOffset = ifdStart; } else if(memcmp("KYOCERA\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x00\x00\x00", pval, 22) == 0) { @@ -519,14 +518,15 @@ Process Exif directory @param dib Input FIBITMAP @param tiffp Pointer to the TIFF header -@param offset 0th IFD offset -@param length Length of the datafile -@param msb_order Endianess order of the datafile +@param dwOffsetIfd0 Offset to the 0th IFD (first IFD) +@param dwLength Length of the Exif file +@param dwProfileOffset File offset to be used when reading 'offset/value' tags +@param msb_order Endianness order of the Exif file (TRUE if big-endian, FALSE if little-endian) @param starting_md_model Metadata model of the IFD (should be TagLib::EXIF_MAIN for a jpeg) @return Returns TRUE if sucessful, returns FALSE otherwise */ static BOOL -jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsigned int length, BOOL msb_order, TagLib::MDMODEL starting_md_model) { +jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, DWORD dwOffsetIfd0, DWORD dwLength, DWORD dwProfileOffset, BOOL msb_order, TagLib::MDMODEL starting_md_model) { WORD de, nde; std::stack<WORD> destack; // directory entries stack @@ -553,7 +553,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig // set the pointer to the first IFD (0th IFD) and follow it were it leads. - const BYTE *ifd0th = (BYTE*)tiffp + offset; + const BYTE *ifd0th = (BYTE*)tiffp + (size_t)dwOffsetIfd0; const BYTE *ifdp = ifd0th; @@ -577,6 +577,10 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig // determine how many entries there are in the current IFD nde = ReadUint16(msb_order, ifdp); + if (((size_t)(ifdp - tiffp) + 12 * nde) > (size_t)dwLength) { + // suspicious IFD offset, ignore + continue; + } for(; de < nde; de++) { char *pde = NULL; // pointer to the directory entry @@ -590,7 +594,9 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig pde = (char*) DIR_ENTRY_ADDR(ifdp, de); // get the tag ID - FreeImage_SetTagID(tag, ReadUint16(msb_order, pde)); + WORD tag_id = ReadUint16(msb_order, pde); + FreeImage_SetTagID(tag, tag_id); + // get the tag type WORD tag_type = (WORD)ReadUint16(msb_order, pde + 2); if((tag_type - 1) >= EXIF_NUM_FORMATS) { @@ -602,7 +608,9 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig FreeImage_SetTagType(tag, (FREE_IMAGE_MDTYPE)tag_type); // get number of components - FreeImage_SetTagCount(tag, ReadUint32(msb_order, pde + 4)); + DWORD tag_count = ReadUint32(msb_order, pde + 4); + FreeImage_SetTagCount(tag, tag_count); + // check that tag length (size of the tag value in bytes) will fit in a DWORD unsigned tag_data_width = FreeImage_TagDataWidth(FreeImage_GetTagType(tag)); if (tag_data_width != 0 && FreeImage_GetTagCount(tag) > ~(DWORD)0 / tag_data_width) { @@ -616,17 +624,21 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig // 4 bytes or less and value is in the dir entry itself pval = pde + 8; } else { - // if its bigger than 4 bytes, the directory entry contains an offset - // first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data + // if its bigger than 4 bytes, the directory entry contains an offset DWORD offset_value = ReadUint32(msb_order, pde + 8); - if(offset_value > length) { + // the offset can be relative to tiffp or to an external reference (see JPEG-XR) + if(dwProfileOffset) { + offset_value -= dwProfileOffset; + } + // first check if offset exceeds buffer, at this stage FreeImage_GetTagLength may return invalid data + if(offset_value > dwLength) { // a problem occured : delete the tag (not free'd after) FreeImage_DeleteTag(tag); // jump to next entry continue; } // now check that length does not exceed the buffer size - if(FreeImage_GetTagLength(tag) > length - offset_value){ + if(FreeImage_GetTagLength(tag) > dwLength - offset_value){ // a problem occured : delete the tag (not free'd after) FreeImage_DeleteTag(tag); // jump to next entry @@ -659,10 +671,10 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig next_ifd = (BYTE*)tiffp + sub_offset; } - if((sub_offset < (DWORD) length) && (next_mdmodel != TagLib::UNKNOWN)) { + if((sub_offset < dwLength) && (next_mdmodel != TagLib::UNKNOWN)) { // push our current directory state onto the stack ifdstack.push(ifdp); - // bump to the next entry + // jump to the next entry de++; destack.push(de); @@ -708,7 +720,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig const WORD entriesCount0th = ReadUint16(msb_order, ifd0th); DWORD next_offset = ReadUint32(msb_order, DIR_ENTRY_ADDR(ifd0th, entriesCount0th)); - if((next_offset == 0) || (next_offset >= length)) { + if((next_offset == 0) || (next_offset >= dwLength)) { return TRUE; //< no thumbnail } @@ -726,7 +738,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig // check for buffer overflow const size_t remaining = (size_t)base + 12 - (size_t)tiffp; - if(remaining >= length) { + if(remaining >= dwLength) { // bad IFD1 directory, ignore it return FALSE; } @@ -734,9 +746,9 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig // get the tag ID WORD tag = ReadUint16(msb_order, base); // get the tag type - WORD type = ReadUint16(msb_order, base + sizeof(WORD)); + /*WORD type = */ReadUint16(msb_order, base + sizeof(WORD)); // get number of components - DWORD count = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD)); + /*DWORD count = */ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD)); // get the tag value DWORD offset = ReadUint32(msb_order, base + sizeof(WORD) + sizeof(WORD) + sizeof(DWORD)); @@ -768,7 +780,7 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig return TRUE; } - if(thOffset + thSize > length) { + if(thOffset + thSize > dwLength) { return TRUE; } @@ -791,43 +803,45 @@ jpeg_read_exif_dir(FIBITMAP *dib, const BYTE *tiffp, unsigned long offset, unsig // -------------------------------------------------------------------------- /** - Read and decode 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 +Read and decode JPEG_APP1 marker (Exif profile) +@param dib Input FIBITMAP +@param data Pointer to the APP1 marker +@param length APP1 marker length +@return Returns TRUE if successful, FALSE otherwise */ BOOL -jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) { +jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *data, unsigned length) { // marker identifying string for Exif = "Exif\0\0" BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; - BYTE lsb_first[4] = { 0x49, 0x49, 0x2A, 0x00 }; // Intel order - BYTE msb_first[4] = { 0x4D, 0x4D, 0x00, 0x2A }; // Motorola order + BYTE lsb_first[4] = { 0x49, 0x49, 0x2A, 0x00 }; // Classic TIFF signature - little-endian order + BYTE msb_first[4] = { 0x4D, 0x4D, 0x00, 0x2A }; // Classic TIFF signature - big-endian order - unsigned int length = datalen; - BYTE *profile = (BYTE*)dataptr; + // profile size is up to 32-bit + DWORD dwProfileLength = (DWORD)length; + BYTE *pbProfile = (BYTE*)data; // verify the identifying string + if(memcmp(exif_signature, pbProfile, sizeof(exif_signature)) == 0) { + // This is an Exif profile + // should contain a TIFF header with up to 2 IFDs (IFD stands for 'Image File Directory') + // 0th IFD : the image attributes, 1st IFD : may be used for thumbnail - if(memcmp(exif_signature, profile, sizeof(exif_signature)) == 0) { - // Exif profile - TIFF header with 2 IFDs. 0th - the image attributes, 1st - may be used for thumbnail - - profile += sizeof(exif_signature); - length -= sizeof(exif_signature); + pbProfile += sizeof(exif_signature); + dwProfileLength -= sizeof(exif_signature); // read the TIFF header (8 bytes) // check the endianess order - BOOL bMotorolaOrder = TRUE; + BOOL bBigEndian = TRUE; - if(memcmp(profile, lsb_first, sizeof(lsb_first)) == 0) { - // Exif section in Intel order - bMotorolaOrder = FALSE; + if(memcmp(pbProfile, lsb_first, sizeof(lsb_first)) == 0) { + // Exif section is in little-endian order + bBigEndian = FALSE; } else { - if(memcmp(profile, msb_first, sizeof(msb_first)) == 0) { - // Exif section in Motorola order - bMotorolaOrder = TRUE; + if(memcmp(pbProfile, msb_first, sizeof(msb_first)) == 0) { + // Exif section is in big-endian order + bBigEndian = TRUE; } else { // Invalid Exif alignment marker return FALSE; @@ -835,8 +849,8 @@ jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) { } // this is the offset to the first IFD (Image File Directory) - unsigned long first_offset = ReadUint32(bMotorolaOrder, profile + 4); - if (first_offset > length) { + DWORD dwFirstOffset = ReadUint32(bBigEndian, pbProfile + 4); + if (dwFirstOffset > dwProfileLength) { // bad Exif data return FALSE; } @@ -844,7 +858,7 @@ jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) { /* Note: as FreeImage 3.14.0, this test is no longer needed for images with similar suspicious offset => tested with Pentax Optio 230, FujiFilm SP-2500 and Canon EOS 300D - if (first_offset < 8 || first_offset > 16) { + if (dwFirstOffset < 8 || dwFirstOffset > 16) { // This is usually set to 8 // but PENTAX Optio 230 has it set differently, and uses it as offset. FreeImage_OutputMessageProc(FIF_JPEG, "Exif: Suspicious offset of first IFD value"); @@ -853,18 +867,22 @@ jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen) { */ // process Exif directories, starting with Exif-TIFF IFD - return jpeg_read_exif_dir(dib, profile, first_offset, length, bMotorolaOrder, TagLib::EXIF_MAIN); + return jpeg_read_exif_dir(dib, pbProfile, dwFirstOffset, dwProfileLength, 0, bBigEndian, TagLib::EXIF_MAIN); } return FALSE; } +// ========================================================== +// Exif JPEG helper routines +// ========================================================== + /** - 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 +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 */ BOOL jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length) { @@ -898,38 +916,48 @@ jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length) return FALSE; } +// ========================================================== +// Exif JPEG-XR helper routines +// ========================================================== + /** Read and decode JPEG-XR Exif IFD @param dib Input FIBITMAP @param profile Pointer to the Exif marker @param length Exif marker length +@param file_offset Reference offset in the original file of each tag value whose length is > 4 @return Returns TRUE if successful, FALSE otherwise */ BOOL -jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length) { +jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset) { // assume Little Endian order - BOOL bMotorolaOrder = FALSE; + BOOL bBigEndian = FALSE; // process Exif specific IFD - return jpeg_read_exif_dir(dib, profile, 0, length, bMotorolaOrder, TagLib::EXIF_EXIF); + return jpeg_read_exif_dir(dib, profile, 0, length, file_offset, bBigEndian, TagLib::EXIF_EXIF); } /** Read and decode JPEG-XR Exif-GPS IFD @param dib Input FIBITMAP -@param profile Pointer to the Exif-GPS marker -@param length Exif-GPS marker length +@param profile Pointer to the Exif-GPS profile +@param length Exif-GPS profile length +@param file_offset Reference offset in the original file of each tag value whose length is > 4 @return Returns TRUE if successful, FALSE otherwise */ BOOL -jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length) { +jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset) { // assume Little Endian order - BOOL bMotorolaOrder = FALSE; + BOOL bBigEndian = FALSE; // process Exif GPS IFD - return jpeg_read_exif_dir(dib, profile, 0, length, bMotorolaOrder, TagLib::EXIF_GPS); + return jpeg_read_exif_dir(dib, profile, 0, length, file_offset, bBigEndian, TagLib::EXIF_GPS); } +// ========================================================== +// Exif common helper routines +// ========================================================== + /** Rotate a dib according to Exif info @param dib Input / Output dib to rotate @@ -943,49 +971,283 @@ RotateExif(FIBITMAP **dib) { // 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; + if((tag != NULL) && (FreeImage_GetTagID(tag) == TAG_ORIENTATION)) { + const WORD orientation = *((WORD *)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; + } + } + } +} + +// ========================================================== +// Exif TIFF JPEG-XR helper routines +// ========================================================== + +class PredicateTagIDCompare { +public: + bool operator()(FITAG *a, FITAG *b) { + WORD tag_id_a = FreeImage_GetTagID(a); + WORD tag_id_b = FreeImage_GetTagID(b); + return (tag_id_a < tag_id_b); + } +}; + +/** +Write a metadata model as a TIF IFD to a FIMEMORY handle. +The entries in the TIF IFD are sorted in ascending order by tag id. +The last entry is written as 0 (4 bytes) which means no more IFD to follow. +Supported metadata models are +<ul> +<li>FIMD_EXIF_MAIN +<li>FIMD_EXIF_EXIF +<li>FIMD_EXIF_GPS +<li>FIMD_EXIF_INTEROP +</ul> +The end of the buffer is filled with 4 bytes equal to 0 (end of IFD offset) + +@param dib Input FIBITMAP +@param md_model Metadata model to write +@param hmem Memory handle +@return Returns TRUE if successful, FALSE otherwise +@see tiff_get_ifd_profile +*/ +static BOOL +tiff_write_ifd(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, FIMEMORY *hmem) { + FITAG *tag = NULL; + FIMETADATA *mdhandle = NULL; + std::vector<FITAG*> vTagList; + TagLib::MDMODEL internal_md_model; + + DWORD ifd_offset = 0; // WORD-aligned IFD value offset + + const BYTE empty_byte = 0; + + // start of the file + const long start_of_file = FreeImage_TellMemory(hmem); + + // get the metadata count + unsigned metadata_count = FreeImage_GetMetadataCount(md_model, dib); + if(metadata_count == 0) { + return FALSE; + } + + TagLib& s = TagLib::instance(); + + // check for supported metadata models + switch(md_model) { + case FIMD_EXIF_MAIN: + internal_md_model = TagLib::EXIF_MAIN; + break; + case FIMD_EXIF_EXIF: + internal_md_model = TagLib::EXIF_EXIF; + break; + case FIMD_EXIF_GPS: + internal_md_model = TagLib::EXIF_GPS; + break; + case FIMD_EXIF_INTEROP: + internal_md_model = TagLib::EXIF_INTEROP; + break; + default: + return FALSE; + } + + try { + // 1) according to the TIFF specifications, + // the entries in a TIF IFD must be sorted in ascending order by tag id + + // store the tags into a vector + vTagList.reserve(metadata_count); + mdhandle = FreeImage_FindFirstMetadata(md_model, dib, &tag); + if(mdhandle) { + // parse the tags and store them inside vTagList + do { + // rewrite the tag id using FreeImage internal database + // (in case the tag id is wrong or missing) + const char *key = FreeImage_GetTagKey(tag); + int tag_id = s.getTagID(internal_md_model, key); + if(tag_id != -1) { + // this is a known tag, set the tag ID + FreeImage_SetTagID(tag, (WORD)tag_id); + // record the tag + vTagList.push_back(tag); + } + // else ignore this tag + } while(FreeImage_FindNextMetadata(mdhandle, &tag)); + + FreeImage_FindCloseMetadata(mdhandle); + + // sort the vector by tag id + std::sort(vTagList.begin(), vTagList.end(), PredicateTagIDCompare()); + + // update the metadata_count + metadata_count = (unsigned)vTagList.size(); + + } else { + throw(1); + } + + // 2) prepare the place for each IFD entries. + + /* + An Image File Directory (IFD) consists of a 2-byte count of the number of directory entries (i.e., the number of fields), + followed by a sequence of 12-byte field entries, + followed by a 4-byte offset of the next IFD (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD. + */ + + { + // prepare place for 2 bytes for number of entries + 12 bytes for each entry + unsigned ifd_size = 2 + 12 * metadata_count; + FreeImage_WriteMemory(&empty_byte, 1, ifd_size, hmem); + // record the offset used to write values > 4-bytes + ifd_offset = FreeImage_TellMemory(hmem); + // rewind + FreeImage_SeekMemory(hmem, start_of_file, SEEK_SET); + } + + // 3) write each IFD entry in tag id ascending order + + // number of directory entries + WORD nde = (WORD)metadata_count; + FreeImage_WriteMemory(&nde, 1, 2, hmem); + + // for each entry ... + for(unsigned i = 0; i < metadata_count; i++) { + FITAG *tag = vTagList[i]; + // tag id + WORD tag_id = FreeImage_GetTagID(tag); + FreeImage_WriteMemory(&tag_id, 1, 2, hmem); + // tag type (compliant with TIFF specification) + WORD tag_type = (WORD)FreeImage_GetTagType(tag); + FreeImage_WriteMemory(&tag_type, 1, 2, hmem); + // tag count + DWORD tag_count = FreeImage_GetTagCount(tag); + FreeImage_WriteMemory(&tag_count, 1, 4, hmem); + // tag value or offset (results are in BYTE's units) + unsigned tag_length = FreeImage_GetTagLength(tag); + if(tag_length <= 4) { + // 4 bytes or less, write the value (left justified) + const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag); + FreeImage_WriteMemory(tag_value, 1, tag_length, hmem); + for(unsigned k = tag_length; k < 4; k++) { + FreeImage_WriteMemory(&empty_byte, 1, 1, hmem); } + } else { + // write an offset + FreeImage_WriteMemory(&ifd_offset, 1, 4, hmem); + // write the value + long current_position = FreeImage_TellMemory(hmem); + FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET); + FreeImage_WriteMemory(FreeImage_GetTagValue(tag), 1, tag_length, hmem); + if(tag_length & 1) { + // align to the next WORD boundary + FreeImage_WriteMemory(&empty_byte, 1, 1, hmem); + } + // next offset to use + ifd_offset = FreeImage_TellMemory(hmem); + // rewind + FreeImage_SeekMemory(hmem, current_position, SEEK_SET); } } + + // end-of-IFD or next IFD (0 == none) + FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET); + FreeImage_WriteMemory(&empty_byte, 1, 4, hmem); + + return TRUE; + } + catch(int) { + return FALSE; + } +} + +/** +Write a metadata model as a TIF IFD, returns the IFD as a buffer. +The buffer is allocated by the function and must be freed by the caller, using 'free'. +@param dib Input FIBITMAP +@param md_model Metadata model to write +@param ppbProfile Returned buffer +@param uProfileLength Returned buffer size +@return Returns TRUE if successful, FALSE otherwise +@see tiff_write_ifd +*/ +BOOL +tiff_get_ifd_profile(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, BYTE **ppbProfile, unsigned *uProfileLength) { + FIMEMORY *hmem = NULL; + + try { + // open a memory stream + hmem = FreeImage_OpenMemory(NULL, 0); + if(!hmem) { + throw(1); + } + + // write the metadata model as a TIF IFD + BOOL bResult = tiff_write_ifd(dib, md_model, hmem); + + if(bResult) { + BYTE *data = NULL; + DWORD size_in_bytes = 0; + + // get a pointer to the stream buffer + FreeImage_AcquireMemory(hmem, &data, &size_in_bytes); + + // (re-)allocate output buffer + BYTE *pbProfile = *ppbProfile; + pbProfile = (BYTE*)realloc(pbProfile, size_in_bytes); + if(!pbProfile) { + throw(1); + } else { + // copy IFD + memcpy(pbProfile, data, size_in_bytes); + *ppbProfile = pbProfile; + *uProfileLength = size_in_bytes; + } + } + + // free the memory stream + FreeImage_CloseMemory(hmem); + + return bResult; + + } catch(int) { + FreeImage_CloseMemory(hmem); + return FALSE; } } diff --git a/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp b/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp index f8af31f9eb..cc12a5d16b 100644 --- a/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp +++ b/plugins/AdvaImg/src/Metadata/FreeImageTag.cpp @@ -295,11 +295,6 @@ FreeImage_SetTagValue(FITAG *tag, const void *value) { // FITAG internal helper functions // -------------------------------------------------------------------------- -/** -Given a FREE_IMAGE_MDTYPE, calculate the size of this type in bytes unit -@param type Input data type -@return Returns the size of the data type, in bytes unit -*/ unsigned FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type) { static const unsigned format_bytes[] = { @@ -328,3 +323,31 @@ FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type) { format_bytes[type] : 0; } +size_t +FreeImage_GetTagMemorySize(FITAG *tag) { + size_t size = 0; + if (tag) { + FITAGHEADER *tag_header = (FITAGHEADER *)tag->data; + size += sizeof(FITAG); + size += sizeof(FITAGHEADER); + if (tag_header->key) { + size += strlen(tag_header->key) + 1; + } + if (tag_header->description) { + size += strlen(tag_header->description) + 1; + } + if (tag_header->value) { + switch (tag_header->type) { + case FIDT_ASCII: + // for ASCII strings, the value of the count part of an ASCII tag entry includes the NULL. + // however, FreeImage adds another '\0' to be sure that this last character is present. + size += tag_header->length + 1; + break; + default: + size += tag_header->length; + break; + } + } + } + return size; +} diff --git a/plugins/AdvaImg/src/Metadata/FreeImageTag.h b/plugins/AdvaImg/src/Metadata/FreeImageTag.h index f9021ace27..beb8bc84b2 100644 --- a/plugins/AdvaImg/src/Metadata/FreeImageTag.h +++ b/plugins/AdvaImg/src/Metadata/FreeImageTag.h @@ -291,14 +291,22 @@ // Helper functions to deal with the FITAG structure // -------------------------------------------------------------------------- -/** +/** Describes the tag format descriptor +Given a FREE_IMAGE_MDTYPE, calculate the size of this type in bytes unit @param type Tag data type -@return Returns the width of a single element, in bytes +@return Returns the size of the data type, in bytes @see FREE_IMAGE_MDTYPE */ unsigned FreeImage_TagDataWidth(FREE_IMAGE_MDTYPE type); +/** +Calculate the memory size required by a tag, including the size of the structure +@param tag The tag to examine +@return Retuns the memory size used by a tag +*/ +size_t FreeImage_GetTagMemorySize(FITAG *tag); + // -------------------------------------------------------------------------- /** @@ -467,13 +475,18 @@ static const char *g_TagLib_ExifRawFieldName = "ExifRaw"; extern "C" { #endif -// JPEG Exif profile +// JPEG / JPEG-XR Exif profile (see Exif.cpp) +// -------------------------------------------------------------------------- BOOL jpeg_read_exif_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned datalen); BOOL jpeg_read_exif_profile_raw(FIBITMAP *dib, const BYTE *profile, unsigned length); -BOOL jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length); -BOOL jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length); +BOOL jpegxr_read_exif_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset); +BOOL jpegxr_read_exif_gps_profile(FIBITMAP *dib, const BYTE *profile, unsigned length, unsigned file_offset); + +BOOL tiff_get_ifd_profile(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, BYTE **ppbProfile, unsigned *uProfileLength); -// JPEG / TIFF IPTC profile + +// JPEG / TIFF IPTC profile (see IPTC.cpp) +// -------------------------------------------------------------------------- BOOL read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen); BOOL write_iptc_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size); diff --git a/plugins/AdvaImg/src/Metadata/XTIFF.cpp b/plugins/AdvaImg/src/Metadata/XTIFF.cpp index 681eed12c5..d5be902ad4 100644 --- a/plugins/AdvaImg/src/Metadata/XTIFF.cpp +++ b/plugins/AdvaImg/src/Metadata/XTIFF.cpp @@ -48,14 +48,14 @@ For ReadCount, WriteCount, -1 = unknown. */ static const TIFFFieldInfo xtiffFieldInfo[] = { - { TIFFTAG_GEOPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoPixelScale" }, - { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "Intergraph TransformationMatrix" }, - { TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTransformationMatrix" }, - { TIFFTAG_GEOTIEPOINTS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTiePoints" }, - { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, "GeoKeyDirectory" }, - { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoDoubleParams" }, - { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoASCIIParams" }, - { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, "JPL Carto IFD offset" } /** Don't use this! **/ + { TIFFTAG_GEOPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoPixelScale" }, + { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"Intergraph TransformationMatrix" }, + { TIFFTAG_GEOTRANSMATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTransformationMatrix" }, + { TIFFTAG_GEOTIEPOINTS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoTiePoints" }, + { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoKeyDirectory" }, + { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, (char*)"GeoDoubleParams" }, + { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, (char*) "GeoASCIIParams" }, + { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, TRUE, TRUE, (char*)"JPL Carto IFD offset" } /** Don't use this! **/ }; static void @@ -82,19 +82,22 @@ _XTIFFDefaultDirectory(TIFF *tif) { the default directory method, we call it now to allow it to set up the rest of its own methods. */ - if (_ParentExtender) + if (_ParentExtender) { (*_ParentExtender)(tif); + } } /** -XTIFF Initializer -- sets up the callback procedure for the TIFF module +XTIFF Initializer -- sets up the callback procedure for the TIFF module. +@see PluginTIFF::InitTIFF */ void XTIFFInitialize(void) { static int first_time = 1; - if (! first_time) + if (! first_time) { return; /* Been there. Done that. */ + } first_time = 0; // Grab the inherited method and install @@ -105,15 +108,28 @@ XTIFFInitialize(void) { // GeoTIFF tag reading / writing // ---------------------------------------------------------- -void +BOOL tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) { char defaultKey[16]; - size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); + // first check for a mandatory tag + { + short tag_count = 0; + void* data = NULL; + + if(!TIFFGetField(tif, TIFFTAG_GEOKEYDIRECTORY, &tag_count, &data)) { + // no GeoTIFF tag here + return TRUE; + } + } + + // next, read GeoTIFF tags + + const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); TagLib& tag_lib = TagLib::instance(); - for(unsigned i = 0; i < tag_size; i++) { + for(size_t i = 0; i < tag_size; i++) { const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i]; @@ -123,8 +139,9 @@ tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) { if(TIFFGetField(tif, fieldInfo->field_tag, ¶ms)) { // create a tag FITAG *tag = FreeImage_CreateTag(); - if(!tag) - return; + if(!tag) { + return FALSE; + } WORD tag_id = (WORD)fieldInfo->field_tag; @@ -147,8 +164,9 @@ tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) { if(TIFFGetField(tif, fieldInfo->field_tag, &tag_count, &data)) { // create a tag FITAG *tag = FreeImage_CreateTag(); - if(!tag) - return; + if(!tag) { + return FALSE; + } WORD tag_id = (WORD)fieldInfo->field_tag; FREE_IMAGE_MDTYPE tag_type = (FREE_IMAGE_MDTYPE)fieldInfo->field_type; @@ -167,21 +185,24 @@ tiff_read_geotiff_profile(TIFF *tif, FIBITMAP *dib) { } } } // for(tag_size) + + return TRUE; } -void +BOOL tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) { char defaultKey[16]; if(FreeImage_GetMetadataCount(FIMD_GEOTIFF, dib) == 0) { - return; + // no GeoTIFF tag here + return TRUE; } - size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); + const size_t tag_size = sizeof(xtiffFieldInfo) / sizeof(xtiffFieldInfo[0]); TagLib& tag_lib = TagLib::instance(); - for(unsigned i = 0; i < tag_size; i++) { + for(size_t i = 0; i < tag_size; i++) { const TIFFFieldInfo *fieldInfo = &xtiffFieldInfo[i]; FITAG *tag = NULL; @@ -195,93 +216,133 @@ tiff_write_geotiff_profile(TIFF *tif, FIBITMAP *dib) { } } } + + return TRUE; } // ---------------------------------------------------------- -// EXIF tag reading & writing +// TIFF EXIF tag reading & writing // ---------------------------------------------------------- /** -Read a single exif tag +Read a single Exif tag + +@param tif TIFF handle +@param tag_id TIFF Tag ID +@param dib Image being read +@param md_model Metadata model where to store the tag +@return Returns TRUE if successful, returns FALSE otherwise */ static BOOL -tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& tagLib, TIFFDirectory *td, uint32 tag) { - const TIFFField *fip; - uint32 value_count; +tiff_read_exif_tag(TIFF *tif, uint32 tag_id, FIBITMAP *dib, TagLib::MDMODEL md_model) { + uint32 value_count = 0; int mem_alloc = 0; void *raw_data = NULL; - if(tag == TIFFTAG_EXIFIFD) { + if(tag_id == TIFFTAG_EXIFIFD) { + // Exif IFD offset - skip this tag + // md_model should be EXIF_MAIN, the Exif IFD is processed later using the EXIF_EXIF metadata model return TRUE; } + if((tag_id == TIFFTAG_GPSIFD) && (md_model == TagLib::EXIF_MAIN)) { + // Exif GPS IFD offset - skip this tag + // should be processed in another way ... + return TRUE; + } + + TagLib& tagLib = TagLib::instance(); // get the tag key - use NULL to avoid reading GeoTIFF tags - const char *key = tagLib.getTagFieldName(md_model, (WORD)tag, NULL); + const char *key = tagLib.getTagFieldName(md_model, (WORD)tag_id, NULL); if(key == NULL) { return TRUE; } - fip = TIFFFieldWithTag(tif, tag); + const TIFFField *fip = TIFFFieldWithTag(tif, tag_id); if(fip == NULL) { return TRUE; } - if(fip->field_passcount) { //<- "passcount" means "returns count" - if (fip->field_readcount != TIFF_VARIABLE2) { //<- TIFF_VARIABLE2 means "uses LONG count" + if(TIFFFieldPassCount(fip)) { + // a count value is required for 'TIFFGetField' - // assume TIFF_VARIABLE (uses SHORT count) - uint16 value_count16; - if(TIFFGetField(tif, tag, &value_count16, &raw_data) != 1) { + if (TIFFFieldReadCount(fip) != TIFF_VARIABLE2) { + // a count is required, it will be of type uint16 + uint16 value_count16 = 0; + if(TIFFGetField(tif, tag_id, &value_count16, &raw_data) != 1) { + // stop, ignore error return TRUE; } value_count = value_count16; } else { - if(TIFFGetField(tif, tag, &value_count, &raw_data) != 1) { + // a count is required, it will be of type uint32 + uint32 value_count32 = 0; + if(TIFFGetField(tif, tag_id, &value_count32, &raw_data) != 1) { + // stop, ignore error return TRUE; } + value_count = value_count32; } - } else { + } else { // determine count - if (fip->field_readcount == TIFF_VARIABLE || fip->field_readcount == TIFF_VARIABLE2) { + if (TIFFFieldReadCount(fip) == TIFF_VARIABLE || TIFFFieldReadCount(fip) == TIFF_VARIABLE2) { value_count = 1; - } else if (fip->field_readcount == TIFF_SPP) { - value_count = td->td_samplesperpixel; + } else if (TIFFFieldReadCount(fip) == TIFF_SPP) { + uint16 spp; + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + value_count = spp; } else { - value_count = fip->field_readcount; + value_count = TIFFFieldReadCount(fip); } // access fields as pointers to data // (### determining this is NOT robust... and hardly can be. It is implemented looking the _TIFFVGetField code) - if(fip->field_tag == TIFFTAG_TRANSFERFUNCTION) { + if(TIFFFieldTag(fip) == TIFFTAG_TRANSFERFUNCTION) { // reading this tag cause a bug probably located somewhere inside libtiff return TRUE; } - if ((fip->field_type == TIFF_ASCII - || fip->field_readcount == TIFF_VARIABLE - || fip->field_readcount == TIFF_VARIABLE2 - || fip->field_readcount == TIFF_SPP + if ((TIFFFieldDataType(fip) == TIFF_ASCII + || TIFFFieldReadCount(fip) == TIFF_VARIABLE + || TIFFFieldReadCount(fip) == TIFF_VARIABLE2 + || TIFFFieldReadCount(fip) == TIFF_SPP || value_count > 1) - && fip->field_tag != TIFFTAG_PAGENUMBER - && fip->field_tag != TIFFTAG_HALFTONEHINTS - && fip->field_tag != TIFFTAG_YCBCRSUBSAMPLING - && fip->field_tag != TIFFTAG_DOTRANGE + && TIFFFieldTag(fip) != TIFFTAG_PAGENUMBER + && TIFFFieldTag(fip) != TIFFTAG_HALFTONEHINTS + && TIFFFieldTag(fip) != TIFFTAG_YCBCRSUBSAMPLING + && TIFFFieldTag(fip) != TIFFTAG_DOTRANGE - && fip->field_tag != TIFFTAG_BITSPERSAMPLE //<- these two are tricky - - && fip->field_tag != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value + && TIFFFieldTag(fip) != TIFFTAG_BITSPERSAMPLE //<- these two are tricky - + && TIFFFieldTag(fip) != TIFFTAG_COMPRESSION //<- they are defined as TIFF_VARIABLE but in reality return a single value ) { - if(TIFFGetField(tif, tag, &raw_data) != 1) { + if(TIFFGetField(tif, tag_id, &raw_data) != 1) { + // stop, ignore error return TRUE; } } else { + int value_size = 0; // access fields as values - const int value_size = _TIFFDataSize(fip->field_type); + // Note: + // For TIFF_RATIONAL values, TIFFDataWidth() returns 8, but LibTIFF use internaly 4-byte float to represent rationals. + { + TIFFDataType tag_type = TIFFFieldDataType(fip); + switch(tag_type) { + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + value_size = 4; + break; + default: + value_size = TIFFDataWidth(tag_type); + break; + } + } + raw_data = _TIFFmalloc(value_size * value_count); mem_alloc = 1; int ok = FALSE; @@ -291,18 +352,18 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t switch(value_count) { case 1: - ok = TIFFGetField(tif, tag, raw_data); + ok = TIFFGetField(tif, tag_id, raw_data); break; case 2: - ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1); + ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1); break; /* # we might need more in the future: case 3: - ok = TIFFGetField(tif, tag, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2); + ok = TIFFGetField(tif, tag_id, raw_data, (BYTE*)(raw_data) + value_size*1, (BYTE*)(raw_data) + value_size*2); break; */ default: - FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", fip->field_name); + FreeImage_OutputMessageProc(FIF_TIFF, "Unimplemented variable number of parameters for Tiff Tag %s", TIFFFieldName(fip)); break; } if(ok != 1) { @@ -322,62 +383,62 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t return FALSE; } - FreeImage_SetTagID(fitag, (WORD)tag); + FreeImage_SetTagID(fitag, (WORD)tag_id); FreeImage_SetTagKey(fitag, key); - switch(fip->field_type) { + switch(TIFFFieldDataType(fip)) { case TIFF_BYTE: FreeImage_SetTagType(fitag, FIDT_BYTE); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_UNDEFINED: FreeImage_SetTagType(fitag, FIDT_UNDEFINED); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SBYTE: FreeImage_SetTagType(fitag, FIDT_SBYTE); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SHORT: FreeImage_SetTagType(fitag, FIDT_SHORT); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SSHORT: FreeImage_SetTagType(fitag, FIDT_SSHORT); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG: FreeImage_SetTagType(fitag, FIDT_LONG); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD: FreeImage_SetTagType(fitag, FIDT_IFD); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG: FreeImage_SetTagType(fitag, FIDT_SLONG); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; @@ -392,7 +453,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); @@ -409,7 +470,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t rvalue[2*i+1] = rational.getDenominator(); } FreeImage_SetTagType(fitag, FIDT_RATIONAL); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, rvalue); free(rvalue); @@ -418,35 +479,35 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t case TIFF_FLOAT: FreeImage_SetTagType(fitag, FIDT_FLOAT); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_DOUBLE: FreeImage_SetTagType(fitag, FIDT_DOUBLE); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_LONG8: // BigTIFF 64-bit unsigned integer FreeImage_SetTagType(fitag, FIDT_LONG8); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_IFD8: // BigTIFF 64-bit unsigned integer (offset) FreeImage_SetTagType(fitag, FIDT_IFD8); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; case TIFF_SLONG8: // BigTIFF 64-bit signed integer FreeImage_SetTagType(fitag, FIDT_SLONG8); - FreeImage_SetTagLength(fitag, TIFFDataWidth(fip->field_type) * value_count); + FreeImage_SetTagLength(fitag, TIFFDataWidth( TIFFFieldDataType(fip) ) * value_count); FreeImage_SetTagCount(fitag, value_count); FreeImage_SetTagValue(fitag, raw_data); break; @@ -454,7 +515,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t case TIFF_ASCII: default: { size_t length = 0; - if(!mem_alloc && (fip->field_type == TIFF_ASCII) && (fip->field_readcount == TIFF_VARIABLE)) { + if(!mem_alloc && (TIFFFieldDataType(fip) == TIFF_ASCII) && (TIFFFieldReadCount(fip) == TIFF_VARIABLE)) { // when metadata tag is of type ASCII and it's value is of variable size (TIFF_VARIABLE), // tiff_read_exif_tag function gives length of 1 so all strings are truncated ... // ... try to avoid this by using an explicit calculation for 'length' @@ -462,7 +523,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t } else { // remember that raw_data = _TIFFmalloc(value_size * value_count); - const int value_size = _TIFFDataSize(fip->field_type); + const int value_size = TIFFDataWidth( TIFFFieldDataType(fip) ); length = value_size * value_count; } FreeImage_SetTagType(fitag, FIDT_ASCII); @@ -473,7 +534,7 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t break; } - const char *description = tagLib.getTagDescription(md_model, (WORD)tag); + const char *description = tagLib.getTagDescription(md_model, (WORD)tag_id); if(description) { FreeImage_SetTagDescription(fitag, description); } @@ -491,21 +552,22 @@ tiff_read_exif_tag(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib, TagLib& t /** Read all known exif tags + +@param tif TIFF handle +@param md_model Metadata model where to store the tags +@param dib Image being read +@return Returns TRUE if successful, returns FALSE otherwise */ BOOL tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { - int i; - short count; TagLib& tagLib = TagLib::instance(); - TIFFDirectory *td = &tif->tif_dir; - - count = (short) TIFFGetTagListCount(tif); - for(i = 0; i < count; i++) { - uint32 tag = TIFFGetTagListEntry(tif, i); + const int count = TIFFGetTagListCount(tif); + for(int i = 0; i < count; i++) { + uint32 tag_id = TIFFGetTagListEntry(tif, i); // read the tag - if (!tiff_read_exif_tag(tif, md_model, dib, tagLib, td, tag)) + if (!tiff_read_exif_tag(tif, tag_id, dib, md_model)) return FALSE; } @@ -514,22 +576,26 @@ tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { // loop over all Core Directory Tags // ### uses private data, but there is no other way if(md_model == TagLib::EXIF_MAIN) { + const TIFFDirectory *td = &tif->tif_dir; uint32 lastTag = 0; //<- used to prevent reading some tags twice (as stored in tif_fieldinfo) for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) { const TIFFField *fld = tif->tif_fields[fi]; - if(fld->field_tag == lastTag) + const uint32 tag_id = TIFFFieldTag(fld); + + if(tag_id == lastTag) { continue; + } // test if tag value is set - // (lifted directly form LibTiff _TIFFWriteDirectory) + // (lifted directly from LibTiff _TIFFWriteDirectory) if( fld->field_bit == FIELD_CUSTOM ) { - int ci, is_set = FALSE; + int is_set = FALSE; - for( ci = 0; ci < td->td_customValueCount; ci++ ) { + for(int ci = 0; ci < td->td_customValueCount; ci++ ) { is_set |= (td->td_customValues[ci].info == fld); } @@ -543,16 +609,14 @@ tiff_read_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { // process *all* other tags (some will be ignored) - tiff_read_exif_tag(tif, md_model, dib, tagLib, td, fld->field_tag); - + tiff_read_exif_tag(tif, tag_id, dib, md_model); - lastTag = fld->field_tag; + lastTag = tag_id; } } return TRUE; - } @@ -562,27 +626,47 @@ Skip tags that are already handled by the LibTIFF writing process static BOOL skip_write_field(TIFF* tif, uint32 tag) { switch (tag) { - case TIFFTAG_SAMPLEFORMAT: + case TIFFTAG_SUBFILETYPE: + case TIFFTAG_OSUBFILETYPE: case TIFFTAG_IMAGEWIDTH: case TIFFTAG_IMAGELENGTH: - case TIFFTAG_SAMPLESPERPIXEL: case TIFFTAG_BITSPERSAMPLE: + case TIFFTAG_COMPRESSION: case TIFFTAG_PHOTOMETRIC: - case TIFFTAG_PLANARCONFIG: + case TIFFTAG_THRESHHOLDING: + case TIFFTAG_CELLWIDTH: + case TIFFTAG_CELLLENGTH: + case TIFFTAG_FILLORDER: + case TIFFTAG_STRIPOFFSETS: + case TIFFTAG_ORIENTATION: + case TIFFTAG_SAMPLESPERPIXEL: case TIFFTAG_ROWSPERSTRIP: case TIFFTAG_STRIPBYTECOUNTS: - case TIFFTAG_STRIPOFFSETS: - case TIFFTAG_RESOLUTIONUNIT: + case TIFFTAG_MINSAMPLEVALUE: + case TIFFTAG_MAXSAMPLEVALUE: case TIFFTAG_XRESOLUTION: case TIFFTAG_YRESOLUTION: - case TIFFTAG_SUBFILETYPE: + case TIFFTAG_PLANARCONFIG: + case TIFFTAG_FREEOFFSETS: + case TIFFTAG_FREEBYTECOUNTS: + case TIFFTAG_GRAYRESPONSEUNIT: + case TIFFTAG_GRAYRESPONSECURVE: + case TIFFTAG_GROUP3OPTIONS: + case TIFFTAG_GROUP4OPTIONS: + case TIFFTAG_RESOLUTIONUNIT: case TIFFTAG_PAGENUMBER: - case TIFFTAG_COLORMAP: - case TIFFTAG_ORIENTATION: - case TIFFTAG_COMPRESSION: + case TIFFTAG_COLORRESPONSEUNIT: case TIFFTAG_PREDICTOR: - case TIFFTAG_GROUP3OPTIONS: - case TIFFTAG_FILLORDER: + case TIFFTAG_COLORMAP: + case TIFFTAG_HALFTONEHINTS: + case TIFFTAG_TILEWIDTH: + case TIFFTAG_TILELENGTH: + case TIFFTAG_TILEOFFSETS: + case TIFFTAG_TILEBYTECOUNTS: + case TIFFTAG_EXTRASAMPLES: + case TIFFTAG_SAMPLEFORMAT: + case TIFFTAG_SMINSAMPLEVALUE: + case TIFFTAG_SMAXSAMPLEVALUE: // skip always, values have been set in SaveOneTIFF() return TRUE; break; @@ -618,6 +702,11 @@ skip_write_field(TIFF* tif, uint32 tag) { /** Write all known exif tags + +@param tif TIFF handle +@param md_model Metadata model from where to load the tags +@param dib Image being written +@return Returns TRUE if successful, returns FALSE otherwise */ BOOL tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { @@ -636,19 +725,21 @@ tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { for (int fi = 0, nfi = (int)tif->tif_nfields; nfi > 0; nfi--, fi++) { const TIFFField *fld = tif->tif_fields[fi]; + + const uint32 tag_id = TIFFFieldTag(fld); - if(skip_write_field(tif, fld->field_tag)) { + if(skip_write_field(tif, tag_id)) { // skip tags that are already handled by the LibTIFF writing process continue; } FITAG *tag = NULL; // get the tag key - const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)fld->field_tag, defaultKey); + const char *key = tag_lib.getTagFieldName(TagLib::EXIF_MAIN, (WORD)tag_id, defaultKey); if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) { FREE_IMAGE_MDTYPE tag_type = FreeImage_GetTagType(tag); - TIFFDataType tif_tag_type = fld->field_type; + TIFFDataType tif_tag_type = TIFFFieldDataType(fld); // check for identical formats @@ -658,15 +749,15 @@ tiff_write_exif_tags(TIFF *tif, TagLib::MDMODEL md_model, FIBITMAP *dib) { continue; } // type of storage may differ (e.g. rationnal array vs float array type) - if(_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) { + if((unsigned)_TIFFDataSize(tif_tag_type) != FreeImage_TagDataWidth(tag_type)) { // skip tag or _TIFFmemcpy will fail continue; } if(tag_type == FIDT_ASCII) { - TIFFSetField(tif, fld->field_tag, FreeImage_GetTagValue(tag)); + TIFFSetField(tif, tag_id, FreeImage_GetTagValue(tag)); } else { - TIFFSetField(tif, fld->field_tag, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); + TIFFSetField(tif, tag_id, FreeImage_GetTagCount(tag), FreeImage_GetTagValue(tag)); } } } diff --git a/plugins/AdvaImg/src/Quantizers.h b/plugins/AdvaImg/src/Quantizers.h index 2a671fad1c..3db12d387d 100644 --- a/plugins/AdvaImg/src/Quantizers.h +++ b/plugins/AdvaImg/src/Quantizers.h @@ -3,6 +3,7 @@ // // Design and implementation by: // - Hervé Drolon <drolon@infonie.fr> +// - Carsten Klein (cklein05@users.sourceforge.net) // // This file is part of FreeImage 3 // @@ -223,3 +224,131 @@ public: }; +/** + * LFPQUANT - Lossless Fast Pseudo-Quantization Algorithm + * + * The Lossless Fast Pseudo-Quantization algorithm is no real quantization + * algorithm, since it makes no attempt to create a palette, that is suitable + * for all colors of the 24-bit source image. However, it provides very fast + * conversions from 24-bit to 8-bit images, if the number of distinct colors + * in the source image is not greater than the desired palette size. If the + * number of colors in the source image is exceeded, the Quantize method of + * this implementation stops the process and returns NULL. + * + * This implementation uses a very fast hash map implementation to collect + * the source image's colors. It turned out that a customized implementation + * of a hash table with open addressing (using linear probing) provides the + * best performance. The hash table has 512 entries, which prevents the load + * factor to exceed 0.5 as we have 256 entries at most. Each entry consumes + * 64 bits, so the whole hash table takes 4KB of memory. + * + * For large images, the LFPQuantizer is typically up to three times faster + * than the WuQuantizer. + */ +class LFPQuantizer { +public: + /** Constructor */ + LFPQuantizer(unsigned PaletteSize); + + /** Destructor */ + ~LFPQuantizer(); + + /** + * Quantizer + * @param dib input 24-bit or 32-bit bitmap to be quantized + * @return returns the pseudo-quantized 8-bit bitmap + */ + FIBITMAP* Quantize(FIBITMAP *dib, int ReserveSize, RGBQUAD *ReservePalette); + +protected: + /** The maximum size of a palette. */ + static const unsigned MAX_SIZE = 256; + + /** + * The size of the hash table. Must be a power of 2. By sizing it + * MAX_SIZE * 2, we ensure the load factor not to exceed 0.5 at any + * time, since we will have MAX_SIZE entries at most. + */ + static const unsigned MAP_SIZE = MAX_SIZE * 2; + + /** + * With open addressing we need a special value for empty buckets. + * Both entry.color and entry.index are 0xFFFFFFFF for an empty + * entry. + */ + static const unsigned EMPTY_BUCKET = 0xFFFFFFFF; + + /** + * This structure defines a single entry in the hash table. We use + * color as the entry's key. + */ + typedef struct MapEntry { + unsigned color; + unsigned index; + } MapEntry; + + /** The hash table. */ + MapEntry *m_map; + + /** + * The current size of the newly created palette. Since the provided + * reserve palette could contain duplicates, this is not necessarily + * the number of entries in the hash table. Initialized to zero. + */ + unsigned m_size; + + /** + * The desired maximum number of entries in the newly created palette. + * If m_size exceeds this value, the palette is full and the + * quantization process is stopped. Initialized to the desired + * palette size. + */ + unsigned m_limit; + + /** + * The palette index used for the next color added. Initialized to + * zero (the reserve palette is put to the end of the palette). + */ + unsigned m_index; + + /** + * Ensures that hash codes that differ only by constant multiples + * at each bit position have a bounded number of collisions. + * @param h the initial (aka raw) hash code + * @return the modified hash code + */ + static inline unsigned hash(unsigned h) { + h ^= (h >> 20) ^ (h >> 12); + return h ^ (h >> 7) ^ (h >> 4); + } + + /** + * Returns the palette index of the specified color. Tries to put the + * color into the map, if it's not already present in the map. In that + * case, a new index is used for the color. Returns -1, if adding the + * color would exceed the desired maximum number of colors in the + * palette. + * @param color the color to get the index from + * @return the palette index of the specified color or -1, if there + * is no space left in the palette + */ + int GetIndexForColor(unsigned color); + + /** + * Adds the specified number of entries of the specified reserve + * palette to the newly created palette. + * @param *palette a pointer to the reserve palette to copy from + * @param size the number of entries to copy + */ + void AddReservePalette(const void *palette, unsigned size); + + /** + * Copies the newly created palette into the specified destination + * palettte. Although unused palette entries are not overwritten in + * the destination palette, it is assumed to have space for at + * least 256 entries. + * @param palette a pointer to the destination palette + */ + void WritePalette(void *palette); + +}; diff --git a/plugins/AdvaImg/src/Utilities.h b/plugins/AdvaImg/src/Utilities.h index c967de9ccc..79b0c1d59d 100644 --- a/plugins/AdvaImg/src/Utilities.h +++ b/plugins/AdvaImg/src/Utilities.h @@ -5,6 +5,7 @@ // - Floris van den Berg (flvdberg@wxs.nl) // - HervĂ© Drolon <drolon@infonie.fr> // - Ryan Rubley (ryan@lostreality.org) +// - Mihail Naydenov (mnaydenov@users.sourceforge.net) // // This file is part of FreeImage 3 // @@ -72,12 +73,13 @@ Allocate a FIBITMAP with possibly no pixel data (i.e. only header data and some or all metadata) @param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP @param type Image type -@param width -@param height -@param bpp -@param red_mask -@param green_mask -@param blue_mask +@param width Image width +@param height Image height +@param bpp Number of bits per pixel +@param red_mask Image red mask +@param green_mask Image green mask +@param blue_mask Image blue mask +@return Returns the allocated FIBITMAP @see FreeImage_AllocateT */ DLL_API FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeaderT(BOOL header_only, FREE_IMAGE_TYPE type, int width, int height, int bpp FI_DEFAULT(8), unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0)); @@ -86,17 +88,34 @@ DLL_API FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeaderT(BOOL header_only, FREE Allocate a FIBITMAP of type FIT_BITMAP, with possibly no pixel data (i.e. only header data and some or all metadata) @param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP -@param width -@param height -@param bpp -@param red_mask -@param green_mask -@param blue_mask +@param width Image width +@param height Image height +@param bpp Number of bits per pixel +@param red_mask Image red mask +@param green_mask Image green mask +@param blue_mask Image blue mask +@return Returns the allocated FIBITMAP @see FreeImage_Allocate */ DLL_API FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeader(BOOL header_only, int width, int height, int bpp, unsigned red_mask FI_DEFAULT(0), unsigned green_mask FI_DEFAULT(0), unsigned blue_mask FI_DEFAULT(0)); /** +Allocate a FIBITMAP with no pixel data and wrap a user provided pixel buffer +@param ext_bits Pointer to external user's pixel buffer +@param ext_pitch Pointer to external user's pixel buffer pitch +@param type Image type +@param width Image width +@param height Image height +@param bpp Number of bits per pixel +@param red_mask Image red mask +@param green_mask Image green mask +@param blue_mask Image blue mask +@return Returns the allocated FIBITMAP +@see FreeImage_ConvertFromRawBitsEx +*/ +DLL_API FIBITMAP * DLL_CALLCONV FreeImage_AllocateHeaderForBits(BYTE *ext_bits, unsigned ext_pitch, FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask); + +/** Helper for 16-bit FIT_BITMAP @see FreeImage_GetRGBMasks */ @@ -276,7 +295,7 @@ CalculateUsedPaletteEntries(unsigned bit_count) { inline unsigned char * CalculateScanLine(unsigned char *bits, unsigned pitch, int scanline) { - return (bits + (pitch * scanline)); + return bits ? (bits + ((size_t)pitch * scanline)) : NULL; } // ---------------------------------------------------------- @@ -458,7 +477,7 @@ A Standard Default Color Space for the Internet - sRGB. */ #define LUMA_REC709(r, g, b) (0.2126F * r + 0.7152F * g + 0.0722F * b) -#define GREY(r, g, b) (BYTE)LUMA_REC709(r, g, b) +#define GREY(r, g, b) (BYTE)(LUMA_REC709(r, g, b) + 0.5F) /* #define GREY(r, g, b) (BYTE)(((WORD)r * 77 + (WORD)g * 150 + (WORD)b * 29) >> 8) // .299R + .587G + .114B */ |