diff options
Diffstat (limited to 'libs/freeimage/src/FreeImageToolkit/Resize.cpp')
| -rw-r--r-- | libs/freeimage/src/FreeImageToolkit/Resize.cpp | 2117 | 
1 files changed, 2117 insertions, 0 deletions
diff --git a/libs/freeimage/src/FreeImageToolkit/Resize.cpp b/libs/freeimage/src/FreeImageToolkit/Resize.cpp new file mode 100644 index 0000000000..82edb9da72 --- /dev/null +++ b/libs/freeimage/src/FreeImageToolkit/Resize.cpp @@ -0,0 +1,2117 @@ +// ========================================================== +// 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 "../stdafx.h" +#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; +	} +}  | 
