1 // ==========================================================
2 // Upsampling / downsampling classes
3 //
4 // Design and implementation by
5 // - Herv� Drolon (drolon@infonie.fr)
6 // - Detlev Vendt (detlev.vendt@brillit.de)
7 // - Carsten Klein (cklein05@users.sourceforge.net)
8 //
9 // This file is part of FreeImage 3
10 //
11 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
12 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
13 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
14 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
15 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
16 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
17 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
18 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
19 // THIS DISCLAIMER.
20 //
21 // Use at your own risk!
22 // ==========================================================
23 
24 #include "Resize.h"
25 
26 /**
27 Returns the color type of a bitmap. In contrast to FreeImage_GetColorType,
28 this function optionally supports a boolean OUT parameter, that receives TRUE,
29 if the specified bitmap is greyscale, that is, it consists of grey colors only.
30 Although it returns the same value as returned by FreeImage_GetColorType for all
31 image types, this extended function primarily is intended for palletized images,
32 since the boolean pointed to by 'bIsGreyscale' remains unchanged for RGB(A/F)
33 images. However, the outgoing boolean is properly maintained for palletized images,
34 as well as for any non-RGB image type, like FIT_UINTxx and FIT_DOUBLE, for example.
35 @param dib A pointer to a FreeImage bitmap to calculate the extended color type for
36 @param bIsGreyscale A pointer to a boolean, that receives TRUE, if the specified bitmap
37 is greyscale, that is, it consists of grey colors only. This parameter can be NULL.
38 @return the color type of the specified bitmap
39 */
40 static FREE_IMAGE_COLOR_TYPE
GetExtendedColorType(FIBITMAP * dib,BOOL * bIsGreyscale)41 GetExtendedColorType(FIBITMAP *dib, BOOL *bIsGreyscale) {
42 	const unsigned bpp = FreeImage_GetBPP(dib);
43 	const unsigned size = CalculateUsedPaletteEntries(bpp);
44 	const RGBQUAD * const pal = FreeImage_GetPalette(dib);
45 	FREE_IMAGE_COLOR_TYPE color_type = FIC_MINISBLACK;
46 	BOOL bIsGrey = TRUE;
47 
48 	switch (bpp) {
49 		case 1:
50 		{
51 			for (unsigned i = 0; i < size; i++) {
52 				if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
53 					color_type = FIC_PALETTE;
54 					bIsGrey = FALSE;
55 					break;
56 				}
57 			}
58 			if (bIsGrey) {
59 				if (pal[0].rgbBlue == 255 && pal[1].rgbBlue == 0) {
60 					color_type = FIC_MINISWHITE;
61 				} else if (pal[0].rgbBlue != 0 || pal[1].rgbBlue != 255) {
62 					color_type = FIC_PALETTE;
63 				}
64 			}
65 			break;
66 		}
67 
68 		case 4:
69 		case 8:
70 		{
71 			for (unsigned i = 0; i < size; i++) {
72 				if ((pal[i].rgbRed != pal[i].rgbGreen) || (pal[i].rgbRed != pal[i].rgbBlue)) {
73 					color_type = FIC_PALETTE;
74 					bIsGrey = FALSE;
75 					break;
76 				}
77 				if (color_type != FIC_PALETTE && pal[i].rgbBlue != i) {
78 					if ((size - i - 1) != pal[i].rgbBlue) {
79 						color_type = FIC_PALETTE;
80 						if (!bIsGreyscale) {
81 							// exit loop if we're not setting
82 							// bIsGreyscale parameter
83 							break;
84 						}
85 					} else {
86 						color_type = FIC_MINISWHITE;
87 					}
88 				}
89 			}
90 			break;
91 		}
92 
93 		default:
94 		{
95 			color_type = FreeImage_GetColorType(dib);
96 			bIsGrey = (color_type == FIC_MINISBLACK) ? TRUE : FALSE;
97 			break;
98 		}
99 
100 	}
101 	if (bIsGreyscale) {
102 		*bIsGreyscale = bIsGrey;
103 	}
104 
105 	return color_type;
106 }
107 
108 /**
109 Returns a pointer to an RGBA palette, created from the specified bitmap.
110 The RGBA palette is a copy of the specified bitmap's palette, that, additionally
111 contains the bitmap's transparency information in the rgbReserved member
112 of the palette's RGBQUAD elements.
113 @param dib A pointer to a FreeImage bitmap to create the RGBA palette from.
114 @param buffer A pointer to the buffer to store the RGBA palette.
115 @return A pointer to the newly created RGBA palette or NULL, if the specified
116 bitmap is no palletized standard bitmap. If non-NULL, the returned value is
117 actually the pointer passed in parameter 'buffer'.
118 */
119 static inline RGBQUAD *
GetRGBAPalette(FIBITMAP * dib,RGBQUAD * const buffer)120 GetRGBAPalette(FIBITMAP *dib, RGBQUAD * const buffer) {
121 	// clone the palette
122 	const unsigned ncolors = FreeImage_GetColorsUsed(dib);
123 	if (ncolors == 0) {
124 		return NULL;
125 	}
126 	memcpy(buffer, FreeImage_GetPalette(dib), ncolors * sizeof(RGBQUAD));
127 	// merge the transparency table
128 	const unsigned ntransp = MIN(ncolors, FreeImage_GetTransparencyCount(dib));
129 	const BYTE * const tt = FreeImage_GetTransparencyTable(dib);
130 	for (unsigned i = 0; i < ntransp; i++) {
131 		buffer[i].rgbReserved = tt[i];
132 	}
133 	for (unsigned i = ntransp; i < ncolors; i++) {
134 		buffer[i].rgbReserved = 255;
135 	}
136 	return buffer;
137 }
138 
139 // --------------------------------------------------------------------------
140 
CWeightsTable(CGenericFilter * pFilter,unsigned uDstSize,unsigned uSrcSize)141 CWeightsTable::CWeightsTable(CGenericFilter *pFilter, unsigned uDstSize, unsigned uSrcSize) {
142 	double dWidth;
143 	double dFScale;
144 	const double dFilterWidth = pFilter->GetWidth();
145 
146 	// scale factor
147 	const double dScale = double(uDstSize) / double(uSrcSize);
148 
149 	if(dScale < 1.0) {
150 		// minification
151 		dWidth = dFilterWidth / dScale;
152 		dFScale = dScale;
153 	} else {
154 		// magnification
155 		dWidth = dFilterWidth;
156 		dFScale = 1.0;
157 	}
158 
159 	// allocate a new line contributions structure
160 	//
161 	// window size is the number of sampled pixels
162 	m_WindowSize = 2 * (int)ceil(dWidth) + 1;
163 	// length of dst line (no. of rows / cols)
164 	m_LineLength = uDstSize;
165 
166 	 // allocate list of contributions
167 	m_WeightTable = (Contribution*)malloc(m_LineLength * sizeof(Contribution));
168 	for(unsigned u = 0; u < m_LineLength; u++) {
169 		// allocate contributions for every pixel
170 		m_WeightTable[u].Weights = (double*)malloc(m_WindowSize * sizeof(double));
171 	}
172 
173 	// offset for discrete to continuous coordinate conversion
174 	const double dOffset = (0.5 / dScale);
175 
176 	for(unsigned u = 0; u < m_LineLength; u++) {
177 		// scan through line of contributions
178 
179 		// inverse mapping (discrete dst 'u' to continous src 'dCenter')
180 		const double dCenter = (double)u / dScale + dOffset;
181 
182 		// find the significant edge points that affect the pixel
183 		const int iLeft = MAX(0, (int)(dCenter - dWidth + 0.5));
184 		const int iRight = MIN((int)(dCenter + dWidth + 0.5), int(uSrcSize));
185 
186 		m_WeightTable[u].Left = iLeft;
187 		m_WeightTable[u].Right = iRight;
188 
189 		double dTotalWeight = 0;  // sum of weights (initialized to zero)
190 		for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
191 			// calculate weights
192 			const double weight = dFScale * pFilter->Filter(dFScale * ((double)iSrc + 0.5 - dCenter));
193 			// assert((iSrc-iLeft) < m_WindowSize);
194 			m_WeightTable[u].Weights[iSrc-iLeft] = weight;
195 			dTotalWeight += weight;
196 		}
197 		if((dTotalWeight > 0) && (dTotalWeight != 1)) {
198 			// normalize weight of neighbouring points
199 			for(int iSrc = iLeft; iSrc < iRight; iSrc++) {
200 				// normalize point
201 				m_WeightTable[u].Weights[iSrc-iLeft] /= dTotalWeight;
202 			}
203 		}
204 
205 		// simplify the filter, discarding null weights at the right
206 		{
207 			int iTrailing = iRight - iLeft - 1;
208 			while(m_WeightTable[u].Weights[iTrailing] == 0) {
209 				m_WeightTable[u].Right--;
210 				iTrailing--;
211 				if(m_WeightTable[u].Right == m_WeightTable[u].Left) {
212 					break;
213 				}
214 			}
215 
216 		}
217 
218 	} // next dst pixel
219 }
220 
~CWeightsTable()221 CWeightsTable::~CWeightsTable() {
222 	for(unsigned u = 0; u < m_LineLength; u++) {
223 		// free contributions for every pixel
224 		free(m_WeightTable[u].Weights);
225 	}
226 	// free list of pixels contributions
227 	free(m_WeightTable);
228 }
229 
230 // --------------------------------------------------------------------------
231 
scale(FIBITMAP * src,unsigned dst_width,unsigned dst_height,unsigned src_left,unsigned src_top,unsigned src_width,unsigned src_height,unsigned flags)232 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) {
233 
234 	const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
235 	const unsigned src_bpp = FreeImage_GetBPP(src);
236 
237 	// determine the image's color type
238 	BOOL bIsGreyscale = FALSE;
239 	FREE_IMAGE_COLOR_TYPE color_type;
240 	if (src_bpp <= 8) {
241 		color_type = GetExtendedColorType(src, &bIsGreyscale);
242 	} else {
243 		color_type = FIC_RGB;
244 	}
245 
246 	// determine the required bit depth of the destination image
247 	unsigned dst_bpp;
248 	unsigned dst_bpp_s1 = 0;
249 	if (color_type == FIC_PALETTE && !bIsGreyscale) {
250 		// non greyscale FIC_PALETTE images require a high-color destination
251 		// image (24- or 32-bits depending on the image's transparent state)
252 		dst_bpp = FreeImage_IsTransparent(src) ? 32 : 24;
253 	} else if (src_bpp <= 8) {
254 		// greyscale images require an 8-bit destination image
255 		// (or a 32-bit image if the image is transparent);
256 		// however, if flag FI_RESCALE_TRUE_COLOR is set, we will return
257 		// a true color (24 bpp) image
258 		if (FreeImage_IsTransparent(src)) {
259 			dst_bpp = 32;
260 			// additionally, for transparent images we always need a
261 			// palette including transparency information (an RGBA palette)
262 			// so, set color_type accordingly
263 			color_type = FIC_PALETTE;
264 		} else {
265 			dst_bpp = ((flags & FI_RESCALE_TRUE_COLOR) == FI_RESCALE_TRUE_COLOR) ? 24 : 8;
266 			// in any case, we use a fast 8-bit temporary image for the
267 			// first filter operation (stage 1, either horizontal or
268 			// vertical) and implicitly convert to 24 bpp (if requested
269 			// by flag FI_RESCALE_TRUE_COLOR) during the second filter
270 			// operation
271 			dst_bpp_s1 = 8;
272 		}
273 	} else if (src_bpp == 16 && image_type == FIT_BITMAP) {
274 		// 16-bit 555 and 565 RGB images require a high-color destination
275 		// image (fixed to 24 bits, since 16-bit RGBs don't support
276 		// transparency in FreeImage)
277 		dst_bpp = 24;
278 	} else {
279 		// bit depth remains unchanged for all other images
280 		dst_bpp = src_bpp;
281 	}
282 
283 	// make 'stage 1' bpp a copy of the destination bpp if it
284 	// was not explicitly set
285 	if (dst_bpp_s1 == 0) {
286 		dst_bpp_s1 = dst_bpp;
287 	}
288 
289 	// early exit if destination size is equal to source size
290 	if ((src_width == dst_width) && (src_height == dst_height)) {
291 		FIBITMAP *out = src;
292 		FIBITMAP *tmp = src;
293 		if ((src_width != FreeImage_GetWidth(src)) || (src_height != FreeImage_GetHeight(src))) {
294 			out = FreeImage_Copy(tmp, src_left, src_top, src_left + src_width, src_top + src_height);
295 			tmp = out;
296 		}
297 		if (src_bpp != dst_bpp) {
298 			switch (dst_bpp) {
299 				case 8:
300 					out = FreeImage_ConvertToGreyscale(tmp);
301 					break;
302 				case 24:
303 					out = FreeImage_ConvertTo24Bits(tmp);
304 					break;
305 				case 32:
306 					out = FreeImage_ConvertTo32Bits(tmp);
307 					break;
308 				default:
309 					break;
310 			}
311 			if (tmp != src) {
312 				FreeImage_Unload(tmp);
313 				tmp = NULL;
314 			}
315 		}
316 
317 		return (out != src) ? out : FreeImage_Clone(src);
318 	}
319 
320 	RGBQUAD pal_buffer[256];
321 	RGBQUAD *src_pal = NULL;
322 
323 	// provide the source image's palette to the rescaler for
324 	// FIC_PALETTE type images (this includes palletized greyscale
325 	// images with an unordered palette as well as transparent images)
326 	if (color_type == FIC_PALETTE) {
327 		if (dst_bpp == 32) {
328 			// a 32-bit destination image signals transparency, so
329 			// create an RGBA palette from the source palette
330 			src_pal = GetRGBAPalette(src, pal_buffer);
331 		} else {
332 			src_pal = FreeImage_GetPalette(src);
333 		}
334 	}
335 
336 	// allocate the dst image
337 	FIBITMAP *dst = FreeImage_AllocateT(image_type, dst_width, dst_height, dst_bpp, 0, 0, 0);
338 	if (!dst) {
339 		return NULL;
340 	}
341 
342 	if (dst_bpp == 8) {
343 		RGBQUAD * const dst_pal = FreeImage_GetPalette(dst);
344 		if (color_type == FIC_MINISWHITE) {
345 			// build an inverted greyscale palette
346 			CREATE_GREYSCALE_PALETTE_REVERSE(dst_pal, 256);
347 		}
348 		/*
349 		else {
350 			// build a default greyscale palette
351 			// Currently, FreeImage_AllocateT already creates a default
352 			// greyscale palette for 8 bpp images, so we can skip this here.
353 			CREATE_GREYSCALE_PALETTE(dst_pal, 256);
354 		}
355 		*/
356 	}
357 
358 	// calculate x and y offsets; since FreeImage uses bottom-up bitmaps, the
359 	// value of src_offset_y is measured from the bottom of the image
360 	unsigned src_offset_x = src_left;
361 	unsigned src_offset_y = FreeImage_GetHeight(src) - src_height - src_top;
362 
363 	/*
364 	Decide which filtering order (xy or yx) is faster for this mapping.
365 	--- The theory ---
366 	Try to minimize calculations by counting the number of convolution multiplies
367 	if(dst_width*src_height <= src_width*dst_height) {
368 		// xy filtering
369 	} else {
370 		// yx filtering
371 	}
372 	--- The practice ---
373 	Try to minimize calculations by counting the number of vertical convolutions (the most time consuming task)
374 	if(dst_width*dst_height <= src_width*dst_height) {
375 		// xy filtering
376 	} else {
377 		// yx filtering
378 	}
379 	*/
380 
381 	if (dst_width <= src_width) {
382 		// xy filtering
383 		// -------------
384 
385 		FIBITMAP *tmp = NULL;
386 
387 		if (src_width != dst_width) {
388 			// source and destination widths are different so, we must
389 			// filter horizontally
390 			if (src_height != dst_height) {
391 				// source and destination heights are also different so, we need
392 				// a temporary image
393 				tmp = FreeImage_AllocateT(image_type, dst_width, src_height, dst_bpp_s1, 0, 0, 0);
394 				if (!tmp) {
395 					FreeImage_Unload(dst);
396 					return NULL;
397 				}
398 			} else {
399 				// source and destination heights are equal so, we can directly
400 				// scale into destination image (second filter method will not
401 				// be invoked)
402 				tmp = dst;
403 			}
404 
405 			// scale source image horizontally into temporary (or destination) image
406 			horizontalFilter(src, src_height, src_width, src_offset_x, src_offset_y, src_pal, tmp, dst_width);
407 
408 			// set x and y offsets to zero for the second filter method
409 			// invocation (the temporary image only contains the portion of
410 			// the image to be rescaled with no offsets)
411 			src_offset_x = 0;
412 			src_offset_y = 0;
413 
414 			// also ensure, that the second filter method gets no source
415 			// palette (the temporary image is palletized only, if it is
416 			// greyscale; in that case, it is an 8-bit image with a linear
417 			// palette so, the source palette is not needed or will even be
418 			// mismatching, if the source palette is unordered)
419 			src_pal = NULL;
420 		} else {
421 			// source and destination widths are equal so, just copy the
422 			// image pointer
423 			tmp = src;
424 		}
425 
426 		if (src_height != dst_height) {
427 			// source and destination heights are different so, scale
428 			// temporary (or source) image vertically into destination image
429 			verticalFilter(tmp, dst_width, src_height, src_offset_x, src_offset_y, src_pal, dst, dst_height);
430 		}
431 
432 		// free temporary image, if not pointing to either src or dst
433 		if (tmp != src && tmp != dst) {
434 			FreeImage_Unload(tmp);
435 		}
436 
437 	} else {
438 		// yx filtering
439 		// -------------
440 
441 		// Remark:
442 		// The yx filtering branch could be more optimized by taking into,
443 		// account that (src_width != dst_width) is always true, which
444 		// follows from the above condition, which selects filtering order.
445 		// Since (dst_width <= src_width) == TRUE selects xy filtering,
446 		// both widths must be different when performing yx filtering.
447 		// However, to make the code more robust, not depending on that
448 		// condition and more symmetric to the xy filtering case, these
449 		// (src_width != dst_width) conditions are still in place.
450 
451 		FIBITMAP *tmp = NULL;
452 
453 		if (src_height != dst_height) {
454 			// source and destination heights are different so, we must
455 			// filter vertically
456 			if (src_width != dst_width) {
457 				// source and destination widths are also different so, we need
458 				// a temporary image
459 				tmp = FreeImage_AllocateT(image_type, src_width, dst_height, dst_bpp_s1, 0, 0, 0);
460 				if (!tmp) {
461 					FreeImage_Unload(dst);
462 					return NULL;
463 				}
464 			} else {
465 				// source and destination widths are equal so, we can directly
466 				// scale into destination image (second filter method will not
467 				// be invoked)
468 				tmp = dst;
469 			}
470 
471 			// scale source image vertically into temporary (or destination) image
472 			verticalFilter(src, src_width, src_height, src_offset_x, src_offset_y, src_pal, tmp, dst_height);
473 
474 			// set x and y offsets to zero for the second filter method
475 			// invocation (the temporary image only contains the portion of
476 			// the image to be rescaled with no offsets)
477 			src_offset_x = 0;
478 			src_offset_y = 0;
479 
480 			// also ensure, that the second filter method gets no source
481 			// palette (the temporary image is palletized only, if it is
482 			// greyscale; in that case, it is an 8-bit image with a linear
483 			// palette so, the source palette is not needed or will even be
484 			// mismatching, if the source palette is unordered)
485 			src_pal = NULL;
486 
487 		} else {
488 			// source and destination heights are equal so, just copy the
489 			// image pointer
490 			tmp = src;
491 		}
492 
493 		if (src_width != dst_width) {
494 			// source and destination heights are different so, scale
495 			// temporary (or source) image horizontally into destination image
496 			horizontalFilter(tmp, dst_height, src_width, src_offset_x, src_offset_y, src_pal, dst, dst_width);
497 		}
498 
499 		// free temporary image, if not pointing to either src or dst
500 		if (tmp != src && tmp != dst) {
501 			FreeImage_Unload(tmp);
502 		}
503 	}
504 
505 	return dst;
506 }
507 
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)508 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) {
509 
510 	// allocate and calculate the contributions
511 	CWeightsTable weightsTable(m_pFilter, dst_width, src_width);
512 
513 	// step through rows
514 	switch(FreeImage_GetImageType(src)) {
515 		case FIT_BITMAP:
516 		{
517 			switch(FreeImage_GetBPP(src)) {
518 				case 1:
519 				{
520 					switch(FreeImage_GetBPP(dst)) {
521 						case 8:
522 						{
523 							// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
524 							src_offset_x >>= 3;
525 							if (src_pal) {
526 								// we have got a palette
527 								for (unsigned y = 0; y < height; y++) {
528 									// scale each row
529 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
530 									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
531 
532 									for (unsigned x = 0; x < dst_width; x++) {
533 										// loop through row
534 										const unsigned iLeft = weightsTable.getLeftBoundary(x);		// retrieve left boundary
535 										const unsigned iRight = weightsTable.getRightBoundary(x);	// retrieve right boundary
536 										double value = 0;
537 
538 										for (unsigned i = iLeft; i < iRight; i++) {
539 											// scan between boundaries
540 											// accumulate weighted effect of each neighboring pixel
541 											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
542 											value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
543 										}
544 
545 										// clamp and place result in destination pixel
546 										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
547 									}
548 								}
549 							} else {
550 								// we do not have a palette
551 								for (unsigned y = 0; y < height; y++) {
552 									// scale each row
553 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
554 									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
555 
556 									for (unsigned x = 0; x < dst_width; x++) {
557 										// loop through row
558 										const unsigned iLeft = weightsTable.getLeftBoundary(x);		// retrieve left boundary
559 										const unsigned iRight = weightsTable.getRightBoundary(x);	// retrieve right boundary
560 										double value = 0;
561 
562 										for (unsigned i = iLeft; i < iRight; i++) {
563 											// scan between boundaries
564 											// accumulate weighted effect of each neighboring pixel
565 											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
566 											value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
567 										}
568 										value *= 0xFF;
569 
570 										// clamp and place result in destination pixel
571 										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
572 									}
573 								}
574 							}
575 						}
576 						break;
577 
578 						case 24:
579 						{
580 							// transparently convert the non-transparent 1-bit image to 24 bpp
581 							src_offset_x >>= 3;
582 							if (src_pal) {
583 								// we have got a palette
584 								for (unsigned y = 0; y < height; y++) {
585 									// scale each row
586 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
587 									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
588 
589 									for (unsigned x = 0; x < dst_width; x++) {
590 										// loop through row
591 										const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
592 										const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
593 										double r = 0, g = 0, b = 0;
594 
595 										for (unsigned i = iLeft; i < iRight; i++) {
596 											// scan between boundaries
597 											// accumulate weighted effect of each neighboring pixel
598 											const double weight = weightsTable.getWeight(x, i - iLeft);
599 											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
600 											const BYTE * const entry = (BYTE *)&src_pal[pixel];
601 											r += (weight * (double)entry[FI_RGBA_RED]);
602 											g += (weight * (double)entry[FI_RGBA_GREEN]);
603 											b += (weight * (double)entry[FI_RGBA_BLUE]);
604 										}
605 
606 										// clamp and place result in destination pixel
607 										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
608 										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
609 										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
610 										dst_bits += 3;
611 									}
612 								}
613 							} else {
614 								// we do not have a palette
615 								for (unsigned y = 0; y < height; y++) {
616 									// scale each row
617 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
618 									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
619 
620 									for (unsigned x = 0; x < dst_width; x++) {
621 										// loop through row
622 										const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
623 										const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
624 										double value = 0;
625 
626 										for (unsigned i = iLeft; i < iRight; i++) {
627 											// scan between boundaries
628 											// accumulate weighted effect of each neighboring pixel
629 											const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
630 											value += (weightsTable.getWeight(x, i - iLeft) * (double)pixel);
631 										}
632 										value *= 0xFF;
633 
634 										// clamp and place result in destination pixel
635 										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
636 										dst_bits[FI_RGBA_RED]	= bval;
637 										dst_bits[FI_RGBA_GREEN]	= bval;
638 										dst_bits[FI_RGBA_BLUE]	= bval;
639 										dst_bits += 3;
640 									}
641 								}
642 							}
643 						}
644 						break;
645 
646 						case 32:
647 						{
648 							// transparently convert the transparent 1-bit image to 32 bpp;
649 							// we always have got a palette here
650 							src_offset_x >>= 3;
651 
652 							for (unsigned y = 0; y < height; y++) {
653 								// scale each row
654 								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
655 								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
656 
657 								for (unsigned x = 0; x < dst_width; x++) {
658 									// loop through row
659 									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
660 									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
661 									double r = 0, g = 0, b = 0, a = 0;
662 
663 									for (unsigned i = iLeft; i < iRight; i++) {
664 										// scan between boundaries
665 										// accumulate weighted effect of each neighboring pixel
666 										const double weight = weightsTable.getWeight(x, i - iLeft);
667 										const unsigned pixel = (src_bits[i >> 3] & (0x80 >> (i & 0x07))) != 0;
668 										const BYTE * const entry = (BYTE *)&src_pal[pixel];
669 										r += (weight * (double)entry[FI_RGBA_RED]);
670 										g += (weight * (double)entry[FI_RGBA_GREEN]);
671 										b += (weight * (double)entry[FI_RGBA_BLUE]);
672 										a += (weight * (double)entry[FI_RGBA_ALPHA]);
673 									}
674 
675 									// clamp and place result in destination pixel
676 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
677 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
678 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
679 									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
680 									dst_bits += 4;
681 								}
682 							}
683 						}
684 						break;
685 					}
686 				}
687 				break;
688 
689 				case 4:
690 				{
691 					switch(FreeImage_GetBPP(dst)) {
692 						case 8:
693 						{
694 							// transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
695 							// we always have got a palette for 4-bit images
696 							src_offset_x >>= 1;
697 
698 							for (unsigned y = 0; y < height; y++) {
699 								// scale each row
700 								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
701 								BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
702 
703 								for (unsigned x = 0; x < dst_width; x++) {
704 									// loop through row
705 									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
706 									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
707 									double value = 0;
708 
709 									for (unsigned i = iLeft; i < iRight; i++) {
710 										// scan between boundaries
711 										// accumulate weighted effect of each neighboring pixel
712 										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
713 										value += (weightsTable.getWeight(x, i - iLeft) * (double)*(BYTE *)&src_pal[pixel]);
714 									}
715 
716 									// clamp and place result in destination pixel
717 									dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
718 								}
719 							}
720 						}
721 						break;
722 
723 						case 24:
724 						{
725 							// transparently convert the non-transparent 4-bit image to 24 bpp;
726 							// we always have got a palette for 4-bit images
727 							src_offset_x >>= 1;
728 
729 							for (unsigned y = 0; y < height; y++) {
730 								// scale each row
731 								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
732 								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
733 
734 								for (unsigned x = 0; x < dst_width; x++) {
735 									// loop through row
736 									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
737 									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
738 									double r = 0, g = 0, b = 0;
739 
740 									for (unsigned i = iLeft; i < iRight; i++) {
741 										// scan between boundaries
742 										// accumulate weighted effect of each neighboring pixel
743 										const double weight = weightsTable.getWeight(x, i - iLeft);
744 										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
745 										const BYTE * const entry = (BYTE *)&src_pal[pixel];
746 										r += (weight * (double)entry[FI_RGBA_RED]);
747 										g += (weight * (double)entry[FI_RGBA_GREEN]);
748 										b += (weight * (double)entry[FI_RGBA_BLUE]);
749 									}
750 
751 									// clamp and place result in destination pixel
752 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
753 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
754 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
755 									dst_bits += 3;
756 								}
757 							}
758 						}
759 						break;
760 
761 						case 32:
762 						{
763 							// transparently convert the transparent 4-bit image to 32 bpp;
764 							// we always have got a palette for 4-bit images
765 							src_offset_x >>= 1;
766 
767 							for (unsigned y = 0; y < height; y++) {
768 								// scale each row
769 								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
770 								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
771 
772 								for (unsigned x = 0; x < dst_width; x++) {
773 									// loop through row
774 									const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
775 									const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
776 									double r = 0, g = 0, b = 0, a = 0;
777 
778 									for (unsigned i = iLeft; i < iRight; i++) {
779 										// scan between boundaries
780 										// accumulate weighted effect of each neighboring pixel
781 										const double weight = weightsTable.getWeight(x, i - iLeft);
782 										const unsigned pixel = i & 0x01 ? src_bits[i >> 1] & 0x0F : src_bits[i >> 1] >> 4;
783 										const BYTE * const entry = (BYTE *)&src_pal[pixel];
784 										r += (weight * (double)entry[FI_RGBA_RED]);
785 										g += (weight * (double)entry[FI_RGBA_GREEN]);
786 										b += (weight * (double)entry[FI_RGBA_BLUE]);
787 										a += (weight * (double)entry[FI_RGBA_ALPHA]);
788 									}
789 
790 									// clamp and place result in destination pixel
791 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
792 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
793 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
794 									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
795 									dst_bits += 4;
796 								}
797 							}
798 						}
799 						break;
800 					}
801 				}
802 				break;
803 
804 				case 8:
805 				{
806 					switch(FreeImage_GetBPP(dst)) {
807 						case 8:
808 						{
809 							// scale the 8-bit non-transparent greyscale image
810 							// into an 8 bpp destination image
811 							if (src_pal) {
812 								// we have got a palette
813 								for (unsigned y = 0; y < height; y++) {
814 									// scale each row
815 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
816 									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
817 
818 									for (unsigned x = 0; x < dst_width; x++) {
819 										// loop through row
820 										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
821 										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
822 										const BYTE * const pixel = src_bits + iLeft;
823 										double value = 0;
824 
825 										// for(i = iLeft to iRight)
826 										for (unsigned i = 0; i < iLimit; i++) {
827 											// scan between boundaries
828 											// accumulate weighted effect of each neighboring pixel
829 											value += (weightsTable.getWeight(x, i) * (double)*(BYTE *)&src_pal[pixel[i]]);
830 										}
831 
832 										// clamp and place result in destination pixel
833 										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
834 									}
835 								}
836 							} else {
837 								// we do not have a palette
838 								for (unsigned y = 0; y < height; y++) {
839 									// scale each row
840 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
841 									BYTE * const dst_bits = FreeImage_GetScanLine(dst, y);
842 
843 									for (unsigned x = 0; x < dst_width; x++) {
844 										// loop through row
845 										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
846 										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
847 										const BYTE * const pixel = src_bits + iLeft;
848 										double value = 0;
849 
850 										// for(i = iLeft to iRight)
851 										for (unsigned i = 0; i < iLimit; i++) {
852 											// scan between boundaries
853 											// accumulate weighted effect of each neighboring pixel
854 											value += (weightsTable.getWeight(x, i) * (double)pixel[i]);
855 										}
856 
857 										// clamp and place result in destination pixel
858 										dst_bits[x] = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
859 									}
860 								}
861 							}
862 						}
863 						break;
864 
865 						case 24:
866 						{
867 							// transparently convert the non-transparent 8-bit image to 24 bpp
868 							if (src_pal) {
869 								// we have got a palette
870 								for (unsigned y = 0; y < height; y++) {
871 									// scale each row
872 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
873 									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
874 
875 									for (unsigned x = 0; x < dst_width; x++) {
876 										// loop through row
877 										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
878 										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
879 										const BYTE * const pixel = src_bits + iLeft;
880 										double r = 0, g = 0, b = 0;
881 
882 										// for(i = iLeft to iRight)
883 										for (unsigned i = 0; i < iLimit; i++) {
884 											// scan between boundaries
885 											// accumulate weighted effect of each neighboring pixel
886 											const double weight = weightsTable.getWeight(x, i);
887 											const BYTE *const entry = (BYTE *)&src_pal[pixel[i]];
888 											r += (weight * (double)entry[FI_RGBA_RED]);
889 											g += (weight * (double)entry[FI_RGBA_GREEN]);
890 											b += (weight * (double)entry[FI_RGBA_BLUE]);
891 										}
892 
893 										// clamp and place result in destination pixel
894 										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
895 										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
896 										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
897 										dst_bits += 3;
898 									}
899 								}
900 							} else {
901 								// we do not have a palette
902 								for (unsigned y = 0; y < height; y++) {
903 									// scale each row
904 									const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
905 									BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
906 
907 									for (unsigned x = 0; x < dst_width; x++) {
908 										// loop through row
909 										const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
910 										const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
911 										const BYTE * const pixel = src_bits + iLeft;
912 										double value = 0;
913 
914 										// for(i = iLeft to iRight)
915 										for (unsigned i = 0; i < iLimit; i++) {
916 											// scan between boundaries
917 											// accumulate weighted effect of each neighboring pixel
918 											const double weight = weightsTable.getWeight(x, i);
919 											value += (weight * (double)pixel[i]);
920 										}
921 
922 										// clamp and place result in destination pixel
923 										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
924 										dst_bits[FI_RGBA_RED]	= bval;
925 										dst_bits[FI_RGBA_GREEN]	= bval;
926 										dst_bits[FI_RGBA_BLUE]	= bval;
927 										dst_bits += 3;
928 									}
929 								}
930 							}
931 						}
932 						break;
933 
934 						case 32:
935 						{
936 							// transparently convert the transparent 8-bit image to 32 bpp;
937 							// we always have got a palette here
938 							for (unsigned y = 0; y < height; y++) {
939 								// scale each row
940 								const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
941 								BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
942 
943 								for (unsigned x = 0; x < dst_width; x++) {
944 									// loop through row
945 									const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
946 									const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
947 									const BYTE * const pixel = src_bits + iLeft;
948 									double r = 0, g = 0, b = 0, a = 0;
949 
950 									// for(i = iLeft to iRight)
951 									for (unsigned i = 0; i < iLimit; i++) {
952 										// scan between boundaries
953 										// accumulate weighted effect of each neighboring pixel
954 										const double weight = weightsTable.getWeight(x, i);
955 										const BYTE * const entry = (BYTE *)&src_pal[pixel[i]];
956 										r += (weight * (double)entry[FI_RGBA_RED]);
957 										g += (weight * (double)entry[FI_RGBA_GREEN]);
958 										b += (weight * (double)entry[FI_RGBA_BLUE]);
959 										a += (weight * (double)entry[FI_RGBA_ALPHA]);
960 									}
961 
962 									// clamp and place result in destination pixel
963 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
964 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
965 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
966 									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
967 									dst_bits += 4;
968 								}
969 							}
970 						}
971 						break;
972 					}
973 				}
974 				break;
975 
976 				case 16:
977 				{
978 					// transparently convert the 16-bit non-transparent image to 24 bpp
979 					if (IS_FORMAT_RGB565(src)) {
980 						// image has 565 format
981 						for (unsigned y = 0; y < height; y++) {
982 							// scale each row
983 							const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
984 							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
985 
986 							for (unsigned x = 0; x < dst_width; x++) {
987 								// loop through row
988 								const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
989 								const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
990 								const WORD *pixel = src_bits + iLeft;
991 								double r = 0, g = 0, b = 0;
992 
993 								// for(i = iLeft to iRight)
994 								for (unsigned i = 0; i < iLimit; i++) {
995 									// scan between boundaries
996 									// accumulate weighted effect of each neighboring pixel
997 									const double weight = weightsTable.getWeight(x, i);
998 									r += (weight * (double)((*pixel & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
999 									g += (weight * (double)((*pixel & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
1000 									b += (weight * (double)((*pixel & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
1001 									pixel++;
1002 								}
1003 
1004 								// clamp and place result in destination pixel
1005 								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1006 								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
1007 								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1008 								dst_bits += 3;
1009 							}
1010 						}
1011 					} else {
1012 						// image has 555 format
1013 						for (unsigned y = 0; y < height; y++) {
1014 							// scale each row
1015 							const WORD * const src_bits = (WORD *)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x;
1016 							BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
1017 
1018 							for (unsigned x = 0; x < dst_width; x++) {
1019 								// loop through row
1020 								const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
1021 								const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
1022 								const WORD *pixel = src_bits + iLeft;
1023 								double r = 0, g = 0, b = 0;
1024 
1025 								// for(i = iLeft to iRight)
1026 								for (unsigned i = 0; i < iLimit; i++) {
1027 									// scan between boundaries
1028 									// accumulate weighted effect of each neighboring pixel
1029 									const double weight = weightsTable.getWeight(x, i);
1030 									r += (weight * (double)((*pixel & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
1031 									g += (weight * (double)((*pixel & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
1032 									b += (weight * (double)((*pixel & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
1033 									pixel++;
1034 								}
1035 
1036 								// clamp and place result in destination pixel
1037 								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1038 								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1039 								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1040 								dst_bits += 3;
1041 							}
1042 						}
1043 					}
1044 				}
1045 				break;
1046 
1047 				case 24:
1048 				{
1049 					// scale the 24-bit non-transparent image into a 24 bpp destination image
1050 					for (unsigned y = 0; y < height; y++) {
1051 						// scale each row
1052 						const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 3;
1053 						BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
1054 
1055 						for (unsigned x = 0; x < dst_width; x++) {
1056 							// loop through row
1057 							const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
1058 							const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
1059 							const BYTE * pixel = src_bits + iLeft * 3;
1060 							double r = 0, g = 0, b = 0;
1061 
1062 							// for(i = iLeft to iRight)
1063 							for (unsigned i = 0; i < iLimit; i++) {
1064 								// scan between boundaries
1065 								// accumulate weighted effect of each neighboring pixel
1066 								const double weight = weightsTable.getWeight(x, i);
1067 								r += (weight * (double)pixel[FI_RGBA_RED]);
1068 								g += (weight * (double)pixel[FI_RGBA_GREEN]);
1069 								b += (weight * (double)pixel[FI_RGBA_BLUE]);
1070 								pixel += 3;
1071 							}
1072 
1073 							// clamp and place result in destination pixel
1074 							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1075 							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1076 							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1077 							dst_bits += 3;
1078 						}
1079 					}
1080 				}
1081 				break;
1082 
1083 				case 32:
1084 				{
1085 					// scale the 32-bit transparent image into a 32 bpp destination image
1086 					for (unsigned y = 0; y < height; y++) {
1087 						// scale each row
1088 						const BYTE * const src_bits = FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x * 4;
1089 						BYTE *dst_bits = FreeImage_GetScanLine(dst, y);
1090 
1091 						for (unsigned x = 0; x < dst_width; x++) {
1092 							// loop through row
1093 							const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
1094 							const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
1095 							const BYTE *pixel = src_bits + iLeft * 4;
1096 							double r = 0, g = 0, b = 0, a = 0;
1097 
1098 							// for(i = iLeft to iRight)
1099 							for (unsigned i = 0; i < iLimit; i++) {
1100 								// scan between boundaries
1101 								// accumulate weighted effect of each neighboring pixel
1102 								const double weight = weightsTable.getWeight(x, i);
1103 								r += (weight * (double)pixel[FI_RGBA_RED]);
1104 								g += (weight * (double)pixel[FI_RGBA_GREEN]);
1105 								b += (weight * (double)pixel[FI_RGBA_BLUE]);
1106 								a += (weight * (double)pixel[FI_RGBA_ALPHA]);
1107 								pixel += 4;
1108 							}
1109 
1110 							// clamp and place result in destination pixel
1111 							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1112 							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1113 							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1114 							dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
1115 							dst_bits += 4;
1116 						}
1117 					}
1118 				}
1119 				break;
1120 			}
1121 		}
1122 		break;
1123 
1124 		case FIT_UINT16:
1125 		{
1126 			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
1127 			const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
1128 
1129 			for (unsigned y = 0; y < height; y++) {
1130 				// scale each row
1131 				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
1132 				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
1133 
1134 				for (unsigned x = 0; x < dst_width; x++) {
1135 					// loop through row
1136 					const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
1137 					const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
1138 					const WORD *pixel = src_bits + iLeft * wordspp;
1139 					double value = 0;
1140 
1141 					// for(i = iLeft to iRight)
1142 					for (unsigned i = 0; i < iLimit; i++) {
1143 						// scan between boundaries
1144 						// accumulate weighted effect of each neighboring pixel
1145 						const double weight = weightsTable.getWeight(x, i);
1146 						value += (weight * (double)pixel[0]);
1147 						pixel++;
1148 					}
1149 
1150 					// clamp and place result in destination pixel
1151 					dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
1152 					dst_bits += wordspp;
1153 				}
1154 			}
1155 		}
1156 		break;
1157 
1158 		case FIT_RGB16:
1159 		{
1160 			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
1161 			const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
1162 
1163 			for (unsigned y = 0; y < height; y++) {
1164 				// scale each row
1165 				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
1166 				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
1167 
1168 				for (unsigned x = 0; x < dst_width; x++) {
1169 					// loop through row
1170 					const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
1171 					const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
1172 					const WORD *pixel = src_bits + iLeft * wordspp;
1173 					double r = 0, g = 0, b = 0;
1174 
1175 					// for(i = iLeft to iRight)
1176 					for (unsigned i = 0; i < iLimit; i++) {
1177 						// scan between boundaries
1178 						// accumulate weighted effect of each neighboring pixel
1179 						const double weight = weightsTable.getWeight(x, i);
1180 						r += (weight * (double)pixel[0]);
1181 						g += (weight * (double)pixel[1]);
1182 						b += (weight * (double)pixel[2]);
1183 						pixel += wordspp;
1184 					}
1185 
1186 					// clamp and place result in destination pixel
1187 					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
1188 					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
1189 					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
1190 					dst_bits += wordspp;
1191 				}
1192 			}
1193 		}
1194 		break;
1195 
1196 		case FIT_RGBA16:
1197 		{
1198 			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
1199 			const unsigned wordspp = (FreeImage_GetLine(src) / src_width) / sizeof(WORD);
1200 
1201 			for (unsigned y = 0; y < height; y++) {
1202 				// scale each row
1203 				const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(WORD);
1204 				WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y);
1205 
1206 				for (unsigned x = 0; x < dst_width; x++) {
1207 					// loop through row
1208 					const unsigned iLeft = weightsTable.getLeftBoundary(x);				// retrieve left boundary
1209 					const unsigned iLimit = weightsTable.getRightBoundary(x) - iLeft;	// retrieve right boundary
1210 					const WORD *pixel = src_bits + iLeft * wordspp;
1211 					double r = 0, g = 0, b = 0, a = 0;
1212 
1213 					// for(i = iLeft to iRight)
1214 					for (unsigned i = 0; i < iLimit; i++) {
1215 						// scan between boundaries
1216 						// accumulate weighted effect of each neighboring pixel
1217 						const double weight = weightsTable.getWeight(x, i);
1218 						r += (weight * (double)pixel[0]);
1219 						g += (weight * (double)pixel[1]);
1220 						b += (weight * (double)pixel[2]);
1221 						a += (weight * (double)pixel[3]);
1222 						pixel += wordspp;
1223 					}
1224 
1225 					// clamp and place result in destination pixel
1226 					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
1227 					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
1228 					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
1229 					dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
1230 					dst_bits += wordspp;
1231 				}
1232 			}
1233 		}
1234 		break;
1235 
1236 		case FIT_FLOAT:
1237 		case FIT_RGBF:
1238 		case FIT_RGBAF:
1239 		{
1240 			// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
1241 			const unsigned floatspp = (FreeImage_GetLine(src) / src_width) / sizeof(float);
1242 
1243 			for(unsigned y = 0; y < height; y++) {
1244 				// scale each row
1245 				const float *src_bits = (float*)FreeImage_GetScanLine(src, y + src_offset_y) + src_offset_x / sizeof(float);
1246 				float *dst_bits = (float*)FreeImage_GetScanLine(dst, y);
1247 
1248 				for(unsigned x = 0; x < dst_width; x++) {
1249 					// loop through row
1250 					const unsigned iLeft = weightsTable.getLeftBoundary(x);    // retrieve left boundary
1251 					const unsigned iRight = weightsTable.getRightBoundary(x);  // retrieve right boundary
1252 					double value[4] = {0, 0, 0, 0};                            // 4 = 128 bpp max
1253 
1254 					for(unsigned i = iLeft; i < iRight; i++) {
1255 						// scan between boundaries
1256 						// accumulate weighted effect of each neighboring pixel
1257 						const double weight = weightsTable.getWeight(x, i-iLeft);
1258 
1259 						unsigned index = i * floatspp;	// pixel index
1260 						for (unsigned j = 0; j < floatspp; j++) {
1261 							value[j] += (weight * (double)src_bits[index++]);
1262 						}
1263 					}
1264 
1265 					// place result in destination pixel
1266 					for (unsigned j = 0; j < floatspp; j++) {
1267 						dst_bits[j] = (float)value[j];
1268 					}
1269 
1270 					dst_bits += floatspp;
1271 				}
1272 			}
1273 		}
1274 		break;
1275 	}
1276 }
1277 
1278 /// Performs vertical image filtering
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)1279 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) {
1280 
1281 	// allocate and calculate the contributions
1282 	CWeightsTable weightsTable(m_pFilter, dst_height, src_height);
1283 
1284 	// step through columns
1285 	switch(FreeImage_GetImageType(src)) {
1286 		case FIT_BITMAP:
1287 		{
1288 			const unsigned dst_pitch = FreeImage_GetPitch(dst);
1289 			BYTE * const dst_base = FreeImage_GetBits(dst);
1290 
1291 			switch(FreeImage_GetBPP(src)) {
1292 				case 1:
1293 				{
1294 					const unsigned src_pitch = FreeImage_GetPitch(src);
1295 					const BYTE * const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 3);
1296 
1297 					switch(FreeImage_GetBPP(dst)) {
1298 						case 8:
1299 						{
1300 							// transparently convert the 1-bit non-transparent greyscale image to 8 bpp
1301 							if (src_pal) {
1302 								// we have got a palette
1303 								for (unsigned x = 0; x < width; x++) {
1304 									// work on column x in dst
1305 									BYTE *dst_bits = dst_base + x;
1306 									const unsigned index = x >> 3;
1307 									const unsigned mask = 0x80 >> (x & 0x07);
1308 
1309 									// scale each column
1310 									for (unsigned y = 0; y < dst_height; y++) {
1311 										// loop through column
1312 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1313 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1314 										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1315 										double value = 0;
1316 
1317 										for (unsigned i = 0; i < iLimit; i++) {
1318 											// scan between boundaries
1319 											// accumulate weighted effect of each neighboring pixel
1320 											const unsigned pixel = (*src_bits & mask) != 0;
1321 											value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
1322 											src_bits += src_pitch;
1323 										}
1324 										value *= 0xFF;
1325 
1326 										// clamp and place result in destination pixel
1327 										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
1328 										dst_bits += dst_pitch;
1329 									}
1330 								}
1331 							} else {
1332 								// we do not have a palette
1333 								for (unsigned x = 0; x < width; x++) {
1334 									// work on column x in dst
1335 									BYTE *dst_bits = dst_base + x;
1336 									const unsigned index = x >> 3;
1337 									const unsigned mask = 0x80 >> (x & 0x07);
1338 
1339 									// scale each column
1340 									for (unsigned y = 0; y < dst_height; y++) {
1341 										// loop through column
1342 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1343 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1344 										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1345 										double value = 0;
1346 
1347 										for (unsigned i = 0; i < iLimit; i++) {
1348 											// scan between boundaries
1349 											// accumulate weighted effect of each neighboring pixel
1350 											value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
1351 											src_bits += src_pitch;
1352 										}
1353 										value *= 0xFF;
1354 
1355 										// clamp and place result in destination pixel
1356 										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
1357 										dst_bits += dst_pitch;
1358 									}
1359 								}
1360 							}
1361 						}
1362 						break;
1363 
1364 						case 24:
1365 						{
1366 							// transparently convert the non-transparent 1-bit image to 24 bpp
1367 							if (src_pal) {
1368 								// we have got a palette
1369 								for (unsigned x = 0; x < width; x++) {
1370 									// work on column x in dst
1371 									BYTE *dst_bits = dst_base + x * 3;
1372 									const unsigned index = x >> 3;
1373 									const unsigned mask = 0x80 >> (x & 0x07);
1374 
1375 									// scale each column
1376 									for (unsigned y = 0; y < dst_height; y++) {
1377 										// loop through column
1378 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1379 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1380 										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1381 										double r = 0, g = 0, b = 0;
1382 
1383 										for (unsigned i = 0; i < iLimit; i++) {
1384 											// scan between boundaries
1385 											// accumulate weighted effect of each neighboring pixel
1386 											const double weight = weightsTable.getWeight(y, i);
1387 											const unsigned pixel = (*src_bits & mask) != 0;
1388 											const BYTE * const entry = (BYTE *)&src_pal[pixel];
1389 											r += (weight * (double)entry[FI_RGBA_RED]);
1390 											g += (weight * (double)entry[FI_RGBA_GREEN]);
1391 											b += (weight * (double)entry[FI_RGBA_BLUE]);
1392 											src_bits += src_pitch;
1393 										}
1394 
1395 										// clamp and place result in destination pixel
1396 										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1397 										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1398 										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1399 										dst_bits += dst_pitch;
1400 									}
1401 								}
1402 							} else {
1403 								// we do not have a palette
1404 								for (unsigned x = 0; x < width; x++) {
1405 									// work on column x in dst
1406 									BYTE *dst_bits = dst_base + x * 3;
1407 									const unsigned index = x >> 3;
1408 									const unsigned mask = 0x80 >> (x & 0x07);
1409 
1410 									// scale each column
1411 									for (unsigned y = 0; y < dst_height; y++) {
1412 										// loop through column
1413 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1414 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1415 										const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1416 										double value = 0;
1417 
1418 										for (unsigned i = 0; i < iLimit; i++) {
1419 											// scan between boundaries
1420 											// accumulate weighted effect of each neighboring pixel
1421 											value += (weightsTable.getWeight(y, i) * (double)((*src_bits & mask) != 0));
1422 											src_bits += src_pitch;
1423 										}
1424 										value *= 0xFF;
1425 
1426 										// clamp and place result in destination pixel
1427 										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
1428 										dst_bits[FI_RGBA_RED]	= bval;
1429 										dst_bits[FI_RGBA_GREEN]	= bval;
1430 										dst_bits[FI_RGBA_BLUE]	= bval;
1431 										dst_bits += dst_pitch;
1432 									}
1433 								}
1434 							}
1435 						}
1436 						break;
1437 
1438 						case 32:
1439 						{
1440 							// transparently convert the transparent 1-bit image to 32 bpp;
1441 							// we always have got a palette here
1442 							for (unsigned x = 0; x < width; x++) {
1443 								// work on column x in dst
1444 								BYTE *dst_bits = dst_base + x * 4;
1445 								const unsigned index = x >> 3;
1446 								const unsigned mask = 0x80 >> (x & 0x07);
1447 
1448 								// scale each column
1449 								for (unsigned y = 0; y < dst_height; y++) {
1450 									// loop through column
1451 									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1452 									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1453 									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1454 									double r = 0, g = 0, b = 0, a = 0;
1455 
1456 									for (unsigned i = 0; i < iLimit; i++) {
1457 										// scan between boundaries
1458 										// accumulate weighted effect of each neighboring pixel
1459 										const double weight = weightsTable.getWeight(y, i);
1460 										const unsigned pixel = (*src_bits & mask) != 0;
1461 										const BYTE * const entry = (BYTE *)&src_pal[pixel];
1462 										r += (weight * (double)entry[FI_RGBA_RED]);
1463 										g += (weight * (double)entry[FI_RGBA_GREEN]);
1464 										b += (weight * (double)entry[FI_RGBA_BLUE]);
1465 										a += (weight * (double)entry[FI_RGBA_ALPHA]);
1466 										src_bits += src_pitch;
1467 									}
1468 
1469 									// clamp and place result in destination pixel
1470 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1471 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1472 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1473 									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
1474 									dst_bits += dst_pitch;
1475 								}
1476 							}
1477 						}
1478 						break;
1479 					}
1480 				}
1481 				break;
1482 
1483 				case 4:
1484 				{
1485 					const unsigned src_pitch = FreeImage_GetPitch(src);
1486 					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + (src_offset_x >> 1);
1487 
1488 					switch(FreeImage_GetBPP(dst)) {
1489 						case 8:
1490 						{
1491 							// transparently convert the non-transparent 4-bit greyscale image to 8 bpp;
1492 							// we always have got a palette for 4-bit images
1493 							for (unsigned x = 0; x < width; x++) {
1494 								// work on column x in dst
1495 								BYTE *dst_bits = dst_base + x;
1496 								const unsigned index = x >> 1;
1497 
1498 								// scale each column
1499 								for (unsigned y = 0; y < dst_height; y++) {
1500 									// loop through column
1501 									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1502 									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1503 									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1504 									double value = 0;
1505 
1506 									for (unsigned i = 0; i < iLimit; i++) {
1507 										// scan between boundaries
1508 										// accumulate weighted effect of each neighboring pixel
1509 										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
1510 										value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[pixel]);
1511 										src_bits += src_pitch;
1512 									}
1513 
1514 									// clamp and place result in destination pixel
1515 									*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
1516 									dst_bits += dst_pitch;
1517 								}
1518 							}
1519 						}
1520 						break;
1521 
1522 						case 24:
1523 						{
1524 							// transparently convert the non-transparent 4-bit image to 24 bpp;
1525 							// we always have got a palette for 4-bit images
1526 							for (unsigned x = 0; x < width; x++) {
1527 								// work on column x in dst
1528 								BYTE *dst_bits = dst_base + x * 3;
1529 								const unsigned index = x >> 1;
1530 
1531 								// scale each column
1532 								for (unsigned y = 0; y < dst_height; y++) {
1533 									// loop through column
1534 									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1535 									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1536 									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1537 									double r = 0, g = 0, b = 0;
1538 
1539 									for (unsigned i = 0; i < iLimit; i++) {
1540 										// scan between boundaries
1541 										// accumulate weighted effect of each neighboring pixel
1542 										const double weight = weightsTable.getWeight(y, i);
1543 										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
1544 										const BYTE *const entry = (BYTE *)&src_pal[pixel];
1545 										r += (weight * (double)entry[FI_RGBA_RED]);
1546 										g += (weight * (double)entry[FI_RGBA_GREEN]);
1547 										b += (weight * (double)entry[FI_RGBA_BLUE]);
1548 										src_bits += src_pitch;
1549 									}
1550 
1551 									// clamp and place result in destination pixel
1552 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1553 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1554 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1555 									dst_bits += dst_pitch;
1556 								}
1557 							}
1558 						}
1559 						break;
1560 
1561 						case 32:
1562 						{
1563 							// transparently convert the transparent 4-bit image to 32 bpp;
1564 							// we always have got a palette for 4-bit images
1565 							for (unsigned x = 0; x < width; x++) {
1566 								// work on column x in dst
1567 								BYTE *dst_bits = dst_base + x * 4;
1568 								const unsigned index = x >> 1;
1569 
1570 								// scale each column
1571 								for (unsigned y = 0; y < dst_height; y++) {
1572 									// loop through column
1573 									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1574 									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1575 									const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1576 									double r = 0, g = 0, b = 0, a = 0;
1577 
1578 									for (unsigned i = 0; i < iLimit; i++) {
1579 										// scan between boundaries
1580 										// accumulate weighted effect of each neighboring pixel
1581 										const double weight = weightsTable.getWeight(y, i);
1582 										const unsigned pixel = x & 0x01 ? *src_bits & 0x0F : *src_bits >> 4;
1583 										const BYTE *const entry = (BYTE *)&src_pal[pixel];
1584 										r += (weight * (double)entry[FI_RGBA_RED]);
1585 										g += (weight * (double)entry[FI_RGBA_GREEN]);
1586 										b += (weight * (double)entry[FI_RGBA_BLUE]);
1587 										a += (weight * (double)entry[FI_RGBA_ALPHA]);
1588 										src_bits += src_pitch;
1589 									}
1590 
1591 									// clamp and place result in destination pixel
1592 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1593 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1594 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1595 									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
1596 									dst_bits += dst_pitch;
1597 								}
1598 							}
1599 						}
1600 						break;
1601 					}
1602 				}
1603 				break;
1604 
1605 				case 8:
1606 				{
1607 					const unsigned src_pitch = FreeImage_GetPitch(src);
1608 					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
1609 
1610 					switch(FreeImage_GetBPP(dst)) {
1611 						case 8:
1612 						{
1613 							// scale the 8-bit non-transparent greyscale image into an 8 bpp destination image
1614 							if (src_pal) {
1615 								// we have got a palette
1616 								for (unsigned x = 0; x < width; x++) {
1617 									// work on column x in dst
1618 									BYTE *dst_bits = dst_base + x;
1619 
1620 									// scale each column
1621 									for (unsigned y = 0; y < dst_height; y++) {
1622 										// loop through column
1623 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1624 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1625 										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
1626 										double value = 0;
1627 
1628 										for (unsigned i = 0; i < iLimit; i++) {
1629 											// scan between boundaries
1630 											// accumulate weighted effect of each neighboring pixel
1631 											value += (weightsTable.getWeight(y, i) * (double)*(BYTE *)&src_pal[*src_bits]);
1632 											src_bits += src_pitch;
1633 										}
1634 
1635 										// clamp and place result in destination pixel
1636 										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
1637 										dst_bits += dst_pitch;
1638 									}
1639 								}
1640 							} else {
1641 								// we do not have a palette
1642 								for (unsigned x = 0; x < width; x++) {
1643 									// work on column x in dst
1644 									BYTE *dst_bits = dst_base + x;
1645 
1646 									// scale each column
1647 									for (unsigned y = 0; y < dst_height; y++) {
1648 										// loop through column
1649 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1650 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1651 										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
1652 										double value = 0;
1653 
1654 										for (unsigned i = 0; i < iLimit; i++) {
1655 											// scan between boundaries
1656 											// accumulate weighted effect of each neighboring pixel
1657 											value += (weightsTable.getWeight(y, i) * (double)*src_bits);
1658 											src_bits += src_pitch;
1659 										}
1660 
1661 										// clamp and place result in destination pixel
1662 										*dst_bits = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
1663 										dst_bits += dst_pitch;
1664 									}
1665 								}
1666 							}
1667 						}
1668 						break;
1669 
1670 						case 24:
1671 						{
1672 							// transparently convert the non-transparent 8-bit image to 24 bpp
1673 							if (src_pal) {
1674 								// we have got a palette
1675 								for (unsigned x = 0; x < width; x++) {
1676 									// work on column x in dst
1677 									BYTE *dst_bits = dst_base + x * 3;
1678 
1679 									// scale each column
1680 									for (unsigned y = 0; y < dst_height; y++) {
1681 										// loop through column
1682 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1683 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1684 										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
1685 										double r = 0, g = 0, b = 0;
1686 
1687 										for (unsigned i = 0; i < iLimit; i++) {
1688 											// scan between boundaries
1689 											// accumulate weighted effect of each neighboring pixel
1690 											const double weight = weightsTable.getWeight(y, i);
1691 											const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
1692 											r += (weight * (double)entry[FI_RGBA_RED]);
1693 											g += (weight * (double)entry[FI_RGBA_GREEN]);
1694 											b += (weight * (double)entry[FI_RGBA_BLUE]);
1695 											src_bits += src_pitch;
1696 										}
1697 
1698 										// clamp and place result in destination pixel
1699 										dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1700 										dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1701 										dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1702 										dst_bits += dst_pitch;
1703 									}
1704 								}
1705 							} else {
1706 								// we do not have a palette
1707 								for (unsigned x = 0; x < width; x++) {
1708 									// work on column x in dst
1709 									BYTE *dst_bits = dst_base + x * 3;
1710 
1711 									// scale each column
1712 									for (unsigned y = 0; y < dst_height; y++) {
1713 										// loop through column
1714 										const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1715 										const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1716 										const BYTE *src_bits = src_base + iLeft * src_pitch + x;
1717 										double value = 0;
1718 
1719 										for (unsigned i = 0; i < iLimit; i++) {
1720 											// scan between boundaries
1721 											// accumulate weighted effect of each neighboring pixel
1722 											value += (weightsTable.getWeight(y, i) * (double)*src_bits);
1723 											src_bits += src_pitch;
1724 										}
1725 
1726 										// clamp and place result in destination pixel
1727 										const BYTE bval = (BYTE)CLAMP<int>((int)(value + 0.5), 0, 0xFF);
1728 										dst_bits[FI_RGBA_RED]	= bval;
1729 										dst_bits[FI_RGBA_GREEN]	= bval;
1730 										dst_bits[FI_RGBA_BLUE]	= bval;
1731 										dst_bits += dst_pitch;
1732 									}
1733 								}
1734 							}
1735 						}
1736 						break;
1737 
1738 						case 32:
1739 						{
1740 							// transparently convert the transparent 8-bit image to 32 bpp;
1741 							// we always have got a palette here
1742 							for (unsigned x = 0; x < width; x++) {
1743 								// work on column x in dst
1744 								BYTE *dst_bits = dst_base + x * 4;
1745 
1746 								// scale each column
1747 								for (unsigned y = 0; y < dst_height; y++) {
1748 									// loop through column
1749 									const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1750 									const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1751 									const BYTE *src_bits = src_base + iLeft * src_pitch + x;
1752 									double r = 0, g = 0, b = 0, a = 0;
1753 
1754 									for (unsigned i = 0; i < iLimit; i++) {
1755 										// scan between boundaries
1756 										// accumulate weighted effect of each neighboring pixel
1757 										const double weight = weightsTable.getWeight(y, i);
1758 										const BYTE * const entry = (BYTE *)&src_pal[*src_bits];
1759 										r += (weight * (double)entry[FI_RGBA_RED]);
1760 										g += (weight * (double)entry[FI_RGBA_GREEN]);
1761 										b += (weight * (double)entry[FI_RGBA_BLUE]);
1762 										a += (weight * (double)entry[FI_RGBA_ALPHA]);
1763 										src_bits += src_pitch;
1764 									}
1765 
1766 									// clamp and place result in destination pixel
1767 									dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(r + 0.5), 0, 0xFF);
1768 									dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(g + 0.5), 0, 0xFF);
1769 									dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(b + 0.5), 0, 0xFF);
1770 									dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int)(a + 0.5), 0, 0xFF);
1771 									dst_bits += dst_pitch;
1772 								}
1773 							}
1774 						}
1775 						break;
1776 					}
1777 				}
1778 				break;
1779 
1780 				case 16:
1781 				{
1782 					// transparently convert the 16-bit non-transparent image to 24 bpp
1783 					const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
1784 					const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x;
1785 
1786 					if (IS_FORMAT_RGB565(src)) {
1787 						// image has 565 format
1788 						for (unsigned x = 0; x < width; x++) {
1789 							// work on column x in dst
1790 							BYTE *dst_bits = dst_base + x * 3;
1791 
1792 							// scale each column
1793 							for (unsigned y = 0; y < dst_height; y++) {
1794 								// loop through column
1795 								const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1796 								const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1797 								const WORD *src_bits = src_base + iLeft * src_pitch + x;
1798 								double r = 0, g = 0, b = 0;
1799 
1800 								for (unsigned i = 0; i < iLimit; i++) {
1801 									// scan between boundaries
1802 									// accumulate weighted effect of each neighboring pixel
1803 									const double weight = weightsTable.getWeight(y, i);
1804 									r += (weight * (double)((*src_bits & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT));
1805 									g += (weight * (double)((*src_bits & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT));
1806 									b += (weight * (double)((*src_bits & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT));
1807 									src_bits += src_pitch;
1808 								}
1809 
1810 								// clamp and place result in destination pixel
1811 								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1812 								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x3F) + 0.5), 0, 0xFF);
1813 								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1814 								dst_bits += dst_pitch;
1815 							}
1816 						}
1817 					} else {
1818 						// image has 555 format
1819 						for (unsigned x = 0; x < width; x++) {
1820 							// work on column x in dst
1821 							BYTE *dst_bits = dst_base + x * 3;
1822 
1823 							// scale each column
1824 							for (unsigned y = 0; y < dst_height; y++) {
1825 								// loop through column
1826 								const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1827 								const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1828 								const WORD *src_bits = src_base + iLeft * src_pitch + x;
1829 								double r = 0, g = 0, b = 0;
1830 
1831 								for (unsigned i = 0; i < iLimit; i++) {
1832 									// scan between boundaries
1833 									// accumulate weighted effect of each neighboring pixel
1834 									const double weight = weightsTable.getWeight(y, i);
1835 									r += (weight * (double)((*src_bits & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT));
1836 									g += (weight * (double)((*src_bits & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT));
1837 									b += (weight * (double)((*src_bits & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT));
1838 									src_bits += src_pitch;
1839 								}
1840 
1841 								// clamp and place result in destination pixel
1842 								dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int)(((r * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1843 								dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int)(((g * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1844 								dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int)(((b * 0xFF) / 0x1F) + 0.5), 0, 0xFF);
1845 								dst_bits += dst_pitch;
1846 							}
1847 						}
1848 					}
1849 				}
1850 				break;
1851 
1852 				case 24:
1853 				{
1854 					// scale the 24-bit transparent image into a 24 bpp destination image
1855 					const unsigned src_pitch = FreeImage_GetPitch(src);
1856 					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 3;
1857 
1858 					for (unsigned x = 0; x < width; x++) {
1859 						// work on column x in dst
1860 						const unsigned index = x * 3;
1861 						BYTE *dst_bits = dst_base + index;
1862 
1863 						// scale each column
1864 						for (unsigned y = 0; y < dst_height; y++) {
1865 							// loop through column
1866 							const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1867 							const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1868 							const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1869 							double r = 0, g = 0, b = 0;
1870 
1871 							for (unsigned i = 0; i < iLimit; i++) {
1872 								// scan between boundaries
1873 								// accumulate weighted effect of each neighboring pixel
1874 								const double weight = weightsTable.getWeight(y, i);
1875 								r += (weight * (double)src_bits[FI_RGBA_RED]);
1876 								g += (weight * (double)src_bits[FI_RGBA_GREEN]);
1877 								b += (weight * (double)src_bits[FI_RGBA_BLUE]);
1878 								src_bits += src_pitch;
1879 							}
1880 
1881 							// clamp and place result in destination pixel
1882 							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
1883 							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
1884 							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
1885 							dst_bits += dst_pitch;
1886 						}
1887 					}
1888 				}
1889 				break;
1890 
1891 				case 32:
1892 				{
1893 					// scale the 32-bit transparent image into a 32 bpp destination image
1894 					const unsigned src_pitch = FreeImage_GetPitch(src);
1895 					const BYTE *const src_base = FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * 4;
1896 
1897 					for (unsigned x = 0; x < width; x++) {
1898 						// work on column x in dst
1899 						const unsigned index = x * 4;
1900 						BYTE *dst_bits = dst_base + index;
1901 
1902 						// scale each column
1903 						for (unsigned y = 0; y < dst_height; y++) {
1904 							// loop through column
1905 							const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1906 							const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1907 							const BYTE *src_bits = src_base + iLeft * src_pitch + index;
1908 							double r = 0, g = 0, b = 0, a = 0;
1909 
1910 							for (unsigned i = 0; i < iLimit; i++) {
1911 								// scan between boundaries
1912 								// accumulate weighted effect of each neighboring pixel
1913 								const double weight = weightsTable.getWeight(y, i);
1914 								r += (weight * (double)src_bits[FI_RGBA_RED]);
1915 								g += (weight * (double)src_bits[FI_RGBA_GREEN]);
1916 								b += (weight * (double)src_bits[FI_RGBA_BLUE]);
1917 								a += (weight * (double)src_bits[FI_RGBA_ALPHA]);
1918 								src_bits += src_pitch;
1919 							}
1920 
1921 							// clamp and place result in destination pixel
1922 							dst_bits[FI_RGBA_RED]	= (BYTE)CLAMP<int>((int) (r + 0.5), 0, 0xFF);
1923 							dst_bits[FI_RGBA_GREEN]	= (BYTE)CLAMP<int>((int) (g + 0.5), 0, 0xFF);
1924 							dst_bits[FI_RGBA_BLUE]	= (BYTE)CLAMP<int>((int) (b + 0.5), 0, 0xFF);
1925 							dst_bits[FI_RGBA_ALPHA]	= (BYTE)CLAMP<int>((int) (a + 0.5), 0, 0xFF);
1926 							dst_bits += dst_pitch;
1927 						}
1928 					}
1929 				}
1930 				break;
1931 			}
1932 		}
1933 		break;
1934 
1935 		case FIT_UINT16:
1936 		{
1937 			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
1938 			const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
1939 
1940 			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
1941 			WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
1942 
1943 			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
1944 			const WORD *const src_base = (WORD *)FreeImage_GetBits(src)	+ src_offset_y * src_pitch + src_offset_x * wordspp;
1945 
1946 			for (unsigned x = 0; x < width; x++) {
1947 				// work on column x in dst
1948 				const unsigned index = x * wordspp;	// pixel index
1949 				WORD *dst_bits = dst_base + index;
1950 
1951 				// scale each column
1952 				for (unsigned y = 0; y < dst_height; y++) {
1953 					// loop through column
1954 					const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1955 					const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1956 					const WORD *src_bits = src_base + iLeft * src_pitch + index;
1957 					double value = 0;
1958 
1959 					for (unsigned i = 0; i < iLimit; i++) {
1960 						// scan between boundaries
1961 						// accumulate weighted effect of each neighboring pixel
1962 						const double weight = weightsTable.getWeight(y, i);
1963 						value += (weight * (double)src_bits[0]);
1964 						src_bits += src_pitch;
1965 					}
1966 
1967 					// clamp and place result in destination pixel
1968 					dst_bits[0] = (WORD)CLAMP<int>((int)(value + 0.5), 0, 0xFFFF);
1969 
1970 					dst_bits += dst_pitch;
1971 				}
1972 			}
1973 		}
1974 		break;
1975 
1976 		case FIT_RGB16:
1977 		{
1978 			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
1979 			const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
1980 
1981 			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
1982 			WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
1983 
1984 			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
1985 			const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
1986 
1987 			for (unsigned x = 0; x < width; x++) {
1988 				// work on column x in dst
1989 				const unsigned index = x * wordspp;	// pixel index
1990 				WORD *dst_bits = dst_base + index;
1991 
1992 				// scale each column
1993 				for (unsigned y = 0; y < dst_height; y++) {
1994 					// loop through column
1995 					const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
1996 					const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
1997 					const WORD *src_bits = src_base + iLeft * src_pitch + index;
1998 					double r = 0, g = 0, b = 0;
1999 
2000 					for (unsigned i = 0; i < iLimit; i++) {
2001 						// scan between boundaries
2002 						// accumulate weighted effect of each neighboring pixel
2003 						const double weight = weightsTable.getWeight(y, i);
2004 						r += (weight * (double)src_bits[0]);
2005 						g += (weight * (double)src_bits[1]);
2006 						b += (weight * (double)src_bits[2]);
2007 
2008 						src_bits += src_pitch;
2009 					}
2010 
2011 					// clamp and place result in destination pixel
2012 					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
2013 					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
2014 					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
2015 
2016 					dst_bits += dst_pitch;
2017 				}
2018 			}
2019 		}
2020 		break;
2021 
2022 		case FIT_RGBA16:
2023 		{
2024 			// Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit)
2025 			const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD);
2026 
2027 			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(WORD);
2028 			WORD *const dst_base = (WORD *)FreeImage_GetBits(dst);
2029 
2030 			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(WORD);
2031 			const WORD *const src_base = (WORD *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * wordspp;
2032 
2033 			for (unsigned x = 0; x < width; x++) {
2034 				// work on column x in dst
2035 				const unsigned index = x * wordspp;	// pixel index
2036 				WORD *dst_bits = dst_base + index;
2037 
2038 				// scale each column
2039 				for (unsigned y = 0; y < dst_height; y++) {
2040 					// loop through column
2041 					const unsigned iLeft = weightsTable.getLeftBoundary(y);				// retrieve left boundary
2042 					const unsigned iLimit = weightsTable.getRightBoundary(y) - iLeft;	// retrieve right boundary
2043 					const WORD *src_bits = src_base + iLeft * src_pitch + index;
2044 					double r = 0, g = 0, b = 0, a = 0;
2045 
2046 					for (unsigned i = 0; i < iLimit; i++) {
2047 						// scan between boundaries
2048 						// accumulate weighted effect of each neighboring pixel
2049 						const double weight = weightsTable.getWeight(y, i);
2050 						r += (weight * (double)src_bits[0]);
2051 						g += (weight * (double)src_bits[1]);
2052 						b += (weight * (double)src_bits[2]);
2053 						a += (weight * (double)src_bits[3]);
2054 
2055 						src_bits += src_pitch;
2056 					}
2057 
2058 					// clamp and place result in destination pixel
2059 					dst_bits[0] = (WORD)CLAMP<int>((int)(r + 0.5), 0, 0xFFFF);
2060 					dst_bits[1] = (WORD)CLAMP<int>((int)(g + 0.5), 0, 0xFFFF);
2061 					dst_bits[2] = (WORD)CLAMP<int>((int)(b + 0.5), 0, 0xFFFF);
2062 					dst_bits[3] = (WORD)CLAMP<int>((int)(a + 0.5), 0, 0xFFFF);
2063 
2064 					dst_bits += dst_pitch;
2065 				}
2066 			}
2067 		}
2068 		break;
2069 
2070 		case FIT_FLOAT:
2071 		case FIT_RGBF:
2072 		case FIT_RGBAF:
2073 		{
2074 			// Calculate the number of floats per pixel (1 for 32-bit, 3 for 96-bit or 4 for 128-bit)
2075 			const unsigned floatspp = (FreeImage_GetLine(src) / width) / sizeof(float);
2076 
2077 			const unsigned dst_pitch = FreeImage_GetPitch(dst) / sizeof(float);
2078 			float *const dst_base = (float *)FreeImage_GetBits(dst);
2079 
2080 			const unsigned src_pitch = FreeImage_GetPitch(src) / sizeof(float);
2081 			const float *const src_base = (float *)FreeImage_GetBits(src) + src_offset_y * src_pitch + src_offset_x * floatspp;
2082 
2083 			for (unsigned x = 0; x < width; x++) {
2084 				// work on column x in dst
2085 				const unsigned index = x * floatspp;	// pixel index
2086 				float *dst_bits = (float *)dst_base + index;
2087 
2088 				// scale each column
2089 				for (unsigned y = 0; y < dst_height; y++) {
2090 					// loop through column
2091 					const unsigned iLeft = weightsTable.getLeftBoundary(y);    // retrieve left boundary
2092 					const unsigned iRight = weightsTable.getRightBoundary(y);  // retrieve right boundary
2093 					const float *src_bits = src_base + iLeft * src_pitch + index;
2094 					double value[4] = {0, 0, 0, 0};                            // 4 = 128 bpp max
2095 
2096 					for (unsigned i = iLeft; i < iRight; i++) {
2097 						// scan between boundaries
2098 						// accumulate weighted effect of each neighboring pixel
2099 						const double weight = weightsTable.getWeight(y, i - iLeft);
2100 						for (unsigned j = 0; j < floatspp; j++) {
2101 							value[j] += (weight * (double)src_bits[j]);
2102 						}
2103 						src_bits += src_pitch;
2104 					}
2105 
2106 					// place result in destination pixel
2107 					for (unsigned j = 0; j < floatspp; j++) {
2108 						dst_bits[j] = (float)value[j];
2109 					}
2110 					dst_bits += dst_pitch;
2111 				}
2112 			}
2113 		}
2114 		break;
2115 	}
2116 }
2117