blob: dbc738ffd97adcc45afc38053560eb9047fe9041 [file] [log] [blame]
// ==========================================================
// Upsampling / downsampling classes
//
// Design and implementation by
// - Hervé Drolon (drolon@infonie.fr)
// - Detlev Vendt (detlev.vendt@brillit.de)
// - Carsten Klein (cklein05@users.sourceforge.net)
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================
#include "Resize.h"
/**
Returns the color type of a bitmap. In contrast to FreeImage_GetColorType,
this function optionally supports a boolean OUT parameter, that receives TRUE,
if the specified bitmap is greyscale, that is, it consists of grey colors only.
Although it returns the same value as returned by FreeImage_GetColorType for all
image types, this extended function primarily is intended for palletized images,
since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F)
images. However, the outgoing boolean is properly maintained for palletized images,
as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example.
@param dib A pointer to a FreeImage bitmap to calculate the extended color type for
@param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap
is greyscale, that is, it consists of grey colors only. This parameter can be NULL.
@return the color type of the specified bitmap
*/
static FREE_IMAGE_COLOR_TYPE
GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) {
const unsigned bpp = FreeImage_GetBPP(dib);
const unsigned size = CalculateUsedPaletteEntries(bpp);
const RGBQUAD * const pal = FreeImage_GetPalette(dib);
FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK;
BOOL bIsGrey = TRUE;
switch (bpp) {
case 1:
{
for (unsigned i = 0; i < size; i++) {
if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
color_type = FIC_PALETTE;
bIsGrey = FALSE;
break;
}
}
if (bIsGrey) {
if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) {
color_type = FIC_MINISWHITE;
} else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) {
color_type = FIC_PALETTE;
}
}
break;
}
case 4:
case 8:
{
for (unsigned i = 0; i < size; i++) {
if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
color_type = FIC_PALETTE;
bIsGrey = FALSE;
break;
}
if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) {
if ((size - i - 1) != pal[i].rgbBlue) {
color_type = FIC_PALETTE;
if (!bIsGreyscale) {
// exit loop if we're not setting
// bIsGreyscale parameter
break;
}
} else {
color_type = FIC_MINISWHITE;
}
}
}
break;
}
default:
{
color_type = FreeImage_GetColorType(dib);
bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE;
break;
}
}
if (bIsGreyscale) {
*bIsGreyscale = bIsGrey;
}
return color_type;
}
/**
Returns a pointer to an RGBA palette, created from the specified bitmap.
The RGBA palette is a copy of the specified bitmap's palette, that, additionally
contains the bitmap's transparency information in the rgbReserved member
of the palette's RGBQUAD elements.
@param dib A pointer to a FreeImage bitmap to create the RGBA palette from.
@param buffer A pointer to the buffer to store the RGBA palette.
@return A pointer to the newly created RGBA palette or NULL, if the specified
bitmap is no palletized standard bitmap. If non-NULL, the returned value is
actually the pointer passed in parameter 'buffer'.
*/
static inline RGBQUAD *
GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) {
// clone the palette
const unsigned ncolors = FreeImage_GetColorsUsed(dib);
if (ncolors == 0) {
return NULL;
}
memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD));
// merge the transparency table
const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib));
const BYTE * const tt = FreeImage_GetTransparencyTable(dib);
for (unsigned i = 0; i < ntransp; i++) {
buffer[i].rgbReserved = tt[i];
}
for (unsigned i = ntransp; i < ncolors; i++) {
buffer[i].rgbReserved = 255;
}
return buffer;
}
// --------------------------------------------------------------------------
CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
double dWidth;
double dFScale;
const double dFilterWidth = pFilter->GetWidth();
// scale factor
const double dScale = double(uDstSize) / double(uSrcSize);
if(dScale < 1.0) {
// minification
dWidth = dFilterWidth / dScale;
dFScale = dScale;
} else {
// magnification
dWidth = dFilterWidth;
dFScale = 1.0;
}
// allocate a new line contributions structure
//
// window size is the number of sampled pixels
m_WindowSize = 2 * (int)ceil(dWidth) + 1;
// length of dst line (no. of rows / cols)
m_LineLength = uDstSize;
// allocate list of contributions
m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
for(unsigned u = 0; u < m_LineLength; u++) {
// allocate contributions for every pixel
m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
}
// offset for discrete to continuous coordinate conversion
const double dOffset = (0.5 / dScale);
for(unsigned u = 0; u < m_LineLength; u++) {
// scan through line of contributions
// inverse mapping (discrete dst 'u' to continous src 'dCenter')
const double dCenter = (double)u / dScale + dOffset;
// find the significant edge points that affect the pixel
const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5));
const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize));
m_WeightTable[u].Left = iLeft;
m_WeightTable[u].Right = iRight;
double dTotalWeight = 0; // sum of weights (initialized to zero)
for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
// calculate weights
const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
// assert((iSrc-iLeft) < m_WindowSize);
m_WeightTable[u].Weights[iSrc-iLeft] = weight;
dTotalWeight += weight;
}
if((dTotalWeight > 0) && (dTotalWeight != 1)) {
// normalize weight of neighbouring points
for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
// normalize point
m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
}
}
// simplify the filter, discarding null weights at the right
{
int iTrailing = iRight - iLeft - 1;
while(m_WeightTable[u].Weights[iTrailing] == 0) {
m_WeightTable[u].Right--;
iTrailing--;
if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
break;
}
}
}
} // next dst pixel
}
CWeightsTable::~CWeightsTable() {
for(unsigned u = 0; u < m_LineLength; u++) {
// free contributions for every pixel
free(m_WeightTable[u].Weights);
}
// free list of pixels contributions
free(m_WeightTable);
}
// --------------------------------------------------------------------------
FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags) {
const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
const unsigned src_bpp = FreeImage_GetBPP(src);
// determine the image's color type
BOOL bIsGreyscale = FALSE;
FREE_IMAGE_COLOR_TYPE color_type;
if (src_bpp <= 8) {
color_type = GetExtendedColorType(src, &bIsGreyscale);
} else {
color_type = FIC_RGB;
}
// determine the required bit depth of the destination image
unsigned dst_bpp;
unsigned dst_bpp_s1 = 0;
if (color_type == FIC_PALETTE && !bIsGreyscale) {
// non greyscale FIC_PALETTE images require a high-color destination
// image (24- or 32-bits depending on the image's transparent state)
dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
} else if (src_bpp <= 8) {
// greyscale images require an 8-bit destination image
// (or a 32-bit image if the image is transparent);
// however, if flag FI_RESCALE_TRUE_COLOR is set, we will return
// a true color (24 bpp) image
if (FreeImage_IsTransparent(src)) {
dst_bpp = 32;
// additionally, for transparent images we always need a
// palette including transparency information (an RGBA palette)
// so, set color_type accordingly
color_type = FIC_PALETTE;
} else {
dst_bpp = ((flags & FI_RESCALE_TRUE_COLOR) == FI_RESCALE_TRUE_COLOR) ? 24 : 8;
// in any case, we use a fast 8-bit temporary image for the
// first filter operation (stage 1, either horizontal or
// vertical) and implicitly convert to 24 bpp (if requested
// by flag FI_RESCALE_TRUE_COLOR) during the second filter
// operation
dst_bpp_s1 = 8;
}
} else if (src_bpp == 16 && image_type == FIT_BITMAP) {
// 16-bit 555 and 565 RGB images require a high-color destination
// image (fixed to 24 bits, since 16-bit RGBs don't support
// transparency in FreeImage)
dst_bpp = 24;
} else {
// bit depth remains unchanged for all other images
dst_bpp = src_bpp;
}
// make 'stage 1' bpp a copy of the destination bpp if it
// was not explicitly set
if (dst_bpp_s1 == 0) {
dst_bpp_s1 = dst_bpp;
}
// early exit if destination size is equal to source size
if ((src_width == dst_width) && (src_height == dst_height)) {
FIBITMAP *out = src;
FIBITMAP *tmp = src;
if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
tmp = out;
}
if (src_bpp != dst_bpp) {
switch (dst_bpp) {
case 8:
out = FreeImage_ConvertToGreyscale(tmp);
break;
case 24:
out = FreeImage_ConvertTo24Bits(tmp);
break;
case 32:
out = FreeImage_ConvertTo32Bits(tmp);
break;
default:
break;
}
if (tmp != src) {
FreeImage_Unload(tmp);
tmp = NULL;
}
}
return (out != src) ? out : FreeImage_Clone(src);
}
RGBQUAD pal_buffer[256];
RGBQUAD *src_pal = NULL;
// provide the source image's palette to the rescaler for
// FIC_PALETTE type images (this includes palletized greyscale
// images with an unordered palette as well as transparent images)
if (color_type == FIC_PALETTE) {
if (dst_bpp == 32) {
// a 32-bit destination image signals transparency, so
// create an RGBA palette from the source palette
src_pal = GetRGBAPalette(src, pal_buffer);
} else {
src_pal = FreeImage_GetPalette(src);
}
}
// allocate the dst image
FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
if (!dst) {
return NULL;
}
if (dst_bpp == 8) {
RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
if (color_type == FIC_MINISWHITE) {
// build an inverted greyscale palette
CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
}
/*
else {
// build a default greyscale palette
// Currently, FreeImage_AllocateT already creates a default
// greyscale palette for 8 bpp images, so we can skip this here.
CREATE_GREYSCALE_PALETTE(dst_pal, 256);
}
*/
}
// calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
// value of src_offset_y is measured from the bottom of the image
unsigned src_offset_x = src_left;
unsigned src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;
/*
Decide which filtering order (xy or yx) is faster for this mapping.
--- The theory ---
Try to minimize calculations by counting the number of convolution multiplies
if(dst_width*src_height <= src_width*dst_height) {
// xy filtering
} else {
// yx filtering
}
--- The practice ---
Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
if(dst_width*dst_height <= src_width*dst_height) {
// xy filtering
} else {
// yx filtering
}
*/
if (dst_width <= src_width) {
// xy filtering
// -------------
FIBITMAP *tmp = NULL;
if (src_width != dst_width) {
// source and destination widths are different so, we must
// filter horizontally
if (src_height != dst_height) {
// source and destination heights are also different so, we need
// a temporary image
tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp_s1, 0, 0, 0);
if (!tmp) {
FreeImage_Unload(dst);
return NULL;
}
} else {
// source and destination heights are equal so, we can directly
// scale into destination image (second filter method will not
// be invoked)
tmp = dst;
}
// scale source image horizontally into temporary (or destination) image
horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width);
// set x and y offsets to zero for the second filter method
// invocation (the temporary image only contains the portion of
// the image to be rescaled with no offsets)
src_offset_x = 0;
src_offset_y = 0;
// also ensure, that the second filter method gets no source
// palette (the temporary image is palletized only, if it is
// greyscale; in that case, it is an 8-bit image with a linear
// palette so, the source palette is not needed or will even be
// mismatching, if the source palette is unordered)
src_pal = NULL;
} else {
// source and destination widths are equal so, just copy the
// image pointer
tmp = src;
}
if (src_height != dst_height) {
// source and destination heights are different so, scale
// temporary (or source) image vertically into destination image
verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height);
}
// free temporary image, if not pointing to either src or dst
if (tmp != src && tmp != dst) {
FreeImage_Unload(tmp);
}
} else {
// yx filtering
// -------------
// Remark:
// The yx filtering branch could be more optimized by taking into,
// account that (src_width != dst_width) is always true, which
// follows from the above condition, which selects filtering order.
// Since (dst_width <= src_width) == TRUE selects xy filtering,
// both widths must be different when performing yx filtering.
// However, to make the code more robust, not depending on that
// condition and more symmetric to the xy filtering case, these
// (src_width != dst_width) conditions are still in place.
FIBITMAP *tmp = NULL;
if (src_height != dst_height) {
// source and destination heights are different so, we must
// filter vertically
if (src_width != dst_width) {
// source and destination widths are also different so, we need
// a temporary image
tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp_s1, 0, 0, 0);
if (!tmp) {
FreeImage_Unload(dst);
return NULL;
}
} else {
// source and destination widths are equal so, we can directly
// scale into destination image (second filter method will not
// be invoked)
tmp = dst;
}
// scale source image vertically into temporary (or destination) image
verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);
// set x and y offsets to zero for the second filter method
// invocation (the temporary image only contains the portion of
// the image to be rescaled with no offsets)
src_offset_x = 0;
src_offset_y = 0;
// also ensure, that the second filter method gets no source
// palette (the temporary image is palletized only, if it is
// greyscale; in that case, it is an 8-bit image with a linear
// palette so, the source palette is not needed or will even be
// mismatching, if the source palette is unordered)
src_pal = NULL;
} else {
// source and destination heights are equal so, just copy the
// image pointer
tmp = src;
}
if (src_width != dst_width) {
// source and destination heights are different so, scale
// temporary (or source) image horizontally into destination image
horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
}
// free temporary image, if not pointing to either src or dst
if (tmp != src && tmp != dst) {
FreeImage_Unload(tmp);
}
}
return dst;
}
void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) {
// allocate and calculate the contributions
CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
// step through rows
switch(FreeImage_GetImageType(src)) {
case FIT_BITMAP:
{
switch(FreeImage_GetBPP(src)) {
case 1:
{
switch(FreeImage_GetBPP(dst)) {
case 8:
{
// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
src_offset_x >>= 3;
if (src_pal) {
// we have got a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double value = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
}
// clamp and place result in destination pixel
dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
}
}
} else {
// we do not have a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double value = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
}
value *= 0xFF;
// clamp and place result in destination pixel
dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
}
}
}
}
break;
case 24:
{
// transparently convert the non-transparent 1-bit image to 24 bpp
src_offset_x >>= 3;
if (src_pal) {
// we have got a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double r = 0, g = 0, b = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i - iLeft);
const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
const BYTE * const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits += 3;
}
}
} else {
// we do not have a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double value = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
}
value *= 0xFF;
// clamp and place result in destination pixel
const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_RED] = bval;
dst_bits[FI_RGBA_GREEN] = bval;
dst_bits[FI_RGBA_BLUE] = bval;
dst_bits += 3;
}
}
}
}
break;
case 32:
{
// transparently convert the transparent 1-bit image to 32 bpp;
// we always have got a palette here
src_offset_x >>= 3;
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double r = 0, g = 0, b = 0, a = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i - iLeft);
const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
const BYTE * const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
a += (weight * (double)entry[FI_RGBA_ALPHA]);
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
dst_bits += 4;
}
}
}
break;
}
}
break;
case 4:
{
switch(FreeImage_GetBPP(dst)) {
case 8:
{
// transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
// we always have got a palette for 4-bit images
src_offset_x >>= 1;
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double value = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
}
// clamp and place result in destination pixel
dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
}
}
}
break;
case 24:
{
// transparently convert the non-transparent 4-bit image to 24 bpp;
// we always have got a palette for 4-bit images
src_offset_x >>= 1;
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double r = 0, g = 0, b = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i - iLeft);
const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
const BYTE * const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits += 3;
}
}
}
break;
case 32:
{
// transparently convert the transparent 4-bit image to 32 bpp;
// we always have got a palette for 4-bit images
src_offset_x >>= 1;
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double r = 0, g = 0, b = 0, a = 0;
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i - iLeft);
const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
const BYTE * const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
a += (weight * (double)entry[FI_RGBA_ALPHA]);
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
dst_bits += 4;
}
}
}
break;
}
}
break;
case 8:
{
switch(FreeImage_GetBPP(dst)) {
case 8:
{
// scale the 8-bit non-transparent greyscale image
// into an 8 bpp destination image
if (src_pal) {
// we have got a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const BYTE * const pixel = src_bits + iLeft;
double value = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
value += (weightsTable.getWeight(x, i) * (double)*(BYTE *)&src_pal[pixel[i]]);
}
// clamp and place result in destination pixel
dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
}
}
} else {
// we do not have a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const BYTE * const pixel = src_bits + iLeft;
double value = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
}
// clamp and place result in destination pixel
dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
}
}
}
}
break;
case 24:
{
// transparently convert the non-transparent 8-bit image to 24 bpp
if (src_pal) {
// we have got a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const BYTE * const pixel = src_bits + iLeft;
double r = 0, g = 0, b = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits += 3;
}
}
} else {
// we do not have a palette
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const BYTE * const pixel = src_bits + iLeft;
double value = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
value += (weight * (double)pixel[i]);
}
// clamp and place result in destination pixel
const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_RED] = bval;
dst_bits[FI_RGBA_GREEN] = bval;
dst_bits[FI_RGBA_BLUE] = bval;
dst_bits += 3;
}
}
}
}
break;
case 32:
{
// transparently convert the transparent 8-bit image to 32 bpp;
// we always have got a palette here
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const BYTE * const pixel = src_bits + iLeft;
double r = 0, g = 0, b = 0, a = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
a += (weight * (double)entry[FI_RGBA_ALPHA]);
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
dst_bits += 4;
}
}
}
break;
}
}
break;
case 16:
{
// transparently convert the 16-bit non-transparent image to 24 bpp
if (IS_FORMAT_RGB565(src)) {
// image has 565 format
for (unsigned y = 0; y < height; y++) {
// scale each row
const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const WORD *pixel = src_bits + iLeft;
double r = 0, g = 0, b = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
pixel++;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits += 3;
}
}
} else {
// image has 555 format
for (unsigned y = 0; y < height; y++) {
// scale each row
const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const WORD *pixel = src_bits + iLeft;
double r = 0, g = 0, b = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
pixel++;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits += 3;
}
}
}
}
break;
case 24:
{
// scale the 24-bit non-transparent image into a 24 bpp destination image
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const BYTE * pixel = src_bits + iLeft * 3;
double r = 0, g = 0, b = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
r += (weight * (double)pixel[FI_RGBA_RED]);
g += (weight * (double)pixel[FI_RGBA_GREEN]);
b += (weight * (double)pixel[FI_RGBA_BLUE]);
pixel += 3;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits += 3;
}
}
}
break;
case 32:
{
// scale the 32-bit transparent image into a 32 bpp destination image
for (unsigned y = 0; y < height; y++) {
// scale each row
const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const BYTE *pixel = src_bits + iLeft * 4;
double r = 0, g = 0, b = 0, a = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
r += (weight * (double)pixel[FI_RGBA_RED]);
g += (weight * (double)pixel[FI_RGBA_GREEN]);
b += (weight * (double)pixel[FI_RGBA_BLUE]);
a += (weight * (double)pixel[FI_RGBA_ALPHA]);
pixel += 4;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
dst_bits += 4;
}
}
}
break;
}
}
break;
case FIT_UINT16:
{
// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
for (unsigned y = 0; y < height; y++) {
// scale each row
const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const WORD *pixel = src_bits + iLeft * wordspp;
double value = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
value += (weight * (double)pixel[0]);
pixel++;
}
// clamp and place result in destination pixel
dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
dst_bits += wordspp;
}
}
}
break;
case FIT_RGB16:
{
// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
for (unsigned y = 0; y < height; y++) {
// scale each row
const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const WORD *pixel = src_bits + iLeft * wordspp;
double r = 0, g = 0, b = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
r += (weight * (double)pixel[0]);
g += (weight * (double)pixel[1]);
b += (weight * (double)pixel[2]);
pixel += wordspp;
}
// clamp and place result in destination pixel
dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
dst_bits += wordspp;
}
}
}
break;
case FIT_RGBA16:
{
// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
for (unsigned y = 0; y < height; y++) {
// scale each row
const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
for (unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft; // retrieve right boundary
const WORD *pixel = src_bits + iLeft * wordspp;
double r = 0, g = 0, b = 0, a = 0;
// for(i = iLeft to iRight)
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i);
r += (weight * (double)pixel[0]);
g += (weight * (double)pixel[1]);
b += (weight * (double)pixel[2]);
a += (weight * (double)pixel[3]);
pixel += wordspp;
}
// clamp and place result in destination pixel
dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
dst_bits += wordspp;
}
}
}
break;
case FIT_FLOAT:
case FIT_RGBF:
case FIT_RGBAF:
{
// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);
for(unsigned y = 0; y < height; y++) {
// scale each row
const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
for(unsigned x = 0; x < dst_width; x++) {
// loop through row
const unsigned iLeft = weightsTable.getLeftBoundary(x); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(x); // retrieve right boundary
double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
for(unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(x, i-iLeft);
unsigned index = i * floatspp; // pixel index
for (unsigned j = 0; j < floatspp; j++) {
value[j] += (weight * (double)src_bits[index++]);
}
}
// place result in destination pixel
for (unsigned j = 0; j < floatspp; j++) {
dst_bits[j] = (float)value[j];
}
dst_bits += floatspp;
}
}
}
break;
}
}
/// Performs vertical image filtering
void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) {
// allocate and calculate the contributions
CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
// step through columns
switch(FreeImage_GetImageType(src)) {
case FIT_BITMAP:
{
const unsigned dst_pitch = FreeImage_GetPitch(dst);
BYTE * const dst_base = FreeImage_GetBits(dst);
switch(FreeImage_GetBPP(src)) {
case 1:
{
const unsigned src_pitch = FreeImage_GetPitch(src);
const BYTE * const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 3);
switch(FreeImage_GetBPP(dst)) {
case 8:
{
// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
if (src_pal) {
// we have got a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x;
const unsigned index = x >> 3;
const unsigned mask = 0x80 >> (x & 0x07);
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const unsigned pixel = (*src_bits & mask) != 0;
value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
src_bits += src_pitch;
}
value *= 0xFF;
// clamp and place result in destination pixel
*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
} else {
// we do not have a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x;
const unsigned index = x >> 3;
const unsigned mask = 0x80 >> (x & 0x07);
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
src_bits += src_pitch;
}
value *= 0xFF;
// clamp and place result in destination pixel
*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
}
break;
case 24:
{
// transparently convert the non-transparent 1-bit image to 24 bpp
if (src_pal) {
// we have got a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 3;
const unsigned index = x >> 3;
const unsigned mask = 0x80 >> (x & 0x07);
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
const unsigned pixel = (*src_bits & mask) != 0;
const BYTE * const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
} else {
// we do not have a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 3;
const unsigned index = x >> 3;
const unsigned mask = 0x80 >> (x & 0x07);
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
src_bits += src_pitch;
}
value *= 0xFF;
// clamp and place result in destination pixel
const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_RED] = bval;
dst_bits[FI_RGBA_GREEN] = bval;
dst_bits[FI_RGBA_BLUE] = bval;
dst_bits += dst_pitch;
}
}
}
}
break;
case 32:
{
// transparently convert the transparent 1-bit image to 32 bpp;
// we always have got a palette here
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 4;
const unsigned index = x >> 3;
const unsigned mask = 0x80 >> (x & 0x07);
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0, a = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
const unsigned pixel = (*src_bits & mask) != 0;
const BYTE * const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
a += (weight * (double)entry[FI_RGBA_ALPHA]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
break;
}
}
break;
case 4:
{
const unsigned src_pitch = FreeImage_GetPitch(src);
const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);
switch(FreeImage_GetBPP(dst)) {
case 8:
{
// transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
// we always have got a palette for 4-bit images
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x;
const unsigned index = x >> 1;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
break;
case 24:
{
// transparently convert the non-transparent 4-bit image to 24 bpp;
// we always have got a palette for 4-bit images
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 3;
const unsigned index = x >> 1;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
const BYTE *const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
break;
case 32:
{
// transparently convert the transparent 4-bit image to 32 bpp;
// we always have got a palette for 4-bit images
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 4;
const unsigned index = x >> 1;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0, a = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
const BYTE *const entry = (BYTE *)&src_pal[pixel];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
a += (weight * (double)entry[FI_RGBA_ALPHA]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
break;
}
}
break;
case 8:
{
const unsigned src_pitch = FreeImage_GetPitch(src);
const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
switch(FreeImage_GetBPP(dst)) {
case 8:
{
// scale the 8-bit non-transparent greyscale image into an 8 bpp destination image
if (src_pal) {
// we have got a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + x;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[*src_bits]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
} else {
// we do not have a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + x;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
value += (weightsTable.getWeight(y, i) * (double)*src_bits);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
}
break;
case 24:
{
// transparently convert the non-transparent 8-bit image to 24 bpp
if (src_pal) {
// we have got a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 3;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + x;
double r = 0, g = 0, b = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
} else {
// we do not have a palette
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 3;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + x;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
value += (weightsTable.getWeight(y, i) * (double)*src_bits);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_RED] = bval;
dst_bits[FI_RGBA_GREEN] = bval;
dst_bits[FI_RGBA_BLUE] = bval;
dst_bits += dst_pitch;
}
}
}
}
break;
case 32:
{
// transparently convert the transparent 8-bit image to 32 bpp;
// we always have got a palette here
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 4;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + x;
double r = 0, g = 0, b = 0, a = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
r += (weight * (double)entry[FI_RGBA_RED]);
g += (weight * (double)entry[FI_RGBA_GREEN]);
b += (weight * (double)entry[FI_RGBA_BLUE]);
a += (weight * (double)entry[FI_RGBA_ALPHA]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
break;
}
}
break;
case 16:
{
// transparently convert the 16-bit non-transparent image to 24 bpp
const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
if (IS_FORMAT_RGB565(src)) {
// image has 565 format
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 3;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const WORD *src_bits = src_base + iLeft * src_pitch + x;
double r = 0, g = 0, b = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
} else {
// image has 555 format
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
BYTE *dst_bits = dst_base + x * 3;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const WORD *src_bits = src_base + iLeft * src_pitch + x;
double r = 0, g = 0, b = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
}
break;
case 24:
{
// scale the 24-bit transparent image into a 24 bpp destination image
const unsigned src_pitch = FreeImage_GetPitch(src);
const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
const unsigned index = x * 3;
BYTE *dst_bits = dst_base + index;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
r += (weight * (double)src_bits[FI_RGBA_RED]);
g += (weight * (double)src_bits[FI_RGBA_GREEN]);
b += (weight * (double)src_bits[FI_RGBA_BLUE]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
break;
case 32:
{
// scale the 32-bit transparent image into a 32 bpp destination image
const unsigned src_pitch = FreeImage_GetPitch(src);
const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
const unsigned index = x * 4;
BYTE *dst_bits = dst_base + index;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const BYTE *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0, a = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
r += (weight * (double)src_bits[FI_RGBA_RED]);
g += (weight * (double)src_bits[FI_RGBA_GREEN]);
b += (weight * (double)src_bits[FI_RGBA_BLUE]);
a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[FI_RGBA_RED] = (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_GREEN] = (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_BLUE] = (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
dst_bits[FI_RGBA_ALPHA] = (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
dst_bits += dst_pitch;
}
}
}
break;
}
}
break;
case FIT_UINT16:
{
// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
const unsigned index = x * wordspp; // pixel index
WORD *dst_bits = dst_base + index;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const WORD *src_bits = src_base + iLeft * src_pitch + index;
double value = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
value += (weight * (double)src_bits[0]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
dst_bits += dst_pitch;
}
}
}
break;
case FIT_RGB16:
{
// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
const unsigned index = x * wordspp; // pixel index
WORD *dst_bits = dst_base + index;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const WORD *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
r += (weight * (double)src_bits[0]);
g += (weight * (double)src_bits[1]);
b += (weight * (double)src_bits[2]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
dst_bits += dst_pitch;
}
}
}
break;
case FIT_RGBA16:
{
// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
const unsigned index = x * wordspp; // pixel index
WORD *dst_bits = dst_base + index;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft; // retrieve right boundary
const WORD *src_bits = src_base + iLeft * src_pitch + index;
double r = 0, g = 0, b = 0, a = 0;
for (unsigned i = 0; i < iLimit; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i);
r += (weight * (double)src_bits[0]);
g += (weight * (double)src_bits[1]);
b += (weight * (double)src_bits[2]);
a += (weight * (double)src_bits[3]);
src_bits += src_pitch;
}
// clamp and place result in destination pixel
dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
dst_bits += dst_pitch;
}
}
}
break;
case FIT_FLOAT:
case FIT_RGBF:
case FIT_RGBAF:
{
// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float);
const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
float *const dst_base = (float *)FreeImage_GetBits(dst);
const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp;
for (unsigned x = 0; x < width; x++) {
// work on column x in dst
const unsigned index = x * floatspp; // pixel index
float *dst_bits = (float *)dst_base + index;
// scale each column
for (unsigned y = 0; y < dst_height; y++) {
// loop through column
const unsigned iLeft = weightsTable.getLeftBoundary(y); // retrieve left boundary
const unsigned iRight = weightsTable.getRightBoundary(y); // retrieve right boundary
const float *src_bits = src_base + iLeft * src_pitch + index;
double value[4] = {0, 0, 0, 0}; // 4 = 128 bpp max
for (unsigned i = iLeft; i < iRight; i++) {
// scan between boundaries
// accumulate weighted effect of each neighboring pixel
const double weight = weightsTable.getWeight(y, i - iLeft);
for (unsigned j = 0; j < floatspp; j++) {
value[j] += (weight * (double)src_bits[j]);
}
src_bits += src_pitch;
}
// place result in destination pixel
for (unsigned j = 0; j < floatspp; j++) {
dst_bits[j] = (float)value[j];
}
dst_bits += dst_pitch;
}
}
}
break;
}
}