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-2013 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 BMP loader and writer.
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"
43 #include "loaders/GP_ByteUtils.h"
44 #include "loaders/GP_LineConvert.h"
45 #include "loaders/GP_BMP.h"
47 #define BMP_HEADER_OFFSET 0x0a /* info header offset - 4 bytes */
49 #define BUF_TO_4(buf, off) \
50 (buf[off] + (buf[off+1]<<8) + (buf[off+2]<<16) + (buf[off+3]<<24))
52 #define BUF_TO_2(buf, off) \
53 (buf[off] + (buf[off+1]<<8))
55 struct bitmap_info_header
{
57 * Offset to image data.
59 uint32_t pixel_offset
;
62 * Header size (palette is on offset header_size + 14)
67 * Image size in pixels.
68 * If h is negative image is top-down (bottom-up is default)
74 uint32_t compress_type
;
76 * if 0 image uses whole range (2^bpp colors)
78 uint32_t palette_colors
;
80 * RGBA masks for bitfields compression
88 enum bitmap_compress
{
89 COMPRESS_RGB
= 0, /* uncompressed */
90 COMPRESS_RLE8
= 1, /* run-length encoded bitmap */
91 COMPRESS_RLE4
= 2, /* run-length encoded bitmap */
92 COMPRESS_BITFIELDS
= 3, /* bitfield for each channel */
93 COMPRESS_JPEG
= 4, /* only for printers */
94 COMPRESS_PNG
= 5, /* only for printers */
95 COMPRESS_ALPHABITFIELDS
= 6,
96 COMPRESS_MAX
= COMPRESS_ALPHABITFIELDS
,
99 static const char *bitmap_compress_names
[] = {
109 static const char *bitmap_compress_name(uint32_t compress
)
111 if (compress
>= COMPRESS_MAX
)
114 return bitmap_compress_names
[compress
];
117 enum bitmap_info_header_sizes
{
118 BITMAPCOREHEADER
= 12, /* old OS/2 format + win 3.0 */
119 BITMAPCOREHEADER2
= 64, /* OS/2 */
120 BITMAPINFOHEADER
= 40, /* most common */
121 BITMAPINFOHEADER2
= 52, /* Undocummented */
122 BITMAPINFOHEADER3
= 56, /* Undocummented */
123 BITMAPINFOHEADER4
= 108, /* adds color space + gamma - win 95/NT4 */
124 BITMAPINFOHEADER5
= 124, /* adds ICC color profiles win 98+ */
127 static const char *bitmap_header_size_name(uint32_t size
)
130 case BITMAPCOREHEADER
:
131 return "BitmapCoreHeader";
132 case BITMAPCOREHEADER2
:
133 return "BitmapCoreHeader2";
134 case BITMAPINFOHEADER
:
135 return "BitmapInfoHeader";
136 case BITMAPINFOHEADER2
:
137 return "BitmapInfoHeader2";
138 case BITMAPINFOHEADER3
:
139 return "BitmapInfoHeader3";
140 case BITMAPINFOHEADER4
:
141 return "BitmapInfoHeader4";
142 case BITMAPINFOHEADER5
:
143 return "BitmapInfoHeader5";
149 static uint32_t get_palette_size(struct bitmap_info_header
*header
)
152 if (header
->palette_colors
)
153 return header
->palette_colors
;
155 return (1 << header
->bpp
);
158 static int read_bitfields(FILE *f
, struct bitmap_info_header
*header
)
162 ret
= GP_FRead(f
, "L4 L4 L4",
168 GP_DEBUG(1, "Failed to read BITFIELDS");
174 GP_DEBUG(1, "BITFIELDS R=0x%08x, G=0x%08x, B=0x%08x",
175 header
->R_mask
, header
->G_mask
, header
->B_mask
);
180 static int read_alphabitfields(FILE *f
, struct bitmap_info_header
*header
)
184 ret
= GP_FRead(f
, "L4 L4 L4 L4",
191 GP_DEBUG(1, "Failed to read BITFIELDS");
195 GP_DEBUG(1, "BITFILES R=0x%08x, G=0x%08x, B=0x%08x, A=0x%08x",
196 header
->R_mask
, header
->G_mask
, header
->B_mask
,
202 static int read_bitmap_info_header(FILE *f
, struct bitmap_info_header
*header
)
206 if (GP_FRead(f
, "L4 L4 L2 L2 L4 I12 L4 I4",
207 &header
->w
, &header
->h
, &nr_planes
, &header
->bpp
,
208 &header
->compress_type
, &header
->palette_colors
) != 8) {
210 GP_DEBUG(1, "Failed to read bitmap info header");
214 /* This must be 1 according to specs */
216 GP_DEBUG(1, "Number of planes is %"PRId16
" should be 1",
219 GP_DEBUG(2, "Have BMP bitmap size %"PRId32
"x%"PRId32
" %"PRIu16
" "
220 "bpp, %"PRIu32
" pallete colors, '%s' compression",
221 header
->w
, header
->h
, header
->bpp
,
222 get_palette_size(header
),
223 bitmap_compress_name(header
->compress_type
));
225 switch (header
->compress_type
) {
226 case COMPRESS_BITFIELDS
:
227 switch (header
->header_size
) {
228 case BITMAPINFOHEADER
:
229 case BITMAPINFOHEADER2
:
230 return read_bitfields(f
, header
);
232 /* Alpha is default in BITMAPINFOHEADER3 and newer */
233 return read_alphabitfields(f
, header
);
235 /* Only in BITMAPINFOHEADER */
236 case COMPRESS_ALPHABITFIELDS
:
237 if (header
->header_size
!= BITMAPINFOHEADER
)
238 GP_DEBUG(1, "Unexpected ALPHABITFIELDS in %s",
239 bitmap_header_size_name(header
->header_size
));
240 return read_alphabitfields(f
, header
);
246 static int read_bitmap_core_header(FILE *f
, struct bitmap_info_header
*header
)
250 if (fread(buf
, 1, sizeof(buf
), f
) != sizeof(buf
)) {
251 GP_DEBUG(1, "Failed to read bitmap core header");
255 header
->w
= BUF_TO_2(buf
, 0);
256 header
->h
= BUF_TO_2(buf
, 2);
257 header
->bpp
= BUF_TO_2(buf
, 6);
258 header
->compress_type
= COMPRESS_RGB
;
259 header
->palette_colors
= 0;
261 uint16_t nr_planes
= BUF_TO_2(buf
, 4);
263 /* This must be 1 according to specs */
265 GP_DEBUG(1, "Number of planes is %"PRId16
" should be 1",
268 GP_DEBUG(2, "Have BMP bitmap size %"PRId32
"x%"PRId32
" %"PRIu16
" bpp",
269 header
->h
, header
->w
, header
->bpp
);
274 static int read_bitmap_header(FILE *f
, struct bitmap_info_header
*header
)
279 if (fseek(f
, BMP_HEADER_OFFSET
, SEEK_SET
)) {
281 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
282 BMP_HEADER_OFFSET
, strerror(errno
));
286 /* Read info header size, header size determines header type */
287 if (fread(buf
, 1, sizeof(buf
), f
) != sizeof(buf
)) {
288 GP_DEBUG(1, "Failed to read info header size");
292 header
->pixel_offset
= BUF_TO_4(buf
, 0);
293 header
->header_size
= BUF_TO_4(buf
, 4);
295 GP_DEBUG(2, "BMP header type '%s'",
296 bitmap_header_size_name(header
->header_size
));
298 switch (header
->header_size
) {
299 case BITMAPCOREHEADER
:
300 err
= read_bitmap_core_header(f
, header
);
302 case BITMAPCOREHEADER2
:
304 /* The bitmap core header only adds filelds to the end of the header */
305 case BITMAPINFOHEADER
:
306 case BITMAPINFOHEADER2
:
307 case BITMAPINFOHEADER3
:
308 case BITMAPINFOHEADER4
:
309 err
= read_bitmap_info_header(f
, header
);
312 GP_DEBUG(1, "Unknown header type, continuing anyway");
313 err
= read_bitmap_info_header(f
, header
);
321 * Reads palette, the format is R G B X, each one byte.
323 static int read_bitmap_palette(FILE *f
, struct bitmap_info_header
*header
,
326 uint32_t palette_colors
= get_palette_size(header
);
327 uint32_t palette_offset
= header
->header_size
+ 14;
332 switch (header
->header_size
) {
333 case BITMAPCOREHEADER
:
341 GP_DEBUG(2, "Offset to BMP palette is 0x%x (%ubytes) "
342 "pixel size %"PRIu8
"bytes",
343 palette_offset
, palette_offset
, pixel_size
);
345 if (fseek(f
, palette_offset
, SEEK_SET
)) {
347 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
348 BMP_HEADER_OFFSET
, strerror(errno
));
352 for (i
= 0; i
< palette_colors
; i
++) {
355 if (fread(buf
, 1, pixel_size
, f
) != pixel_size
) {
356 GP_DEBUG(1, "Failed to read palette %"PRIu32
, i
);
360 palette
[i
] = GP_Pixel_CREATE_RGB888(buf
[2], buf
[1], buf
[0]);
362 GP_DEBUG(3, "Palette[%"PRIu32
"] = [0x%02x, 0x%02x, 0x%02x]", i
,
363 GP_Pixel_GET_R_RGB888(palette
[i
]),
364 GP_Pixel_GET_G_RGB888(palette
[i
]),
365 GP_Pixel_GET_B_RGB888(palette
[i
]));
371 static int seek_pixels_offset(struct bitmap_info_header
*header
, FILE *f
)
375 GP_DEBUG(2, "Offset to BMP pixels is 0x%x (%ubytes)",
376 header
->pixel_offset
, header
->pixel_offset
);
378 if (fseek(f
, header
->pixel_offset
, SEEK_SET
)) {
380 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
381 header
->pixel_offset
, strerror(errno
));
388 static GP_PixelType
match_pixel_type(struct bitmap_info_header
*header
)
390 /* handle bitfields */
391 switch (header
->compress_type
) {
392 case COMPRESS_BITFIELDS
:
393 case COMPRESS_ALPHABITFIELDS
:
394 return GP_PixelRGBMatch(header
->R_mask
, header
->G_mask
,
395 header
->B_mask
, header
->A_mask
,
399 switch (header
->bpp
) {
400 /* palette formats -> expanded to RGB888 */
407 return GP_PIXEL_RGB888
;
409 #ifdef GP_PIXEL_RGB555
410 //TODO: May have 1-bit ALPHA channel for BITMAPINFOHEADER3 and newer
411 return GP_PIXEL_RGB555
;
413 //TODO: Conversion to RGB888?
417 //TODO: May have ALPHA channel for BITMAPINFOHEADER3 and newer
418 return GP_PIXEL_xRGB8888
;
421 return GP_PIXEL_UNKNOWN
;
425 * Returns four byte aligned row size for palette formats.
427 static uint32_t bitmap_row_size(struct bitmap_info_header
*header
)
429 uint32_t row_size
= 0;
431 /* align width to whole bytes */
432 switch (header
->bpp
) {
434 row_size
= header
->w
/ 8 + !!(header
->w
%8);
437 row_size
= header
->w
/ 4 + !!(header
->w
%4);
440 row_size
= header
->w
/ 2 + !!(header
->w
%2);
443 row_size
= header
->w
;
447 /* align row_size to four byte boundary */
448 switch (row_size
% 4) {
459 GP_DEBUG(2, "bpp = %"PRIu16
", width = %"PRId32
", row_size = %"PRIu32
,
460 header
->bpp
, header
->w
, row_size
);
465 static uint8_t get_idx(struct bitmap_info_header
*header
,
466 uint8_t row
[], int32_t x
)
468 switch (header
->bpp
) {
470 return !!(row
[x
/8] & (1<<(7 - x
%8)));
472 return (row
[x
/4] >> (2*(3 - x
%4))) & 0x03;
474 return (row
[x
/2] >> (4*(!(x
%2)))) & 0x0f;
483 #include "GP_BMP_RLE.h"
485 static int read_palette(FILE *f
, struct bitmap_info_header
*header
,
486 GP_Context
*context
, GP_ProgressCallback
*callback
)
488 uint32_t palette_size
= get_palette_size(header
);
489 GP_Pixel palette
[get_palette_size(header
)];
492 if ((err
= read_bitmap_palette(f
, header
, palette
)))
495 if ((err
= seek_pixels_offset(header
, f
)))
498 uint32_t row_size
= bitmap_row_size(header
);
501 for (y
= 0; y
< GP_ABS(header
->h
); y
++) {
503 uint8_t row
[row_size
];
505 if (fread(row
, 1, row_size
, f
) != row_size
) {
506 GP_DEBUG(1, "Failed to read row %"PRId32
, y
);
510 for (x
= 0; x
< header
->w
; x
++) {
511 uint8_t idx
= get_idx(header
, row
, x
);
514 if (idx
>= palette_size
) {
515 GP_DEBUG(1, "Index out of palette, ignoring");
526 ry
= GP_ABS(header
->h
) - 1 - y
;
528 GP_PutPixel_Raw_24BPP(context
, x
, ry
, p
);
531 if (GP_ProgressCallbackReport(callback
, y
,
532 context
->h
, context
->w
)) {
533 GP_DEBUG(1, "Operation aborted");
538 GP_ProgressCallbackDone(callback
);
542 static int read_bitfields_or_rgb(FILE *f
, struct bitmap_info_header
*header
,
544 GP_ProgressCallback
*callback
)
546 uint32_t row_size
= header
->w
* (header
->bpp
/ 8);
550 if ((err
= seek_pixels_offset(header
, f
)))
553 for (y
= 0; y
< GP_ABS(header
->h
); y
++) {
559 ry
= GP_ABS(header
->h
) - 1 - y
;
561 uint8_t *row
= GP_PIXEL_ADDR(context
, 0, ry
);
563 if (fread(row
, 1, row_size
, f
) != row_size
) {
564 GP_DEBUG(1, "Failed to read row %"PRId32
, y
);
568 /* Rows are four byte aligned */
569 switch (row_size
% 4) {
580 if (GP_ProgressCallbackReport(callback
, y
,
581 context
->h
, context
->w
)) {
582 GP_DEBUG(1, "Operation aborted");
587 GP_ProgressCallbackDone(callback
);
591 static int read_bitmap_pixels(FILE *f
, struct bitmap_info_header
*header
,
592 GP_Context
*context
, GP_ProgressCallback
*callback
)
594 if (header
->compress_type
== COMPRESS_RLE8
)
595 return read_RLE8(f
, header
, context
, callback
);
597 switch (header
->bpp
) {
599 /* I haven't been able to locate 2bpp palette bmp file => not tested */
603 return read_palette(f
, header
, context
, callback
);
607 return read_bitfields_or_rgb(f
, header
, context
, callback
);
613 int GP_MatchBMP(const void *buf
)
615 return !memcmp(buf
, "BM", 2);
618 int GP_OpenBMP(const char *src_path
, FILE **f
,
619 GP_Size
*w
, GP_Size
*h
, GP_PixelType
*pixel_type
)
623 *f
= fopen(src_path
, "rb");
627 GP_DEBUG(1, "Failed to open '%s' : %s",
628 src_path
, strerror(errno
));
635 if (ch1
!= 'B' || ch2
!= 'M') {
636 GP_DEBUG(1, "Unexpected bitmap header 0x%02x (%c) 0x%02x (%c)",
637 ch1
, isascii(ch1
) ? ch1
: ' ',
638 ch2
, isascii(ch2
) ? ch2
: ' ');
643 if (w
!= NULL
|| h
!= NULL
|| pixel_type
!= NULL
) {
644 struct bitmap_info_header header
;
646 if ((err
= read_bitmap_header(*f
, &header
)))
655 if (pixel_type
!= NULL
)
656 *pixel_type
= match_pixel_type(&header
);
667 GP_Context
*GP_ReadBMP(FILE *f
, GP_ProgressCallback
*callback
)
669 struct bitmap_info_header header
;
670 GP_PixelType pixel_type
;
674 if ((err
= read_bitmap_header(f
, &header
)))
677 if (header
.w
<= 0 || header
.h
== 0) {
678 GP_WARN("Width and/or Height is not > 0");
683 switch (header
.compress_type
) {
685 case COMPRESS_BITFIELDS
:
686 case COMPRESS_ALPHABITFIELDS
:
690 GP_DEBUG(2, "Unknown/Unimplemented compression type");
695 if ((pixel_type
= match_pixel_type(&header
)) == GP_PIXEL_UNKNOWN
) {
696 GP_DEBUG(2, "Unknown pixel type");
701 context
= GP_ContextAlloc(header
.w
, GP_ABS(header
.h
), pixel_type
);
703 if (context
== NULL
) {
708 if ((err
= read_bitmap_pixels(f
, &header
, context
, callback
)))
713 GP_ContextFree(context
);
719 GP_Context
*GP_LoadBMP(const char *src_path
, GP_ProgressCallback
*callback
)
724 if (GP_OpenBMP(src_path
, &f
, NULL
, NULL
, NULL
))
727 res
= GP_ReadBMP(f
, callback
);
734 * Rows in bmp are four byte aligned.
736 static uint32_t bmp_align_row_size(uint32_t width_bits
)
738 uint32_t bytes
= (width_bits
/8) + !!(width_bits
%8);
741 bytes
+= 4 - bytes
%4;
747 * For uncompressd images
749 static uint32_t bmp_count_bitmap_size(struct bitmap_info_header
*header
)
751 return header
->h
* bmp_align_row_size(header
->bpp
* header
->w
);
754 static int bmp_write_header(struct bitmap_info_header
*header
, FILE *f
)
756 uint32_t bitmap_size
= bmp_count_bitmap_size(header
);
757 uint32_t file_size
= bitmap_size
+ header
->header_size
+ 14;
760 if (GP_FWrite(f
, "A2 L4 0x00 0x00 0x00 0x00 L4", "BM",
761 file_size
, header
->pixel_offset
) != 7)
764 /* Bitmap Info Header */
765 if (GP_FWrite(f
, "L4 L4 L4 L2 L2 L4 L4 L4 L4 L4 L4",
766 header
->header_size
, header
->w
, header
->h
, 1,
767 header
->bpp
, header
->compress_type
, bitmap_size
, 0, 0,
768 header
->palette_colors
, 0) != 11)
774 static GP_PixelType out_pixel_types
[] = {
779 static int bmp_fill_header(const GP_Context
*src
, struct bitmap_info_header
*header
)
781 GP_PixelType out_pix
;
783 out_pix
= GP_LineConvertible(src
->pixel_type
, out_pixel_types
);
785 if (out_pix
== GP_PIXEL_UNKNOWN
) {
786 GP_DEBUG(1, "Unsupported pixel type %s",
787 GP_PixelTypeName(src
->pixel_type
));
795 /* Most common 40 bytes Info Header */
796 header
->header_size
= BITMAPINFOHEADER
;
798 /* No compression or palette */
799 header
->palette_colors
= 0;
800 header
->compress_type
= COMPRESS_RGB
;
802 /* image data follows the header */
803 header
->pixel_offset
= header
->header_size
+ 14;
808 static int bmp_write_data(FILE *f
, const GP_Context
*src
, GP_ProgressCallback
*callback
)
811 uint32_t padd_len
= 0;
813 uint8_t tmp
[3 * src
->w
];
814 GP_LineConvert Convert
;
816 Convert
= GP_LineConvertGet(src
->pixel_type
, GP_PIXEL_RGB888
);
818 if (src
->bytes_per_row
%4)
819 padd_len
= 4 - src
->bytes_per_row
%4;
821 for (y
= src
->h
- 1; y
>= 0; y
--) {
822 void *row
= GP_PIXEL_ADDR(src
, 0, y
);
824 if (src
->pixel_type
!= GP_PIXEL_RGB888
) {
825 Convert(row
, tmp
, src
->w
);
829 if (fwrite(row
, src
->bytes_per_row
, 1, f
) != 1)
834 if (fwrite(padd
, padd_len
, 1, f
) != 1)
837 if (GP_ProgressCallbackReport(callback
, y
, src
->h
, src
->w
)) {
838 GP_DEBUG(1, "Operation aborted");
846 int GP_SaveBMP(const GP_Context
*src
, const char *dst_path
,
847 GP_ProgressCallback
*callback
)
849 struct bitmap_info_header header
;
853 GP_DEBUG(1, "Saving BMP Image '%s'", dst_path
);
855 if ((err
= bmp_fill_header(src
, &header
)))
858 f
= fopen(dst_path
, "wb");
862 GP_DEBUG(1, "Failed to open '%s' for writing: %s",
863 dst_path
, strerror(errno
));
867 if ((err
= bmp_write_header(&header
, f
)))
870 if ((err
= bmp_write_data(f
, src
, callback
)))
875 GP_DEBUG(1, "Failed to close file '%s': %s",
876 dst_path
, strerror(errno
));
880 GP_ProgressCallbackDone(callback
);