Alien-FreeImage

 view release on metacpan or  search on metacpan

src/Source/FreeImageToolkit/Resize.cpp  view on Meta::CPAN

			// calculate weights
			const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
			// assert((iSrc-iLeft) < m_WindowSize);
			m_WeightTable[u].Weights[iSrc-iLeft] = weight;
			dTotalWeight += weight;
		}
		if((dTotalWeight > 0) && (dTotalWeight != 1)) {
			// normalize weight of neighbouring points
			for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
				// normalize point
				m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight; 
			}
		}

		// simplify the filter, discarding null weights at the right
		{			
			int iTrailing = iRight - iLeft - 1;
			while(m_WeightTable[u].Weights[iTrailing] == 0) {
				m_WeightTable[u].Right--;
				iTrailing--;
				if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
					break;
				}
			}
			
		}

	} // next dst pixel
}

CWeightsTable::~CWeightsTable() {
	for(unsigned u = 0; u < m_LineLength; u++) {
		// free contributions for every pixel
		free(m_WeightTable[u].Weights);
	}
	// free list of pixels contributions
	free(m_WeightTable);
}

// --------------------------------------------------------------------------

FIBITMAP* CResizeEngine::scale(FIBITMAP *src, unsigned dst_width, unsigned dst_height, unsigned src_left, unsigned src_top, unsigned src_width, unsigned src_height, unsigned flags) {

	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
	const unsigned src_bpp = FreeImage_GetBPP(src);

	// determine the image's color type
	BOOL bIsGreyscale = FALSE;
	FREE_IMAGE_COLOR_TYPE color_type;
	if (src_bpp <= 8) {
		color_type = GetExtendedColorType(src, &bIsGreyscale);
	} else {
		color_type = FIC_RGB;
	}

	// determine the required bit depth of the destination image
	unsigned dst_bpp;
	unsigned dst_bpp_s1 = 0;
	if (color_type == FIC_PALETTE && !bIsGreyscale) {
		// non greyscale FIC_PALETTE images require a high-color destination
		// image (24- or 32-bits depending on the image's transparent state)
		dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
	} else if (src_bpp <= 8) {
		// greyscale images require an 8-bit destination image
		// (or a 32-bit image if the image is transparent);
		// however, if flag FI_RESCALE_TRUE_COLOR is set, we will return
		// a true color (24 bpp) image
		if (FreeImage_IsTransparent(src)) {
			dst_bpp = 32;
			// additionally, for transparent images we always need a
			// palette including transparency information (an RGBA palette)
			// so, set color_type accordingly
			color_type = FIC_PALETTE;
		} else {
			dst_bpp = ((flags & FI_RESCALE_TRUE_COLOR) == FI_RESCALE_TRUE_COLOR) ? 24 : 8;
			// in any case, we use a fast 8-bit temporary image for the
			// first filter operation (stage 1, either horizontal or
			// vertical) and implicitly convert to 24 bpp (if requested
			// by flag FI_RESCALE_TRUE_COLOR) during the second filter
			// operation
			dst_bpp_s1 = 8;
		}
	} else if (src_bpp == 16 && image_type == FIT_BITMAP) {
		// 16-bit 555 and 565 RGB images require a high-color destination
		// image (fixed to 24 bits, since 16-bit RGBs don't support
		// transparency in FreeImage)
		dst_bpp = 24;
	} else {
		// bit depth remains unchanged for all other images
		dst_bpp = src_bpp;
	}

	// make 'stage 1' bpp a copy of the destination bpp if it
	// was not explicitly set
	if (dst_bpp_s1 == 0) {
		dst_bpp_s1 = dst_bpp;
	}

	// early exit if destination size is equal to source size
	if ((src_width == dst_width) && (src_height == dst_height)) {
		FIBITMAP *out = src;
		FIBITMAP *tmp = src;
		if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
			out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
			tmp = out;
		}
		if (src_bpp != dst_bpp) {
			switch (dst_bpp) {
				case 8:
					out = FreeImage_ConvertToGreyscale(tmp);
					break;
				case 24:
					out = FreeImage_ConvertTo24Bits(tmp);
					break;
				case 32:
					out = FreeImage_ConvertTo32Bits(tmp);
					break;
				default:
					break;
			}
			if (tmp != src) {
				FreeImage_Unload(tmp);
				tmp = NULL;
			}
		}

		return (out != src) ? out : FreeImage_Clone(src);
	}

	RGBQUAD pal_buffer[256];
	RGBQUAD *src_pal = NULL;

	// provide the source image's palette to the rescaler for
	// FIC_PALETTE type images (this includes palletized greyscale
	// images with an unordered palette as well as transparent images)
	if (color_type == FIC_PALETTE) {
		if (dst_bpp == 32) {
			// a 32-bit destination image signals transparency, so
			// create an RGBA palette from the source palette
			src_pal = GetRGBAPalette(src, pal_buffer);
		} else {
			src_pal = FreeImage_GetPalette(src);
		}
	}

	// allocate the dst image
	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
	if (!dst) {
		return NULL;
	}
	
	if (dst_bpp == 8) {
		RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
		if (color_type == FIC_MINISWHITE) {
			// build an inverted greyscale palette
			CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
		} 
		/*
		else {
			// build a default greyscale palette
			// Currently, FreeImage_AllocateT already creates a default
			// greyscale palette for 8 bpp images, so we can skip this here.
			CREATE_GREYSCALE_PALETTE(dst_pal, 256);
		}
		*/
	}

	// calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
	// value of src_offset_y is measured from the bottom of the image
	unsigned src_offset_x = src_left;
	unsigned src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;

	/*
	Decide which filtering order (xy or yx) is faster for this mapping. 
	--- The theory ---
	Try to minimize calculations by counting the number of convolution multiplies
	if(dst_width*src_height <= src_width*dst_height) {
		// xy filtering
	} else {
		// yx filtering
	}
	--- The practice ---
	Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
	if(dst_width*dst_height <= src_width*dst_height) {
		// xy filtering
	} else {
		// yx filtering
	}
	*/

	if (dst_width <= src_width) {
		// xy filtering
		// -------------

		FIBITMAP *tmp = NULL;

src/Source/FreeImageToolkit/Resize.cpp  view on Meta::CPAN

				}
			} else {
				// source and destination widths are equal so, we can directly
				// scale into destination image (second filter method will not
				// be invoked)
				tmp = dst;
			}

			// scale source image vertically into temporary (or destination) image
			verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);

			// set x and y offsets to zero for the second filter method
			// invocation (the temporary image only contains the portion of
			// the image to be rescaled with no offsets)
			src_offset_x = 0;
			src_offset_y = 0;

			// also ensure, that the second filter method gets no source
			// palette (the temporary image is palletized only, if it is
			// greyscale; in that case, it is an 8-bit image with a linear
			// palette so, the source palette is not needed or will even be
			// mismatching, if the source palette is unordered)
			src_pal = NULL;

		} else {
			// source and destination heights are equal so, just copy the
			// image pointer
			tmp = src;
		}

		if (src_width != dst_width) {
			// source and destination heights are different so, scale
			// temporary (or source) image horizontally into destination image
			horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
		}

		// free temporary image, if not pointing to either src or dst
		if (tmp != src && tmp != dst) {
			FreeImage_Unload(tmp);
		}
	}

	return dst;
} 

void CResizeEngine::horizontalFilter(FIBITMAP *const src, unsigned height, unsigned src_width, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_width) {

	// allocate and calculate the contributions
	CWeightsTable weightsTable(m_pFilter, dst_width, src_width);

	// step through rows
	switch(FreeImage_GetImageType(src)) {
		case FIT_BITMAP:
		{
			switch(FreeImage_GetBPP(src)) {
				case 1:
				{
					switch(FreeImage_GetBPP(dst)) {
						case 8:
						{
							// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
							src_offset_x >>= 3;
							if (src_pal) {
								// we have got a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);		// retrieve left boundary
										const unsigned iRight = weightsTable.getRightBoundary(x);	// retrieve right boundary
										double value = 0;

										for (unsigned i = iLeft; i < iRight; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
											value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
										}

										// clamp and place result in destination pixel
										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
									}
								}
							} else {
								// we do not have a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);		// retrieve left boundary
										const unsigned iRight = weightsTable.getRightBoundary(x);	// retrieve right boundary
										double value = 0;

										for (unsigned i = iLeft; i < iRight; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
											value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
										}
										value *= 0xFF;

										// clamp and place result in destination pixel
										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
									}
								}
							}
						}
						break;

						case 24:
						{
							// transparently convert the non-transparent 1-bit image to 24 bpp
							src_offset_x >>= 3;
							if (src_pal) {
								// we have got a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
										const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
										double r = 0, g = 0, b = 0;

										for (unsigned i = iLeft; i < iRight; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const double weight = weightsTable.getWeight(x, i - iLeft);
											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
											const BYTE * const entry = (BYTE *)&src_pal[pixel];
											r += (weight * (double)entry[FI_RGBA_RED]);
											g += (weight * (double)entry[FI_RGBA_GREEN]);
											b += (weight * (double)entry[FI_RGBA_BLUE]);
										}

										// clamp and place result in destination pixel
										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
										dst_bits += 3;
									}
								}
							} else {
								// we do not have a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
										const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
										double value = 0;

										for (unsigned i = iLeft; i < iRight; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
											value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
										}
										value *= 0xFF;

										// clamp and place result in destination pixel
										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_RED]	= bval;
										dst_bits[FI_RGBA_GREEN]	= bval;
										dst_bits[FI_RGBA_BLUE]	= bval;
										dst_bits += 3;
									}
								}
							}
						}
						break;

						case 32:
						{
							// transparently convert the transparent 1-bit image to 32 bpp; 
							// we always have got a palette here
							src_offset_x >>= 3;

							for (unsigned y = 0; y < height; y++) {
								// scale each row
								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

								for (unsigned x = 0; x < dst_width; x++) {
									// loop through row
									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
									double r = 0, g = 0, b = 0, a = 0;

									for (unsigned i = iLeft; i < iRight; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(x, i - iLeft);
										const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
										const BYTE * const entry = (BYTE *)&src_pal[pixel];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
										a += (weight * (double)entry[FI_RGBA_ALPHA]);
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
									dst_bits += 4;
								}
							}
						}
						break;
					}
				}
				break;

				case 4:
				{
					switch(FreeImage_GetBPP(dst)) {
						case 8:
						{
							// transparently convert the non-transparent 4-bit greyscale image to 8 bpp; 
							// we always have got a palette for 4-bit images
							src_offset_x >>= 1;

							for (unsigned y = 0; y < height; y++) {
								// scale each row
								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
								BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);

								for (unsigned x = 0; x < dst_width; x++) {
									// loop through row
									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
									double value = 0;

									for (unsigned i = iLeft; i < iRight; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
										value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
									}

									// clamp and place result in destination pixel
									dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
								}
							}
						}
						break;

						case 24:
						{
							// transparently convert the non-transparent 4-bit image to 24 bpp; 
							// we always have got a palette for 4-bit images
							src_offset_x >>= 1;

							for (unsigned y = 0; y < height; y++) {
								// scale each row
								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

								for (unsigned x = 0; x < dst_width; x++) {
									// loop through row
									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
									double r = 0, g = 0, b = 0;

									for (unsigned i = iLeft; i < iRight; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(x, i - iLeft);
										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
										const BYTE * const entry = (BYTE *)&src_pal[pixel];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits += 3;
								}
							}
						}
						break;

						case 32:
						{
							// transparently convert the transparent 4-bit image to 32 bpp; 
							// we always have got a palette for 4-bit images
							src_offset_x >>= 1;

							for (unsigned y = 0; y < height; y++) {
								// scale each row
								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

								for (unsigned x = 0; x < dst_width; x++) {
									// loop through row
									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
									double r = 0, g = 0, b = 0, a = 0;

									for (unsigned i = iLeft; i < iRight; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(x, i - iLeft);
										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
										const BYTE * const entry = (BYTE *)&src_pal[pixel];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
										a += (weight * (double)entry[FI_RGBA_ALPHA]);
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
									dst_bits += 4;
								}
							}
						}
						break;
					}
				}
				break;

				case 8:
				{
					switch(FreeImage_GetBPP(dst)) {
						case 8:
						{
							// scale the 8-bit non-transparent greyscale image
							// into an 8 bpp destination image
							if (src_pal) {
								// we have got a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
										const BYTE * const pixel = src_bits + iLeft;
										double value = 0;

										// for(i = iLeft to iRight)
										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											value += (weightsTable.getWeight(x, i) * (double)*(BYTE *)&src_pal[pixel[i]]);
										}

										// clamp and place result in destination pixel
										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
									}
								}
							} else {
								// we do not have a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
										const BYTE * const pixel = src_bits + iLeft;
										double value = 0;

										// for(i = iLeft to iRight)
										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
										}

										// clamp and place result in destination pixel
										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
									}
								}
							}
						}
						break;

						case 24:
						{
							// transparently convert the non-transparent 8-bit image to 24 bpp
							if (src_pal) {
								// we have got a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
										const BYTE * const pixel = src_bits + iLeft;
										double r = 0, g = 0, b = 0;

										// for(i = iLeft to iRight)
										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const double weight = weightsTable.getWeight(x, i);
											const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
											r += (weight * (double)entry[FI_RGBA_RED]);
											g += (weight * (double)entry[FI_RGBA_GREEN]);
											b += (weight * (double)entry[FI_RGBA_BLUE]);
										}

										// clamp and place result in destination pixel
										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
										dst_bits += 3;
									}
								}
							} else {
								// we do not have a palette
								for (unsigned y = 0; y < height; y++) {
									// scale each row
									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

									for (unsigned x = 0; x < dst_width; x++) {
										// loop through row
										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
										const BYTE * const pixel = src_bits + iLeft;
										double value = 0;

										// for(i = iLeft to iRight)
										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const double weight = weightsTable.getWeight(x, i);
											value += (weight * (double)pixel[i]);
										}

										// clamp and place result in destination pixel
										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_RED]	= bval;
										dst_bits[FI_RGBA_GREEN]	= bval;
										dst_bits[FI_RGBA_BLUE]	= bval;
										dst_bits += 3;
									}
								}
							}
						}
						break;

						case 32:
						{
							// transparently convert the transparent 8-bit image to 32 bpp; 
							// we always have got a palette here
							for (unsigned y = 0; y < height; y++) {
								// scale each row
								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

								for (unsigned x = 0; x < dst_width; x++) {
									// loop through row
									const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
									const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
									const BYTE * const pixel = src_bits + iLeft;
									double r = 0, g = 0, b = 0, a = 0;

									// for(i = iLeft to iRight)
									for (unsigned i = 0; i < iLimit; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(x, i);
										const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
										a += (weight * (double)entry[FI_RGBA_ALPHA]);
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
									dst_bits += 4;
								}
							}
						}
						break;
					}
				}
				break;

				case 16:
				{
					// transparently convert the 16-bit non-transparent image to 24 bpp
					if (IS_FORMAT_RGB565(src)) {
						// image has 565 format
						for (unsigned y = 0; y < height; y++) {
							// scale each row
							const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

							for (unsigned x = 0; x < dst_width; x++) {
								// loop through row
								const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
								const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
								const WORD *pixel = src_bits + iLeft;
								double r = 0, g = 0, b = 0;

								// for(i = iLeft to iRight)
								for (unsigned i = 0; i < iLimit; i++) {
									// scan between boundaries
									// accumulate weighted effect of each neighboring pixel
									const double weight = weightsTable.getWeight(x, i);
									r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
									g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
									b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
									pixel++;
								}

								// clamp and place result in destination pixel
								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits += 3;
							}
						}
					} else {
						// image has 555 format
						for (unsigned y = 0; y < height; y++) {
							// scale each row
							const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

							for (unsigned x = 0; x < dst_width; x++) {
								// loop through row
								const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
								const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
								const WORD *pixel = src_bits + iLeft;
								double r = 0, g = 0, b = 0;

								// for(i = iLeft to iRight)
								for (unsigned i = 0; i < iLimit; i++) {
									// scan between boundaries
									// accumulate weighted effect of each neighboring pixel
									const double weight = weightsTable.getWeight(x, i);
									r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
									g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
									b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
									pixel++;
								}

								// clamp and place result in destination pixel
								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits += 3;
							}
						}
					}
				}
				break;

				case 24:
				{
					// scale the 24-bit non-transparent image into a 24 bpp destination image
					for (unsigned y = 0; y < height; y++) {
						// scale each row
						const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
						BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

						for (unsigned x = 0; x < dst_width; x++) {
							// loop through row
							const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
							const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
							const BYTE * pixel = src_bits + iLeft * 3;
							double r = 0, g = 0, b = 0;

							// for(i = iLeft to iRight)
							for (unsigned i = 0; i < iLimit; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								const double weight = weightsTable.getWeight(x, i);
								r += (weight * (double)pixel[FI_RGBA_RED]);
								g += (weight * (double)pixel[FI_RGBA_GREEN]);
								b += (weight * (double)pixel[FI_RGBA_BLUE]);
								pixel += 3;
							}

							// clamp and place result in destination pixel
							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
							dst_bits += 3;
						}
					}
				}
				break;

				case 32:
				{
					// scale the 32-bit transparent image into a 32 bpp destination image
					for (unsigned y = 0; y < height; y++) {
						// scale each row
						const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
						BYTE *dst_bits = FreeImage_GetScanLine(dst, y);

						for (unsigned x = 0; x < dst_width; x++) {
							// loop through row
							const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
							const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
							const BYTE *pixel = src_bits + iLeft * 4;
							double r = 0, g = 0, b = 0, a = 0;

							// for(i = iLeft to iRight)
							for (unsigned i = 0; i < iLimit; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								const double weight = weightsTable.getWeight(x, i);
								r += (weight * (double)pixel[FI_RGBA_RED]);
								g += (weight * (double)pixel[FI_RGBA_GREEN]);
								b += (weight * (double)pixel[FI_RGBA_BLUE]);
								a += (weight * (double)pixel[FI_RGBA_ALPHA]);
								pixel += 4;
							}

							// clamp and place result in destination pixel
							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
							dst_bits += 4;
						}
					}
				}
				break;
			}
		}
		break;

		case FIT_UINT16:
		{
			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
			const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);

			for (unsigned y = 0; y < height; y++) {
				// scale each row
				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);

				for (unsigned x = 0; x < dst_width; x++) {
					// loop through row
					const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
					const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
					const WORD *pixel = src_bits + iLeft * wordspp;
					double value = 0;

					// for(i = iLeft to iRight)
					for (unsigned i = 0; i < iLimit; i++) {
						// scan between boundaries
						// accumulate weighted effect of each neighboring pixel
						const double weight = weightsTable.getWeight(x, i);						

src/Source/FreeImageToolkit/Resize.cpp  view on Meta::CPAN

			// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
			const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);

			for(unsigned y = 0; y < height; y++) {
				// scale each row
				const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
				float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);

				for(unsigned x = 0; x < dst_width; x++) {
					// loop through row
					const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
					const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
					double value[4] = {0, 0, 0, 0};                            // 4 = 128 bpp max

					for(unsigned i = iLeft; i < iRight; i++) {
						// scan between boundaries
						// accumulate weighted effect of each neighboring pixel
						const double weight = weightsTable.getWeight(x, i-iLeft);

						unsigned index = i * floatspp;	// pixel index
						for (unsigned j = 0; j < floatspp; j++) {
							value[j] += (weight * (double)src_bits[index++]);
						}
					}

					// place result in destination pixel
					for (unsigned j = 0; j < floatspp; j++) {
						dst_bits[j] = (float)value[j];
					}

					dst_bits += floatspp;
				}
			}
		}
		break;
	}
}

/// Performs vertical image filtering
void CResizeEngine::verticalFilter(FIBITMAP *const src, unsigned width, unsigned src_height, unsigned src_offset_x, unsigned src_offset_y, const RGBQUAD *const src_pal, FIBITMAP *const dst, unsigned dst_height) {

	// allocate and calculate the contributions
	CWeightsTable weightsTable(m_pFilter, dst_height, src_height);

	// step through columns
	switch(FreeImage_GetImageType(src)) {
		case FIT_BITMAP:
		{
			const unsigned dst_pitch = FreeImage_GetPitch(dst);
			BYTE * const dst_base = FreeImage_GetBits(dst);

			switch(FreeImage_GetBPP(src)) {
				case 1:
				{
					const unsigned src_pitch = FreeImage_GetPitch(src);
					const BYTE * const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 3);

					switch(FreeImage_GetBPP(dst)) {
						case 8:
						{
							// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
							if (src_pal) {
								// we have got a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x;
									const unsigned index = x >> 3;
									const unsigned mask = 0x80 >> (x & 0x07);

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
										double value = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const unsigned pixel = (*src_bits & mask) != 0;
											value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
											src_bits += src_pitch;
										}
										value *= 0xFF;

										// clamp and place result in destination pixel
										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits += dst_pitch;
									}
								}
							} else {
								// we do not have a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x;
									const unsigned index = x >> 3;
									const unsigned mask = 0x80 >> (x & 0x07);

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
										double value = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
											src_bits += src_pitch;
										}
										value *= 0xFF;

										// clamp and place result in destination pixel
										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits += dst_pitch;
									}
								}
							}
						}
						break;

						case 24:
						{
							// transparently convert the non-transparent 1-bit image to 24 bpp
							if (src_pal) {
								// we have got a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x * 3;
									const unsigned index = x >> 3;
									const unsigned mask = 0x80 >> (x & 0x07);

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
										double r = 0, g = 0, b = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const double weight = weightsTable.getWeight(y, i);
											const unsigned pixel = (*src_bits & mask) != 0;
											const BYTE * const entry = (BYTE *)&src_pal[pixel];
											r += (weight * (double)entry[FI_RGBA_RED]);
											g += (weight * (double)entry[FI_RGBA_GREEN]);
											b += (weight * (double)entry[FI_RGBA_BLUE]);
											src_bits += src_pitch;
										}

										// clamp and place result in destination pixel
										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
										dst_bits += dst_pitch;
									}
								}
							} else {
								// we do not have a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x * 3;
									const unsigned index = x >> 3;
									const unsigned mask = 0x80 >> (x & 0x07);

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
										double value = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
											src_bits += src_pitch;
										}
										value *= 0xFF;

										// clamp and place result in destination pixel
										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_RED]	= bval;
										dst_bits[FI_RGBA_GREEN]	= bval;
										dst_bits[FI_RGBA_BLUE]	= bval;
										dst_bits += dst_pitch;
									}
								}
							}
						}
						break;

						case 32:
						{
							// transparently convert the transparent 1-bit image to 32 bpp; 
							// we always have got a palette here
							for (unsigned x = 0; x < width; x++) {
								// work on column x in dst
								BYTE *dst_bits = dst_base + x * 4;
								const unsigned index = x >> 3;
								const unsigned mask = 0x80 >> (x & 0x07);

								// scale each column
								for (unsigned y = 0; y < dst_height; y++) {
									// loop through column
									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
									double r = 0, g = 0, b = 0, a = 0;

									for (unsigned i = 0; i < iLimit; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(y, i);
										const unsigned pixel = (*src_bits & mask) != 0;
										const BYTE * const entry = (BYTE *)&src_pal[pixel];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
										a += (weight * (double)entry[FI_RGBA_ALPHA]);
										src_bits += src_pitch;
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
									dst_bits += dst_pitch;
								}
							}
						}
						break;
					}
				}
				break;

				case 4:
				{
					const unsigned src_pitch = FreeImage_GetPitch(src);
					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);

					switch(FreeImage_GetBPP(dst)) {
						case 8:
						{
							// transparently convert the non-transparent 4-bit greyscale image to 8 bpp; 
							// we always have got a palette for 4-bit images
							for (unsigned x = 0; x < width; x++) {
								// work on column x in dst
								BYTE *dst_bits = dst_base + x;
								const unsigned index = x >> 1;

								// scale each column
								for (unsigned y = 0; y < dst_height; y++) {
									// loop through column
									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
									double value = 0;

									for (unsigned i = 0; i < iLimit; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
										value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
										src_bits += src_pitch;
									}

									// clamp and place result in destination pixel
									*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
									dst_bits += dst_pitch;
								}
							}
						}
						break;

						case 24:
						{
							// transparently convert the non-transparent 4-bit image to 24 bpp; 
							// we always have got a palette for 4-bit images
							for (unsigned x = 0; x < width; x++) {
								// work on column x in dst
								BYTE *dst_bits = dst_base + x * 3;
								const unsigned index = x >> 1;

								// scale each column
								for (unsigned y = 0; y < dst_height; y++) {
									// loop through column
									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
									double r = 0, g = 0, b = 0;

									for (unsigned i = 0; i < iLimit; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(y, i);
										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
										const BYTE *const entry = (BYTE *)&src_pal[pixel];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
										src_bits += src_pitch;
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits += dst_pitch;
								}
							}
						}
						break;

						case 32:
						{
							// transparently convert the transparent 4-bit image to 32 bpp; 
							// we always have got a palette for 4-bit images
							for (unsigned x = 0; x < width; x++) {
								// work on column x in dst
								BYTE *dst_bits = dst_base + x * 4;
								const unsigned index = x >> 1;

								// scale each column
								for (unsigned y = 0; y < dst_height; y++) {
									// loop through column
									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
									double r = 0, g = 0, b = 0, a = 0;

									for (unsigned i = 0; i < iLimit; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(y, i);
										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
										const BYTE *const entry = (BYTE *)&src_pal[pixel];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
										a += (weight * (double)entry[FI_RGBA_ALPHA]);
										src_bits += src_pitch;
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
									dst_bits += dst_pitch;
								}
							}
						}
						break;
					}
				}
				break;

				case 8:
				{
					const unsigned src_pitch = FreeImage_GetPitch(src);
					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;

					switch(FreeImage_GetBPP(dst)) {
						case 8:
						{
							// scale the 8-bit non-transparent greyscale image into an 8 bpp destination image
							if (src_pal) {
								// we have got a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x;

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
										double value = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[*src_bits]);
											src_bits += src_pitch;
										}

										// clamp and place result in destination pixel
										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits += dst_pitch;
									}
								}
							} else {
								// we do not have a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x;

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
										double value = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											value += (weightsTable.getWeight(y, i) * (double)*src_bits);
											src_bits += src_pitch;
										}

										// clamp and place result in destination pixel
										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits += dst_pitch;
									}
								}
							}
						}
						break;

						case 24:
						{
							// transparently convert the non-transparent 8-bit image to 24 bpp
							if (src_pal) {
								// we have got a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x * 3;

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
										double r = 0, g = 0, b = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											const double weight = weightsTable.getWeight(y, i);
											const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
											r += (weight * (double)entry[FI_RGBA_RED]);
											g += (weight * (double)entry[FI_RGBA_GREEN]);
											b += (weight * (double)entry[FI_RGBA_BLUE]);
											src_bits += src_pitch;
										}

										// clamp and place result in destination pixel
										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
										dst_bits += dst_pitch;
									}
								}
							} else {
								// we do not have a palette
								for (unsigned x = 0; x < width; x++) {
									// work on column x in dst
									BYTE *dst_bits = dst_base + x * 3;

									// scale each column
									for (unsigned y = 0; y < dst_height; y++) {
										// loop through column
										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
										double value = 0;

										for (unsigned i = 0; i < iLimit; i++) {
											// scan between boundaries
											// accumulate weighted effect of each neighboring pixel
											value += (weightsTable.getWeight(y, i) * (double)*src_bits);
											src_bits += src_pitch;
										}

										// clamp and place result in destination pixel
										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
										dst_bits[FI_RGBA_RED]	= bval;
										dst_bits[FI_RGBA_GREEN]	= bval;
										dst_bits[FI_RGBA_BLUE]	= bval;
										dst_bits += dst_pitch;
									}
								}
							}
						}
						break;

						case 32:
						{
							// transparently convert the transparent 8-bit image to 32 bpp; 
							// we always have got a palette here
							for (unsigned x = 0; x < width; x++) {
								// work on column x in dst
								BYTE *dst_bits = dst_base + x * 4;

								// scale each column
								for (unsigned y = 0; y < dst_height; y++) {
									// loop through column
									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
									const BYTE *src_bits = src_base + iLeft * src_pitch + x;
									double r = 0, g = 0, b = 0, a = 0;

									for (unsigned i = 0; i < iLimit; i++) {
										// scan between boundaries
										// accumulate weighted effect of each neighboring pixel
										const double weight = weightsTable.getWeight(y, i);
										const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
										r += (weight * (double)entry[FI_RGBA_RED]);
										g += (weight * (double)entry[FI_RGBA_GREEN]);
										b += (weight * (double)entry[FI_RGBA_BLUE]);
										a += (weight * (double)entry[FI_RGBA_ALPHA]);
										src_bits += src_pitch;
									}

									// clamp and place result in destination pixel
									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
									dst_bits += dst_pitch;
								}
							}
						}
						break;
					}
				}
				break;

				case 16:
				{
					// transparently convert the 16-bit non-transparent image to 24 bpp
					const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
					const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;

					if (IS_FORMAT_RGB565(src)) {
						// image has 565 format
						for (unsigned x = 0; x < width; x++) {
							// work on column x in dst
							BYTE *dst_bits = dst_base + x * 3;

							// scale each column
							for (unsigned y = 0; y < dst_height; y++) {
								// loop through column
								const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
								const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
								const WORD *src_bits = src_base + iLeft * src_pitch + x;
								double r = 0, g = 0, b = 0;

								for (unsigned i = 0; i < iLimit; i++) {
									// scan between boundaries
									// accumulate weighted effect of each neighboring pixel
									const double weight = weightsTable.getWeight(y, i);
									r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
									g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
									b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
									src_bits += src_pitch;
								}

								// clamp and place result in destination pixel
								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits += dst_pitch;
							}
						}
					} else {
						// image has 555 format
						for (unsigned x = 0; x < width; x++) {
							// work on column x in dst
							BYTE *dst_bits = dst_base + x * 3;

							// scale each column
							for (unsigned y = 0; y < dst_height; y++) {
								// loop through column
								const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
								const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
								const WORD *src_bits = src_base + iLeft * src_pitch + x;
								double r = 0, g = 0, b = 0;

								for (unsigned i = 0; i < iLimit; i++) {
									// scan between boundaries
									// accumulate weighted effect of each neighboring pixel
									const double weight = weightsTable.getWeight(y, i);
									r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
									g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
									b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
									src_bits += src_pitch;
								}

								// clamp and place result in destination pixel
								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
								dst_bits += dst_pitch;
							}
						}
					}
				}
				break;

				case 24:
				{
					// scale the 24-bit transparent image into a 24 bpp destination image
					const unsigned src_pitch = FreeImage_GetPitch(src);
					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;

					for (unsigned x = 0; x < width; x++) {
						// work on column x in dst
						const unsigned index = x * 3;
						BYTE *dst_bits = dst_base + index;

						// scale each column
						for (unsigned y = 0; y < dst_height; y++) {
							// loop through column
							const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
							const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
							const BYTE *src_bits = src_base + iLeft * src_pitch + index;
							double r = 0, g = 0, b = 0;

							for (unsigned i = 0; i < iLimit; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								const double weight = weightsTable.getWeight(y, i);
								r += (weight * (double)src_bits[FI_RGBA_RED]);
								g += (weight * (double)src_bits[FI_RGBA_GREEN]);
								b += (weight * (double)src_bits[FI_RGBA_BLUE]);
								src_bits += src_pitch;
							}

							// clamp and place result in destination pixel
							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
							dst_bits += dst_pitch;
						}
					}
				}
				break;

				case 32:
				{
					// scale the 32-bit transparent image into a 32 bpp destination image
					const unsigned src_pitch = FreeImage_GetPitch(src);
					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;

					for (unsigned x = 0; x < width; x++) {
						// work on column x in dst
						const unsigned index = x * 4;
						BYTE *dst_bits = dst_base + index;

						// scale each column
						for (unsigned y = 0; y < dst_height; y++) {
							// loop through column
							const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
							const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
							const BYTE *src_bits = src_base + iLeft * src_pitch + index;
							double r = 0, g = 0, b = 0, a = 0;

							for (unsigned i = 0; i < iLimit; i++) {
								// scan between boundaries
								// accumulate weighted effect of each neighboring pixel
								const double weight = weightsTable.getWeight(y, i);
								r += (weight * (double)src_bits[FI_RGBA_RED]);
								g += (weight * (double)src_bits[FI_RGBA_GREEN]);
								b += (weight * (double)src_bits[FI_RGBA_BLUE]);
								a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
								src_bits += src_pitch;
							}

							// clamp and place result in destination pixel
							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
							dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
							dst_bits += dst_pitch;
						}
					}
				}
				break;
			}
		}
		break;

		case FIT_UINT16:
		{
			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
			const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);

			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
			WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);

			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
			const WORD *const src_base = (WORD *)FreeImage_GetBits(src)	+ src_offset_y * src_pitch + src_offset_x * wordspp;

			for (unsigned x = 0; x < width; x++) {
				// work on column x in dst
				const unsigned index = x * wordspp;	// pixel index
				WORD *dst_bits = dst_base + index;

				// scale each column
				for (unsigned y = 0; y < dst_height; y++) {
					// loop through column



( run in 0.777 second using v1.01-cache-2.11-cpan-787462296c9 )