1 /*
2 * Copyright (c) 2004 Ximian
3 * Copyright (C) 2003-2004,2007 Novell, Inc (http://www.novell.com)
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
6 * and associated documentation files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all copies or substantial
12 * portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
18 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 *
20 * bmpcodec.c : Contains function definitions for encoding decoding bmp images
21 *
22 * Authors:
23 * Jordi Mas i Hernandez (jordi@ximian.com)
24 * Sanjay Gupta (gsanjay@novell.com)
25 * Mark Steele (ms@rapidsys.com)
26 * Jonathan Gilbert (logic@deltaq.org)
27 * Sebastien Pouliot <sebastien@ximian.com>
28 *
29 * Useful documentation about bitmaps
30 *
31 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_4v1h.asp
32 * http://www.csdn.net/Dev/Format/windows/Bmp.html
33 * http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html
34 *
35 * Header structure
36 * BITMAPFILEHEADER
37 * BITMAPINFOHEADER or BITMAPV4HEADER or BITMAPV5HEADER or BITMAPCOREHEADER
38 * RGBQUADS or RGBTRIPLE (optional)
39 * Bitmap data
40 */
41
42 #include "gdiplus-private.h"
43 #include "bmpcodec.h"
44
45 GUID gdip_bmp_image_format_guid = {0xb96b3cabU, 0x0728U, 0x11d3U, {0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e}};
46
47 /* Codecinfo related data*/
48 static ImageCodecInfo bmp_codec;
49 static const WCHAR bmp_codecname[] = {'B', 'u', 'i','l', 't', '-','i', 'n', ' ', 'B', 'M', 'P', ' ', 'C', 'o', 'd', 'e', 'c', 0}; /* Built-in BMP Codec */
50 static const WCHAR bmp_extension[] = {'*','.','B', 'M', 'P',';', '*','.', 'D','I', 'B',';', '*','.', 'R', 'L', 'E',0}; /* *.BMP;*.DIB;*.RLE */
51 static const WCHAR bmp_mimetype[] = {'i', 'm', 'a','g', 'e', '/', 'b', 'm', 'p', 0}; /* image/bmp */
52 static const WCHAR bmp_format[] = {'B', 'M', 'P', 0}; /* BMP */
53 static const BYTE bmp_sig_pattern[] = { 0x42, 0x4D };
54 static const BYTE bmp_sig_mask[] = { 0xFF, 0xFF };
55
56
57 ImageCodecInfo *
gdip_getcodecinfo_bmp()58 gdip_getcodecinfo_bmp ()
59 {
60 bmp_codec.Clsid = (CLSID) { 0x557cf400, 0x1a04, 0x11d3, { 0x9a, 0x73, 0x0, 0x0, 0xf8, 0x1e, 0xf3, 0x2e } };
61 bmp_codec.FormatID = gdip_bmp_image_format_guid;
62 bmp_codec.CodecName = (const WCHAR*) bmp_codecname;
63 bmp_codec.DllName = NULL;
64 bmp_codec.FormatDescription = (const WCHAR*) bmp_format;
65 bmp_codec.FilenameExtension = (const WCHAR*) bmp_extension;
66 bmp_codec.MimeType = (const WCHAR*) bmp_mimetype;
67 bmp_codec.Flags = ImageCodecFlagsEncoder | ImageCodecFlagsDecoder | ImageCodecFlagsSupportBitmap | ImageCodecFlagsBuiltin;
68 bmp_codec.Version = 1;
69 bmp_codec.SigCount = 1;
70 bmp_codec.SigSize = 2;
71 bmp_codec.SigPattern = bmp_sig_pattern;
72 bmp_codec.SigMask = bmp_sig_mask;
73
74 return &bmp_codec;
75 }
76
77 static GpStatus
gdip_get_bmp_stride(PixelFormat format,INT width,INT * strideResult,BOOL cairoHacks)78 gdip_get_bmp_stride (PixelFormat format, INT width, INT *strideResult, BOOL cairoHacks)
79 {
80 INT stride;
81 /* stride is a (signed) _int_ and once multiplied by 4 it should hold a value that can be allocated by GdipAlloc
82 * this effectively limits 'width' to 536870911 pixels */
83 unsigned long long int widthBuff = width;
84
85 switch (format) {
86 case PixelFormat1bppIndexed:
87 stride = (width + 7) / 8;
88 break;
89 case PixelFormat4bppIndexed:
90 stride = (width + 1) / 2;
91 break;
92 case PixelFormat8bppIndexed:
93 stride = width;
94 break;
95 case PixelFormat16bppRGB555:
96 case PixelFormat16bppRGB565:
97 widthBuff *= 2;
98 if (widthBuff > G_MAXINT32)
99 return InvalidParameter;
100
101 stride = widthBuff;
102 break;
103 case PixelFormat24bppRGB:
104 widthBuff *= cairoHacks ? 4 : 3;
105 if (widthBuff > G_MAXINT32)
106 return InvalidParameter;
107
108 stride = widthBuff;
109 break;
110 default:
111 widthBuff *= 4;
112 if (widthBuff > G_MAXINT32)
113 return InvalidParameter;
114
115 stride = widthBuff;
116 break;
117 }
118
119 /* Ensure 32bits alignment */
120 gdip_align_stride (stride);
121 *strideResult = stride;
122 return Ok;
123 }
124
125 static GpStatus
gdip_get_bmp_pixelformat(const BITMAPV5HEADER * bih,PixelFormat * sourceFormatResult,PixelFormat * conversionFormatResult)126 gdip_get_bmp_pixelformat (const BITMAPV5HEADER *bih, PixelFormat *sourceFormatResult, PixelFormat *conversionFormatResult)
127 {
128 PixelFormat sourceFormat;
129 PixelFormat conversionFormat;
130
131 if (bih->bV5Compression == BI_BITFIELDS) {
132 if (bih->bV5BitCount != 16)
133 return OutOfMemory;
134
135 if ((bih->bV5RedMask == 0x7C00) && (bih->bV5GreenMask == 0x3E0) && (bih->bV5BlueMask == 0x1F))
136 sourceFormat = PixelFormat16bppRGB555;
137 else if ((bih->bV5RedMask == 0xF800) && (bih->bV5GreenMask == 0x7E0) && (bih->bV5BlueMask == 0x1F))
138 sourceFormat = PixelFormat16bppRGB565;
139 else
140 sourceFormat = PixelFormat16bppRGB555;
141
142 conversionFormat = PixelFormat32bppRGB;
143 } else {
144 switch (bih->bV5BitCount) {
145 case 64:
146 sourceFormat = PixelFormat64bppARGB;
147 conversionFormat = sourceFormat;
148 break;
149 case 32:
150 sourceFormat = PixelFormat32bppRGB;
151 conversionFormat = sourceFormat;
152 break;
153 case 24:
154 sourceFormat = PixelFormat24bppRGB;
155 conversionFormat = sourceFormat;
156 break;
157 case 16:
158 sourceFormat = PixelFormat16bppRGB555;
159 conversionFormat = PixelFormat32bppRGB;
160 break;
161 case 8:
162 sourceFormat = PixelFormat8bppIndexed;
163 conversionFormat = sourceFormat;
164 break;
165 case 4:
166 sourceFormat = PixelFormat4bppIndexed;
167 conversionFormat = sourceFormat;
168 break;
169 case 1:
170 sourceFormat = PixelFormat1bppIndexed;
171 conversionFormat = sourceFormat;
172 break;
173 default:
174 return OutOfMemory;
175 }
176 }
177
178 *sourceFormatResult = sourceFormat;
179 *conversionFormatResult = conversionFormat;
180 return Ok;
181 }
182
183 static void
gdip_bitmap_fill_info_header(GpBitmap * bitmap,PBITMAPINFOHEADER bmi)184 gdip_bitmap_fill_info_header (GpBitmap *bitmap, PBITMAPINFOHEADER bmi)
185 {
186 PixelFormat format = bitmap->active_bitmap->pixel_format;
187
188 memset (bmi, 0, sizeof (BITMAPINFOHEADER));
189 #ifdef WORDS_BIGENDIAN
190 bmi->biSize = GUINT32_FROM_LE (sizeof (BITMAPINFOHEADER));
191 bmi->biWidth = GULONG_FROM_LE (bitmap->active_bitmap->width);
192 bmi->biHeight = GULONG_FROM_LE (bitmap->active_bitmap->height);
193 bmi->biPlanes = GUINT16_FROM_LE (1);
194 if (format != PixelFormat24bppRGB)
195 bmi->biBitCount = GUINT16_FROM_LE (gdip_get_pixel_format_bpp (bitmap->active_bitmap->pixel_format));
196 else
197 bmi->biBitCount = GUINT16_FROM_LE (24);
198 bmi->biCompression = GUINT32_FROM_LE (BI_RGB);
199 bmi->biSizeImage = GUINT32_FROM_LE (0); /* Many tools expect this may be set to zero for BI_RGB bitmaps */
200 bmi->biXPelsPerMeter = GULONG_FROM_LE ((int) (0.5f + ((gdip_get_display_dpi() * 3937) / 100)));
201 bmi->biYPelsPerMeter = GULONG_FROM_LE ((int) (0.5f + ((gdip_get_display_dpi() * 3937) / 100))); /* 1 meter is = 39.37 */
202 #else
203 bmi->biSize = sizeof (BITMAPINFOHEADER);
204 bmi->biWidth = bitmap->active_bitmap->width;
205 bmi->biHeight = bitmap->active_bitmap->height;
206 bmi->biPlanes = 1;
207 if (format != PixelFormat24bppRGB)
208 bmi->biBitCount = gdip_get_pixel_format_bpp (bitmap->active_bitmap->pixel_format);
209 else
210 bmi->biBitCount = 24;
211
212 bmi->biCompression = BI_RGB;
213 bmi->biSizeImage = 0; /* Many tools expect this may be set to zero for BI_RGB bitmaps */
214 bmi->biXPelsPerMeter = (int) (0.5f + ((gdip_get_display_dpi() * 3937) / 100));
215 bmi->biYPelsPerMeter = (int) (0.5f + ((gdip_get_display_dpi() * 3937) / 100)); /* 1 meter is = 39.37 */
216 #endif
217 }
218
219 static void
gdip_read_bmp_rle_8bit(void * pointer,BYTE * scan0,BOOL upsidedown,int stride,int scanWidth,int scanCount,ImageSource source)220 gdip_read_bmp_rle_8bit (void *pointer, BYTE *scan0, BOOL upsidedown, int stride, int scanWidth, int scanCount, ImageSource source)
221 {
222 BYTE code;
223 int bytes_read;
224
225 int col_offset = 0;
226 int row_offset = (scanCount - 1) * stride;
227 int row_delta = -stride;
228 int rows_remaining = scanCount;
229 int size = scanCount * stride;
230 BOOL new_row = FALSE;
231
232 if (!upsidedown)
233 return; /* top to bottom images can't be compressed */
234
235 if (scanWidth > stride)
236 return;
237
238 while ((rows_remaining > 0)
239 || ((row_offset == 0) && (col_offset < scanWidth))) {
240 bytes_read = gdip_read_bmp_data (pointer, &code, 1, source);
241
242 if (bytes_read < 1)
243 return; /* TODO?: Add an "unexpected end of file" error code */
244
245 if (code == 0) { /* RLE escape code */
246 bytes_read = gdip_read_bmp_data (pointer, &code, 1, source);
247
248 if (bytes_read < 1)
249 return; /* TODO?: Add an "unexpected end of file" error code */
250
251 switch (code)
252 {
253 case 0: /* skip remainder of scan */
254 {
255 if (new_row)
256 new_row = FALSE;
257 else {
258 row_offset += row_delta;
259 rows_remaining--;
260 col_offset = 0;
261 }
262 break;
263 }
264 case 1: /* skip remainder of image -- in other words, we're finished :-) */
265 {
266 return;
267 }
268 case 2: /* jump forward (dx, dy) coordinates */
269 {
270 BYTE dx, dy;
271
272 bytes_read = gdip_read_bmp_data (pointer, &dx, 1, source);
273 bytes_read += gdip_read_bmp_data (pointer, &dy, 1, source);
274
275 if (bytes_read < 2)
276 return; /* TODO?: Add an "unexpected end of file" error code */
277
278 /* not really sure how to handle the case where the X delta goes
279 * past the end of the scan. in the interest of not crashing,
280 * let's wrap it back around.
281 */
282 col_offset = (col_offset + dx) % scanWidth;
283 row_offset -= dy * stride; /* BMPs go from bottom to top */
284
285 new_row = FALSE;
286 break;
287 }
288 default: /* an uncompressed section, 'code' pixels wide */
289 {
290 /* uncompressed sections must be an even number of bytes long,
291 * even if they are an odd number of *pixels* long.
292 */
293 BOOL pad_byte_present = ((code & 1) != 0);
294 int bytes_to_read = code;
295
296 /* wrap rows properly, even though they are inverted in memory */
297 while (bytes_to_read > 0) {
298 int bytes_to_read_this_scan = scanWidth - col_offset;
299
300 if (bytes_to_read_this_scan > bytes_to_read)
301 bytes_to_read_this_scan = bytes_to_read;
302
303 int pixel_index = row_offset + col_offset;
304
305 if (pixel_index < 0 || pixel_index >= size)
306 return;
307
308 bytes_read = gdip_read_bmp_data (
309 pointer,
310 &scan0[pixel_index],
311 bytes_to_read_this_scan,
312 source);
313
314 if (bytes_read < bytes_to_read_this_scan)
315 return; /* TODO?: Add an "unexpected end of file" error code */
316
317 col_offset += bytes_read;
318 bytes_to_read -= bytes_read;
319
320 if (col_offset >= scanWidth)
321 {
322 col_offset = 0;
323 row_offset += row_delta;
324 rows_remaining--;
325
326 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
327 return;
328
329 new_row = TRUE;
330 }
331 else
332 new_row = FALSE;
333 }
334
335 if (pad_byte_present) {
336 bytes_read = gdip_read_bmp_data(pointer, &code, 1, source);
337
338 if (bytes_read < 1)
339 return; /* TODO?: Add an "unexpected end of file" error code */
340 }
341
342 break;
343 }
344 }
345 }
346 else {
347 /* we have a run of length 'code'. the colour of the run is the next byte in the file. */
348 int run_length = code;
349 BYTE pixel_value;
350
351 bytes_read = gdip_read_bmp_data(pointer, &pixel_value, 1, source);
352
353 if (bytes_read < 1)
354 return; /* TODO?: Add an "unexpected end of file" error code */
355
356 while (run_length > 0) {
357 int bytes_to_run_this_scan = scanWidth - col_offset;
358
359 if (bytes_to_run_this_scan > run_length)
360 bytes_to_run_this_scan = run_length;
361
362 int pixel_index = row_offset + col_offset;
363
364 if (pixel_index < 0 || pixel_index >= size)
365 return;
366
367 if (bytes_to_run_this_scan < 0 || pixel_index + bytes_to_run_this_scan > size)
368 return;
369
370 memset (scan0 + pixel_index, pixel_value, bytes_to_run_this_scan);
371
372 col_offset += bytes_to_run_this_scan;
373 run_length -= bytes_to_run_this_scan;
374
375 if (col_offset >= scanWidth)
376 {
377 col_offset = 0;
378 row_offset += row_delta;
379 rows_remaining--;
380
381 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
382 return;
383
384 new_row = TRUE;
385 }
386 else
387 new_row = FALSE;
388 }
389 }
390 }
391
392 return;
393 }
394
395 // PixelFormat32bppARGB.
396 ARGB
gdip_getpixel_32bppARGB(BYTE * scan,INT x)397 gdip_getpixel_32bppARGB (BYTE *scan, INT x)
398 {
399 ARGB pixel = ((ARGB *) scan)[x];
400 return pixel;
401 }
402
403 void
gdip_setpixel_32bppARGB(BYTE * scan,INT x,BYTE a,BYTE r,BYTE g,BYTE b)404 gdip_setpixel_32bppARGB (BYTE *scan, INT x, BYTE a, BYTE r, BYTE g, BYTE b)
405 {
406 set_pixel_bgra (scan, x * 4, b, g, r, a);
407 }
408
409 // PixelFormat16bppRGB555.
410 ARGB
gdip_getpixel_16bppRGB555(BYTE * scan,INT x)411 gdip_getpixel_16bppRGB555 (BYTE *scan, INT x)
412 {
413 WORD pixel = ((WORD *) scan)[x];
414 return ((pixel & 0x1F) >> 2) | 8 * ((pixel & 0x1F) | 8 * (((((pixel >> 5) & 0x1F) | (((pixel >> 10) & 0x1C) << 8)) & 0xFFFFFFFC) | 32 * (((pixel >> 5) & 0x1F) | ((((pixel >> 10) & 0x1F) | 0xFFFFFFE0) << 8))));
415 }
416
417 // PixelFormat16bppRGB565.
418 ARGB
gdip_getpixel_16bppRGB565(BYTE * scan,INT x)419 gdip_getpixel_16bppRGB565 (BYTE *scan, INT x)
420 {
421 WORD pixel = ((WORD *) scan)[x];
422 return ((pixel & 0x1F) >> 2) | 8 * ((pixel & 0x1F) | 2 * (((((pixel >> 5) & 0x3F) | (((pixel >> 11) & 0xFFFFFFFC) << 10)) & 0xFFFFFFF0) | ((((pixel >> 5) & 0x3F) | ((((pixel >> 11)) | 0xFFFFFFE0) << 9)) << 6)));
423 }
424
425 static void
gdip_read_bmp_rle_4bit(void * pointer,BYTE * scan0,BOOL upsidedown,int stride,int scanWidth,int scanCount,ImageSource source)426 gdip_read_bmp_rle_4bit (void *pointer, BYTE *scan0, BOOL upsidedown, int stride, int scanWidth, int scanCount, ImageSource source)
427 {
428 BYTE code;
429 int bytes_read;
430
431 int col_offset = 0;
432 int row_offset = (scanCount - 1) * stride;
433 int row_delta = -stride;
434 int rows_remaining = scanCount;
435 int size = scanCount * stride;
436 BOOL new_row = FALSE;
437
438 if (!upsidedown)
439 return; /* top to bottom images can't be compressed */
440
441 if ((scanWidth & 1) != 0)
442 scanWidth++;
443
444 if (scanWidth > stride * 2)
445 return;
446
447 while (rows_remaining > 0) {
448 bytes_read = gdip_read_bmp_data (pointer, &code, 1, source);
449
450 if (bytes_read < 1)
451 return; /* TODO?: Add an "unexpected end of file" error code */
452
453 if (code == 0) { /* RLE escape code */
454 bytes_read = gdip_read_bmp_data (pointer, &code, 1, source);
455
456 if (bytes_read < 1)
457 return; /* TODO?: Add an "unexpected end of file" error code */
458
459 switch (code)
460 {
461 case 0: /* skip remainder of scan */
462 {
463 if (new_row)
464 new_row = FALSE;
465 else {
466 row_offset += row_delta;
467 rows_remaining--;
468 col_offset = 0;
469 }
470 break;
471 }
472 case 1: /* skip remainder of image -- in other words, we're finished :-) */
473 {
474 return;
475 }
476 case 2: /* jump forward (dx, dy) coordinates */
477 {
478 BYTE dx, dy;
479
480 bytes_read = gdip_read_bmp_data (pointer, &dx, 1, source);
481 bytes_read += gdip_read_bmp_data (pointer, &dy, 1, source);
482
483 if (bytes_read < 2)
484 return; /* TODO?: Add an "unexpected end of file" error code */
485
486 /* not really sure how to handle the case where the X delta goes
487 * past the end of the scan. in the interest of not crashing,
488 * let's wrap it back around.
489 */
490 col_offset = (col_offset + dx) % scanWidth;
491 row_offset -= dy * stride; /* BMPs go from bottom to top */
492
493 new_row = FALSE;
494
495 break;
496 }
497 default: /* an uncompressed section, 'code' pixels wide */
498 {
499 int pixels_to_read = code;
500 int bytes_of_data = (pixels_to_read + 1) / 2;
501
502 /* uncompressed sections must be an even number of bytes long,
503 * even if they are an odd number of *pixels* long.
504 */
505 BOOL pad_byte_present = ((bytes_of_data & 1) != 0);
506
507 int bytes_to_read = pixels_to_read / 2; /* leave off the last pixel for now */
508
509 /* wrap rows properly, even though they are inverted in memory */
510 while (bytes_to_read > 0) {
511 if ((scanWidth - col_offset) == 1) {
512 /* special case: a pair of pixels is split across two rows. */
513 BYTE pixels, same_row_pixel, next_row_pixel;
514 int pixel_index = row_offset + col_offset / 2;
515
516 if (pixel_index < 0 || pixel_index >= size)
517 return;
518
519 bytes_read = gdip_read_bmp_data (pointer, &pixels, 1, source);
520
521 if (bytes_read < 1)
522 return; /* TODO?: Add an "unexpected end of file" error code */
523
524 same_row_pixel = (pixels >> 4) & 0x0F;
525 next_row_pixel = pixels & 0x0F;
526
527 if ((col_offset & 1) != 0) {
528 BYTE old_pixel = 0xF0 & scan0[pixel_index];
529 scan0[pixel_index] = (old_pixel & 0xF0) | same_row_pixel;
530 }
531 else
532 scan0[pixel_index] = same_row_pixel << 4;
533
534 col_offset = 1;
535 row_offset += row_delta;
536 rows_remaining--;
537
538 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
539 return;
540
541 if (row_offset < 0 || row_offset >= size)
542 return;
543
544 scan0[row_offset] = next_row_pixel << 4;
545
546 new_row = FALSE;
547 }
548 else if ((col_offset & 1) == 0) {
549 /* alignment is good; we can read pairs of pixels as bytes.
550 * if there are an odd number of pixels in a scan, though,
551 * then the last pixel will need to be special-cased. also,
552 * if the scan width is odd, then a byte will be split
553 * across a row ending. I don't know if this is in the spec,
554 * but it is the most resistant to crashing.
555 */
556 int bytes_to_read_this_scan = (scanWidth - col_offset) / 2;
557
558 if (bytes_to_read_this_scan > bytes_to_read)
559 bytes_to_read_this_scan = bytes_to_read;
560
561 int pixel_index = row_offset + col_offset / 2;
562
563 if (pixel_index < 0 || pixel_index >= size)
564 return;
565
566 bytes_read = gdip_read_bmp_data (
567 pointer,
568 &scan0[pixel_index],
569 bytes_to_read_this_scan,
570 source);
571
572 if (bytes_read < bytes_to_read_this_scan)
573 return; /* TODO?: Add an "unexpected end of file" error code */
574
575 col_offset += bytes_read * 2;
576 bytes_to_read -= bytes_read;
577
578 new_row = FALSE;
579 }
580 else {
581 /* bad alignment; nybble-swapping will be required */
582 int pixel_index = row_offset + col_offset / 2;
583
584 if (pixel_index < 0 || pixel_index >= size)
585 return;
586
587 BYTE last_high_nybble = 0xF0 & scan0[pixel_index];
588
589 int bytes_to_read_this_scan = (scanWidth - col_offset) / 2;
590
591 if (bytes_to_read_this_scan > bytes_to_read)
592 bytes_to_read_this_scan = bytes_to_read;
593
594 while (bytes_to_read_this_scan >= 0) {
595 BYTE pixels;
596
597 bytes_read = gdip_read_bmp_data (pointer, &pixels, 1, source);
598
599 if (bytes_read < 1)
600 return; /* TODO?: Add an "unexpected end of file" error code */
601
602 pixel_index = row_offset + col_offset / 2;
603
604 if (pixel_index < 0 || pixel_index >= size)
605 return;
606
607 scan0[pixel_index] = last_high_nybble | (pixels >> 4);
608
609 last_high_nybble = (pixels << 4) & 0xF0;
610
611 col_offset += 2; /* two pixels processed */
612 bytes_to_read_this_scan--;
613 }
614
615 new_row = FALSE;
616 }
617
618 if (col_offset >= scanWidth) {
619 col_offset = 0;
620 row_offset += row_delta;
621 rows_remaining--;
622
623 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
624 return;
625
626 new_row = TRUE;
627 }
628 }
629
630 if ((pixels_to_read & 1) != 0) {
631 /* half of a byte remains to be inserted into the correct nybble */
632 BYTE pixel;
633 int pixel_index = row_offset + col_offset / 2;
634
635 if (pixel_index < 0 || pixel_index >= size)
636 return;
637
638 bytes_read = gdip_read_bmp_data (pointer, &pixel, 1, source);
639
640 if (bytes_read < 1)
641 return; /* TODO?: Add an "unexpected end of file" error code */
642
643 pixel >>= 4; /* the last pixel is in the high nybble */
644
645 if ((col_offset & 1) != 0) {
646 BYTE old_pixel = 0xF0 & scan0[pixel_index];
647 scan0[pixel_index] = (old_pixel & 0xF0) | pixel;
648 }
649 else
650 scan0[pixel_index] = pixel << 4;
651
652 col_offset++;
653
654 if (col_offset >= scanWidth) {
655 col_offset = 0;
656 row_offset += row_delta;
657 rows_remaining--;
658
659 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
660 return;
661
662 new_row = TRUE;
663 }
664 else
665 new_row = FALSE;
666 }
667
668 if (pad_byte_present) {
669 bytes_read = gdip_read_bmp_data(pointer, &code, 1, source);
670
671 if (bytes_read < 1)
672 return; /* TODO?: Add an "unexpected end of file" error code */
673 }
674
675 break;
676 }
677 }
678 }
679 else {
680 /* we have a run of length 'code'. the colour of the run is the next byte in the file.
681 * something weird is happening here in 4-bit land, though; a byte stores two pixels.
682 * what happens is rather odd: the run is actually of two alternating colours (which
683 * may, of course, be the same, but are not required to be). we need to make sure that
684 * the colours end up in the right nybbles of the output bytes.
685 */
686 int run_pixels = code;
687 int run_length = run_pixels / 2;
688
689 BYTE pixel_values;
690 BYTE inverted_pixel_values;
691
692 bytes_read = gdip_read_bmp_data(pointer, &pixel_values, 1, source);
693
694 if (bytes_read < 1)
695 return; /* TODO?: Add an "unexpected end of file" error code */
696
697 inverted_pixel_values = ((pixel_values & 0x0F) << 4) | ((pixel_values & 0xF0) >> 4);
698
699 if ((col_offset & 1) != 0) {
700 BYTE temp = inverted_pixel_values;
701 inverted_pixel_values = pixel_values;
702 pixel_values = temp;
703 }
704
705 while (run_length > 0) {
706 if ((scanWidth - col_offset) == 1) {
707 /* special case: a pair of pixels is split across two rows. */
708 BYTE same_row_pixel = (pixel_values >> 4) & 0x0F;
709 BYTE next_row_pixel = pixel_values & 0x0F;
710
711 int pixel_index = row_offset + col_offset / 2;
712
713 if (pixel_index < 0 || pixel_index >= size)
714 return;
715
716 if ((col_offset & 1) != 0) {
717 BYTE old_pixel = 0xF0 & scan0[pixel_index];
718 scan0[pixel_index] = (old_pixel & 0xF0) | same_row_pixel;
719 }
720 else
721 scan0[pixel_index] = same_row_pixel << 4;
722
723 col_offset = 1;
724 row_offset += row_delta;
725 rows_remaining--;
726
727 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
728 return;
729
730 if (row_offset < 0 || row_offset >= size)
731 return;
732
733 scan0[row_offset] = next_row_pixel << 4;
734
735 new_row = FALSE;
736
737 if ((scanWidth & 1) != 0) {
738 /* if the width of the scan is odd, then the nybbles swap
739 * places each time they cross from one row to the next
740 */
741 BYTE temp = inverted_pixel_values;
742 inverted_pixel_values = pixel_values;
743 pixel_values = temp;
744 }
745 }
746 else {
747 int bytes_to_run_this_scan;
748 int pixel_index = row_offset + col_offset / 2;
749
750 if (pixel_index < 0 || pixel_index >= size)
751 return;
752
753 /* make sure we're byte-aligned; if we're not, we need to store a nybble first */
754 if ((col_offset & 1) != 0) {
755 BYTE old_pixel = 0xF0 & scan0[pixel_index];
756 scan0[pixel_index] = (old_pixel & 0xF0) | (pixel_values & 0x0F);
757
758 col_offset++;
759 }
760
761 bytes_to_run_this_scan = (scanWidth - col_offset) / 2;
762
763 if (bytes_to_run_this_scan > run_length)
764 bytes_to_run_this_scan = run_length;
765
766 if (bytes_to_run_this_scan < 0 || pixel_index + bytes_to_run_this_scan > size)
767 return;
768
769 memset (scan0 + pixel_index, pixel_values, bytes_to_run_this_scan);
770
771 col_offset += bytes_to_run_this_scan * 2;
772 run_length -= bytes_to_run_this_scan;
773
774 if (col_offset >= scanWidth) {
775 col_offset = 0;
776 row_offset += row_delta;
777 rows_remaining--;
778
779 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
780 return;
781
782 new_row = TRUE;
783
784 if ((scanWidth & 1) != 0) {
785 /* if the width of the scan is odd, then the nybbles swap
786 * places each time they cross from one row to the next
787 */
788 BYTE temp = inverted_pixel_values;
789 inverted_pixel_values = pixel_values;
790 pixel_values = temp;
791 }
792 }
793 else
794 new_row = FALSE;
795 }
796 }
797
798 if ((run_pixels & 1) != 0) {
799 /* half of a byte remains to be inserted into the correct nybble */
800 BYTE pixel = pixel_values >> 4; /* the last pixel is in the high nybble */
801 int pixel_index = row_offset + col_offset / 2;
802
803 if (pixel_index < 0 || pixel_index >= size)
804 return;
805
806 if ((col_offset & 1) != 0) {
807 BYTE old_pixel = 0xF0 & scan0[pixel_index];
808 scan0[pixel_index] = (old_pixel & 0xF0) | pixel;
809 }
810 else
811 scan0[pixel_index] = pixel << 4;
812
813 col_offset++;
814
815 if (col_offset >= scanWidth) {
816 col_offset = 0;
817 row_offset += row_delta;
818 rows_remaining--;
819
820 if (rows_remaining <= 0) /* more data than expected -- let's not make this a fatal error */
821 return;
822
823 new_row = TRUE;
824 }
825 else
826 new_row = FALSE;
827 }
828 }
829 }
830
831 return;
832 }
833
834 GpStatus
gdip_read_BITMAPINFOHEADER(void * pointer,ImageSource source,BITMAPV5HEADER * bmi,BOOL * upsidedown)835 gdip_read_BITMAPINFOHEADER (void *pointer, ImageSource source, BITMAPV5HEADER *bmi, BOOL *upsidedown)
836 {
837 DWORD dw = 0;
838 BYTE *data_read = (BYTE*)&dw;
839 int size = sizeof (DWORD);
840 int size_read = gdip_read_bmp_data (pointer, data_read, size, source);
841 if (size_read < size)
842 return OutOfMemory;
843
844 DWORD headerSize = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
845 switch (headerSize) {
846 case BITMAPCOREHEADER_SIZE:
847 bmi->bV5Size = headerSize;
848
849 /* Old OS/2 format. Width and Height fields are WORDs instead of DWORDS */
850 dw = 0;
851 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
852 if (size_read < size)
853 return OutOfMemory;
854 bmi->bV5Width = (data_read[1]<<8 | data_read[0]);
855 bmi->bV5Height = (data_read[3]<<8 | data_read[2]);
856
857 break;
858 case sizeof (BITMAPINFOHEADER):
859 case sizeof (BITMAPV3HEADER):
860 case sizeof (BITMAPV4HEADER):
861 case sizeof (BITMAPV5HEADER):
862 bmi->bV5Size = headerSize;
863
864 dw = 0;
865 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
866 if (size_read < size)
867 return OutOfMemory;
868 bmi->bV5Width = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
869
870 // Width can't be negative (as opposed to Height where that indicates a top-down image)
871 if (bmi->bV5Width <= 0)
872 return OutOfMemory;
873
874 dw = 0;
875 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
876 if (size_read < size)
877 return OutOfMemory;
878 bmi->bV5Height = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
879
880 if (bmi->bV5Height == 0)
881 return OutOfMemory;
882
883 break;
884 default:
885 /* This is an unknown or invalid header. */
886 return OutOfMemory;
887 }
888
889 dw = 0;
890 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
891 if (size_read < size)
892 return OutOfMemory;
893 bmi->bV5Planes = (data_read[1]<<8 | data_read[0]);
894 bmi->bV5BitCount = (data_read[3]<<8 | data_read[2]);
895
896 /* The OS/2 format doesn't have any of these other fields */
897 if (bmi->bV5Size == BITMAPCOREHEADER_SIZE) {
898 bmi->bV5Compression = 0;
899 bmi->bV5SizeImage = 0;
900 bmi->bV5XPelsPerMeter = 0;
901 bmi->bV5YPelsPerMeter = 0;
902 bmi->bV5ClrUsed = 0;
903 bmi->bV5ClrImportant = 0;
904
905 return Ok;
906 }
907
908 dw = 0;
909 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
910 if (size_read < size)
911 return OutOfMemory;
912 bmi->bV5Compression = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
913
914 /* If the height is negative then the bitmap is sideup. */
915 if (bmi->bV5Height < 0) {
916 *upsidedown = FALSE;
917 bmi->bV5Height = -bmi->bV5Height;
918 }
919
920 dw = 0;
921 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
922 if (size_read < size)
923 return OutOfMemory;
924 bmi->bV5SizeImage = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
925
926 dw = 0;
927 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
928 if (size_read < size)
929 return OutOfMemory;
930 bmi->bV5XPelsPerMeter = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
931
932 dw = 0;
933 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
934 if (size_read < size)
935 return OutOfMemory;
936 bmi->bV5YPelsPerMeter = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
937
938 dw = 0;
939 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
940 if (size_read < size)
941 return OutOfMemory;
942 bmi->bV5ClrUsed = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
943
944 dw = 0;
945 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
946 if (size_read < size)
947 return OutOfMemory;
948 bmi->bV5ClrImportant = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
949
950 /* We've finished reading the BITMAPINFOHEADER but later versions are larger. */
951 if (bmi->bV5Size == sizeof (BITMAPINFOHEADER)) {
952 // A 16bpp BITMAPINFOHEADER BI_BITFIELDS image is followed by a red, green and blue mask.
953 if (bmi->bV5BitCount != 16 || bmi->bV5Compression != BI_BITFIELDS)
954 return Ok;
955 }
956
957 dw = 0;
958 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
959 if (size_read < size)
960 return OutOfMemory;
961 bmi->bV5RedMask = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
962
963 dw = 0;
964 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
965 if (size_read < size)
966 return OutOfMemory;
967 bmi->bV5GreenMask = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
968
969 dw = 0;
970 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
971 if (size_read < size)
972 return OutOfMemory;
973 bmi->bV5BlueMask = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
974
975 if (bmi->bV5Size == sizeof (BITMAPINFOHEADER))
976 return Ok;
977
978 dw = 0;
979 size_read = gdip_read_bmp_data (pointer, data_read, size, source);
980 if (size_read < size)
981 return OutOfMemory;
982 bmi->bV5AlphaMask = ((guint32)data_read[3]<<24 | data_read[2]<<16 | data_read[1]<<8 | data_read[0]);
983
984 /* We don't use any information in BITMAPV4HEADER or BITMAPV5HEADER, so there is no point going through all of
985 * the other fields. This leaves the rest of the structure uninitialized. */
986 size = headerSize - sizeof (BITMAPV3HEADER);
987 if (size > 0) {
988 while (size > sizeof (DWORD)) {
989 if (gdip_read_bmp_data (pointer, data_read, sizeof (DWORD), source) != sizeof (DWORD))
990 return OutOfMemory;
991 size -= sizeof (DWORD);
992 }
993 if (gdip_read_bmp_data (pointer, data_read, size, source) != size)
994 return OutOfMemory;
995 }
996
997 return Ok;
998 }
999
1000 static GpStatus
gdip_readbmp_palette(void * pointer,ImageSource source,const BITMAPV5HEADER * bmi,ColorPalette ** result)1001 gdip_readbmp_palette (void *pointer, ImageSource source, const BITMAPV5HEADER *bmi, ColorPalette **result)
1002 {
1003 UINT numberOfColors = bmi->bV5ClrUsed;
1004 if (bmi->bV5BitCount <= 8) {
1005 int defaultNumberOfColors = 1 << bmi->bV5BitCount;
1006 // A color count of 0 means use the default. Also use the default color count
1007 // if the bitmap has specified an unsupported number of colors (i.e. greater
1008 // than the default).
1009 if (bmi->bV5ClrUsed == 0 || bmi->bV5ClrUsed > defaultNumberOfColors)
1010 numberOfColors = defaultNumberOfColors;
1011 }
1012
1013 // Nothing to do.
1014 if (numberOfColors == 0)
1015 return Ok;
1016
1017 BYTE buffer[4];
1018 INT colorEntrySize = bmi->bV5Size == BITMAPCOREHEADER_SIZE ? 3 : 4;
1019 unsigned long long int palette_size = (unsigned long long int)sizeof (ColorPalette) + sizeof (ARGB) * numberOfColors;
1020
1021 /* ensure total 'palette_size' does not overflow an integer and fits inside our 2GB limit */
1022 if (palette_size > G_MAXINT32) {
1023 return OutOfMemory;
1024 }
1025
1026 ColorPalette *palette = GdipAlloc (palette_size);
1027 if (!palette)
1028 return OutOfMemory;
1029
1030 palette->Flags = 0;
1031 palette->Count = numberOfColors;
1032
1033 for (int i = 0; i < palette->Count; i++) {
1034 int size_read = gdip_read_bmp_data (pointer, buffer, colorEntrySize, source);
1035 if (size_read < colorEntrySize) {
1036 GdipFree (palette);
1037 return OutOfMemory;
1038 }
1039
1040 set_pixel_bgra (palette->Entries, i * 4, buffer[0], buffer[1], buffer[2], 0xFF);
1041 }
1042
1043 *result = palette;
1044 return Ok;
1045 }
1046
1047 static GpStatus
gdip_read_bmp_scans(void * pointer,BYTE * pixels,BOOL upsidedown,PixelFormat format,INT srcStride,INT destStride,INT width,INT height,ImageSource source)1048 gdip_read_bmp_scans (void *pointer, BYTE *pixels, BOOL upsidedown, PixelFormat format, INT srcStride, INT destStride, INT width, INT height, ImageSource source)
1049 {
1050 BYTE *scan = (BYTE *) GdipAlloc (srcStride);
1051 if (!scan)
1052 return OutOfMemory;
1053
1054 for (int y = 0; y < height; y++) {
1055 int currentLine = upsidedown ? height - y - 1 : y;
1056 int size_read = gdip_read_bmp_data (pointer, scan, srcStride, source);
1057 if (size_read < srcStride) {
1058 GdipFree (scan);
1059 return OutOfMemory;
1060 }
1061
1062 BYTE *destScan = pixels + currentLine * destStride;
1063 switch (format) {
1064 case PixelFormat1bppIndexed:
1065 case PixelFormat4bppIndexed:
1066 case PixelFormat8bppIndexed:
1067 memcpy (destScan, scan, srcStride);
1068 continue;
1069 case PixelFormat16bppRGB555: {
1070 for (int x = 0; x < width; x++) {
1071 ARGB argb = gdip_getpixel_16bppRGB555 (scan, x);
1072
1073 BYTE a = (argb & 0xFF000000) >> 24;
1074 BYTE r = (argb & 0x00FF0000) >> 16;
1075 BYTE g = (argb & 0x0000FF00) >> 8;
1076 BYTE b = (argb & 0x000000FF);
1077 gdip_setpixel_32bppARGB (destScan, x, a, r, g, b);
1078 }
1079 continue;
1080 }
1081 case PixelFormat16bppRGB565: {
1082 for (int x = 0; x < width; x++) {
1083 ARGB argb = gdip_getpixel_16bppRGB565 (scan, x);
1084
1085 BYTE a = (argb & 0xFF000000) >> 24;
1086 BYTE r = (argb & 0x00FF0000) >> 16;
1087 BYTE g = (argb & 0x0000FF00) >> 8;
1088 BYTE b = (argb & 0x000000FF);
1089 gdip_setpixel_32bppARGB (destScan, x, a, r, g, b);
1090 }
1091 continue;
1092 }
1093 case PixelFormat24bppRGB: {
1094 for (int x = 0; x < width; x++) {
1095 gdip_setpixel_32bppARGB (destScan, x, 0xFF, scan[x * 3 + 2], scan[x * 3 + 1], scan[x * 3]);
1096 }
1097 continue;
1098 }
1099 case PixelFormat32bppRGB: {
1100 for (int x = 0; x < width; x++) {
1101 gdip_setpixel_32bppARGB (destScan, x, 0xFF, scan[x * 4 + 2], scan[x * 4 + 1], scan[x * 4]);
1102 }
1103 continue;
1104 }
1105 default:
1106 GdipFree(scan);
1107 return NotImplemented;
1108 }
1109 }
1110
1111 GdipFree(scan);
1112 return Ok;
1113 }
1114
1115 static GpStatus
gdip_read_bmp_indexed(void * pointer,BYTE * pixels,BOOL upsidedown,PixelFormat format,INT stride,INT width,INT height,ImageSource source)1116 gdip_read_bmp_indexed (void *pointer, BYTE *pixels, BOOL upsidedown, PixelFormat format, INT stride, INT width, INT height, ImageSource source)
1117 {
1118 if (upsidedown)
1119 return gdip_read_bmp_scans (pointer, pixels, upsidedown, format, stride, stride, width, height, source);
1120
1121 int size_read = gdip_read_bmp_data (pointer, pixels, stride * height, source);
1122 if (size_read < stride)
1123 return OutOfMemory;
1124
1125 return Ok;
1126 }
1127
1128 /* For use with in-memory bitmaps, where the BITMAPFILEHEADER doesn't exists */
1129 GpStatus
gdip_read_bmp_image(void * pointer,GpImage ** image,ImageSource source)1130 gdip_read_bmp_image (void *pointer, GpImage **image, ImageSource source)
1131 {
1132 BITMAPV5HEADER bmi;
1133 GpBitmap *result;
1134 BYTE *pixels;
1135 PixelFormat originalFormat;
1136 INT originalStride;
1137 BOOL upsidedown = TRUE;
1138 GpStatus status;
1139 unsigned long long int size;
1140
1141 status = gdip_read_BITMAPINFOHEADER (pointer, source, &bmi, &upsidedown);
1142 if (status != Ok)
1143 return status;
1144
1145 result = gdip_bitmap_new_with_frame (NULL, TRUE);
1146 if (!result)
1147 return OutOfMemory;
1148
1149 status = gdip_get_bmp_pixelformat (&bmi, &originalFormat, &result->active_bitmap->pixel_format);
1150 if (status != Ok) {
1151 gdip_bitmap_dispose (result);
1152 return status;
1153 }
1154
1155 status = gdip_get_bmp_stride (result->active_bitmap->pixel_format, bmi.bV5Width, &result->active_bitmap->stride, /* cairoHacks */ TRUE);
1156 if (status != Ok) {
1157 gdip_bitmap_dispose (result);
1158 return status;
1159 }
1160
1161 status = gdip_get_bmp_stride (originalFormat, bmi.bV5Width, &originalStride, /* cairoHacks */ FALSE);
1162 if (status != Ok) {
1163 gdip_bitmap_dispose (result);
1164 return status;
1165 }
1166
1167 result->type = ImageTypeBitmap;
1168 result->image_format = BMP;
1169 result->active_bitmap->width = bmi.bV5Width;
1170 result->active_bitmap->height = bmi.bV5Height;
1171
1172 status = gdip_readbmp_palette (pointer, source, &bmi, &result->active_bitmap->palette);
1173 if (status != Ok) {
1174 gdip_bitmap_dispose (result);
1175 return status;
1176 }
1177
1178 /* ensure total 'size' does not overflow an integer and fits inside our 2GB limit */
1179 size = (unsigned long long int)result->active_bitmap->stride * result->active_bitmap->height;
1180 if (size > G_MAXINT32) {
1181 gdip_bitmap_dispose (result);
1182 return OutOfMemory;
1183 }
1184
1185 pixels = GdipAlloc (size);
1186 if (!pixels) {
1187 gdip_bitmap_dispose (result);
1188 return OutOfMemory;
1189 }
1190
1191 if (gdip_is_an_indexed_pixelformat (result->active_bitmap->pixel_format)) {
1192 if (bmi.bV5Compression == BI_RLE4)
1193 gdip_read_bmp_rle_4bit (pointer, pixels, upsidedown, result->active_bitmap->stride, result->active_bitmap->width, result->active_bitmap->height, source);
1194 else if (bmi.bV5Compression == BI_RLE8)
1195 gdip_read_bmp_rle_8bit (pointer, pixels, upsidedown, result->active_bitmap->stride, result->active_bitmap->width, result->active_bitmap->height, source);
1196 else {
1197 status = gdip_read_bmp_indexed (pointer, pixels, upsidedown, originalFormat, originalStride, result->active_bitmap->width, result->active_bitmap->height, source);
1198 if (status != Ok) {
1199 gdip_bitmap_dispose (result);
1200 return status;
1201 }
1202 }
1203 } else {
1204 status = gdip_read_bmp_scans (pointer, pixels, upsidedown, originalFormat, originalStride, result->active_bitmap->stride, result->active_bitmap->width, result->active_bitmap->height, source);
1205 if (status != Ok) {
1206 gdip_bitmap_dispose (result);
1207 return status;
1208 }
1209 }
1210
1211 result->active_bitmap->scan0 = pixels;
1212 result->active_bitmap->reserved = GBD_OWN_SCAN0;
1213 result->active_bitmap->image_flags = ImageFlagsReadOnly | ImageFlagsHasRealPixelSize | ImageFlagsColorSpaceRGB;
1214 if (bmi.bV5XPelsPerMeter != 0 && bmi.bV5YPelsPerMeter != 0)
1215 result->active_bitmap->image_flags |= ImageFlagsHasRealDPI;
1216
1217 *image = result;
1218 return Ok;
1219 }
1220
1221 /* BMP read from files have a BITMAPFILEHEADER but this isn't the case for the GDI API
1222 * (e.g. displaying a bitmap) */
1223 static void
BitmapFileHeaderFromLE(BITMAPFILEHEADER * bitmapFileHeader)1224 BitmapFileHeaderFromLE (BITMAPFILEHEADER *bitmapFileHeader)
1225 {
1226 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1227 bitmapFileHeader->bfType = GUINT16_FROM_LE (bitmapFileHeader->bfType);
1228 bitmapFileHeader->bfSize = GUINT32_FROM_LE (bitmapFileHeader->bfSize);
1229 bitmapFileHeader->bfReserved1 = GUINT16_FROM_LE (bitmapFileHeader->bfReserved1);
1230 bitmapFileHeader->bfReserved2 = GUINT16_FROM_LE (bitmapFileHeader->bfReserved2);
1231 bitmapFileHeader->bfOffBits = GUINT32_FROM_LE (bitmapFileHeader->bfOffBits);
1232 #endif
1233 }
1234
1235 static GpStatus
gdip_read_bmp_image_from_file_stream(void * pointer,GpImage ** image,ImageSource source)1236 gdip_read_bmp_image_from_file_stream (void *pointer, GpImage **image, ImageSource source)
1237 {
1238 BITMAPFILEHEADER bmfh;
1239 int size_read;
1240
1241 size_read = gdip_read_bmp_data (pointer, (BYTE *) &bmfh, sizeof (bmfh), source);
1242 if (size_read < sizeof (bmfh)) {
1243 return OutOfMemory;
1244 }
1245
1246 BitmapFileHeaderFromLE (&bmfh);
1247 if (bmfh.bfType != BFT_BITMAP) {
1248 return UnknownImageFormat;
1249 }
1250
1251 return gdip_read_bmp_image (pointer, image, source);
1252 }
1253
1254 GpStatus
gdip_load_bmp_image_from_file(FILE * fp,GpImage ** image)1255 gdip_load_bmp_image_from_file (FILE *fp, GpImage **image)
1256 {
1257 return gdip_read_bmp_image_from_file_stream ((void*)fp, image, File);
1258 }
1259
1260 GpStatus
gdip_load_bmp_image_from_stream_delegate(dstream_t * loader,GpImage ** image)1261 gdip_load_bmp_image_from_stream_delegate (dstream_t *loader, GpImage **image)
1262 {
1263 return gdip_read_bmp_image_from_file_stream ((void*)loader, image, DStream);
1264 }
1265
1266 int
gdip_read_bmp_data(void * pointer,BYTE * data,int size,ImageSource source)1267 gdip_read_bmp_data (void *pointer, BYTE *data, int size, ImageSource source)
1268 {
1269 switch (source) {
1270 case File:
1271 return fread (data, 1, size, (FILE*) pointer);
1272 case DStream: {
1273 /* Streams are not required to return the number of bytes
1274 requested, they could return less yet our code seems to assume
1275 it will always get what it's asking for; lets loop until we
1276 get what was requested or we get an error */
1277 int got;
1278 int total;
1279 dstream_t *loader;
1280
1281 loader = (dstream_t *) pointer;
1282 total = 0;
1283
1284 do {
1285 got = dstream_read (loader, data + total, size - total, 0);
1286 if (got < 1) { /* 0 = end of stream, -1 = error */
1287 return total;
1288 }
1289 total += got;
1290 } while (total < size);
1291
1292 return total;
1293 }
1294 case Memory: {
1295 MemorySource *ms = (MemorySource*)pointer;
1296 int len = (ms->pos + size < ms->size) ? size : ms->size - ms->pos;
1297 if (len > 0) {
1298 memcpy (data, ms->ptr + ms->pos, len);
1299 ms->pos += len;
1300 }
1301 return len;
1302 }
1303 default:
1304 return -1;
1305 }
1306 }
1307
1308 static void
gdip_write_bmp_data(void * pointer,BYTE * data,int size,BOOL useFile)1309 gdip_write_bmp_data (void *pointer, BYTE *data, int size, BOOL useFile)
1310 {
1311 if (useFile)
1312 fwrite (data, 1, size, (FILE*) pointer);
1313 else
1314 ((PutBytesDelegate)(pointer))(data, size);
1315 }
1316
1317 static GpStatus
gdip_save_bmp_image_to_file_stream(void * pointer,GpImage * image,BOOL useFile)1318 gdip_save_bmp_image_to_file_stream (void *pointer, GpImage *image, BOOL useFile)
1319 {
1320 BITMAPFILEHEADER bmfh;
1321 BITMAPINFOHEADER bmi;
1322 int bitmapLen;
1323 int i;
1324 ARGB color;
1325 int colours = 0;
1326 ARGB *entries;
1327 int palette_entries;
1328 ActiveBitmapData *activebmp;
1329 BYTE *scan0;
1330
1331 activebmp = image->active_bitmap;
1332 if (activebmp->pixel_format != PixelFormat24bppRGB) {
1333 bitmapLen = activebmp->stride * activebmp->height;
1334 } else {
1335 bitmapLen = activebmp->width * 3;
1336 bitmapLen += 3;
1337 bitmapLen &= ~3;
1338 bitmapLen *= activebmp->height;
1339 }
1340
1341 if (activebmp->palette) {
1342 colours = activebmp->palette->Count;
1343 }
1344
1345 bmfh.bfReserved1 = bmfh.bfReserved2 = 0;
1346 bmfh.bfType = BFT_BITMAP;
1347 bmfh.bfOffBits = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER) + colours * sizeof (RGBQUAD);
1348 bmfh.bfSize = (bmfh.bfOffBits + bitmapLen);
1349 BitmapFileHeaderFromLE (&bmfh);
1350
1351 gdip_write_bmp_data (pointer, (BYTE *) &bmfh, sizeof (bmfh), useFile);
1352 gdip_bitmap_fill_info_header (image, &bmi);
1353 gdip_write_bmp_data (pointer, (BYTE*) &bmi, sizeof (bmi), useFile);
1354
1355 if (colours) {
1356 palette_entries = activebmp->palette->Count;
1357
1358 if (activebmp->pixel_format == PixelFormat4bppIndexed) {
1359 palette_entries = 16;
1360 }
1361
1362 entries = (ARGB *) GdipAlloc (palette_entries * sizeof (ARGB));
1363 if (entries == NULL)
1364 return OutOfMemory;
1365
1366 for (i = 0; i < palette_entries; i++) {
1367 color = activebmp->palette->Entries[i];
1368 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
1369 *(entries + i) = color;
1370 #else
1371 *(entries + i) = GUINT32_FROM_LE (color);
1372 #endif
1373 }
1374 gdip_write_bmp_data (pointer, (BYTE *) entries, palette_entries * sizeof (ARGB), useFile);
1375 GdipFree (entries);
1376 }
1377
1378 scan0 = activebmp->scan0;
1379 if (activebmp->pixel_format == PixelFormat24bppRGB) {
1380 int width = activebmp->width;
1381 int height = activebmp->height;
1382 int mystride;
1383 int k;
1384 BYTE *current_line;
1385
1386 /* rows need to be padded up to the next multiple of 4 */
1387 mystride = width * 3;
1388 mystride += 3;
1389 mystride &= ~3;
1390 current_line = (BYTE*) GdipAlloc (mystride);
1391 if (!current_line) {
1392 return OutOfMemory;
1393 }
1394
1395 memset (current_line, 0, mystride); /* Zero padding at the end if needed */
1396 for (i = height - 1; i >= 0; i--) {
1397 BYTE *ptr;
1398 guint32 *iptr;
1399
1400 iptr = (guint32 *) (scan0 + i * activebmp->stride);
1401 ptr = current_line;
1402 for (k = 0; k < width; k++) {
1403 guint32 color = *iptr++;
1404 *ptr++ = (color & 0x000000ff);
1405 *ptr++ = ((color & 0x0000ff00) >> 8);
1406 *ptr++ = ((color & 0x00ff0000) >> 16);
1407 }
1408 gdip_write_bmp_data (pointer, current_line, mystride, useFile);
1409 }
1410 GdipFree (current_line);
1411 return Ok;
1412 }
1413
1414 /* Writes bitmap upside down. Many tools can only process bmp stored this way*/
1415 #ifdef WORDS_BIGENDIAN
1416 if (gdip_is_an_indexed_pixelformat (activebmp->pixel_format) == FALSE) {
1417 int j;
1418 BYTE *row_pointer = GdipAlloc (activebmp->width * 4);
1419
1420 if (row_pointer == NULL) {
1421 return OutOfMemory;
1422 }
1423
1424 for (i = activebmp->height -1; i >= 0; i--) {
1425 for (j = 0; j < activebmp->width; j++) {
1426 row_pointer[j*4] = *((BYTE*)scan0 + (activebmp->stride * i) + (j*4) + 3);
1427 row_pointer[j*4+1] = *((BYTE*)scan0 + (activebmp->stride * i) + (j*4) + 2);
1428 row_pointer[j*4+2] = *((BYTE*)scan0 + (activebmp->stride * i) + (j*4) + 1);
1429 row_pointer[j*4+3] = *((BYTE*)scan0 + (activebmp->stride * i) + (j*4) + 0);
1430 }
1431 gdip_write_bmp_data (pointer, row_pointer, activebmp->stride, useFile);
1432 }
1433 GdipFree (row_pointer);
1434 }
1435 else
1436 #endif /* WORDS_BIGENDIAN */
1437 for (i = activebmp->height - 1; i >= 0; i--) {
1438 gdip_write_bmp_data (pointer, scan0 + i * activebmp->stride, activebmp->stride, useFile);
1439 }
1440
1441 return Ok;
1442 }
1443
1444 GpStatus
gdip_save_bmp_image_to_file(FILE * fp,GpImage * image)1445 gdip_save_bmp_image_to_file (FILE *fp, GpImage *image)
1446 {
1447 return gdip_save_bmp_image_to_file_stream ( (void *)fp, image, TRUE);
1448 }
1449
1450 GpStatus
gdip_save_bmp_image_to_stream_delegate(PutBytesDelegate putBytesFunc,GpImage * image)1451 gdip_save_bmp_image_to_stream_delegate (PutBytesDelegate putBytesFunc, GpImage *image)
1452 {
1453 return gdip_save_bmp_image_to_file_stream ( (void *)putBytesFunc, image, FALSE);
1454 }
1455