diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp | 2648 |
1 files changed, 1992 insertions, 656 deletions
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp index 5421bd47ab..f342bb794e 100644 --- a/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp +++ b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp @@ -1,656 +1,1992 @@ -// ========================================================== -// Upsampling / downsampling classes -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// - Detlev Vendt (detlev.vendt@brillit.de) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "Resize.h" - -/** - Filter weights table. - This class stores contribution information for an entire line (row or column). -*/ -CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) { - unsigned u; - double dWidth; - double dFScale = 1.0; - 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; - } - - // allocate a new line contributions structure - // - // window size is the number of sampled pixels - m_WindowSize = 2 * (int)ceil(dWidth) + 1; - m_LineLength = uDstSize; - // allocate list of contributions - m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution)); - for(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) - 0.5; - - - for(u = 0; u < m_LineLength; u++) { - // scan through line of contributions - const double dCenter = (double)u / dScale + dOffset; // reverse mapping - // find the significant edge points that affect the pixel - int iLeft = MAX (0, (int)floor (dCenter - dWidth)); - int iRight = MIN ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1); - - // cut edge points to fit in filter window in case of spill-off - if ((iRight - iLeft + 1) > int(m_WindowSize)) { - if(iLeft < (int(uSrcSize) - 1 / 2)) { - iLeft++; - } else { - iRight--; - } - } - - m_WeightTable[u].Left = iLeft; - m_WeightTable[u].Right = iRight; - - int iSrc = 0; - double dTotalWeight = 0; // zero sum of weights - for(iSrc = iLeft; iSrc <= iRight; iSrc++) { - // calculate weights - const double weight = dFScale * pFilter->Filter(dFScale * (dCenter - (double)iSrc)); - m_WeightTable[u].Weights[iSrc-iLeft] = weight; - dTotalWeight += weight; - } - if ((dTotalWeight > 0) && (dTotalWeight != 1)) { - // normalize weight of neighbouring points - for(iSrc = iLeft; iSrc <= iRight; iSrc++) { - // normalize point - m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight; - } - // simplify the filter, discarding null weights at the right - iSrc = iRight - iLeft; - while(m_WeightTable[u].Weights[iSrc] == 0){ - m_WeightTable[u].Right--; - iSrc--; - if(m_WeightTable[u].Right == m_WeightTable[u].Left) - break; - } - - } - } -} - -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); -} - -// --------------------------------------------- - -/** - 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 8-, 24- and 32-bit buffers.<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 - -*/ - -FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height) { - unsigned src_width = FreeImage_GetWidth(src); - unsigned src_height = FreeImage_GetHeight(src); - - unsigned redMask = FreeImage_GetRedMask(src); - unsigned greenMask = FreeImage_GetGreenMask(src); - unsigned blueMask = FreeImage_GetBlueMask(src); - - unsigned bpp = FreeImage_GetBPP(src); - if(bpp == 1) { - // convert output to 8-bit - bpp = 8; - } - - FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src); - - // allocate the dst image - FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, bpp, redMask, greenMask, blueMask); - if (!dst) return NULL; - - if(bpp == 8) { - if(FreeImage_GetColorType(src) == FIC_MINISWHITE) { - // build an inverted greyscale palette - RGBQUAD *dst_pal = FreeImage_GetPalette(dst); - for(unsigned i = 0; i < 256; i++) { - dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)(255 - i); - } - } else { - // build a greyscale palette - RGBQUAD *dst_pal = FreeImage_GetPalette(dst); - for(unsigned i = 0; i < 256; i++) { - dst_pal[i].rgbRed = dst_pal[i].rgbGreen = dst_pal[i].rgbBlue = (BYTE)i; - } - } - } - - /** - 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 - // ------------- - - // allocate a temporary image - FIBITMAP *tmp = FreeImage_AllocateT(image_type, dst_width, src_height, bpp, redMask, greenMask, blueMask); - if (!tmp) { - FreeImage_Unload(dst); - return NULL; - } - - // scale source image horizontally into temporary image - horizontalFilter(src, src_width, src_height, tmp, dst_width, src_height); - - // scale temporary image vertically into result image - verticalFilter(tmp, dst_width, src_height, dst, dst_width, dst_height); - - // free temporary image - FreeImage_Unload(tmp); - - } else { - // yx filtering - // ------------- - - // allocate a temporary image - FIBITMAP *tmp = FreeImage_AllocateT(image_type, src_width, dst_height, bpp, redMask, greenMask, blueMask); - if (!tmp) { - FreeImage_Unload(dst); - return NULL; - } - - // scale source image vertically into temporary image - verticalFilter(src, src_width, src_height, tmp, src_width, dst_height); - - // scale temporary image horizontally into result image - horizontalFilter(tmp, src_width, dst_height, dst, dst_width, dst_height); - - // free temporary image - FreeImage_Unload(tmp); - } - - return dst; -} - - -/// Performs horizontal image filtering -void CResizeEngine::horizontalFilter(FIBITMAP *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) { - if(dst_width == src_width) { - // no scaling required, just copy - switch(FreeImage_GetBPP(src)) { - case 1: - { - if(FreeImage_GetBPP(dst) != 8) break; - for(unsigned y = 0; y < dst_height; y++) { - // convert each row - BYTE *src_bits = FreeImage_GetScanLine(src, y); - BYTE *dst_bits = FreeImage_GetScanLine(dst, y); - FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width); - } - } - break; - - default: - { - const BYTE *src_bits = FreeImage_GetBits(src); - BYTE *dst_bits = FreeImage_GetBits(dst); - memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst)); - } - break; - } - } - else { - - // 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: - { - // scale and convert to 8-bit - if(FreeImage_GetBPP(dst) != 8) break; - - for(unsigned y = 0; y < dst_height; y++) { - // scale each row - const BYTE *src_bits = FreeImage_GetScanLine(src, y); - BYTE *dst_bits = FreeImage_GetScanLine(dst, y); - - for(unsigned x = 0; x < dst_width; x++) { - // loop through row - double value = 0; - const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary - - 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 BYTE pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0; - value += (weight * (double)pixel); - } - value *= 255; - - // clamp and place result in destination pixel - dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 255); - } - } - } - break; - - case 8: - case 24: - case 32: - { - // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit) - const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); - - for(unsigned y = 0; y < dst_height; y++) { - // scale each row - const BYTE *src_bits = FreeImage_GetScanLine(src, y); - BYTE *dst_bits = FreeImage_GetScanLine(dst, y); - - for(unsigned x = 0; x < dst_width; x++) { - // loop through row - double value[4] = {0, 0, 0, 0}; // 4 = 32 bpp max - const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary - - 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 * bytespp; // pixel index - for (unsigned j = 0; j < bytespp; j++) { - value[j] += (weight * (double)src_bits[index++]); - } - } - - // clamp and place result in destination pixel - for (unsigned j = 0; j < bytespp; j++) { - dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF); - } - - dst_bits += bytespp; - } - } - } - break; - } - } - break; - - case FIT_UINT16: - case FIT_RGB16: - 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) / FreeImage_GetWidth(src)) / sizeof(WORD); - - for(unsigned y = 0; y < dst_height; y++) { - // scale each row - const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y); - WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); - - for(unsigned x = 0; x < dst_width; x++) { - // loop through row - double value[4] = {0, 0, 0, 0}; // 4 = 64 bpp max - const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary - - 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 * wordspp; // pixel index - for (unsigned j = 0; j < wordspp; j++) { - value[j] += (weight * (double)src_bits[index++]); - } - } - - // clamp and place result in destination pixel - for (unsigned j = 0; j < wordspp; j++) { - dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 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) / FreeImage_GetWidth(src)) / sizeof(float); - - for(unsigned y = 0; y < dst_height; y++) { - // scale each row - const float *src_bits = (float*)FreeImage_GetScanLine(src, y); - float *dst_bits = (float*)FreeImage_GetScanLine(dst, y); - - for(unsigned x = 0; x < dst_width; x++) { - // loop through row - double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max - const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary - - 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 *src, unsigned src_width, unsigned src_height, FIBITMAP *dst, unsigned dst_width, unsigned dst_height) { - if(src_height == dst_height) { - // no scaling required, just copy - switch(FreeImage_GetBPP(src)) { - case 1: - { - if(FreeImage_GetBPP(dst) != 8) break; - for(unsigned y = 0; y < dst_height; y++) { - // convert each row - BYTE *src_bits = FreeImage_GetScanLine(src, y); - BYTE *dst_bits = FreeImage_GetScanLine(dst, y); - FreeImage_ConvertLine1To8(dst_bits, src_bits, dst_width); - } - } - break; - - default: - { - const BYTE *src_bits = FreeImage_GetBits(src); - BYTE *dst_bits = FreeImage_GetBits(dst); - memcpy(dst_bits, src_bits, dst_height * FreeImage_GetPitch(dst)); - } - break; - } - - } - else { - - // allocate and calculate the contributions - CWeightsTable weightsTable(m_pFilter, dst_height, src_height); - - // step through columns - switch(FreeImage_GetImageType(src)) { - case FIT_BITMAP: - { - switch(FreeImage_GetBPP(src)) { - case 1: - { - // scale and convert to 8-bit - if(FreeImage_GetBPP(dst) != 8) break; - - const unsigned src_pitch = FreeImage_GetPitch(src); - const unsigned dst_pitch = FreeImage_GetPitch(dst); - - for(unsigned x = 0; x < dst_width; x++) { - - // work on column x in dst - BYTE *dst_bits = FreeImage_GetBits(dst) + x; - - // scale each column - for(unsigned y = 0; y < dst_height; y++) { - // loop through column - double value = 0; - const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary - - BYTE *src_bits = FreeImage_GetScanLine(src, iLeft); - - 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); - - const BYTE pixel = (src_bits[x >> 3] & (0x80 >> (x & 0x07))) != 0; - value += (weight * (double)pixel); - - src_bits += src_pitch; - } - value *= 255; - - // clamp and place result in destination pixel - *dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF); - - dst_bits += dst_pitch; - } - } - } - break; - - case 8: - case 24: - case 32: - { - // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit) - const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); - - const unsigned src_pitch = FreeImage_GetPitch(src); - const unsigned dst_pitch = FreeImage_GetPitch(dst); - - for(unsigned x = 0; x < dst_width; x++) { - const unsigned index = x * bytespp; // pixel index - - // work on column x in dst - BYTE *dst_bits = FreeImage_GetBits(dst) + index; - - // scale each column - for(unsigned y = 0; y < dst_height; y++) { - // loop through column - double value[4] = {0, 0, 0, 0}; // 4 = 32 bpp max - const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary - - const BYTE *src_bits = FreeImage_GetScanLine(src, iLeft) + index; - - 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 < bytespp; j++) { - value[j] += (weight * (double)src_bits[j]); - } - - src_bits += src_pitch; - } - - // clamp and place result in destination pixel - for (unsigned j = 0; j < bytespp; j++) { - dst_bits[j] = (BYTE)CLAMP<int>((int)(value[j] + 0.5), 0, 0xFF); - } - - dst_bits += dst_pitch; - } - } - } - break; - } - } - break; - - case FIT_UINT16: - case FIT_RGB16: - 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) / FreeImage_GetWidth(src)) / sizeof(WORD); - - const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD); - const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD); - - for(unsigned x = 0; x < dst_width; x++) { - const unsigned index = x * wordspp; // pixel index - - // work on column x in dst - WORD *dst_bits = (WORD*)FreeImage_GetBits(dst) + index; - - // scale each column - for(unsigned y = 0; y < dst_height; y++) { - // loop through column - double value[4] = {0, 0, 0, 0}; // 4 = 64 bpp max - const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary - - const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, iLeft) + index; - - 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 < wordspp; j++) { - value[j] += (weight * (double)src_bits[j]); - } - - src_bits += src_pitch; - } - - // clamp and place result in destination pixel - for (unsigned j = 0; j < wordspp; j++) { - dst_bits[j] = (WORD)CLAMP<int>((int)(value[j] + 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) / FreeImage_GetWidth(src)) / sizeof(float); - - const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float); - const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float); - - for(unsigned x = 0; x < dst_width; x++) { - const unsigned index = x * floatspp; // pixel index - - // work on column x in dst - float *dst_bits = (float*)FreeImage_GetBits(dst) + index; - - // scale each column - for(unsigned y = 0; y < dst_height; y++) { - // loop through column - double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max - const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary - const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary - - const float *src_bits = (float*)FreeImage_GetScanLine(src, iLeft) + index; - - 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; - } - - // clamp and 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) {
+ unsigned u;
+ double dWidth;
+ double dFScale = 1.0;
+ 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;
+ }
+
+ // allocate a new line contributions structure
+ //
+ // window size is the number of sampled pixels
+ m_WindowSize = 2 * (int)ceil(dWidth) + 1;
+ m_LineLength = uDstSize;
+ // allocate list of contributions
+ m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
+ for(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) - 0.5;
+
+ for(u = 0; u < m_LineLength; u++) {
+ // scan through line of contributions
+ const double dCenter = (double)u / dScale + dOffset; // reverse mapping
+ // find the significant edge points that affect the pixel
+ int iLeft = MAX (0, (int)floor (dCenter - dWidth));
+ int iRight = MIN ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1);
+
+ // cut edge points to fit in filter window in case of spill-off
+ if((iRight - iLeft + 1) > int(m_WindowSize)) {
+ if(iLeft < (int(uSrcSize) - 1 / 2)) {
+ iLeft++;
+ } else {
+ iRight--;
+ }
+ }
+
+ m_WeightTable[u].Left = iLeft;
+ m_WeightTable[u].Right = iRight;
+
+ int iSrc = 0;
+ double dTotalWeight = 0; // zero sum of weights
+ for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
+ // calculate weights
+ const double weight = dFScale * pFilter->Filter(dFScale * (dCenter - (double)iSrc));
+ m_WeightTable[u].Weights[iSrc-iLeft] = weight;
+ dTotalWeight += weight;
+ }
+ if((dTotalWeight > 0) && (dTotalWeight != 1)) {
+ // normalize weight of neighbouring points
+ for(iSrc = iLeft; iSrc <= iRight; iSrc++) {
+ // normalize point
+ m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
+ }
+ // simplify the filter, discarding null weights at the right
+ iSrc = iRight - iLeft;
+ while(m_WeightTable[u].Weights[iSrc] == 0) {
+ m_WeightTable[u].Right--;
+ iSrc--;
+ if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
+ break;
+ }
+ }
+
+ }
+ }
+}
+
+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;
+ } 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)
+ 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;
+ }
+}
|