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