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