build: Add --mandir to configure and install.mk
[gfxprim/pasky.git] / libs / loaders / GP_BMP.c
blobd7ea61fd14fea8015f06027a826e22c05f99a211
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
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. *
8 * *
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. *
13 * *
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 *
18 * *
19 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 BMP loader and writer.
27 Thanks Wikipedia for the format specification.
31 #include <stdint.h>
32 #include <inttypes.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <stdio.h>
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)
64 uint32_t header_size;
67 * Image size in pixels.
68 * If h is negative image is top-down (bottom-up is default)
70 int32_t w;
71 int32_t h;
73 uint16_t bpp;
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
82 uint32_t R_mask;
83 uint32_t G_mask;
84 uint32_t B_mask;
85 uint32_t A_mask;
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[] = {
100 "RGB",
101 "RLE8",
102 "RLE4",
103 "BITFIELDS",
104 "JPEG",
105 "PNG",
106 "ALPHABITFIELDS",
109 static const char *bitmap_compress_name(uint32_t compress)
111 if (compress >= COMPRESS_MAX)
112 return "Unknown";
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)
129 switch (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";
146 return "Unknown";
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)
160 int ret;
162 ret = GP_FRead(f, "L4 L4 L4",
163 &header->R_mask,
164 &header->G_mask,
165 &header->B_mask);
167 if (ret != 3) {
168 GP_DEBUG(1, "Failed to read BITFIELDS");
169 return EIO;
172 header->A_mask = 0;
174 GP_DEBUG(1, "BITFIELDS R=0x%08x, G=0x%08x, B=0x%08x",
175 header->R_mask, header->G_mask, header->B_mask);
177 return 0;
180 static int read_alphabitfields(FILE *f, struct bitmap_info_header *header)
182 int ret;
184 ret = GP_FRead(f, "L4 L4 L4 L4",
185 &header->R_mask,
186 &header->G_mask,
187 &header->B_mask,
188 &header->A_mask);
190 if (ret != 4) {
191 GP_DEBUG(1, "Failed to read BITFIELDS");
192 return EIO;
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,
197 header->A_mask);
199 return 0;
202 static int read_bitmap_info_header(FILE *f, struct bitmap_info_header *header)
204 uint16_t nr_planes;
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");
211 return EIO;
214 /* This must be 1 according to specs */
215 if (nr_planes != 1)
216 GP_DEBUG(1, "Number of planes is %"PRId16" should be 1",
217 nr_planes);
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);
231 default:
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);
243 return 0;
246 static int read_bitmap_core_header(FILE *f, struct bitmap_info_header *header)
248 uint8_t buf[12];
250 if (fread(buf, 1, sizeof(buf), f) != sizeof(buf)) {
251 GP_DEBUG(1, "Failed to read bitmap core header");
252 return EIO;
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 */
264 if (nr_planes != 1)
265 GP_DEBUG(1, "Number of planes is %"PRId16" should be 1",
266 nr_planes);
268 GP_DEBUG(2, "Have BMP bitmap size %"PRId32"x%"PRId32" %"PRIu16" bpp",
269 header->h, header->w, header->bpp);
271 return 0;
274 static int read_bitmap_header(FILE *f, struct bitmap_info_header *header)
276 uint8_t buf[8];
277 int err;
279 if (fseek(f, BMP_HEADER_OFFSET, SEEK_SET)) {
280 err = errno;
281 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
282 BMP_HEADER_OFFSET, strerror(errno));
283 return err;
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");
289 return EIO;
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);
301 break;
302 case BITMAPCOREHEADER2:
303 return ENOSYS;
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);
310 break;
311 default:
312 GP_DEBUG(1, "Unknown header type, continuing anyway");
313 err = read_bitmap_info_header(f, header);
314 break;
317 return err;
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,
324 GP_Pixel *palette)
326 uint32_t palette_colors = get_palette_size(header);
327 uint32_t palette_offset = header->header_size + 14;
328 uint8_t pixel_size;
329 uint32_t i;
330 int err;
332 switch (header->header_size) {
333 case BITMAPCOREHEADER:
334 pixel_size = 3;
335 break;
336 default:
337 pixel_size = 4;
338 break;
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)) {
346 err = errno;
347 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
348 BMP_HEADER_OFFSET, strerror(errno));
349 return err;
352 for (i = 0; i < palette_colors; i++) {
353 uint8_t buf[4];
355 if (fread(buf, 1, pixel_size, f) != pixel_size) {
356 GP_DEBUG(1, "Failed to read palette %"PRIu32, i);
357 return EIO;
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]));
368 return 0;
371 static int seek_pixels_offset(struct bitmap_info_header *header, FILE *f)
373 int err;
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)) {
379 err = errno;
380 GP_DEBUG(1, "fseek(f, 0x%02x) failed: '%s'",
381 header->pixel_offset, strerror(errno));
382 return err;
385 return 0;
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,
396 header->bpp);
399 switch (header->bpp) {
400 /* palette formats -> expanded to RGB888 */
401 case 1:
402 case 2:
403 case 4:
404 case 8:
405 /* RGB888 */
406 case 24:
407 return GP_PIXEL_RGB888;
408 case 16:
409 #ifdef GP_PIXEL_RGB555
410 //TODO: May have 1-bit ALPHA channel for BITMAPINFOHEADER3 and newer
411 return GP_PIXEL_RGB555;
412 #else
413 //TODO: Conversion to RGB888?
414 break;
415 #endif
416 case 32:
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) {
433 case 1:
434 row_size = header->w / 8 + !!(header->w%8);
435 break;
436 case 2:
437 row_size = header->w / 4 + !!(header->w%4);
438 break;
439 case 4:
440 row_size = header->w / 2 + !!(header->w%2);
441 break;
442 case 8:
443 row_size = header->w;
444 break;
447 /* align row_size to four byte boundary */
448 switch (row_size % 4) {
449 case 1:
450 row_size++;
451 case 2:
452 row_size++;
453 case 3:
454 row_size++;
455 case 0:
456 break;
459 GP_DEBUG(2, "bpp = %"PRIu16", width = %"PRId32", row_size = %"PRIu32,
460 header->bpp, header->w, row_size);
462 return row_size;
465 static uint8_t get_idx(struct bitmap_info_header *header,
466 uint8_t row[], int32_t x)
468 switch (header->bpp) {
469 case 1:
470 return !!(row[x/8] & (1<<(7 - x%8)));
471 case 2:
472 return (row[x/4] >> (2*(3 - x%4))) & 0x03;
473 case 4:
474 return (row[x/2] >> (4*(!(x%2)))) & 0x0f;
475 break;
476 case 8:
477 return row[x];
480 return 0;
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)];
490 int err;
492 if ((err = read_bitmap_palette(f, header, palette)))
493 return err;
495 if ((err = seek_pixels_offset(header, f)))
496 return err;
498 uint32_t row_size = bitmap_row_size(header);
499 int32_t y;
501 for (y = 0; y < GP_ABS(header->h); y++) {
502 int32_t x;
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);
507 return EIO;
510 for (x = 0; x < header->w; x++) {
511 uint8_t idx = get_idx(header, row, x);
512 GP_Pixel p;
514 if (idx >= palette_size) {
515 GP_DEBUG(1, "Index out of palette, ignoring");
516 p = 0;
517 } else {
518 p = palette[idx];
521 int32_t ry;
523 if (header->h < 0)
524 ry = y;
525 else
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");
534 return ECANCELED;
538 GP_ProgressCallbackDone(callback);
539 return 0;
542 static int read_bitfields_or_rgb(FILE *f, struct bitmap_info_header *header,
543 GP_Context *context,
544 GP_ProgressCallback *callback)
546 uint32_t row_size = header->w * (header->bpp / 8);
547 int32_t y;
548 int err;
550 if ((err = seek_pixels_offset(header, f)))
551 return err;
553 for (y = 0; y < GP_ABS(header->h); y++) {
554 int32_t ry;
556 if (header->h < 0)
557 ry = y;
558 else
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);
565 return EIO;
568 /* Rows are four byte aligned */
569 switch (row_size % 4) {
570 case 1:
571 fgetc(f);
572 case 2:
573 fgetc(f);
574 case 3:
575 fgetc(f);
576 case 0:
577 break;
580 if (GP_ProgressCallbackReport(callback, y,
581 context->h, context->w)) {
582 GP_DEBUG(1, "Operation aborted");
583 return ECANCELED;
587 GP_ProgressCallbackDone(callback);
588 return 0;
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) {
598 case 1:
599 /* I haven't been able to locate 2bpp palette bmp file => not tested */
600 case 2:
601 case 4:
602 case 8:
603 return read_palette(f, header, context, callback);
604 case 16:
605 case 24:
606 case 32:
607 return read_bitfields_or_rgb(f, header, context, callback);
610 return ENOSYS;
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)
621 int err;
623 *f = fopen(src_path, "rb");
625 if (*f == NULL) {
626 err = errno;
627 GP_DEBUG(1, "Failed to open '%s' : %s",
628 src_path, strerror(errno));
629 goto err2;
632 int ch1 = fgetc(*f);
633 int ch2 = fgetc(*f);
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 : ' ');
639 err = EIO;
640 goto err1;
643 if (w != NULL || h != NULL || pixel_type != NULL) {
644 struct bitmap_info_header header;
646 if ((err = read_bitmap_header(*f, &header)))
647 goto err1;
649 if (w != NULL)
650 *w = header.w;
652 if (h != NULL)
653 *h = header.h;
655 if (pixel_type != NULL)
656 *pixel_type = match_pixel_type(&header);
659 return 0;
660 err1:
661 fclose(*f);
662 err2:
663 errno = err;
664 return 1;
667 GP_Context *GP_ReadBMP(FILE *f, GP_ProgressCallback *callback)
669 struct bitmap_info_header header;
670 GP_PixelType pixel_type;
671 GP_Context *context;
672 int err;
674 if ((err = read_bitmap_header(f, &header)))
675 goto err1;
677 if (header.w <= 0 || header.h == 0) {
678 GP_WARN("Width and/or Height is not > 0");
679 err = EIO;
680 goto err1;
683 switch (header.compress_type) {
684 case COMPRESS_RGB:
685 case COMPRESS_BITFIELDS:
686 case COMPRESS_ALPHABITFIELDS:
687 case COMPRESS_RLE8:
688 break;
689 default:
690 GP_DEBUG(2, "Unknown/Unimplemented compression type");
691 err = ENOSYS;
692 goto err1;
695 if ((pixel_type = match_pixel_type(&header)) == GP_PIXEL_UNKNOWN) {
696 GP_DEBUG(2, "Unknown pixel type");
697 err = ENOSYS;
698 goto err1;
701 context = GP_ContextAlloc(header.w, GP_ABS(header.h), pixel_type);
703 if (context == NULL) {
704 err = ENOMEM;
705 goto err1;
708 if ((err = read_bitmap_pixels(f, &header, context, callback)))
709 goto err2;
711 return context;
712 err2:
713 GP_ContextFree(context);
714 err1:
715 errno = err;
716 return NULL;
719 GP_Context *GP_LoadBMP(const char *src_path, GP_ProgressCallback *callback)
721 FILE *f;
722 GP_Context *res;
724 if (GP_OpenBMP(src_path, &f, NULL, NULL, NULL))
725 return NULL;
727 res = GP_ReadBMP(f, callback);
728 fclose(f);
730 return res;
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);
740 if (bytes%4)
741 bytes += 4 - bytes%4;
743 return bytes;
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;
759 /* Bitmap Header */
760 if (GP_FWrite(f, "A2 L4 0x00 0x00 0x00 0x00 L4", "BM",
761 file_size, header->pixel_offset) != 7)
762 return EIO;
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)
769 return EIO;
771 return 0;
774 static GP_PixelType out_pixel_types[] = {
775 GP_PIXEL_RGB888,
776 GP_PIXEL_UNKNOWN,
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));
788 return ENOSYS;
791 header->bpp = 24;
792 header->w = src->w;
793 header->h = src->h;
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;
805 return 0;
808 static int bmp_write_data(FILE *f, const GP_Context *src, GP_ProgressCallback *callback)
810 int y;
811 uint32_t padd_len = 0;
812 char padd[3] = {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);
826 row = tmp;
829 if (fwrite(row, src->bytes_per_row, 1, f) != 1)
830 return EIO;
832 /* write padding */
833 if (padd_len)
834 if (fwrite(padd, padd_len, 1, f) != 1)
835 return EIO;
837 if (GP_ProgressCallbackReport(callback, y, src->h, src->w)) {
838 GP_DEBUG(1, "Operation aborted");
839 return ECANCELED;
843 return 0;
846 int GP_SaveBMP(const GP_Context *src, const char *dst_path,
847 GP_ProgressCallback *callback)
849 struct bitmap_info_header header;
850 FILE *f;
851 int err;
853 GP_DEBUG(1, "Saving BMP Image '%s'", dst_path);
855 if ((err = bmp_fill_header(src, &header)))
856 goto err0;
858 f = fopen(dst_path, "wb");
860 if (f == NULL) {
861 err = errno;
862 GP_DEBUG(1, "Failed to open '%s' for writing: %s",
863 dst_path, strerror(errno));
864 goto err0;
867 if ((err = bmp_write_header(&header, f)))
868 goto err1;
870 if ((err = bmp_write_data(f, src, callback)))
871 goto err1;
873 if (fclose(f)) {
874 err = errno;
875 GP_DEBUG(1, "Failed to close file '%s': %s",
876 dst_path, strerror(errno));
877 goto err1;
880 GP_ProgressCallbackDone(callback);
882 return 0;
883 err1:
884 fclose(f);
885 err0:
886 unlink(dst_path);
887 errno = err;
888 return 1;