1 // ==========================================================
2 // Bitmap conversion routines
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 // - Hervé Drolon (drolon@infonie.fr)
7 // - Jani Kajala (janik@remedy.fi)
8 // - Mihail Naydenov (mnaydenov@users.sourceforge.net)
9 // - Carsten Klein (cklein05@users.sourceforge.net)
10 //
11 // This file is part of FreeImage 3
12 //
13 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
14 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
15 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
16 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
17 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
18 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
19 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
20 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
21 // THIS DISCLAIMER.
22 //
23 // Use at your own risk!
24 // ==========================================================
25
26 #include "FreeImage.h"
27 #include "Utilities.h"
28 #include "Quantizers.h"
29
30 // ----------------------------------------------------------
31
32 #define CONVERT(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib)); break;
33
34 #define CONVERTWITHPALETTE(from, to) case to : FreeImage_ConvertLine##from##To##to(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); break;
35
36 #define CONVERTTO32WITHPALETTE(from) case 32 : \
37 if (bIsTransparent) { \
38 FreeImage_ConvertLine##from##To32MapTransparency(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib)); \
39 } else { \
40 FreeImage_ConvertLine##from##To32(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \
41 } \
42 break;
43
44 #define CONVERTTO16(from) \
45 case 16 : \
46 if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \
47 FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib)); \
48 } else { \
49 FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib)); \
50 } \
51 break;
52
53 #define CONVERTTO16WITHPALETTE(from) \
54 case 16 : \
55 if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) { \
56 FreeImage_ConvertLine##from##To16_555(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \
57 } else { \
58 FreeImage_ConvertLine##from##To16_565(bits, scanline, FreeImage_GetWidth(dib), FreeImage_GetPalette(dib)); \
59 } \
60 break;
61
62 // ==========================================================
63 // Utility functions declared in Utilities.h
64
SwapRedBlue32(FIBITMAP * dib)65 BOOL SwapRedBlue32(FIBITMAP* dib) {
66 if(FreeImage_GetImageType(dib) != FIT_BITMAP) {
67 return FALSE;
68 }
69
70 const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8;
71 if(bytesperpixel > 4 || bytesperpixel < 3) {
72 return FALSE;
73 }
74
75 const unsigned height = FreeImage_GetHeight(dib);
76 const unsigned pitch = FreeImage_GetPitch(dib);
77 const unsigned lineSize = FreeImage_GetLine(dib);
78
79 BYTE* line = FreeImage_GetBits(dib);
80 for(unsigned y = 0; y < height; ++y, line += pitch) {
81 for(BYTE* pixel = line; pixel < line + lineSize ; pixel += bytesperpixel) {
82 INPLACESWAP(pixel[0], pixel[2]);
83 }
84 }
85
86 return TRUE;
87 }
88
89 // ----------------------------------------------------------
90
91 static inline void
assignRGB(WORD r,WORD g,WORD b,WORD * out)92 assignRGB(WORD r, WORD g, WORD b, WORD* out) {
93 out[0] = r;
94 out[1] = g;
95 out[2] = b;
96 }
97
98 static inline void
assignRGB(BYTE r,BYTE g,BYTE b,BYTE * out)99 assignRGB(BYTE r, BYTE g, BYTE b, BYTE* out) {
100 out[FI_RGBA_RED] = r;
101 out[FI_RGBA_GREEN] = g;
102 out[FI_RGBA_BLUE] = b;
103 }
104
105 /**
106 CMYK -> CMY -> RGB conversion from http://www.easyrgb.com/
107
108 CMYK to CMY [0-1]: C,M,Y * (1 - K) + K
109 CMY to RGB [0-1]: (1 - C,M,Y)
110
111 => R,G,B = (1 - C,M,Y) * (1 - K)
112 mapped to [0-MAX_VAL]:
113 (MAX_VAL - C,M,Y) * (MAX_VAL - K) / MAX_VAL
114 */
115 template <class T>
116 static inline void
CMYKToRGB(T C,T M,T Y,T K,T * out)117 CMYKToRGB(T C, T M, T Y, T K, T* out) {
118 unsigned max_val = std::numeric_limits<T>::max();
119
120 unsigned r = (max_val - C) * (max_val - K) / max_val;
121 unsigned g = (max_val - M) * (max_val - K) / max_val;
122 unsigned b = (max_val - Y) * (max_val - K) / max_val;
123
124 // clamp values to [0..max_val]
125 T red = (T)CLAMP(r, (unsigned)0, max_val);
126 T green = (T)CLAMP(g, (unsigned)0, max_val);
127 T blue = (T)CLAMP(b, (unsigned)0, max_val);
128
129 assignRGB(red, green, blue, out);
130 }
131
132 template <class T>
133 static void
_convertCMYKtoRGBA(unsigned width,unsigned height,BYTE * line_start,unsigned pitch,unsigned samplesperpixel)134 _convertCMYKtoRGBA(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) {
135 const BOOL hasBlack = (samplesperpixel > 3) ? TRUE : FALSE;
136 const T MAX_VAL = std::numeric_limits<T>::max();
137
138 T K = 0;
139 for(unsigned y = 0; y < height; y++) {
140 T *line = (T*)line_start;
141
142 for(unsigned x = 0; x < width; x++) {
143 if(hasBlack) {
144 K = line[FI_RGBA_ALPHA];
145 line[FI_RGBA_ALPHA] = MAX_VAL; // TODO write the first extra channel as alpha!
146 }
147
148 CMYKToRGB<T>(line[0], line[1], line[2], K, line);
149
150 line += samplesperpixel;
151 }
152 line_start += pitch;
153 }
154 }
155
156 BOOL
ConvertCMYKtoRGBA(FIBITMAP * dib)157 ConvertCMYKtoRGBA(FIBITMAP* dib) {
158 if(!FreeImage_HasPixels(dib)) {
159 return FALSE;
160 }
161
162 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
163 const unsigned bytesperpixel = FreeImage_GetBPP(dib)/8;
164
165 unsigned channelSize = 1;
166 if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) {
167 channelSize = sizeof(WORD);
168 } else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) {
169 return FALSE;
170 }
171
172 const unsigned width = FreeImage_GetWidth(dib);
173 const unsigned height = FreeImage_GetHeight(dib);
174 BYTE *line_start = FreeImage_GetScanLine(dib, 0);
175 const unsigned pitch = FreeImage_GetPitch(dib);
176
177 unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize;
178
179 if(channelSize == sizeof(WORD)) {
180 _convertCMYKtoRGBA<WORD>(width, height, line_start, pitch, samplesperpixel);
181 } else {
182 _convertCMYKtoRGBA<BYTE>(width, height, line_start, pitch, samplesperpixel);
183 }
184
185 return TRUE;
186 }
187
188 // ----------------------------------------------------------
189
190 /**
191 CIELab -> XYZ conversion from http://www.easyrgb.com/
192 */
193 static void
CIELabToXYZ(float L,float a,float b,float * X,float * Y,float * Z)194 CIELabToXYZ(float L, float a, float b, float *X, float *Y, float *Z) {
195 float pow_3;
196
197 // CIELab -> XYZ conversion
198 // ------------------------
199 float var_Y = (L + 16.F ) / 116.F;
200 float var_X = a / 500.F + var_Y;
201 float var_Z = var_Y - b / 200.F;
202
203 pow_3 = powf(var_Y, 3);
204 if(pow_3 > 0.008856F) {
205 var_Y = pow_3;
206 } else {
207 var_Y = ( var_Y - 16.F / 116.F ) / 7.787F;
208 }
209 pow_3 = powf(var_X, 3);
210 if(pow_3 > 0.008856F) {
211 var_X = pow_3;
212 } else {
213 var_X = ( var_X - 16.F / 116.F ) / 7.787F;
214 }
215 pow_3 = powf(var_Z, 3);
216 if(pow_3 > 0.008856F) {
217 var_Z = pow_3;
218 } else {
219 var_Z = ( var_Z - 16.F / 116.F ) / 7.787F;
220 }
221
222 static const float ref_X = 95.047F;
223 static const float ref_Y = 100.000F;
224 static const float ref_Z = 108.883F;
225
226 *X = ref_X * var_X; // ref_X = 95.047 (Observer= 2°, Illuminant= D65)
227 *Y = ref_Y * var_Y; // ref_Y = 100.000
228 *Z = ref_Z * var_Z; // ref_Z = 108.883
229 }
230
231 /**
232 XYZ -> RGB conversion from http://www.easyrgb.com/
233 */
234 static void
XYZToRGB(float X,float Y,float Z,float * R,float * G,float * B)235 XYZToRGB(float X, float Y, float Z, float *R, float *G, float *B) {
236 float var_X = X / 100; // X from 0 to 95.047 (Observer = 2°, Illuminant = D65)
237 float var_Y = Y / 100; // Y from 0 to 100.000
238 float var_Z = Z / 100; // Z from 0 to 108.883
239
240 float var_R = var_X * 3.2406F + var_Y * -1.5372F + var_Z * -0.4986F;
241 float var_G = var_X * -0.9689F + var_Y * 1.8758F + var_Z * 0.0415F;
242 float var_B = var_X * 0.0557F + var_Y * -0.2040F + var_Z * 1.0570F;
243
244 float exponent = 1.F / 2.4F;
245
246 if(var_R > 0.0031308F) {
247 var_R = 1.055F * powf(var_R, exponent) - 0.055F;
248 } else {
249 var_R = 12.92F * var_R;
250 }
251 if(var_G > 0.0031308F) {
252 var_G = 1.055F * powf(var_G, exponent) - 0.055F;
253 } else {
254 var_G = 12.92F * var_G;
255 }
256 if(var_B > 0.0031308F) {
257 var_B = 1.055F * powf(var_B, exponent) - 0.055F;
258 } else {
259 var_B = 12.92F * var_B;
260 }
261
262 *R = var_R;
263 *G = var_G;
264 *B = var_B;
265 }
266
267 template<class T>
268 static void
CIELabToRGB(float L,float a,float b,T * rgb)269 CIELabToRGB(float L, float a, float b, T *rgb) {
270 float X, Y, Z;
271 float R, G, B;
272 const float max_val = std::numeric_limits<T>::max();
273
274 CIELabToXYZ(L, a, b, &X, &Y, &Z);
275 XYZToRGB(X, Y, Z, &R, &G, &B);
276
277 // clamp values to [0..max_val]
278 T red = (T)CLAMP(R * max_val, 0.0F, max_val);
279 T green = (T)CLAMP(G * max_val, 0.0F, max_val);
280 T blue = (T)CLAMP(B * max_val, 0.0F, max_val);
281
282 assignRGB(red, green, blue, rgb);
283 }
284
285 template<class T>
286 static void
_convertLABtoRGB(unsigned width,unsigned height,BYTE * line_start,unsigned pitch,unsigned samplesperpixel)287 _convertLABtoRGB(unsigned width, unsigned height, BYTE* line_start, unsigned pitch, unsigned samplesperpixel) {
288 const unsigned max_val = std::numeric_limits<T>::max();
289 const float sL = 100.F / max_val;
290 const float sa = 256.F / max_val;
291 const float sb = 256.F / max_val;
292
293 for(unsigned y = 0; y < height; y++) {
294 T *line = (T*)line_start;
295
296 for(unsigned x = 0; x < width; x++) {
297 CIELabToRGB(line[0]* sL, line[1]* sa - 128.F, line[2]* sb - 128.F, line);
298
299 line += samplesperpixel;
300 }
301 line_start += pitch;
302 }
303 }
304
305 BOOL
ConvertLABtoRGB(FIBITMAP * dib)306 ConvertLABtoRGB(FIBITMAP* dib) {
307 if(!FreeImage_HasPixels(dib)) {
308 return FALSE;
309 }
310
311 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
312 const unsigned bytesperpixel = FreeImage_GetBPP(dib) / 8;
313
314 unsigned channelSize = 1;
315 if (image_type == FIT_RGBA16 || image_type == FIT_RGB16) {
316 channelSize = sizeof(WORD);
317 } else if (!(image_type == FIT_BITMAP && (bytesperpixel > 2))) {
318 return FALSE;
319 }
320
321 const unsigned width = FreeImage_GetWidth(dib);
322 const unsigned height = FreeImage_GetHeight(dib);
323 BYTE *line_start = FreeImage_GetScanLine(dib, 0);
324 const unsigned pitch = FreeImage_GetPitch(dib);
325
326 unsigned samplesperpixel = FreeImage_GetLine(dib) / width / channelSize;
327
328 if(channelSize == 1) {
329 _convertLABtoRGB<BYTE>(width, height, line_start, pitch, samplesperpixel);
330 }
331 else {
332 _convertLABtoRGB<WORD>(width, height, line_start, pitch, samplesperpixel);
333 }
334
335 return TRUE;
336 }
337
338 // ----------------------------------------------------------
339
340 FIBITMAP*
RemoveAlphaChannel(FIBITMAP * src)341 RemoveAlphaChannel(FIBITMAP* src) {
342
343 if(!FreeImage_HasPixels(src)) {
344 return NULL;
345 }
346
347 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src);
348
349 switch(image_type) {
350 case FIT_BITMAP:
351 if(FreeImage_GetBPP(src) == 32) {
352 // convert to 24-bit
353 return FreeImage_ConvertTo24Bits(src);
354 }
355 break;
356 case FIT_RGBA16:
357 // convert to RGB16
358 return FreeImage_ConvertToRGB16(src);
359 case FIT_RGBAF:
360 // convert to RGBF
361 return FreeImage_ConvertToRGBF(src);
362 default:
363 // unsupported image type
364 return NULL;
365 }
366
367 return NULL;
368 }
369
370
371 // ==========================================================
372
373 FIBITMAP * DLL_CALLCONV
FreeImage_ColorQuantize(FIBITMAP * dib,FREE_IMAGE_QUANTIZE quantize)374 FreeImage_ColorQuantize(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize) {
375 return FreeImage_ColorQuantizeEx(dib, quantize);
376 }
377
378 FIBITMAP * DLL_CALLCONV
FreeImage_ColorQuantizeEx(FIBITMAP * dib,FREE_IMAGE_QUANTIZE quantize,int PaletteSize,int ReserveSize,RGBQUAD * ReservePalette)379 FreeImage_ColorQuantizeEx(FIBITMAP *dib, FREE_IMAGE_QUANTIZE quantize, int PaletteSize, int ReserveSize, RGBQUAD *ReservePalette) {
380 if( PaletteSize < 2 ) PaletteSize = 2;
381 if( PaletteSize > 256 ) PaletteSize = 256;
382 if( ReserveSize < 0 ) ReserveSize = 0;
383 if( ReserveSize > PaletteSize ) ReserveSize = PaletteSize;
384 if (FreeImage_HasPixels(dib)) {
385 const unsigned bpp = FreeImage_GetBPP(dib);
386 if((FreeImage_GetImageType(dib) == FIT_BITMAP) && (bpp == 24 || bpp == 32)) {
387 switch(quantize) {
388 case FIQ_WUQUANT :
389 {
390 try {
391 WuQuantizer Q (dib);
392 FIBITMAP *dst = Q.Quantize(PaletteSize, ReserveSize, ReservePalette);
393 if(dst) {
394 // copy metadata from src to dst
395 FreeImage_CloneMetadata(dst, dib);
396 }
397 return dst;
398 } catch (const char *) {
399 return NULL;
400 }
401 break;
402 }
403 case FIQ_NNQUANT :
404 {
405 if (bpp == 32) {
406 // 32-bit images not supported by NNQUANT
407 return NULL;
408 }
409 // sampling factor in range 1..30.
410 // 1 => slower (but better), 30 => faster. Default value is 1
411 const int sampling = 1;
412
413 NNQuantizer Q(PaletteSize);
414 FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette, sampling);
415 if(dst) {
416 // copy metadata from src to dst
417 FreeImage_CloneMetadata(dst, dib);
418 }
419 return dst;
420 }
421 case FIQ_LFPQUANT :
422 {
423 LFPQuantizer Q(PaletteSize);
424 FIBITMAP *dst = Q.Quantize(dib, ReserveSize, ReservePalette);
425 if(dst) {
426 // copy metadata from src to dst
427 FreeImage_CloneMetadata(dst, dib);
428 }
429 return dst;
430 }
431 }
432 }
433 }
434
435 return NULL;
436 }
437
438 // ==========================================================
439
440 FIBITMAP * DLL_CALLCONV
FreeImage_ConvertFromRawBitsEx(BOOL copySource,BYTE * bits,FREE_IMAGE_TYPE type,int width,int height,int pitch,unsigned bpp,unsigned red_mask,unsigned green_mask,unsigned blue_mask,BOOL topdown)441 FreeImage_ConvertFromRawBitsEx(BOOL copySource, BYTE *bits, FREE_IMAGE_TYPE type, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
442 FIBITMAP *dib = NULL;
443
444 if(copySource) {
445 // allocate a FIBITMAP with internally managed pixel buffer
446 dib = FreeImage_AllocateT(type, width, height, bpp, red_mask, green_mask, blue_mask);
447 if(!dib) {
448 return NULL;
449 }
450 // copy user provided pixel buffer into the dib
451 const unsigned linesize = FreeImage_GetLine(dib);
452 for(int y = 0; y < height; y++) {
453 memcpy(FreeImage_GetScanLine(dib, y), bits, linesize);
454 // next line in user's buffer
455 bits += pitch;
456 }
457 // flip pixels vertically if needed
458 if(topdown) {
459 FreeImage_FlipVertical(dib);
460 }
461 }
462 else {
463 // allocate a FIBITMAP using a wrapper to user provided pixel buffer
464 dib = FreeImage_AllocateHeaderForBits(bits, pitch, type, width, height, bpp, red_mask, green_mask, blue_mask);
465 if(!dib) {
466 return NULL;
467 }
468 // flip pixels vertically if needed
469 if(topdown) {
470 FreeImage_FlipVertical(dib);
471 }
472 }
473
474 return dib;
475 }
476
477 FIBITMAP * DLL_CALLCONV
FreeImage_ConvertFromRawBits(BYTE * bits,int width,int height,int pitch,unsigned bpp,unsigned red_mask,unsigned green_mask,unsigned blue_mask,BOOL topdown)478 FreeImage_ConvertFromRawBits(BYTE *bits, int width, int height, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
479 return FreeImage_ConvertFromRawBitsEx(TRUE /* copySource */, bits, FIT_BITMAP, width, height, pitch, bpp, red_mask, green_mask, blue_mask, topdown);
480 }
481
482 void DLL_CALLCONV
FreeImage_ConvertToRawBits(BYTE * bits,FIBITMAP * dib,int pitch,unsigned bpp,unsigned red_mask,unsigned green_mask,unsigned blue_mask,BOOL topdown)483 FreeImage_ConvertToRawBits(BYTE *bits, FIBITMAP *dib, int pitch, unsigned bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask, BOOL topdown) {
484 if (FreeImage_HasPixels(dib) && (bits != NULL)) {
485 for (unsigned i = 0; i < FreeImage_GetHeight(dib); ++i) {
486 BYTE *scanline = FreeImage_GetScanLine(dib, topdown ? (FreeImage_GetHeight(dib) - i - 1) : i);
487
488 if ((bpp == 16) && (FreeImage_GetBPP(dib) == 16)) {
489 // convert 555 to 565 or vice versa
490
491 if ((red_mask == FI16_555_RED_MASK) && (green_mask == FI16_555_GREEN_MASK) && (blue_mask == FI16_555_BLUE_MASK)) {
492 if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
493 FreeImage_ConvertLine16_565_To16_555(bits, scanline, FreeImage_GetWidth(dib));
494 } else {
495 memcpy(bits, scanline, FreeImage_GetLine(dib));
496 }
497 } else {
498 if ((FreeImage_GetRedMask(dib) == FI16_555_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_555_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_555_BLUE_MASK)) {
499 FreeImage_ConvertLine16_555_To16_565(bits, scanline, FreeImage_GetWidth(dib));
500 } else {
501 memcpy(bits, scanline, FreeImage_GetLine(dib));
502 }
503 }
504 } else if (FreeImage_GetBPP(dib) != bpp) {
505 BOOL bIsTransparent = FreeImage_IsTransparent(dib);
506 switch(FreeImage_GetBPP(dib)) {
507 case 1 :
508 switch(bpp) {
509 CONVERT(1, 8)
510 CONVERTTO16WITHPALETTE(1)
511 CONVERTWITHPALETTE(1, 24)
512 CONVERTTO32WITHPALETTE(1)
513 }
514
515 break;
516
517 case 4 :
518 switch(bpp) {
519 CONVERT(4, 8)
520 CONVERTTO16WITHPALETTE(4)
521 CONVERTWITHPALETTE(4, 24)
522 CONVERTTO32WITHPALETTE(4)
523 }
524
525 break;
526
527 case 8 :
528 switch(bpp) {
529 CONVERTTO16WITHPALETTE(8)
530 CONVERTWITHPALETTE(8, 24)
531 CONVERTTO32WITHPALETTE(8)
532 }
533
534 break;
535
536 case 24 :
537 switch(bpp) {
538 CONVERT(24, 8)
539 CONVERTTO16(24)
540 CONVERT(24, 32)
541 }
542
543 break;
544
545 case 32 :
546 switch(bpp) {
547 CONVERT(32, 8)
548 CONVERTTO16(32)
549 CONVERT(32, 24)
550 }
551
552 break;
553 }
554 } else {
555 memcpy(bits, scanline, FreeImage_GetLine(dib));
556 }
557
558 bits += pitch;
559 }
560 }
561 }
562