1 /*****************************************************************************
2 * This file is part of gfxprim library. *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
27 Thanks wikipedia for the format specification.
39 #include "core/GP_Debug.h"
40 #include "core/GP_Pixel.h"
41 #include "core/GP_GetPutPixel.h"
44 #define BMP_HEADER_OFFSET 0x0a /* info header offset - 4 bytes */
46 #define BUF_TO_4(buf, off) \
47 (buf[off] + (buf[off+1]<<8) + (buf[off+2]<<16) + (buf[off+3]<<24))
49 #define BUF_TO_2(buf, off) \
50 (buf[off] + (buf[off+1]<<8))
53 struct bitmap_info_header
{
55 * Offset to image data.
57 uint32_t pixel_offset
;
60 * Header size (palette is on offset header_size + 14)
65 * Image size in pixels.
66 * If h is negative image is top-down (bottom-up is default)
72 uint32_t compress_type
;
74 * if 0 image uses whole range (2^bpp colors)
76 uint32_t palette_colors
;
79 enum bitmap_compress
{
80 COMPRESS_RGB
= 0, /* uncompressed */
81 COMPRESS_RLE8
= 1, /* run-length encoded bitmap */
82 COMPRESS_RLE4
= 2, /* run-length encoded bitmap */
83 COMPRESS_BITFIELDS
= 3, /* bitfield for each channel */
84 COMPRESS_JPEG
= 4, /* only for printers */
85 COMPRESS_PNG
= 5, /* only for printers */
86 COMPRESS_ALPHABITFIELDS
= 6,
87 COMPRESS_MAX
= COMPRESS_ALPHABITFIELDS
,
90 static const char *bitmap_compress_names
[] = {
100 static const char *bitmap_compress_name(uint32_t compress
)
102 if (compress
>= COMPRESS_MAX
)
105 return bitmap_compress_names
[compress
];
108 enum bitmap_info_header_sizes
{
109 BITMAPCOREHEADER
= 12, /* old OS/2 format + win 3.0 */
110 BITMAPCOREHEADER2
= 64, /* OS/2 */
111 BITMAPINFOHEADER
= 40, /* most common */
112 BITMAPINFOHEADER2
= 52, /* Undocummented */
113 BITMAPINFOHEADER3
= 56, /* Undocummented */
114 BITMAPINFOHEADER4
= 108, /* adds color space + gamma - win 95/NT4 */
115 BITMAPINFOHEADER5
= 124, /* adds ICC color profiles win 98+ */
118 static const char *bitmap_header_size_name(uint32_t size
)
121 case BITMAPCOREHEADER
:
122 return "BitmapCoreHeader";
123 case BITMAPCOREHEADER2
:
124 return "BitmapCoreHeader2";
125 case BITMAPINFOHEADER
:
126 return "BitmapInfoHeader";
127 case BITMAPINFOHEADER2
:
128 return "BitmapInfoHeader2";
129 case BITMAPINFOHEADER3
:
130 return "BitmapInfoHeader3";
131 case BITMAPINFOHEADER4
:
132 return "BitmapInfoHeader4";
133 case BITMAPINFOHEADER5
:
134 return "BitmapInfoHeader5";
140 static uint32_t get_palette_size(struct bitmap_info_header
*header
)
143 if (header
->palette_colors
)
144 return header
->palette_colors
;
146 return (1 << header
->bpp
);
149 static int read_bitmap_info_header(FILE *f
, struct bitmap_info_header
*header
)
153 if (fread(buf
, 1, sizeof(buf
), f
) != sizeof(buf
)) {
154 GP_DEBUG(1, "Failed to read bitmap info header");
158 header
->w
= BUF_TO_4(buf
, 0);
159 header
->h
= BUF_TO_4(buf
, 4);
160 header
->bpp
= BUF_TO_2(buf
, 10);
161 header
->compress_type
= BUF_TO_4(buf
, 12);
162 header
->palette_colors
= BUF_TO_4(buf
, 28);
164 uint16_t nr_planes
= BUF_TO_2(buf
, 8);
166 /* This must be 1 according to specs */
168 GP_DEBUG(1, "Number of planes is %"PRId16
" should be 1",
171 GP_DEBUG(2, "Have BMP bitmap size %"PRId32
"x%"PRId32
" %"PRIu16
" "
172 "bpp, %"PRIu32
" pallete colors, '%s' compression",
173 header
->w
, header
->h
, header
->bpp
,
174 get_palette_size(header
),
175 bitmap_compress_name(header
->compress_type
));
180 static int read_bitmap_core_header(FILE *f
, struct bitmap_info_header
*header
)
184 if (fread(buf
, 1, sizeof(buf
), f
) != sizeof(buf
)) {
185 GP_DEBUG(1, "Failed to read bitmap core header");
189 header
->w
= BUF_TO_2(buf
, 0);
190 header
->h
= BUF_TO_2(buf
, 2);
191 header
->bpp
= BUF_TO_2(buf
, 6);
192 header
->compress_type
= COMPRESS_RGB
;
193 header
->palette_colors
= 0;
195 uint16_t nr_planes
= BUF_TO_2(buf
, 4);
197 /* This must be 1 according to specs */
199 GP_DEBUG(1, "Number of planes is %"PRId16
" should be 1",
202 GP_DEBUG(2, "Have BMP bitmap size %"PRId32
"x%"PRId32
" %"PRIu16
" bpp",
203 header
->h
, header
->w
, header
->bpp
);
208 static int read_bitmap_header(FILE *f
, struct bitmap_info_header
*header
)
213 if (fseek(f
, BMP_HEADER_OFFSET
, SEEK_SET
)) {
215 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
216 BMP_HEADER_OFFSET
, strerror(errno
));
220 /* Read info header size, header size determines header type */
221 if (fread(buf
, 1, sizeof(buf
), f
) != sizeof(buf
)) {
222 GP_DEBUG(1, "Failed to read info header size");
226 header
->pixel_offset
= BUF_TO_4(buf
, 0);
227 header
->header_size
= BUF_TO_4(buf
, 4);
229 GP_DEBUG(2, "BMP header type '%s'",
230 bitmap_header_size_name(header
->header_size
));
232 switch (header
->header_size
) {
233 case BITMAPCOREHEADER
:
234 err
= read_bitmap_core_header(f
, header
);
236 case BITMAPCOREHEADER2
:
238 /* The bitmap core header only adds filelds to the end of the header */
239 case BITMAPINFOHEADER
:
240 case BITMAPINFOHEADER2
:
241 case BITMAPINFOHEADER3
:
242 case BITMAPINFOHEADER4
:
243 err
= read_bitmap_info_header(f
, header
);
246 GP_DEBUG(1, "Unknown header type, continuing anyway");
247 err
= read_bitmap_info_header(f
, header
);
255 * Reads palette, the format is R G B X, each one byte.
257 static int read_bitmap_palette(FILE *f
, struct bitmap_info_header
*header
,
260 uint32_t palette_colors
= get_palette_size(header
);
261 uint32_t palette_offset
= header
->header_size
+ 14;
266 switch (header
->header_size
) {
267 case BITMAPCOREHEADER
:
275 GP_DEBUG(2, "Offset to BMP palette is 0x%x (%ubytes) "
276 "pixel size %"PRIu8
"bytes",
277 palette_offset
, palette_offset
, pixel_size
);
279 if (fseek(f
, palette_offset
, SEEK_SET
)) {
281 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
282 BMP_HEADER_OFFSET
, strerror(errno
));
286 for (i
= 0; i
< palette_colors
; i
++) {
289 if (fread(buf
, 1, pixel_size
, f
) != pixel_size
) {
290 GP_DEBUG(1, "Failed to read palette %"PRIu32
, i
);
294 palette
[i
] = GP_Pixel_CREATE_RGB888(buf
[2], buf
[1], buf
[0]);
296 GP_DEBUG(3, "Palette[%"PRIu32
"] = [0x%02x, 0x%02x, 0x%02x]", i
,
297 GP_Pixel_GET_R_RGB888(palette
[i
]),
298 GP_Pixel_GET_G_RGB888(palette
[i
]),
299 GP_Pixel_GET_B_RGB888(palette
[i
]));
305 static int seek_pixels_offset(struct bitmap_info_header
*header
, FILE *f
)
309 GP_DEBUG(2, "Offset to BMP pixels is 0x%x (%ubytes)",
310 header
->pixel_offset
, header
->pixel_offset
);
312 if (fseek(f
, header
->pixel_offset
, SEEK_SET
)) {
314 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
315 header
->pixel_offset
, strerror(errno
));
322 static GP_PixelType
match_pixel_type(struct bitmap_info_header
*header
)
324 switch (header
->bpp
) {
330 return GP_PIXEL_RGB888
;
333 return GP_PIXEL_UNKNOWN
;
337 * Returns four byte aligned row size for palette formats.
339 static uint32_t bitmap_row_size(struct bitmap_info_header
*header
)
341 uint32_t row_size
= 0;
343 /* align width to whole bytes */
344 switch (header
->bpp
) {
346 row_size
= header
->w
/ 8 + !!(header
->w
%8);
349 row_size
= header
->w
/ 4 + !!(header
->w
%4);
352 row_size
= header
->w
/ 2 + !!(header
->w
%2);
355 row_size
= header
->w
;
359 /* align row_size to four byte boundary */
360 switch (row_size
% 4) {
371 GP_DEBUG(2, "bpp = %"PRIu16
", width = %"PRId32
", row_size = %"PRIu32
,
372 header
->bpp
, header
->w
, row_size
);
377 static uint8_t get_idx(struct bitmap_info_header
*header
,
378 uint8_t row
[], int32_t x
)
380 switch (header
->bpp
) {
382 return !!(row
[x
/8] & (1<<(7 - x
%8)));
384 return (row
[x
/4] >> (2*(3 - x
%4))) & 0x03;
386 return (row
[x
/2] >> (4*(!(x
%2)))) & 0x0f;
395 static int read_palette(FILE *f
, struct bitmap_info_header
*header
,
396 GP_Context
*context
, GP_ProgressCallback
*callback
)
398 uint32_t palette_size
= get_palette_size(header
);
399 GP_Pixel palette
[get_palette_size(header
)];
402 if ((err
= read_bitmap_palette(f
, header
, palette
)))
405 if ((err
= seek_pixels_offset(header
, f
)))
408 uint32_t row_size
= bitmap_row_size(header
);
411 for (y
= 0; y
< GP_ABS(header
->h
); y
++) {
413 uint8_t row
[row_size
];
415 if (fread(row
, 1, row_size
, f
) != row_size
) {
416 GP_DEBUG(1, "Failed to read row %"PRId32
, y
);
420 for (x
= 0; x
< header
->w
; x
++) {
421 uint8_t idx
= get_idx(header
, row
, x
);
424 if (idx
>= palette_size
) {
425 GP_DEBUG(1, "Index out of palette, ignoring");
436 ry
= GP_ABS(header
->h
) - 1 - y
;
438 GP_PutPixel_Raw_24BPP(context
, x
, ry
, p
);
441 if (GP_ProgressCallbackReport(callback
, header
->h
- y
-1,
442 context
->h
, context
->w
)) {
443 GP_DEBUG(1, "Operation aborted");
448 GP_ProgressCallbackDone(callback
);
452 static int read_rgb888(FILE *f
, struct bitmap_info_header
*header
,
453 GP_Context
*context
, GP_ProgressCallback
*callback
)
455 uint32_t row_size
= 3 * header
->w
;
459 if ((err
= seek_pixels_offset(header
, f
)))
462 for (y
= 0; y
< GP_ABS(header
->h
); y
++) {
468 ry
= GP_ABS(header
->h
) - 1 - y
;
470 uint8_t *row
= GP_PIXEL_ADDR(context
, 0, ry
);
472 if (fread(row
, 1, row_size
, f
) != row_size
) {
473 GP_DEBUG(1, "Failed to read row %"PRId32
, y
);
477 /* Rows are four byte aligned */
478 switch (row_size
% 4) {
489 if (GP_ProgressCallbackReport(callback
, header
->h
- y
-1,
490 context
->h
, context
->w
)) {
491 GP_DEBUG(1, "Operation aborted");
496 GP_ProgressCallbackDone(callback
);
500 static int read_bitmap_pixels(FILE *f
, struct bitmap_info_header
*header
,
501 GP_Context
*context
, GP_ProgressCallback
*callback
)
503 switch (header
->bpp
) {
505 /* I haven't been able to locate 2bpp palette bmp file => not tested */
509 return read_palette(f
, header
, context
, callback
);
511 return read_rgb888(f
, header
, context
, callback
);
517 int GP_MatchBMP(const void *buf
)
519 return !memcmp(buf
, "BM", 2);
522 int GP_OpenBMP(const char *src_path
, FILE **f
,
523 GP_Size
*w
, GP_Size
*h
, GP_PixelType
*pixel_type
)
527 *f
= fopen(src_path
, "rb");
531 GP_DEBUG(1, "Failed to open '%s' : %s",
532 src_path
, strerror(errno
));
539 if (ch1
!= 'B' || ch2
!= 'M') {
540 GP_DEBUG(1, "Unexpected bitmap header 0x%02x (%c) 0x%02x (%c)",
541 ch1
, isascii(ch1
) ? ch1
: ' ',
542 ch2
, isascii(ch2
) ? ch2
: ' ');
547 if (w
!= NULL
|| h
!= NULL
|| pixel_type
!= NULL
) {
548 struct bitmap_info_header header
;
550 if ((err
= read_bitmap_header(*f
, &header
)))
559 if (pixel_type
!= NULL
)
560 *pixel_type
= match_pixel_type(&header
);
571 GP_Context
*GP_ReadBMP(FILE *f
, GP_ProgressCallback
*callback
)
573 struct bitmap_info_header header
;
574 GP_PixelType pixel_type
;
578 if ((err
= read_bitmap_header(f
, &header
)))
581 if (header
.compress_type
!= COMPRESS_RGB
) {
582 GP_DEBUG(2, "Unknown compression type");
587 if ((pixel_type
= match_pixel_type(&header
)) == GP_PIXEL_UNKNOWN
) {
588 GP_DEBUG(2, "Unknown pixel type");
593 context
= GP_ContextAlloc(header
.w
, GP_ABS(header
.h
), pixel_type
);
595 if (context
== NULL
) {
600 if ((err
= read_bitmap_pixels(f
, &header
, context
, callback
)))
605 GP_ContextFree(context
);
612 GP_Context
*GP_LoadBMP(const char *src_path
, GP_ProgressCallback
*callback
)
616 if (GP_OpenBMP(src_path
, &f
, NULL
, NULL
, NULL
))
619 return GP_ReadBMP(f
, callback
);