1 | /* -*- mode: C; c-file-style: "linux" -*- */ |
2 | /* GdkPixbuf library - Windows Bitmap image loader |
3 | * |
4 | * Copyright (C) 1999 The Free Software Foundation |
5 | * |
6 | * Authors: Arjan van de Ven <arjan@fenrus.demon.nl> |
7 | * Federico Mena-Quintero <federico@gimp.org> |
8 | * |
9 | * Based on io-ras.c |
10 | * |
11 | * This library is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU Lesser General Public |
13 | * License as published by the Free Software Foundation; either |
14 | * version 2 of the License, or (at your option) any later version. |
15 | * |
16 | * This library is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
19 | * Lesser General Public License for more details. |
20 | * |
21 | * You should have received a copy of the GNU Lesser General Public |
22 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | #include <stdio.h> |
27 | #ifdef HAVE_UNISTD_H |
28 | #include <unistd.h> |
29 | #endif |
30 | #include <string.h> |
31 | #include <glib-object.h> |
32 | #include <glib/gi18n-lib.h> |
33 | |
34 | #include "gdk-pixbuf-core.h" |
35 | #include "gdk-pixbuf-io.h" |
36 | |
37 | #define DUMPBIH 0 |
38 | |
39 | |
40 | |
41 | #if 0 |
42 | /* If these structures were unpacked, they would define the two headers of the |
43 | * BMP file. After them comes the palette, and then the image data. |
44 | * |
45 | * We do not use these structures; we just keep them here for reference. |
46 | */ |
47 | struct BitmapFileHeader { |
48 | guint16 magic; |
49 | guint32 file_size; |
50 | guint32 reserved; |
51 | guint32 data_offset; |
52 | }; |
53 | |
54 | struct BitmapInfoHeader { |
55 | guint32 header_size; |
56 | guint32 width; |
57 | guint32 height; |
58 | guint16 planes; |
59 | guint16 bpp; |
60 | guint32 compression; |
61 | guint32 data_size; |
62 | guint32 x_ppm; |
63 | guint32 y_ppm; |
64 | guint32 n_colors; |
65 | guint32 n_important_colors; |
66 | }; |
67 | #endif |
68 | |
69 | /* Compression values */ |
70 | |
71 | #define BI_RGB 0 |
72 | #define BI_RLE8 1 |
73 | #define BI_RLE4 2 |
74 | #define BI_BITFIELDS 3 |
75 | |
76 | /* State machine */ |
77 | typedef enum { |
78 | , /* Reading the bitmap file header and bitmap info header */ |
79 | READ_STATE_PALETTE, /* Reading the palette */ |
80 | READ_STATE_BITMASKS, /* Reading the bitmasks for BI_BITFIELDS */ |
81 | READ_STATE_DATA, /* Reading the actual image data */ |
82 | READ_STATE_ERROR, /* An error occurred; further data will be ignored */ |
83 | READ_STATE_DONE /* Done reading the image; further data will be ignored */ |
84 | } ReadState; |
85 | |
86 | /* |
87 | |
88 | DumpBIH printf's the values in a BitmapInfoHeader to the screen, for |
89 | debugging purposes. |
90 | |
91 | */ |
92 | #if DUMPBIH |
93 | static void DumpBIH(unsigned char *BIH) |
94 | { |
95 | printf("biSize = %i \n" , |
96 | (int) (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + |
97 | (BIH[0])); |
98 | printf("biWidth = %i \n" , |
99 | (int) (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + |
100 | (BIH[4])); |
101 | printf("biHeight = %i \n" , |
102 | (int) (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + |
103 | (BIH[8])); |
104 | printf("biPlanes = %i \n" , (int) (BIH[13] << 8) + (BIH[12])); |
105 | printf("biBitCount = %i \n" , (int) (BIH[15] << 8) + (BIH[14])); |
106 | printf("biCompress = %i \n" , |
107 | (int) (BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) + |
108 | (BIH[16])); |
109 | printf("biSizeImage = %i \n" , |
110 | (int) (BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) + |
111 | (BIH[20])); |
112 | printf("biXPels = %i \n" , |
113 | (int) (BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) + |
114 | (BIH[24])); |
115 | printf("biYPels = %i \n" , |
116 | (int) (BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) + |
117 | (BIH[28])); |
118 | printf("biClrUsed = %i \n" , |
119 | (int) (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + |
120 | (BIH[32])); |
121 | printf("biClrImprtnt= %i \n" , |
122 | (int) (BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) + |
123 | (BIH[36])); |
124 | } |
125 | #endif |
126 | /* struct headerpair contains the decoded width/height/depth info for |
127 | the current bitmap */ |
128 | |
129 | struct { |
130 | guint32 ; |
131 | gint32 ; |
132 | gint32 ; |
133 | guint ; |
134 | guint ; /* Negative = 1 -> top down BMP, |
135 | Negative = 0 -> bottom up BMP */ |
136 | guint ; |
137 | }; |
138 | |
139 | /* Data needed for the "state" during decompression */ |
140 | struct bmp_compression_state { |
141 | gint phase; |
142 | gint run; |
143 | gint count; |
144 | gint x, y; |
145 | guchar *p; |
146 | }; |
147 | |
148 | /* Progressive loading */ |
149 | |
150 | struct bmp_progressive_state { |
151 | GdkPixbufModuleSizeFunc size_func; |
152 | GdkPixbufModulePreparedFunc prepared_func; |
153 | GdkPixbufModuleUpdatedFunc updated_func; |
154 | gpointer user_data; |
155 | |
156 | ReadState read_state; |
157 | |
158 | guint LineWidth; |
159 | guint Lines; /* # of finished lines */ |
160 | |
161 | guchar *buff; |
162 | guint BufferSize; |
163 | guint BufferPadding; |
164 | guint BufferDone; |
165 | |
166 | guchar (*Colormap)[3]; |
167 | |
168 | gint Type; /* |
169 | 32 = RGB + alpha |
170 | 24 = RGB |
171 | 16 = RGB |
172 | 4 = 4 bpp colormapped |
173 | 8 = 8 bpp colormapped |
174 | 1 = 1 bit bitonal |
175 | */ |
176 | guint Compressed; |
177 | struct bmp_compression_state compr; |
178 | |
179 | |
180 | struct headerpair ; /* Decoded (BE->CPU) header */ |
181 | |
182 | /* Bit masks, shift amounts, and significant bits for BI_BITFIELDS coding */ |
183 | int r_mask, r_shift, r_bits; |
184 | int g_mask, g_shift, g_bits; |
185 | int b_mask, b_shift, b_bits; |
186 | int a_mask, a_shift, a_bits; |
187 | |
188 | GdkPixbuf *pixbuf; /* Our "target" */ |
189 | }; |
190 | |
191 | static gpointer |
192 | gdk_pixbuf__bmp_image_begin_load(GdkPixbufModuleSizeFunc size_func, |
193 | GdkPixbufModulePreparedFunc prepared_func, |
194 | GdkPixbufModuleUpdatedFunc updated_func, |
195 | gpointer user_data, |
196 | GError **error); |
197 | |
198 | static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error); |
199 | static gboolean gdk_pixbuf__bmp_image_load_increment(gpointer data, |
200 | const guchar * buf, |
201 | guint size, |
202 | GError **error); |
203 | |
204 | |
205 | /* Picks up a 32-bit little-endian integer starting at the specified location. |
206 | * Does it by hand instead of dereferencing a simple (gint *) cast due to |
207 | * alignment constraints many platforms. |
208 | */ |
209 | static int |
210 | lsb_32 (guchar *src) |
211 | { |
212 | return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); |
213 | } |
214 | |
215 | /* Same as above, but for 16-bit little-endian integers. */ |
216 | static short |
217 | lsb_16 (guchar *src) |
218 | { |
219 | return src[0] | (src[1] << 8); |
220 | } |
221 | |
222 | static gboolean grow_buffer (struct bmp_progressive_state *State, |
223 | GError **error) |
224 | { |
225 | guchar *tmp; |
226 | |
227 | if (State->BufferSize == 0) { |
228 | g_set_error_literal (err: error, |
229 | GDK_PIXBUF_ERROR, |
230 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
231 | _("BMP image has bogus header data" )); |
232 | State->read_state = READ_STATE_ERROR; |
233 | return FALSE; |
234 | } |
235 | |
236 | tmp = g_try_realloc (mem: State->buff, n_bytes: State->BufferSize); |
237 | |
238 | if (!tmp) { |
239 | g_set_error_literal (err: error, |
240 | GDK_PIXBUF_ERROR, |
241 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
242 | _("Not enough memory to load bitmap image" )); |
243 | State->read_state = READ_STATE_ERROR; |
244 | return FALSE; |
245 | } |
246 | |
247 | State->buff = tmp; |
248 | return TRUE; |
249 | } |
250 | |
251 | static gboolean |
252 | decode_bitmasks (guchar *buf, |
253 | struct bmp_progressive_state *State, |
254 | GError **error); |
255 | |
256 | static gboolean (unsigned char *BFH, unsigned char *BIH, |
257 | struct bmp_progressive_state *State, |
258 | GError **error) |
259 | { |
260 | gint clrUsed; |
261 | guint bytesPerPixel; |
262 | |
263 | /* First check for the two first bytes content. A sane |
264 | BMP file must start with bytes 0x42 0x4D. */ |
265 | if (*BFH != 0x42 || *(BFH + 1) != 0x4D) { |
266 | g_set_error_literal (err: error, |
267 | GDK_PIXBUF_ERROR, |
268 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
269 | _("BMP image has bogus header data" )); |
270 | State->read_state = READ_STATE_ERROR; |
271 | return FALSE; |
272 | } |
273 | |
274 | /* FIXME this is totally unrobust against bogus image data. */ |
275 | if (State->BufferSize < lsb_32 (src: &BIH[0]) + 14) { |
276 | State->BufferSize = lsb_32 (src: &BIH[0]) + 14; |
277 | if (!grow_buffer (State, error)) |
278 | return FALSE; |
279 | return TRUE; |
280 | } |
281 | |
282 | #if DUMPBIH |
283 | DumpBIH(BIH); |
284 | #endif |
285 | |
286 | State->Header.size = lsb_32 (src: &BIH[0]); |
287 | if (State->Header.size == 124) { |
288 | /* BMP v5 */ |
289 | State->Header.width = lsb_32 (src: &BIH[4]); |
290 | State->Header.height = lsb_32 (src: &BIH[8]); |
291 | State->Header.depth = lsb_16 (src: &BIH[14]); |
292 | State->Compressed = lsb_32 (src: &BIH[16]); |
293 | } else if (State->Header.size == 108) { |
294 | /* BMP v4 */ |
295 | State->Header.width = lsb_32 (src: &BIH[4]); |
296 | State->Header.height = lsb_32 (src: &BIH[8]); |
297 | State->Header.depth = lsb_16 (src: &BIH[14]); |
298 | State->Compressed = lsb_32 (src: &BIH[16]); |
299 | } else if (State->Header.size == 64) { |
300 | /* BMP OS/2 v2 */ |
301 | State->Header.width = lsb_32 (src: &BIH[4]); |
302 | State->Header.height = lsb_32 (src: &BIH[8]); |
303 | State->Header.depth = lsb_16 (src: &BIH[14]); |
304 | State->Compressed = lsb_32 (src: &BIH[16]); |
305 | } else if (State->Header.size == 56) { |
306 | /* BMP v3 with RGBA bitmasks */ |
307 | State->Header.width = lsb_32 (src: &BIH[4]); |
308 | State->Header.height = lsb_32 (src: &BIH[8]); |
309 | State->Header.depth = lsb_16 (src: &BIH[14]); |
310 | State->Compressed = lsb_32 (src: &BIH[16]); |
311 | } else if (State->Header.size == 52) { |
312 | /* BMP v3 with RGB bitmasks */ |
313 | State->Header.width = lsb_32 (src: &BIH[4]); |
314 | State->Header.height = lsb_32 (src: &BIH[8]); |
315 | State->Header.depth = lsb_16 (src: &BIH[14]); |
316 | State->Compressed = lsb_32 (src: &BIH[16]); |
317 | } else if (State->Header.size == 40) { |
318 | /* BMP v3 */ |
319 | State->Header.width = lsb_32 (src: &BIH[4]); |
320 | State->Header.height = lsb_32 (src: &BIH[8]); |
321 | State->Header.depth = lsb_16 (src: &BIH[14]); |
322 | State->Compressed = lsb_32 (src: &BIH[16]); |
323 | } else if (State->Header.size == 12) { |
324 | /* BMP OS/2 */ |
325 | State->Header.width = lsb_16 (src: &BIH[4]); |
326 | State->Header.height = lsb_16 (src: &BIH[6]); |
327 | State->Header.depth = lsb_16 (src: &BIH[10]); |
328 | State->Compressed = BI_RGB; |
329 | } else { |
330 | g_set_error_literal (err: error, |
331 | GDK_PIXBUF_ERROR, |
332 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
333 | _("BMP image has unsupported header size" )); |
334 | State->read_state = READ_STATE_ERROR; |
335 | return FALSE; |
336 | } |
337 | |
338 | if (State->Header.depth > 32) |
339 | { |
340 | g_set_error_literal (err: error, |
341 | GDK_PIXBUF_ERROR, |
342 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
343 | _("BMP image has unsupported depth" )); |
344 | State->read_state = READ_STATE_ERROR; |
345 | return FALSE; |
346 | } |
347 | |
348 | if (State->Header.size == 12) |
349 | clrUsed = 1 << State->Header.depth; |
350 | else |
351 | clrUsed = (int) (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]); |
352 | |
353 | if (clrUsed > (1 << State->Header.depth)) |
354 | { |
355 | g_set_error_literal (err: error, |
356 | GDK_PIXBUF_ERROR, |
357 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
358 | _("BMP image has oversize palette" )); |
359 | State->read_state = READ_STATE_ERROR; |
360 | return FALSE; |
361 | } |
362 | |
363 | if (clrUsed != 0) |
364 | State->Header.n_colors = clrUsed; |
365 | else |
366 | State->Header.n_colors = (1 << State->Header.depth); |
367 | |
368 | State->Type = State->Header.depth; /* This may be less trivial someday */ |
369 | |
370 | /* Negative heights indicates bottom-down pixelorder */ |
371 | if (State->Header.height < 0) { |
372 | if (State->Header.height == INT_MIN) { |
373 | g_set_error_literal (err: error, |
374 | GDK_PIXBUF_ERROR, |
375 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
376 | _("BMP image has bogus header data" )); |
377 | State->read_state = READ_STATE_ERROR; |
378 | return FALSE; |
379 | } |
380 | State->Header.height = -State->Header.height; |
381 | State->Header.Negative = 1; |
382 | } |
383 | |
384 | if (State->Header.Negative && |
385 | (State->Compressed != BI_RGB && State->Compressed != BI_BITFIELDS)) |
386 | { |
387 | g_set_error_literal (err: error, |
388 | GDK_PIXBUF_ERROR, |
389 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
390 | _("Topdown BMP images cannot be compressed" )); |
391 | State->read_state = READ_STATE_ERROR; |
392 | return FALSE; |
393 | } |
394 | |
395 | if (State->Header.width <= 0 || State->Header.height == 0 || |
396 | (State->Compressed == BI_RLE4 && State->Type != 4) || |
397 | (State->Compressed == BI_RLE8 && State->Type != 8) || |
398 | (State->Compressed == BI_BITFIELDS && !(State->Type == 16 || State->Type == 32)) || |
399 | (State->Compressed > BI_BITFIELDS)) { |
400 | g_set_error_literal (err: error, |
401 | GDK_PIXBUF_ERROR, |
402 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
403 | _("BMP image has bogus header data" )); |
404 | State->read_state = READ_STATE_ERROR; |
405 | return FALSE; |
406 | } |
407 | |
408 | if ((State->Type >= 8) && (State->Type <= 32) && (State->Type % 8 == 0)) { |
409 | bytesPerPixel = State->Type / 8; |
410 | State->LineWidth = State->Header.width * bytesPerPixel; |
411 | if (State->Header.width != State->LineWidth / bytesPerPixel) { |
412 | g_set_error_literal (err: error, |
413 | GDK_PIXBUF_ERROR, |
414 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
415 | _("BMP image width too large" )); |
416 | return FALSE; |
417 | } |
418 | } else if (State->Type == 4) |
419 | State->LineWidth = (State->Header.width + 1) / 2; |
420 | else if (State->Type == 1) { |
421 | State->LineWidth = State->Header.width / 8; |
422 | if ((State->Header.width & 7) != 0) |
423 | State->LineWidth++; |
424 | } else { |
425 | g_set_error_literal (err: error, |
426 | GDK_PIXBUF_ERROR, |
427 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
428 | _("BMP image has bogus header data" )); |
429 | State->read_state = READ_STATE_ERROR; |
430 | return FALSE; |
431 | } |
432 | |
433 | /* Pad to a 32 bit boundary */ |
434 | if (((State->LineWidth % 4) > 0) |
435 | && (State->Compressed == BI_RGB || State->Compressed == BI_BITFIELDS)) |
436 | State->LineWidth = (State->LineWidth / 4) * 4 + 4; |
437 | |
438 | if (State->pixbuf == NULL) { |
439 | guint64 len; |
440 | int rowstride; |
441 | gboolean has_alpha; |
442 | |
443 | { |
444 | gint width = State->Header.width; |
445 | gint height = State->Header.height; |
446 | |
447 | (*State->size_func) (&width, &height, State->user_data); |
448 | if (width == 0 || height == 0) { |
449 | State->read_state = READ_STATE_DONE; |
450 | State->BufferSize = 0; |
451 | return TRUE; |
452 | } |
453 | } |
454 | |
455 | /* rowstride is always >= width, so do an early check for bogus header */ |
456 | if (State->Header.width <= 0 || |
457 | State->Header.height <= 0 || |
458 | !g_uint64_checked_mul (&len, State->Header.width, State->Header.height) || |
459 | len > G_MAXINT) { |
460 | g_set_error_literal (err: error, |
461 | GDK_PIXBUF_ERROR, |
462 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
463 | _("BMP image has bogus header data" )); |
464 | State->read_state = READ_STATE_ERROR; |
465 | return FALSE; |
466 | } |
467 | |
468 | if (State->Type == 32 || |
469 | State->Compressed == BI_RLE4 || |
470 | State->Compressed == BI_RLE8) |
471 | has_alpha = TRUE; |
472 | else |
473 | has_alpha = FALSE; |
474 | |
475 | rowstride = gdk_pixbuf_calculate_rowstride (colorspace: GDK_COLORSPACE_RGB, has_alpha, bits_per_sample: 8, |
476 | width: (gint) State->Header.width, |
477 | height: (gint) State->Header.height); |
478 | |
479 | if (rowstride <= 0 || |
480 | !g_uint64_checked_mul (&len, rowstride, State->Header.height) || |
481 | len > G_MAXINT) { |
482 | g_set_error_literal (err: error, |
483 | GDK_PIXBUF_ERROR, |
484 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
485 | _("BMP image has bogus header data" )); |
486 | State->read_state = READ_STATE_ERROR; |
487 | return FALSE; |
488 | } |
489 | |
490 | State->pixbuf = gdk_pixbuf_new (colorspace: GDK_COLORSPACE_RGB, has_alpha, bits_per_sample: 8, |
491 | width: (gint) State->Header.width, |
492 | height: (gint) State->Header.height); |
493 | |
494 | if (State->pixbuf == NULL) { |
495 | g_set_error_literal (err: error, |
496 | GDK_PIXBUF_ERROR, |
497 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
498 | _("Not enough memory to load bitmap image" )); |
499 | State->read_state = READ_STATE_ERROR; |
500 | return FALSE; |
501 | } |
502 | |
503 | /* Notify the client that we are ready to go */ |
504 | (*State->prepared_func) (State->pixbuf, NULL, State->user_data); |
505 | |
506 | /* make all pixels initially transparent */ |
507 | if (State->Compressed == BI_RLE4 || State->Compressed == BI_RLE8) { |
508 | gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: State->pixbuf); |
509 | |
510 | memset (s: gdk_pixbuf_get_pixels (pixbuf: State->pixbuf), c: 0, n: rowstride * State->Header.height); |
511 | State->compr.p = gdk_pixbuf_get_pixels (pixbuf: State->pixbuf) |
512 | + rowstride * (State->Header.height- 1); |
513 | } |
514 | } |
515 | |
516 | State->BufferDone = 0; |
517 | if (State->Type <= 8) { |
518 | gint samples; |
519 | |
520 | State->read_state = READ_STATE_PALETTE; |
521 | |
522 | /* Allocate enough to hold the palette */ |
523 | samples = (State->Header.size == 12 ? 3 : 4); |
524 | State->BufferSize = State->Header.n_colors * samples; |
525 | |
526 | /* Skip over everything between the palette and the data. |
527 | This protects us against a malicious BFH[10] value. |
528 | */ |
529 | State->BufferPadding = (lsb_32 (src: &BFH[10]) - 14 - State->Header.size) - State->BufferSize; |
530 | |
531 | } else if (State->Compressed == BI_RGB) { |
532 | if (State->BufferSize < lsb_32 (src: &BFH[10])) |
533 | { |
534 | /* skip over padding between headers and image data */ |
535 | State->read_state = READ_STATE_HEADERS; |
536 | State->BufferDone = State->BufferSize; |
537 | State->BufferSize = lsb_32 (src: &BFH[10]); |
538 | } |
539 | else |
540 | { |
541 | State->read_state = READ_STATE_DATA; |
542 | State->BufferSize = State->LineWidth; |
543 | } |
544 | } else if (State->Compressed == BI_BITFIELDS) { |
545 | if (State->Header.size == 52 || State->Header.size == 56 || |
546 | State->Header.size == 108 || State->Header.size == 124) |
547 | { |
548 | /* extended v3, v4 and v5 have the bitmasks in the header */ |
549 | if (!decode_bitmasks (buf: &BIH[40], State, error)) { |
550 | State->read_state = READ_STATE_ERROR; |
551 | return FALSE; |
552 | } |
553 | } |
554 | else |
555 | { |
556 | State->read_state = READ_STATE_BITMASKS; |
557 | State->BufferSize = 12; |
558 | } |
559 | } else { |
560 | g_set_error_literal (err: error, |
561 | GDK_PIXBUF_ERROR, |
562 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
563 | _("BMP image has bogus header data" )); |
564 | State->read_state = READ_STATE_ERROR; |
565 | return FALSE; |
566 | } |
567 | |
568 | if (!grow_buffer (State, error)) |
569 | return FALSE; |
570 | |
571 | return TRUE; |
572 | } |
573 | |
574 | static gboolean DecodeColormap (guchar *buff, |
575 | struct bmp_progressive_state *State, |
576 | GError **error) |
577 | { |
578 | gint i; |
579 | gint samples; |
580 | guint newbuffersize; |
581 | |
582 | g_assert (State->read_state == READ_STATE_PALETTE); |
583 | |
584 | samples = (State->Header.size == 12 ? 3 : 4); |
585 | newbuffersize = State->Header.n_colors * samples; |
586 | if (newbuffersize / samples != State->Header.n_colors) /* Integer overflow check */ |
587 | return FALSE; |
588 | if (State->BufferSize < newbuffersize) { |
589 | State->BufferSize = newbuffersize; |
590 | if (!grow_buffer (State, error)) |
591 | return FALSE; |
592 | return TRUE; |
593 | } |
594 | |
595 | State->Colormap = g_malloc0 (n_bytes: (1 << State->Header.depth) * sizeof (*State->Colormap)); |
596 | for (i = 0; i < State->Header.n_colors; i++) |
597 | |
598 | { |
599 | State->Colormap[i][0] = buff[i * samples]; |
600 | State->Colormap[i][1] = buff[i * samples + 1]; |
601 | State->Colormap[i][2] = buff[i * samples + 2]; |
602 | #ifdef DUMPCMAP |
603 | g_print ("color %d %x %x %x\n" , i, |
604 | State->Colormap[i][0], |
605 | State->Colormap[i][1], |
606 | State->Colormap[i][2]); |
607 | #endif |
608 | } |
609 | |
610 | State->read_state = READ_STATE_DATA; |
611 | |
612 | State->BufferDone = 0; |
613 | if (!(State->Compressed == BI_RGB || State->Compressed == BI_BITFIELDS)) |
614 | State->BufferSize = 2; |
615 | else |
616 | State->BufferSize = State->LineWidth; |
617 | |
618 | if (!grow_buffer (State, error)) |
619 | return FALSE; |
620 | |
621 | return TRUE; |
622 | } |
623 | |
624 | /* Finds the lowest set bit and the number of set bits */ |
625 | static void |
626 | find_bits (int n, int *lowest, int *n_set) |
627 | { |
628 | unsigned int i; |
629 | |
630 | *lowest = 0; |
631 | *n_set = 0; |
632 | |
633 | for (i = 31; n != 0; n <<= 1, i--) |
634 | if (n & ((unsigned int) 1 << 31)) { |
635 | *lowest = i; |
636 | (*n_set)++; |
637 | } |
638 | } |
639 | |
640 | /* Decodes the bitmasks for BI_BITFIELDS coding */ |
641 | static gboolean |
642 | decode_bitmasks (guchar *buf, |
643 | struct bmp_progressive_state *State, |
644 | GError **error) |
645 | { |
646 | State->a_mask = State->a_shift = State->a_bits = 0; |
647 | State->r_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); |
648 | buf += 4; |
649 | |
650 | State->g_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); |
651 | buf += 4; |
652 | |
653 | State->b_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); |
654 | |
655 | find_bits (n: State->r_mask, lowest: &State->r_shift, n_set: &State->r_bits); |
656 | find_bits (n: State->g_mask, lowest: &State->g_shift, n_set: &State->g_bits); |
657 | find_bits (n: State->b_mask, lowest: &State->b_shift, n_set: &State->b_bits); |
658 | |
659 | /* extended v3, v4 and v5 have an alpha mask */ |
660 | if (State->Header.size == 56 || State->Header.size == 108 || State->Header.size == 124) { |
661 | buf += 4; |
662 | State->a_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); |
663 | find_bits (n: State->a_mask, lowest: &State->a_shift, n_set: &State->a_bits); |
664 | } |
665 | |
666 | if (State->r_bits == 0 || State->g_bits == 0 || State->b_bits == 0) { |
667 | if (State->Type == 16) { |
668 | State->r_mask = 0x7c00; |
669 | State->r_shift = 10; |
670 | State->g_mask = 0x03e0; |
671 | State->g_shift = 5; |
672 | State->b_mask = 0x001f; |
673 | State->b_shift = 0; |
674 | |
675 | State->r_bits = State->g_bits = State->b_bits = 5; |
676 | } |
677 | else { |
678 | State->r_mask = 0x00ff0000; |
679 | State->r_shift = 16; |
680 | State->g_mask = 0x0000ff00; |
681 | State->g_shift = 8; |
682 | State->b_mask = 0x000000ff; |
683 | State->b_shift = 0; |
684 | State->a_mask = 0xff000000; |
685 | State->a_shift = 24; |
686 | |
687 | State->r_bits = State->g_bits = State->b_bits = State->a_bits = 8; |
688 | } |
689 | } |
690 | |
691 | if (State->r_bits > 8) { |
692 | State->r_shift += State->r_bits - 8; |
693 | State->r_bits = 8; |
694 | } |
695 | if (State->g_bits > 8) { |
696 | State->g_shift += State->g_bits - 8; |
697 | State->g_bits = 8; |
698 | } |
699 | if (State->b_bits > 8) { |
700 | State->b_shift += State->b_bits - 8; |
701 | State->b_bits = 8; |
702 | } |
703 | if (State->a_bits > 8) { |
704 | State->a_shift += State->a_bits - 8; |
705 | State->a_bits = 8; |
706 | } |
707 | |
708 | State->read_state = READ_STATE_DATA; |
709 | State->BufferDone = 0; |
710 | State->BufferSize = State->LineWidth; |
711 | if (!grow_buffer (State, error)) |
712 | return FALSE; |
713 | |
714 | return TRUE; |
715 | } |
716 | |
717 | /* |
718 | * func - called when we have pixmap created (but no image data) |
719 | * user_data - passed as arg 1 to func |
720 | * return context (opaque to user) |
721 | */ |
722 | |
723 | static gpointer |
724 | gdk_pixbuf__bmp_image_begin_load(GdkPixbufModuleSizeFunc size_func, |
725 | GdkPixbufModulePreparedFunc prepared_func, |
726 | GdkPixbufModuleUpdatedFunc updated_func, |
727 | gpointer user_data, |
728 | GError **error) |
729 | { |
730 | struct bmp_progressive_state *context; |
731 | |
732 | g_assert (size_func != NULL); |
733 | g_assert (prepared_func != NULL); |
734 | g_assert (updated_func != NULL); |
735 | |
736 | context = g_new0(struct bmp_progressive_state, 1); |
737 | context->size_func = size_func; |
738 | context->prepared_func = prepared_func; |
739 | context->updated_func = updated_func; |
740 | context->user_data = user_data; |
741 | |
742 | context->read_state = READ_STATE_HEADERS; |
743 | |
744 | context->BufferSize = 26; |
745 | context->BufferPadding = 0; |
746 | context->buff = g_malloc(n_bytes: 26); |
747 | context->BufferDone = 0; |
748 | /* 14 for the BitmapFileHeader, 12 for the BitmapImageHeader */ |
749 | |
750 | context->Colormap = NULL; |
751 | |
752 | context->Lines = 0; |
753 | |
754 | context->Type = 0; |
755 | |
756 | memset(s: &context->Header, c: 0, n: sizeof(struct headerpair)); |
757 | memset(s: &context->compr, c: 0, n: sizeof(struct bmp_compression_state)); |
758 | |
759 | |
760 | context->pixbuf = NULL; |
761 | |
762 | return (gpointer) context; |
763 | } |
764 | |
765 | /* |
766 | * context - returned from image_begin_load |
767 | * |
768 | * free context, unref gdk_pixbuf |
769 | */ |
770 | static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error) |
771 | { |
772 | gboolean retval = TRUE; |
773 | |
774 | struct bmp_progressive_state *context = |
775 | (struct bmp_progressive_state *) data; |
776 | |
777 | /* FIXME this thing needs to report errors if |
778 | * we have unused image data |
779 | */ |
780 | |
781 | g_return_val_if_fail(context != NULL, TRUE); |
782 | |
783 | g_free(mem: context->Colormap); |
784 | |
785 | if (context->pixbuf) |
786 | g_object_unref(object: context->pixbuf); |
787 | |
788 | if (context->read_state == READ_STATE_HEADERS) { |
789 | g_set_error_literal (err: error, |
790 | GDK_PIXBUF_ERROR, |
791 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
792 | _("Premature end-of-file encountered" )); |
793 | retval = FALSE; |
794 | } |
795 | |
796 | g_free(mem: context->buff); |
797 | g_free(mem: context); |
798 | |
799 | return retval; |
800 | } |
801 | |
802 | |
803 | /* |
804 | The OneLineXX functions are called when 1 line worth of data is present. |
805 | OneLine24 is the 24 bpp-version. |
806 | */ |
807 | static void OneLine32(struct bmp_progressive_state *context) |
808 | { |
809 | int i; |
810 | guchar *pixels; |
811 | guchar *src; |
812 | gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf); |
813 | |
814 | if (!context->Header.Negative) |
815 | pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
816 | rowstride * (context->Header.height - context->Lines - 1)); |
817 | else |
818 | pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
819 | rowstride * context->Lines); |
820 | |
821 | src = context->buff; |
822 | |
823 | if (context->Compressed == BI_BITFIELDS) { |
824 | int r_lshift, r_rshift; |
825 | int g_lshift, g_rshift; |
826 | int b_lshift, b_rshift; |
827 | int a_lshift, a_rshift; |
828 | |
829 | r_lshift = 8 - context->r_bits; |
830 | g_lshift = 8 - context->g_bits; |
831 | b_lshift = 8 - context->b_bits; |
832 | a_lshift = 8 - context->a_bits; |
833 | |
834 | r_rshift = context->r_bits - r_lshift; |
835 | g_rshift = context->g_bits - g_lshift; |
836 | b_rshift = context->b_bits - b_lshift; |
837 | a_rshift = context->a_bits - a_lshift; |
838 | |
839 | for (i = 0; i < context->Header.width; i++) { |
840 | unsigned int v, r, g, b, a; |
841 | |
842 | v = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); |
843 | |
844 | r = (v & context->r_mask) >> context->r_shift; |
845 | g = (v & context->g_mask) >> context->g_shift; |
846 | b = (v & context->b_mask) >> context->b_shift; |
847 | a = (v & context->a_mask) >> context->a_shift; |
848 | |
849 | *pixels++ = (r << r_lshift) | (r >> r_rshift); |
850 | *pixels++ = (g << g_lshift) | (g >> g_rshift); |
851 | *pixels++ = (b << b_lshift) | (b >> b_rshift); |
852 | if (context->a_bits) |
853 | *pixels++ = (a << a_lshift) | (a >> a_rshift); |
854 | else |
855 | *pixels++ = 0xff; |
856 | |
857 | src += 4; |
858 | } |
859 | } else |
860 | for (i = 0; i < context->Header.width; i++) { |
861 | *pixels++ = src[2]; |
862 | *pixels++ = src[1]; |
863 | *pixels++ = src[0]; |
864 | *pixels++ = 0xff; |
865 | |
866 | src += 4; |
867 | } |
868 | } |
869 | |
870 | static void OneLine24(struct bmp_progressive_state *context) |
871 | { |
872 | gint X; |
873 | guchar *Pixels; |
874 | gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf); |
875 | |
876 | X = 0; |
877 | if (context->Header.Negative == 0) |
878 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
879 | rowstride * (context->Header.height - context->Lines - 1)); |
880 | else |
881 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
882 | rowstride * context->Lines); |
883 | while (X < context->Header.width) { |
884 | Pixels[X * 3 + 0] = context->buff[X * 3 + 2]; |
885 | Pixels[X * 3 + 1] = context->buff[X * 3 + 1]; |
886 | Pixels[X * 3 + 2] = context->buff[X * 3 + 0]; |
887 | X++; |
888 | } |
889 | |
890 | } |
891 | |
892 | static void OneLine16(struct bmp_progressive_state *context) |
893 | { |
894 | int i; |
895 | guchar *pixels; |
896 | guchar *src; |
897 | gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf); |
898 | |
899 | if (!context->Header.Negative) |
900 | pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
901 | rowstride * (context->Header.height - context->Lines - 1)); |
902 | else |
903 | pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
904 | rowstride * context->Lines); |
905 | |
906 | src = context->buff; |
907 | |
908 | if (context->Compressed == BI_BITFIELDS) { |
909 | int r_lshift, r_rshift; |
910 | int g_lshift, g_rshift; |
911 | int b_lshift, b_rshift; |
912 | |
913 | r_lshift = 8 - context->r_bits; |
914 | g_lshift = 8 - context->g_bits; |
915 | b_lshift = 8 - context->b_bits; |
916 | |
917 | r_rshift = context->r_bits - r_lshift; |
918 | g_rshift = context->g_bits - g_lshift; |
919 | b_rshift = context->b_bits - b_lshift; |
920 | |
921 | for (i = 0; i < context->Header.width; i++) { |
922 | int v, r, g, b; |
923 | |
924 | v = (int) src[0] | ((int) src[1] << 8); |
925 | |
926 | r = (v & context->r_mask) >> context->r_shift; |
927 | g = (v & context->g_mask) >> context->g_shift; |
928 | b = (v & context->b_mask) >> context->b_shift; |
929 | |
930 | *pixels++ = (r << r_lshift) | (r >> r_rshift); |
931 | *pixels++ = (g << g_lshift) | (g >> g_rshift); |
932 | *pixels++ = (b << b_lshift) | (b >> b_rshift); |
933 | |
934 | src += 2; |
935 | } |
936 | } else |
937 | for (i = 0; i < context->Header.width; i++) { |
938 | int v, r, g, b; |
939 | |
940 | v = src[0] | (src[1] << 8); |
941 | |
942 | r = (v >> 10) & 0x1f; |
943 | g = (v >> 5) & 0x1f; |
944 | b = v & 0x1f; |
945 | |
946 | *pixels++ = (r << 3) | (r >> 2); |
947 | *pixels++ = (g << 3) | (g >> 2); |
948 | *pixels++ = (b << 3) | (b >> 2); |
949 | |
950 | src += 2; |
951 | } |
952 | } |
953 | |
954 | static void OneLine8(struct bmp_progressive_state *context) |
955 | { |
956 | gint X; |
957 | guchar *Pixels; |
958 | gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf); |
959 | |
960 | X = 0; |
961 | if (context->Header.Negative == 0) |
962 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
963 | rowstride * (context->Header.height - context->Lines - 1)); |
964 | else |
965 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
966 | rowstride * context->Lines); |
967 | while (X < context->Header.width) { |
968 | Pixels[X * 3 + 0] = |
969 | context->Colormap[context->buff[X]][2]; |
970 | Pixels[X * 3 + 1] = |
971 | context->Colormap[context->buff[X]][1]; |
972 | Pixels[X * 3 + 2] = |
973 | context->Colormap[context->buff[X]][0]; |
974 | X++; |
975 | } |
976 | } |
977 | |
978 | static void OneLine4(struct bmp_progressive_state *context) |
979 | { |
980 | gint X; |
981 | guchar *Pixels; |
982 | gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf); |
983 | |
984 | X = 0; |
985 | if (context->Header.Negative == 0) |
986 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
987 | rowstride * (context->Header.height - context->Lines - 1)); |
988 | else |
989 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
990 | rowstride * context->Lines); |
991 | |
992 | while (X < context->Header.width) { |
993 | guchar Pix; |
994 | |
995 | Pix = context->buff[X / 2]; |
996 | |
997 | Pixels[X * 3 + 0] = |
998 | context->Colormap[Pix >> 4][2]; |
999 | Pixels[X * 3 + 1] = |
1000 | context->Colormap[Pix >> 4][1]; |
1001 | Pixels[X * 3 + 2] = |
1002 | context->Colormap[Pix >> 4][0]; |
1003 | X++; |
1004 | if (X < context->Header.width) { |
1005 | /* Handle the other 4 bit pixel only when there is one */ |
1006 | Pixels[X * 3 + 0] = |
1007 | context->Colormap[Pix & 15][2]; |
1008 | Pixels[X * 3 + 1] = |
1009 | context->Colormap[Pix & 15][1]; |
1010 | Pixels[X * 3 + 2] = |
1011 | context->Colormap[Pix & 15][0]; |
1012 | X++; |
1013 | } |
1014 | } |
1015 | |
1016 | } |
1017 | |
1018 | static void OneLine1(struct bmp_progressive_state *context) |
1019 | { |
1020 | gint X; |
1021 | guchar *Pixels; |
1022 | gint rowstride = gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf); |
1023 | |
1024 | X = 0; |
1025 | if (context->Header.Negative == 0) |
1026 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
1027 | rowstride * (context->Header.height - context->Lines - 1)); |
1028 | else |
1029 | Pixels = (gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) + |
1030 | rowstride * context->Lines); |
1031 | while (X < context->Header.width) { |
1032 | gint Bit; |
1033 | |
1034 | Bit = (context->buff[X / 8]) >> (7 - (X & 7)); |
1035 | Bit = Bit & 1; |
1036 | Pixels[X * 3 + 0] = context->Colormap[Bit][2]; |
1037 | Pixels[X * 3 + 1] = context->Colormap[Bit][1]; |
1038 | Pixels[X * 3 + 2] = context->Colormap[Bit][0]; |
1039 | X++; |
1040 | } |
1041 | } |
1042 | |
1043 | |
1044 | static void OneLine(struct bmp_progressive_state *context) |
1045 | { |
1046 | context->BufferDone = 0; |
1047 | if (context->Lines >= context->Header.height) |
1048 | return; |
1049 | |
1050 | if (context->Type == 32) |
1051 | OneLine32(context); |
1052 | else if (context->Type == 24) |
1053 | OneLine24(context); |
1054 | else if (context->Type == 16) |
1055 | OneLine16(context); |
1056 | else if (context->Type == 8) |
1057 | OneLine8(context); |
1058 | else if (context->Type == 4) |
1059 | OneLine4(context); |
1060 | else if (context->Type == 1) |
1061 | OneLine1(context); |
1062 | else |
1063 | g_assert_not_reached (); |
1064 | |
1065 | context->Lines++; |
1066 | |
1067 | (*context->updated_func) (context->pixbuf, |
1068 | 0, |
1069 | (context->Header.Negative ? |
1070 | (context->Lines - 1) : |
1071 | (context->Header.height - context->Lines)), |
1072 | context->Header.width, |
1073 | 1, |
1074 | context->user_data); |
1075 | |
1076 | } |
1077 | |
1078 | #define NEUTRAL 0 |
1079 | #define ENCODED 1 |
1080 | #define ESCAPE 2 |
1081 | #define DELTA_X 3 |
1082 | #define DELTA_Y 4 |
1083 | #define ABSOLUTE 5 |
1084 | #define SKIP 6 |
1085 | |
1086 | #define END_OF_LINE 0 |
1087 | #define END_OF_BITMAP 1 |
1088 | #define DELTA 2 |
1089 | |
1090 | static gboolean |
1091 | DoCompressed(struct bmp_progressive_state *context, GError **error) |
1092 | { |
1093 | gint i, j; |
1094 | gint y; |
1095 | guchar c; |
1096 | gint idx; |
1097 | |
1098 | /* context->compr.y might be past the last line because we are |
1099 | * on padding past the end of a valid data, or we might have hit |
1100 | * out-of-bounds data. Either way we just eat-and-ignore the |
1101 | * rest of the file. Doing the check only here and not when |
1102 | * we change y below is fine since BufferSize is always 2 here |
1103 | * and the BMP file format always starts new data on 16-bit |
1104 | * boundaries. |
1105 | */ |
1106 | if (context->compr.y >= context->Header.height) { |
1107 | context->BufferDone = 0; |
1108 | return TRUE; |
1109 | } |
1110 | |
1111 | y = context->compr.y; |
1112 | |
1113 | for (i = 0; i < context->BufferSize; i++) { |
1114 | c = context->buff[i]; |
1115 | switch (context->compr.phase) { |
1116 | case NEUTRAL: |
1117 | if (c) { |
1118 | context->compr.run = c; |
1119 | context->compr.phase = ENCODED; |
1120 | } |
1121 | else |
1122 | context->compr.phase = ESCAPE; |
1123 | break; |
1124 | case ENCODED: |
1125 | for (j = 0; j < context->compr.run; j++) { |
1126 | if (context->Compressed == BI_RLE8) |
1127 | idx = c; |
1128 | else if (j & 1) |
1129 | idx = c & 0x0f; |
1130 | else |
1131 | idx = (c >> 4) & 0x0f; |
1132 | if (context->compr.x < context->Header.width) { |
1133 | *context->compr.p++ = context->Colormap[idx][2]; |
1134 | *context->compr.p++ = context->Colormap[idx][1]; |
1135 | *context->compr.p++ = context->Colormap[idx][0]; |
1136 | *context->compr.p++ = 0xff; |
1137 | context->compr.x++; |
1138 | } |
1139 | } |
1140 | context->compr.phase = NEUTRAL; |
1141 | break; |
1142 | case ESCAPE: |
1143 | switch (c) { |
1144 | case END_OF_LINE: |
1145 | context->compr.x = 0; |
1146 | context->compr.y++; |
1147 | context->compr.p = gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) |
1148 | + (gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf) * (context->Header.height - context->compr.y - 1)) |
1149 | + (4 * context->compr.x); |
1150 | context->compr.phase = NEUTRAL; |
1151 | break; |
1152 | case END_OF_BITMAP: |
1153 | context->compr.x = 0; |
1154 | context->compr.y = context->Header.height; |
1155 | context->compr.phase = NEUTRAL; |
1156 | break; |
1157 | case DELTA: |
1158 | context->compr.phase = DELTA_X; |
1159 | break; |
1160 | default: |
1161 | context->compr.run = c; |
1162 | context->compr.count = 0; |
1163 | context->compr.phase = ABSOLUTE; |
1164 | break; |
1165 | } |
1166 | break; |
1167 | case DELTA_X: |
1168 | context->compr.x += c; |
1169 | context->compr.phase = DELTA_Y; |
1170 | break; |
1171 | case DELTA_Y: |
1172 | context->compr.y += c; |
1173 | context->compr.p = gdk_pixbuf_get_pixels (pixbuf: context->pixbuf) |
1174 | + (gdk_pixbuf_get_rowstride (pixbuf: context->pixbuf) * (context->Header.height - context->compr.y - 1)) |
1175 | + (4 * context->compr.x); |
1176 | context->compr.phase = NEUTRAL; |
1177 | break; |
1178 | case ABSOLUTE: |
1179 | if (context->Compressed == BI_RLE8) { |
1180 | idx = c; |
1181 | if (context->compr.x < context->Header.width) { |
1182 | *context->compr.p++ = context->Colormap[idx][2]; |
1183 | *context->compr.p++ = context->Colormap[idx][1]; |
1184 | *context->compr.p++ = context->Colormap[idx][0]; |
1185 | *context->compr.p++ = 0xff; |
1186 | context->compr.x++; |
1187 | } |
1188 | context->compr.count++; |
1189 | |
1190 | if (context->compr.count == context->compr.run) { |
1191 | if (context->compr.run & 1) |
1192 | context->compr.phase = SKIP; |
1193 | else |
1194 | context->compr.phase = NEUTRAL; |
1195 | } |
1196 | } |
1197 | else { |
1198 | for (j = 0; j < 2; j++) { |
1199 | if (context->compr.count & 1) |
1200 | idx = c & 0x0f; |
1201 | else |
1202 | idx = (c >> 4) & 0x0f; |
1203 | if (context->compr.x < context->Header.width) { |
1204 | *context->compr.p++ = context->Colormap[idx][2]; |
1205 | *context->compr.p++ = context->Colormap[idx][1]; |
1206 | *context->compr.p++ = context->Colormap[idx][0]; |
1207 | *context->compr.p++ = 0xff; |
1208 | context->compr.x++; |
1209 | } |
1210 | context->compr.count++; |
1211 | |
1212 | if (context->compr.count == context->compr.run) { |
1213 | if ((context->compr.run & 3) == 1 |
1214 | || (context->compr.run & 3) == 2) |
1215 | context->compr.phase = SKIP; |
1216 | else |
1217 | context->compr.phase = NEUTRAL; |
1218 | break; |
1219 | } |
1220 | } |
1221 | } |
1222 | break; |
1223 | case SKIP: |
1224 | context->compr.phase = NEUTRAL; |
1225 | break; |
1226 | } |
1227 | } |
1228 | |
1229 | if (context->compr.y > y) { |
1230 | gint new_y = MIN (context->compr.y, context->Header.height); |
1231 | (*context->updated_func) (context->pixbuf, |
1232 | 0, |
1233 | context->Header.height - new_y, |
1234 | context->Header.width, |
1235 | new_y - y, |
1236 | context->user_data); |
1237 | } |
1238 | |
1239 | context->BufferDone = 0; |
1240 | return TRUE; |
1241 | } |
1242 | |
1243 | /* |
1244 | * context - from image_begin_load |
1245 | * buf - new image data |
1246 | * size - length of new image data |
1247 | * |
1248 | * append image data onto incrementally built output image |
1249 | */ |
1250 | static gboolean |
1251 | gdk_pixbuf__bmp_image_load_increment(gpointer data, |
1252 | const guchar * buf, |
1253 | guint size, |
1254 | GError **error) |
1255 | { |
1256 | struct bmp_progressive_state *context = |
1257 | (struct bmp_progressive_state *) data; |
1258 | |
1259 | gint BytesToCopy; |
1260 | gint BytesToRemove; |
1261 | |
1262 | if (context->read_state == READ_STATE_DONE) |
1263 | return TRUE; |
1264 | else if (context->read_state == READ_STATE_ERROR) |
1265 | return FALSE; |
1266 | |
1267 | while (size > 0) { |
1268 | if (context->BufferDone < context->BufferSize) { /* We still |
1269 | have headerbytes to do */ |
1270 | BytesToCopy = |
1271 | context->BufferSize - context->BufferDone; |
1272 | if (BytesToCopy > size) |
1273 | BytesToCopy = size; |
1274 | |
1275 | memmove(dest: context->buff + context->BufferDone, |
1276 | src: buf, n: BytesToCopy); |
1277 | |
1278 | size -= BytesToCopy; |
1279 | buf += BytesToCopy; |
1280 | context->BufferDone += BytesToCopy; |
1281 | |
1282 | if (context->BufferDone != context->BufferSize) |
1283 | break; |
1284 | } |
1285 | |
1286 | /* context->buff is full. Now we discard all "padding" */ |
1287 | if (context->BufferPadding != 0) { |
1288 | BytesToRemove = context->BufferPadding - size; |
1289 | if (BytesToRemove > size) { |
1290 | BytesToRemove = size; |
1291 | } |
1292 | size -= BytesToRemove; |
1293 | context->BufferPadding -= BytesToRemove; |
1294 | |
1295 | if (context->BufferPadding != 0) |
1296 | break; |
1297 | } |
1298 | |
1299 | switch (context->read_state) { |
1300 | case READ_STATE_HEADERS: |
1301 | if (!DecodeHeader (BFH: context->buff, |
1302 | BIH: context->buff + 14, State: context, |
1303 | error)) |
1304 | return FALSE; |
1305 | |
1306 | break; |
1307 | |
1308 | case READ_STATE_PALETTE: |
1309 | if (!DecodeColormap (buff: context->buff, State: context, error)) { |
1310 | g_set_error_literal (err: error, |
1311 | GDK_PIXBUF_ERROR, |
1312 | code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE, |
1313 | _("Error while decoding colormap" )); |
1314 | return FALSE; |
1315 | } |
1316 | break; |
1317 | |
1318 | case READ_STATE_BITMASKS: |
1319 | if (!decode_bitmasks (buf: context->buff, State: context, error)) |
1320 | return FALSE; |
1321 | break; |
1322 | |
1323 | case READ_STATE_DATA: |
1324 | if (context->Compressed == BI_RGB || context->Compressed == BI_BITFIELDS) |
1325 | OneLine (context); |
1326 | else if (!DoCompressed (context, error)) |
1327 | return FALSE; |
1328 | |
1329 | break; |
1330 | case READ_STATE_DONE: |
1331 | return TRUE; |
1332 | break; |
1333 | |
1334 | default: |
1335 | g_assert_not_reached (); |
1336 | } |
1337 | } |
1338 | |
1339 | return TRUE; |
1340 | } |
1341 | |
1342 | /* for our convenience when filling file header */ |
1343 | #define put16(buf,data) { guint16 x; \ |
1344 | x = GUINT16_TO_LE (data); \ |
1345 | memcpy(buf, &x, 2); \ |
1346 | buf += 2; } |
1347 | #define put32(buf,data) { guint32 x; \ |
1348 | x = GUINT32_TO_LE (data); \ |
1349 | memcpy(buf, &x, 4); \ |
1350 | buf += 4; } |
1351 | |
1352 | static gboolean |
1353 | gdk_pixbuf__bmp_image_save_to_callback (GdkPixbufSaveFunc save_func, |
1354 | gpointer user_data, |
1355 | GdkPixbuf *pixbuf, |
1356 | gchar **keys, |
1357 | gchar **values, |
1358 | GError **error) |
1359 | { |
1360 | guint width, height, channel, size, stride, src_stride, x, y; |
1361 | guint bf_size; |
1362 | guchar BFH_BIH[54], *pixels, *buf, *src, *dst, *dst_line; |
1363 | gboolean ret; |
1364 | |
1365 | width = gdk_pixbuf_get_width (pixbuf); |
1366 | height = gdk_pixbuf_get_height (pixbuf); |
1367 | channel = gdk_pixbuf_get_n_channels (pixbuf); |
1368 | pixels = gdk_pixbuf_get_pixels (pixbuf); |
1369 | src_stride = gdk_pixbuf_get_rowstride (pixbuf); |
1370 | |
1371 | /* stride = (width * 3 + 3) & ~3 */ |
1372 | if (!g_uint_checked_mul (&stride, width, 3) || |
1373 | !g_uint_checked_add (&stride, stride, 3)) { |
1374 | g_set_error_literal (err: error, GDK_PIXBUF_ERROR, |
1375 | code: GDK_PIXBUF_ERROR_FAILED, |
1376 | _("Image is too wide for BMP format." )); |
1377 | return FALSE; |
1378 | } |
1379 | |
1380 | stride &= ~3; |
1381 | |
1382 | /* size = stride * height |
1383 | * bf_size = size + 14 + 40 */ |
1384 | if (!g_uint_checked_mul (&size, stride, height) || |
1385 | !g_uint_checked_add (&bf_size, size, 14 + 40)) { |
1386 | g_set_error_literal (err: error, GDK_PIXBUF_ERROR, |
1387 | code: GDK_PIXBUF_ERROR_FAILED, |
1388 | _("Image is too wide for BMP format." )); |
1389 | return FALSE; |
1390 | } |
1391 | |
1392 | /* filling BFH */ |
1393 | dst = BFH_BIH; |
1394 | *dst++ = 'B'; /* bfType */ |
1395 | *dst++ = 'M'; |
1396 | put32 (dst, bf_size); /* bfSize */ |
1397 | put32 (dst, 0); /* bfReserved1 + bfReserved2 */ |
1398 | put32 (dst, 14 + 40); /* bfOffBits */ |
1399 | |
1400 | /* filling BIH */ |
1401 | put32 (dst, 40); /* biSize */ |
1402 | put32 (dst, width); /* biWidth */ |
1403 | put32 (dst, height); /* biHeight */ |
1404 | put16 (dst, 1); /* biPlanes */ |
1405 | put16 (dst, 24); /* biBitCount */ |
1406 | put32 (dst, BI_RGB); /* biCompression */ |
1407 | put32 (dst, size); /* biSizeImage */ |
1408 | put32 (dst, 0); /* biXPelsPerMeter */ |
1409 | put32 (dst, 0); /* biYPelsPerMeter */ |
1410 | put32 (dst, 0); /* biClrUsed */ |
1411 | put32 (dst, 0); /* biClrImportant */ |
1412 | |
1413 | if (!save_func ((gchar *)BFH_BIH, 14 + 40, error, user_data)) |
1414 | return FALSE; |
1415 | |
1416 | dst_line = buf = g_try_malloc (n_bytes: size); |
1417 | if (!buf) { |
1418 | g_set_error_literal (err: error, |
1419 | GDK_PIXBUF_ERROR, |
1420 | code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, |
1421 | _("Couldn’t allocate memory for saving BMP file" )); |
1422 | return FALSE; |
1423 | } |
1424 | |
1425 | /* saving as a bottom-up bmp */ |
1426 | pixels += (height - 1) * src_stride; |
1427 | for (y = 0; y < height; ++y, pixels -= src_stride, dst_line += stride) { |
1428 | dst = dst_line; |
1429 | src = pixels; |
1430 | for (x = 0; x < width; ++x, dst += 3, src += channel) { |
1431 | dst[0] = src[2]; |
1432 | dst[1] = src[1]; |
1433 | dst[2] = src[0]; |
1434 | } |
1435 | } |
1436 | ret = save_func ((gchar *)buf, size, error, user_data); |
1437 | g_free (mem: buf); |
1438 | |
1439 | return ret; |
1440 | } |
1441 | |
1442 | static gboolean |
1443 | save_to_file_cb (const gchar *buf, |
1444 | gsize count, |
1445 | GError **error, |
1446 | gpointer data) |
1447 | { |
1448 | gint bytes; |
1449 | |
1450 | while (count > 0) { |
1451 | bytes = fwrite (ptr: buf, size: sizeof (gchar), n: count, s: (FILE *) data); |
1452 | if (bytes <= 0) |
1453 | break; |
1454 | count -= bytes; |
1455 | buf += bytes; |
1456 | } |
1457 | |
1458 | if (count) { |
1459 | g_set_error_literal (err: error, |
1460 | GDK_PIXBUF_ERROR, |
1461 | code: GDK_PIXBUF_ERROR_FAILED, |
1462 | _("Couldn’t write to BMP file" )); |
1463 | return FALSE; |
1464 | } |
1465 | |
1466 | return TRUE; |
1467 | } |
1468 | |
1469 | static gboolean |
1470 | gdk_pixbuf__bmp_image_save (FILE *f, |
1471 | GdkPixbuf *pixbuf, |
1472 | gchar **keys, |
1473 | gchar **values, |
1474 | GError **error) |
1475 | { |
1476 | return gdk_pixbuf__bmp_image_save_to_callback (save_func: save_to_file_cb, |
1477 | user_data: f, pixbuf, keys, |
1478 | values, error); |
1479 | } |
1480 | |
1481 | #ifndef INCLUDE_bmp |
1482 | #define MODULE_ENTRY(function) G_MODULE_EXPORT void function |
1483 | #else |
1484 | #define MODULE_ENTRY(function) void _gdk_pixbuf__bmp_ ## function |
1485 | #endif |
1486 | |
1487 | MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module) |
1488 | { |
1489 | module->begin_load = gdk_pixbuf__bmp_image_begin_load; |
1490 | module->stop_load = gdk_pixbuf__bmp_image_stop_load; |
1491 | module->load_increment = gdk_pixbuf__bmp_image_load_increment; |
1492 | module->save = gdk_pixbuf__bmp_image_save; |
1493 | module->save_to_callback = gdk_pixbuf__bmp_image_save_to_callback; |
1494 | } |
1495 | |
1496 | MODULE_ENTRY (fill_info) (GdkPixbufFormat *info) |
1497 | { |
1498 | static const GdkPixbufModulePattern signature[] = { |
1499 | { "BM" , NULL, 100 }, |
1500 | { NULL, NULL, 0 } |
1501 | }; |
1502 | static const gchar * mime_types[] = { |
1503 | "image/bmp" , |
1504 | "image/x-bmp" , |
1505 | "image/x-MS-bmp" , |
1506 | NULL |
1507 | }; |
1508 | static const gchar * extensions[] = { |
1509 | "bmp" , |
1510 | NULL |
1511 | }; |
1512 | |
1513 | info->name = "bmp" ; |
1514 | info->signature = (GdkPixbufModulePattern *) signature; |
1515 | info->description = NC_("image format" , "BMP" ); |
1516 | info->mime_types = (gchar **) mime_types; |
1517 | info->extensions = (gchar **) extensions; |
1518 | info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE; |
1519 | info->license = "LGPL" ; |
1520 | } |
1521 | |
1522 | |