diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp new file mode 100644 index 0000000000..5421bd47ab --- /dev/null +++ b/plugins/AdvaImg/src/FreeImageToolkit/Resize.cpp @@ -0,0 +1,656 @@ +// ========================================================== +// 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; + + } + } +} + |