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 // - Detlev Vendt (detlev.vendt@brillit.de)
9 //
10 // This file is part of FreeImage 3
11 //
12 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
13 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
14 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
15 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
16 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
17 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
18 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
19 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
20 // THIS DISCLAIMER.
21 //
22 // Use at your own risk!
23 // ==========================================================
24
25 #include "FreeImage.h"
26 #include "Utilities.h"
27
28 // ----------------------------------------------------------
29 // internal conversions X to 32 bits
30 // ----------------------------------------------------------
31
32 void DLL_CALLCONV
FreeImage_ConvertLine1To32(BYTE * target,BYTE * source,int width_in_pixels,RGBQUAD * palette)33 FreeImage_ConvertLine1To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
34 for (int cols = 0; cols < width_in_pixels; cols++) {
35 int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
36
37 target[FI_RGBA_BLUE] = palette[index].rgbBlue;
38 target[FI_RGBA_GREEN] = palette[index].rgbGreen;
39 target[FI_RGBA_RED] = palette[index].rgbRed;
40 target[FI_RGBA_ALPHA] = 0xFF;
41 target += 4;
42 }
43 }
44
45 void DLL_CALLCONV
FreeImage_ConvertLine4To32(BYTE * target,BYTE * source,int width_in_pixels,RGBQUAD * palette)46 FreeImage_ConvertLine4To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
47 BOOL low_nibble = FALSE;
48 int x = 0;
49
50 for (int cols = 0 ; cols < width_in_pixels ; ++cols) {
51 if (low_nibble) {
52 target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue;
53 target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen;
54 target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed;
55
56 x++;
57 } else {
58 target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue;
59 target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen;
60 target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed;
61 }
62
63 low_nibble = !low_nibble;
64
65 target[FI_RGBA_ALPHA] = 0xFF;
66 target += 4;
67 }
68 }
69
70 void DLL_CALLCONV
FreeImage_ConvertLine8To32(BYTE * target,BYTE * source,int width_in_pixels,RGBQUAD * palette)71 FreeImage_ConvertLine8To32(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette) {
72 for (int cols = 0; cols < width_in_pixels; cols++) {
73 target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue;
74 target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen;
75 target[FI_RGBA_RED] = palette[source[cols]].rgbRed;
76 target[FI_RGBA_ALPHA] = 0xFF;
77 target += 4;
78 }
79 }
80
81 void DLL_CALLCONV
FreeImage_ConvertLine16To32_555(BYTE * target,BYTE * source,int width_in_pixels)82 FreeImage_ConvertLine16To32_555(BYTE *target, BYTE *source, int width_in_pixels) {
83 WORD *bits = (WORD *)source;
84
85 for (int cols = 0; cols < width_in_pixels; cols++) {
86 target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_555_RED_MASK) >> FI16_555_RED_SHIFT) * 0xFF) / 0x1F);
87 target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_555_GREEN_MASK) >> FI16_555_GREEN_SHIFT) * 0xFF) / 0x1F);
88 target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_555_BLUE_MASK) >> FI16_555_BLUE_SHIFT) * 0xFF) / 0x1F);
89 target[FI_RGBA_ALPHA] = 0xFF;
90 target += 4;
91 }
92 }
93
94 void DLL_CALLCONV
FreeImage_ConvertLine16To32_565(BYTE * target,BYTE * source,int width_in_pixels)95 FreeImage_ConvertLine16To32_565(BYTE *target, BYTE *source, int width_in_pixels) {
96 WORD *bits = (WORD *)source;
97
98 for (int cols = 0; cols < width_in_pixels; cols++) {
99 target[FI_RGBA_RED] = (BYTE)((((bits[cols] & FI16_565_RED_MASK) >> FI16_565_RED_SHIFT) * 0xFF) / 0x1F);
100 target[FI_RGBA_GREEN] = (BYTE)((((bits[cols] & FI16_565_GREEN_MASK) >> FI16_565_GREEN_SHIFT) * 0xFF) / 0x3F);
101 target[FI_RGBA_BLUE] = (BYTE)((((bits[cols] & FI16_565_BLUE_MASK) >> FI16_565_BLUE_SHIFT) * 0xFF) / 0x1F);
102 target[FI_RGBA_ALPHA] = 0xFF;
103 target += 4;
104 }
105 }
106 /*
107 void DLL_CALLCONV
108 FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) {
109 for (int cols = 0; cols < width_in_pixels; cols++) {
110 *(DWORD *)target = (*(DWORD *) source & FI_RGBA_RGB_MASK) | FI_RGBA_ALPHA_MASK;
111 target += 4;
112 source += 3;
113 }
114 }
115 */
116 /**
117 This unoptimized version of the conversion function avoid an undetermined bug with VC++ SP6.
118 The bug occurs in release mode only, when the image height is equal to 537
119 (try e.g. a size of 432x537 to reproduce the bug with the optimized function).
120 */
121 void DLL_CALLCONV
FreeImage_ConvertLine24To32(BYTE * target,BYTE * source,int width_in_pixels)122 FreeImage_ConvertLine24To32(BYTE *target, BYTE *source, int width_in_pixels) {
123 for (int cols = 0; cols < width_in_pixels; cols++) {
124 target[FI_RGBA_RED] = source[FI_RGBA_RED];
125 target[FI_RGBA_GREEN] = source[FI_RGBA_GREEN];
126 target[FI_RGBA_BLUE] = source[FI_RGBA_BLUE];
127 target[FI_RGBA_ALPHA] = 0xFF;
128 target += 4;
129 source += 3;
130 }
131 }
132
133 // ----------------------------------------------------------
134
135 void DLL_CALLCONV
FreeImage_ConvertLine1To32MapTransparency(BYTE * target,BYTE * source,int width_in_pixels,RGBQUAD * palette,BYTE * table,int transparent_pixels)136 FreeImage_ConvertLine1To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
137 for (int cols = 0; cols < width_in_pixels; cols++) {
138 int index = (source[cols>>3] & (0x80 >> (cols & 0x07))) != 0 ? 1 : 0;
139
140 target[FI_RGBA_BLUE] = palette[index].rgbBlue;
141 target[FI_RGBA_GREEN] = palette[index].rgbGreen;
142 target[FI_RGBA_RED] = palette[index].rgbRed;
143 target[FI_RGBA_ALPHA] = (index < transparent_pixels) ? table[index] : 255;
144 target += 4;
145 }
146 }
147
148 void DLL_CALLCONV
FreeImage_ConvertLine4To32MapTransparency(BYTE * target,BYTE * source,int width_in_pixels,RGBQUAD * palette,BYTE * table,int transparent_pixels)149 FreeImage_ConvertLine4To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
150 BOOL low_nibble = FALSE;
151 int x = 0;
152
153 for (int cols = 0 ; cols < width_in_pixels ; ++cols) {
154 if (low_nibble) {
155 target[FI_RGBA_BLUE] = palette[LOWNIBBLE(source[x])].rgbBlue;
156 target[FI_RGBA_GREEN] = palette[LOWNIBBLE(source[x])].rgbGreen;
157 target[FI_RGBA_RED] = palette[LOWNIBBLE(source[x])].rgbRed;
158 target[FI_RGBA_ALPHA] = (LOWNIBBLE(source[x]) < transparent_pixels) ? table[LOWNIBBLE(source[x])] : 255;
159
160 x++;
161 } else {
162 target[FI_RGBA_BLUE] = palette[HINIBBLE(source[x]) >> 4].rgbBlue;
163 target[FI_RGBA_GREEN] = palette[HINIBBLE(source[x]) >> 4].rgbGreen;
164 target[FI_RGBA_RED] = palette[HINIBBLE(source[x]) >> 4].rgbRed;
165 target[FI_RGBA_ALPHA] = (HINIBBLE(source[x] >> 4) < transparent_pixels) ? table[HINIBBLE(source[x]) >> 4] : 255;
166 }
167
168 low_nibble = !low_nibble;
169
170 target += 4;
171 }
172 }
173
174 void DLL_CALLCONV
FreeImage_ConvertLine8To32MapTransparency(BYTE * target,BYTE * source,int width_in_pixels,RGBQUAD * palette,BYTE * table,int transparent_pixels)175 FreeImage_ConvertLine8To32MapTransparency(BYTE *target, BYTE *source, int width_in_pixels, RGBQUAD *palette, BYTE *table, int transparent_pixels) {
176 for (int cols = 0; cols < width_in_pixels; cols++) {
177 target[FI_RGBA_BLUE] = palette[source[cols]].rgbBlue;
178 target[FI_RGBA_GREEN] = palette[source[cols]].rgbGreen;
179 target[FI_RGBA_RED] = palette[source[cols]].rgbRed;
180 target[FI_RGBA_ALPHA] = (source[cols] < transparent_pixels) ? table[source[cols]] : 255;
181 target += 4;
182 }
183 }
184
185 // ----------------------------------------------------------
186
187 FIBITMAP * DLL_CALLCONV
FreeImage_ConvertTo32Bits(FIBITMAP * dib)188 FreeImage_ConvertTo32Bits(FIBITMAP *dib) {
189 if(!FreeImage_HasPixels(dib)) return NULL;
190
191 const int bpp = FreeImage_GetBPP(dib);
192 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
193
194 if((image_type != FIT_BITMAP) && (image_type != FIT_RGB16) && (image_type != FIT_RGBA16)) {
195 return NULL;
196 }
197
198 const int width = FreeImage_GetWidth(dib);
199 const int height = FreeImage_GetHeight(dib);
200
201 if(image_type == FIT_BITMAP) {
202
203 if(bpp == 32) {
204 return FreeImage_Clone(dib);
205 }
206
207 FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
208 if(new_dib == NULL) {
209 return NULL;
210 }
211
212 // copy metadata from src to dst
213 FreeImage_CloneMetadata(new_dib, dib);
214
215 BOOL bIsTransparent = FreeImage_IsTransparent(dib);
216
217 switch(bpp) {
218 case 1:
219 {
220 if(bIsTransparent) {
221 for (int rows = 0; rows < height; rows++) {
222 FreeImage_ConvertLine1To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
223 }
224 } else {
225 for (int rows = 0; rows < height; rows++) {
226 FreeImage_ConvertLine1To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
227 }
228 }
229
230 return new_dib;
231 }
232
233 case 4:
234 {
235 if(bIsTransparent) {
236 for (int rows = 0; rows < height; rows++) {
237 FreeImage_ConvertLine4To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
238 }
239 } else {
240 for (int rows = 0; rows < height; rows++) {
241 FreeImage_ConvertLine4To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
242 }
243 }
244
245 return new_dib;
246 }
247
248 case 8:
249 {
250 if(bIsTransparent) {
251 for (int rows = 0; rows < height; rows++) {
252 FreeImage_ConvertLine8To32MapTransparency(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib), FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib));
253 }
254 } else {
255 for (int rows = 0; rows < height; rows++) {
256 FreeImage_ConvertLine8To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width, FreeImage_GetPalette(dib));
257 }
258 }
259
260 return new_dib;
261 }
262
263 case 16:
264 {
265 for (int rows = 0; rows < height; rows++) {
266 if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
267 FreeImage_ConvertLine16To32_565(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
268 } else {
269 // includes case where all the masks are 0
270 FreeImage_ConvertLine16To32_555(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
271 }
272 }
273
274 return new_dib;
275 }
276
277 case 24:
278 {
279 for (int rows = 0; rows < height; rows++) {
280 FreeImage_ConvertLine24To32(FreeImage_GetScanLine(new_dib, rows), FreeImage_GetScanLine(dib, rows), width);
281 }
282
283 return new_dib;
284 }
285 }
286
287 } else if(image_type == FIT_RGB16) {
288 FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
289 if(new_dib == NULL) {
290 return NULL;
291 }
292
293 // copy metadata from src to dst
294 FreeImage_CloneMetadata(new_dib, dib);
295
296 const unsigned src_pitch = FreeImage_GetPitch(dib);
297 const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
298 const BYTE *src_bits = FreeImage_GetBits(dib);
299 BYTE *dst_bits = FreeImage_GetBits(new_dib);
300 for (int rows = 0; rows < height; rows++) {
301 const FIRGB16 *src_pixel = (FIRGB16*)src_bits;
302 RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits;
303 for(int cols = 0; cols < width; cols++) {
304 dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8);
305 dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8);
306 dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8);
307 dst_pixel[cols].rgbReserved = (BYTE)0xFF;
308 }
309 src_bits += src_pitch;
310 dst_bits += dst_pitch;
311 }
312
313 return new_dib;
314
315 } else if(image_type == FIT_RGBA16) {
316 FIBITMAP *new_dib = FreeImage_Allocate(width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
317 if(new_dib == NULL) {
318 return NULL;
319 }
320
321 // copy metadata from src to dst
322 FreeImage_CloneMetadata(new_dib, dib);
323
324 const unsigned src_pitch = FreeImage_GetPitch(dib);
325 const unsigned dst_pitch = FreeImage_GetPitch(new_dib);
326 const BYTE *src_bits = FreeImage_GetBits(dib);
327 BYTE *dst_bits = FreeImage_GetBits(new_dib);
328 for (int rows = 0; rows < height; rows++) {
329 const FIRGBA16 *src_pixel = (FIRGBA16*)src_bits;
330 RGBQUAD *dst_pixel = (RGBQUAD*)dst_bits;
331 for(int cols = 0; cols < width; cols++) {
332 dst_pixel[cols].rgbRed = (BYTE)(src_pixel[cols].red >> 8);
333 dst_pixel[cols].rgbGreen = (BYTE)(src_pixel[cols].green >> 8);
334 dst_pixel[cols].rgbBlue = (BYTE)(src_pixel[cols].blue >> 8);
335 dst_pixel[cols].rgbReserved = (BYTE)(src_pixel[cols].alpha >> 8);
336 }
337 src_bits += src_pitch;
338 dst_bits += dst_pitch;
339 }
340
341 return new_dib;
342 }
343
344 return NULL;
345 }
346