loaders: JPG: Fix bussy loop on corrupted file.
[gfxprim.git] / libs / loaders / GP_PNM.c
blob49fe18d5b6ff11c3d0f31b58849ca65e20da5504
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-2014 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
25 PNM portable bitmap header
26 --------------------------
28 Format:
30 a magic number value of 'P' and one of
31 '1' - PBM Bitmap ASCII
32 '2' - PGM Gray ASCII
33 '3' - PPM RGB ASCII
34 '4' - PBM Bitmap Binary
35 '5' - PGM Gray Binary
36 '6' - PPM RGB Binary
37 whitespace (blanks, TABs, CRs, LFs).
38 ascii width
39 whitespace
40 ascii height
41 whitespace
42 maximal value (interval is 0 ... max) (not applicable for PBM)
43 width * height ascii or binary values
45 lines starting with '#' are comments to the end of line
49 #include <stdio.h>
50 #include <stdint.h>
51 #include <inttypes.h>
52 #include <errno.h>
53 #include <ctype.h>
55 #include <string.h>
57 #include "core/GP_Debug.h"
58 #include "core/GP_Pixmap.h"
59 #include "core/GP_GetPutPixel.h"
60 #include "loaders/GP_LineConvert.h"
62 #include "loaders/GP_PNM.h"
64 struct pnm_header {
65 char magic;
66 uint32_t w;
67 uint32_t h;
68 uint32_t depth;
71 static char *pnm_names[] = {
72 "ASCII encoded PBM",
73 "ASCII encoded PGM",
74 "ASCII encoded PPM",
75 "Binary encoded PBM",
76 "Binary encoded PGM",
77 "Binary encoded PPM",
80 static const char *pnm_magic_name(char magic)
82 return pnm_names[magic - '1'];
86 * P1 == ascii
87 * P4 == rawbits
89 static int is_bitmap(char magic)
91 return magic == '1' || magic == '4';
94 int GP_MatchPBM(const void *buf)
96 const char *b = buf;
98 return b[0] == 'P' && is_bitmap(b[1]);
102 * P2 == ascii
103 * P5 == rawbits
105 static int is_graymap(char magic)
107 return magic == '2' || magic == '5';
110 int GP_MatchPGM(const void *buf)
112 const char *b = buf;
114 return b[0] == 'P' && is_graymap(b[1]);
118 * P3 == ascii
119 * P6 == rawbits
121 static int is_pixmap(char magic)
123 return magic == '3' || magic == '6';
126 int GP_MatchPPM(const void *buf)
128 const char *b = buf;
130 return b[0] == 'P' && is_pixmap(b[1]);
133 static int magic_is_valid(char magic)
135 switch (magic) {
136 case '1' ... '9':
137 return 1;
138 default:
139 return 0;
143 int GP_MatchPNM(const void *buf)
145 const char *b = buf;
147 return b[0] == 'P' && magic_is_valid(b[1]);
151 * Header parser
153 enum state {
154 S_START,
155 S_COMMENT,
156 S_INT,
160 * Simple buffer implementation on the top of the GP_IO
162 struct buf {
163 uint8_t buf[100];
164 unsigned int buf_end;
165 unsigned int buf_pos;
166 GP_IO *io;
169 #define DECLARE_BUFFER(name, bio) \
170 struct buf name = {.buf_end = 0, .buf_pos = 0, .io = bio}
172 static int getb(struct buf *buf)
174 int ret;
176 if (buf->buf_pos < buf->buf_end)
177 return buf->buf[buf->buf_pos++];
179 ret = GP_IORead(buf->io, buf->buf, sizeof(buf->buf));
181 if (ret <= 0)
182 return EOF;
184 buf->buf_pos = 1;
185 buf->buf_end = ret;
187 return buf->buf[0];
190 static void ungetb(struct buf *buf, uint8_t byte)
192 buf->buf[--buf->buf_pos] = byte;
195 static int fillb(struct buf *buf, void *ptr, size_t size)
197 unsigned int buffered = buf->buf_end - buf->buf_pos;
199 if (buffered) {
200 unsigned int to_copy = GP_MIN(buffered, size);
201 memcpy(ptr, buf->buf + buf->buf_pos, to_copy);
202 buf->buf_pos += to_copy;
205 //TODO: refill buffer if request < 128
206 if (size > buffered) {
207 return GP_IOFill(buf->io, (char*)ptr + buffered,
208 size - buffered);
211 return 0;
214 static int load_header(struct buf *buf, struct pnm_header *header)
216 int h1, h2, c, state = S_START, val = 0, i = 0, err;
218 h1 = getb(buf);
219 h2 = getb(buf);
221 if (h1 == EOF || h2 == EOF) {
222 GP_DEBUG(1, "Failed to read header");
223 return EIO;
226 if (h1 != 'P' || !magic_is_valid(h2)) {
227 GP_DEBUG(1, "Invalid magic 0x%02x 0x%02x (%c %c)",
228 h1, h2, isprint(h1) ? h1 : ' ', isprint(h2) ? h2 : ' ');
229 return EINVAL;
232 header->magic = h2;
233 header->depth = 1;
234 header->h = 0;
235 header->w = 0;
237 for (;;) {
238 c = getb(buf);
240 if (c == EOF) {
241 err = EIO;
242 goto err;
245 switch (state) {
246 case S_START:
247 switch (c) {
248 case '#':
249 state = S_COMMENT;
250 break;
251 case '0' ... '9':
252 val = c - '0';
253 state = S_INT;
254 break;
255 case '\n':
256 case '\t':
257 case ' ':
258 case '\r':
259 break;
260 default:
261 GP_WARN("Ignoring character 0x%02x (%c)",
262 c, isprint(c) ? c : ' ');
264 break;
265 case S_COMMENT:
266 if (c == '\n')
267 state = S_START;
268 break;
269 case S_INT:
270 switch (c) {
271 case '0' ... '9':
272 val *= 10;
273 val += c - '0';
274 break;
275 default:
276 switch (i++) {
277 case 0:
278 header->w = val;
279 break;
280 case 1:
281 header->h = val;
282 if (is_bitmap(header->magic))
283 goto out;
284 break;
285 case 2:
286 header->depth = val;
287 goto out;
289 ungetb(buf, c);
290 state = S_START;
291 break;
293 break;
297 out:
298 GP_DEBUG(1, "Have header P%c (%s) %"PRIu32"x%"PRIu32" depth=%"PRIu32,
299 header->magic, pnm_magic_name(header->magic),
300 header->w, header->h, header->depth);
301 return 0;
302 err:
303 GP_DEBUG(1, "Unexpected end of file when reading header");
304 return err;
308 * ASCII data parser
310 static int get_ascii_int(struct buf *buf, int *val)
312 int c, in_number = 0;
313 *val = 0;
315 for (;;) {
316 c = getb(buf);
318 switch (c) {
319 case EOF:
320 if (!in_number) {
321 GP_DEBUG(1, "Unexpected end of file");
322 return EIO;
325 return 0;
326 case '0' ... '9':
327 *val *= 10;
328 *val += c - '0';
329 in_number = 1;
330 break;
331 case '\n':
332 case '\t':
333 case ' ':
334 case '\r':
335 if (in_number)
336 return 0;
337 break;
338 default:
339 if (in_number)
340 return 0;
341 else
342 GP_WARN("Ignoring unexpected character 0x%02x %c",
343 c, isprint(c) ? c : ' ');
349 * Five times faster than printf("%u", byte)
351 static inline int write_ascii_byte(GP_IO *io, uint8_t byte)
353 if (byte >= 100)
354 GP_IOPutC(io, '0' + byte/100);
356 if (byte >= 10)
357 GP_IOPutC(io, '0' + (byte%100)/10);
359 GP_IOPutC(io, '0' + (byte%10));
361 return GP_IOPutC(io, ' ');
365 * The PBM ASCII has the values inverted
367 static int load_ascii_g1_inv(struct buf *buf, GP_Pixmap *pixmap,
368 GP_ProgressCallback *cb)
370 uint32_t x, y;
371 int val, err;
373 for (y = 0; y < pixmap->h; y++) {
374 for (x = 0; x < pixmap->w; x++) {
376 if ((err = get_ascii_int(buf, &val)))
377 return err;
379 GP_PutPixel_Raw_1BPP_LE(pixmap, x, y, !val);
382 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
383 GP_DEBUG(1, "Operation aborted");
384 return ECANCELED;
388 GP_ProgressCallbackDone(cb);
389 return 0;
392 //TODO: This is temporary till blit works with bitendian
393 #include "core/GP_BitSwap.h"
395 static int load_raw_g1_inv(struct buf *buf, GP_Pixmap *pixmap,
396 GP_ProgressCallback *cb)
398 uint32_t x, y;
399 uint8_t *addr;
400 int val;
402 for (y = 0; y < pixmap->h; y++) {
403 for (x = 0; x < pixmap->w; x+=8) {
405 if ((val = getb(buf)) == EOF)
406 return EIO;
408 addr = GP_PIXEL_ADDR(pixmap, x, y);
409 *addr = ~GP_BIT_SWAP_B1(val);
412 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
413 GP_DEBUG(1, "Operation aborted");
414 return ECANCELED;
418 GP_ProgressCallbackDone(cb);
419 return 0;
422 static int load_ascii_g1(struct buf *buf, GP_Pixmap *pixmap,
423 GP_ProgressCallback *cb)
425 uint32_t x, y;
426 int val, err;
428 for (y = 0; y < pixmap->h; y++) {
429 for (x = 0; x < pixmap->w; x++) {
431 if ((err = get_ascii_int(buf, &val)))
432 return err;
434 if (val > 1) {
435 GP_WARN("Value too large for 1BPP (%i)", val);
436 val = 1;
439 GP_PutPixel_Raw_1BPP_LE(pixmap, x, y, val);
442 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
443 GP_DEBUG(1, "Operation aborted");
444 return ECANCELED;
448 GP_ProgressCallbackDone(cb);
449 return 0;
452 static int load_ascii_g2(struct buf *buf, GP_Pixmap *pixmap,
453 GP_ProgressCallback *cb)
455 uint32_t x, y;
456 int val, err;
458 for (y = 0; y < pixmap->h; y++) {
459 for (x = 0; x < pixmap->w; x++) {
461 if ((err = get_ascii_int(buf, &val)))
462 return err;
464 if (val > 3) {
465 GP_WARN("Value too large for 2BPP (%i)", val);
466 val = 3;
469 GP_PutPixel_Raw_2BPP_LE(pixmap, x, y, val);
472 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
473 GP_DEBUG(1, "Operation aborted");
474 return ECANCELED;
478 GP_ProgressCallbackDone(cb);
479 return 0;
482 static int load_ascii_g4(struct buf *buf, GP_Pixmap *pixmap,
483 GP_ProgressCallback *cb)
485 uint32_t x, y;
486 int val, err;
488 for (y = 0; y < pixmap->h; y++) {
489 for (x = 0; x < pixmap->w; x++) {
491 if ((err = get_ascii_int(buf, &val)))
492 return err;
494 if (val > 15) {
495 GP_WARN("Value too large for 4BPP (%i)", val);
496 val = 15;
499 GP_PutPixel_Raw_4BPP_LE(pixmap, x, y, val);
502 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
503 GP_DEBUG(1, "Operation aborted");
504 return ECANCELED;
508 GP_ProgressCallbackDone(cb);
509 return 0;
512 static int load_ascii_g8(struct buf *buf, GP_Pixmap *pixmap,
513 GP_ProgressCallback *cb)
515 uint32_t x, y;
516 int val, err;
518 for (y = 0; y < pixmap->h; y++) {
519 for (x = 0; x < pixmap->w; x++) {
521 if ((err = get_ascii_int(buf, &val)))
522 return err;
524 if (val > 255) {
525 GP_WARN("Value too large for 8BPP (%i)", val);
526 val = 255;
529 GP_PutPixel_Raw_8BPP(pixmap, x, y, val);
532 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
533 GP_DEBUG(1, "Operation aborted");
534 return ECANCELED;
538 GP_ProgressCallbackDone(cb);
539 return 0;
542 static int load_bin_g8(struct buf *buf, GP_Pixmap *pixmap,
543 GP_ProgressCallback *cb)
545 uint32_t y;
547 for (y = 0; y < pixmap->h; y++) {
548 uint8_t *addr = GP_PIXEL_ADDR(pixmap, 0, y);
550 if (fillb(buf, addr, pixmap->w))
551 return errno;
553 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
554 GP_DEBUG(1, "Operation aborted");
555 return ECANCELED;
559 GP_ProgressCallbackDone(cb);
560 return 0;
563 static int load_ascii_rgb888(struct buf *buf, GP_Pixmap *pixmap,
564 GP_ProgressCallback *cb)
566 uint32_t x, y;
567 int r, g, b, err;
569 for (y = 0; y < pixmap->h; y++) {
570 for (x = 0; x < pixmap->w; x++) {
572 if ((err = get_ascii_int(buf, &r)))
573 return err;
575 if (r > 255) {
576 GP_WARN("R value too large (%i)", r);
577 r = 255;
580 if ((err = get_ascii_int(buf, &g)))
581 return err;
583 if (g > 255) {
584 GP_WARN("G value too large (%i)", r);
585 g = 255;
588 if ((err = get_ascii_int(buf, &b)))
589 return err;
591 if (b > 255) {
592 GP_WARN("G value too large (%i)", r);
593 b = 255;
596 GP_PutPixel_Raw_24BPP(pixmap, x, y,
597 GP_Pixel_CREATE_RGB888(r, g, b));
600 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
601 GP_DEBUG(1, "Operation aborted");
602 return ECANCELED;
606 GP_ProgressCallbackDone(cb);
607 return 0;
610 static int load_bin_rgb888(struct buf *buf, GP_Pixmap *pixmap,
611 GP_ProgressCallback *cb)
613 uint32_t y, x;
615 for (y = 0; y < pixmap->h; y++) {
616 uint8_t *addr = GP_PIXEL_ADDR(pixmap, 0, y);
618 if (fillb(buf, addr, pixmap->w * 3))
619 return errno;
621 for (x = 0; x < pixmap->w; x++)
622 GP_SWAP(addr[3*x], addr[3*x + 2]);
624 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
625 GP_DEBUG(1, "Operation aborted");
626 return ECANCELED;
630 GP_ProgressCallbackDone(cb);
631 return 0;
634 static int save_ascii(GP_IO *io, const GP_Pixmap *pixmap,
635 GP_ProgressCallback *cb, int inv)
637 uint32_t x, y;
638 int err;
640 for (y = 0; y < pixmap->h; y++) {
641 for (x = 0; x < pixmap->w; x++) {
642 int val = GP_GetPixel_Raw(pixmap, x, y);
644 if (inv)
645 val = !val;
647 if (write_ascii_byte(io, val)) {
648 err = errno;
649 GP_DEBUG(1, "Failed to write data");
650 return err;
654 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
655 GP_DEBUG(1, "Operation aborted");
656 return ECANCELED;
659 if (GP_IOPutC(io, '\n'))
660 return errno;
663 GP_ProgressCallbackDone(cb);
664 return 0;
667 static int read_bitmap(struct buf *buf, struct pnm_header *header,
668 GP_Pixmap **img, GP_ProgressCallback *callback)
670 GP_Pixmap *ret;
671 int err;
673 if (!is_bitmap(header->magic)) {
674 GP_DEBUG(1, "Invalid Bitmap magic P%c", header->magic);
675 err = EINVAL;
676 goto err0;
679 ret = GP_PixmapAlloc(header->w, header->h, GP_PIXEL_G1);
681 if (ret == NULL) {
682 err = ENOMEM;
683 goto err1;
686 if (header->magic == '1')
687 err = load_ascii_g1_inv(buf, ret, callback);
688 else
689 err = load_raw_g1_inv(buf, ret, callback);
691 if (err)
692 goto err1;
694 *img = ret;
695 return 0;
696 err1:
697 GP_PixmapFree(ret);
698 err0:
699 errno = err;
700 return 1;
703 static void fill_meta_data(struct pnm_header *header, GP_DataStorage *storage)
705 if (!storage)
706 return;
708 GP_DataStorageAddInt(storage, NULL, "Width", header->w);
709 GP_DataStorageAddInt(storage, NULL, "Height", header->h);
710 GP_DataStorageAddInt(storage, NULL, "Depth", header->depth);
711 GP_DataStorageAddString(storage, NULL, "Format", pnm_magic_name(header->magic));
714 int GP_ReadPBMEx(GP_IO *io, GP_Pixmap **img, GP_DataStorage *storage,
715 GP_ProgressCallback *callback)
717 struct pnm_header header;
718 DECLARE_BUFFER(buf, io);
719 int err;
721 err = load_header(&buf, &header);
722 if (err) {
723 errno = err;
724 return 1;
727 fill_meta_data(&header, storage);
729 if (!img)
730 return 0;
732 return read_bitmap(&buf, &header, img, callback);
735 static GP_PixelType pbm_save_pixels[] = {
736 GP_PIXEL_G1,
737 GP_PIXEL_UNKNOWN,
740 int GP_WritePBM(const GP_Pixmap *src, GP_IO *io,
741 GP_ProgressCallback *callback)
743 GP_IO *bio;
744 int err;
746 GP_DEBUG(1, "Writing PBM into I/O (%p)", io);
748 if (src->pixel_type != GP_PIXEL_G1) {
749 GP_DEBUG(1, "Invalid pixel type '%s'",
750 GP_PixelTypeName(src->pixel_type));
751 errno = EINVAL;
752 return 1;
755 bio = GP_IOWBuffer(io, 0);
756 if (!bio)
757 return 1;
759 if (GP_IOPrintF(io, "P1\n%u %u\n",
760 (unsigned int) src->w, (unsigned int) src->h)) {
761 err = errno;
762 goto err;
765 if ((err = save_ascii(bio, src, callback, 1)))
766 goto err;
768 return GP_IOClose(bio);
769 err:
770 GP_IOClose(bio);
771 errno = err;
772 return 1;
775 static GP_Pixel depth_to_pixel(int depth)
777 switch (depth) {
778 case 1:
779 return GP_PIXEL_G1;
780 case 3:
781 return GP_PIXEL_G2;
782 case 15:
783 return GP_PIXEL_G4;
784 case 255:
785 return GP_PIXEL_G8;
786 default:
787 return GP_PIXEL_UNKNOWN;
791 static int load_ascii_graymap(struct buf *buf, struct pnm_header *header,
792 GP_Pixmap *ret, GP_ProgressCallback *callback)
794 int err = ENOSYS;
796 switch (header->depth) {
797 case 1:
798 err = load_ascii_g1(buf, ret, callback);
799 break;
800 case 3:
801 err = load_ascii_g2(buf, ret, callback);
802 break;
803 case 15:
804 err = load_ascii_g4(buf, ret, callback);
805 break;
806 case 255:
807 err = load_ascii_g8(buf, ret, callback);
808 break;
811 return err;
814 static int load_bin_graymap(struct buf *buf, struct pnm_header *header,
815 GP_Pixmap *ret, GP_ProgressCallback *callback)
817 int err = ENOSYS;
819 switch (header->depth) {
820 case 255:
821 err = load_bin_g8(buf, ret, callback);
822 break;
825 return err;
828 static int read_graymap(struct buf *buf, struct pnm_header *header,
829 GP_Pixmap **img, GP_ProgressCallback *callback)
831 GP_Pixmap *ret;
832 GP_PixelType pixel_type;
833 int err;
835 if (!is_graymap(header->magic)) {
836 GP_DEBUG(1, "Invalid graymap magic P%c", header->magic);
837 err = EINVAL;
838 goto err0;
841 if ((pixel_type = depth_to_pixel(header->depth)) == GP_PIXEL_UNKNOWN) {
842 GP_DEBUG(1, "Invalid number of grays %u", header->depth);
843 err = EINVAL;
844 goto err0;
847 ret = GP_PixmapAlloc(header->w, header->h, pixel_type);
849 if (ret == NULL) {
850 err = ENOMEM;
851 goto err1;
854 if (header->magic == '5')
855 err = load_bin_graymap(buf, header, ret, callback);
856 else
857 err = load_ascii_graymap(buf, header, ret, callback);
859 if (err)
860 goto err1;
862 *img = ret;
863 return 0;
864 err1:
865 GP_PixmapFree(ret);
866 err0:
867 errno = err;
868 return 1;
871 int GP_ReadPGMEx(GP_IO *io, GP_Pixmap **img, GP_DataStorage *storage,
872 GP_ProgressCallback *callback)
874 struct pnm_header header;
875 DECLARE_BUFFER(buf, io);
876 int err;
878 err = load_header(&buf, &header);
879 if (err) {
880 errno = err;
881 return 1;
884 fill_meta_data(&header, storage);
886 if (!img)
887 return 0;
889 return read_graymap(&buf, &header, img, callback);
892 static int pixel_to_depth(GP_Pixel pixel)
894 switch (pixel) {
895 case GP_PIXEL_G1:
896 return 1;
897 case GP_PIXEL_G2:
898 return 3;
899 case GP_PIXEL_G4:
900 return 15;
901 case GP_PIXEL_G8:
902 return 255;
903 default:
904 return -1;
908 static GP_PixelType pgm_save_pixels[] = {
909 GP_PIXEL_G1,
910 GP_PIXEL_G2,
911 GP_PIXEL_G4,
912 GP_PIXEL_G8,
913 GP_PIXEL_UNKNOWN,
916 int GP_WritePGM(const GP_Pixmap *src, GP_IO *io,
917 GP_ProgressCallback *callback)
919 int err, depth;
920 GP_IO *bio;
922 GP_DEBUG(1, "Writing PGM to I/O (%p)", io);
924 if ((depth = pixel_to_depth(src->pixel_type)) == -1) {
925 GP_DEBUG(1, "Invalid pixel type '%s'",
926 GP_PixelTypeName(src->pixel_type));
927 errno = EINVAL;
928 return 1;
931 bio = GP_IOWBuffer(io, 0);
932 if (!bio)
933 return 1;
935 if (GP_IOPrintF(io, "P2\n%u %u\n%u\n",
936 (unsigned int) src->w, (unsigned int) src->h, depth)) {
937 err = errno;
938 goto err;
941 if ((err = save_ascii(bio, src, callback, 0)))
942 goto err;
944 return GP_IOClose(bio);
945 err:
946 GP_IOClose(bio);
947 errno = err;
948 return 1;
951 static int read_pixmap(struct buf *buf, struct pnm_header *header,
952 GP_Pixmap **img, GP_ProgressCallback *callback)
954 GP_Pixmap *ret;
955 int err = 0;
957 if (!is_pixmap(header->magic)) {
958 GP_DEBUG(1, "Invalid Pixmap magic P%c", header->magic);
959 err = EINVAL;
960 goto err0;
963 if (header->depth != 255) {
964 GP_DEBUG(1, "Unsupported depth %"PRIu32, header->depth);
965 err = ENOSYS;
966 goto err0;
969 ret = GP_PixmapAlloc(header->w, header->h, GP_PIXEL_RGB888);
971 if (ret == NULL) {
972 err = ENOMEM;
973 goto err0;
976 switch (header->magic) {
977 case '3':
978 err = load_ascii_rgb888(buf, ret, callback);
979 break;
980 case '6':
981 err = load_bin_rgb888(buf, ret, callback);
982 break;
985 if (err)
986 goto err1;
988 *img = ret;
989 return 0;
990 err1:
991 GP_PixmapFree(ret);
992 err0:
993 errno = err;
994 return 1;
997 int GP_ReadPPMEx(GP_IO *io, GP_Pixmap **img, GP_DataStorage *storage,
998 GP_ProgressCallback *callback)
1000 struct pnm_header header;
1001 DECLARE_BUFFER(buf, io);
1002 int err;
1004 err = load_header(&buf, &header);
1005 if (err) {
1006 errno = err;
1007 return 1;
1010 fill_meta_data(&header, storage);
1012 if (!img)
1013 return 0;
1015 return read_pixmap(&buf, &header, img, callback);
1018 static int write_binary_ppm(FILE *f, GP_Pixmap *src)
1020 uint32_t x, y;
1022 for (y = 0; y < src->h; y++)
1023 for (x = 0; x < src->w; x++) {
1024 GP_Pixel pix = GP_GetPixel_Raw_24BPP(src, x, y);
1026 uint8_t buf[3] = {GP_Pixel_GET_R_RGB888(pix),
1027 GP_Pixel_GET_G_RGB888(pix),
1028 GP_Pixel_GET_B_RGB888(pix)};
1030 if (fwrite(buf, 3, 1, f) < 1)
1031 return 1;
1034 return 0;
1037 static int save_ascii_rgb888(GP_IO *io, const GP_Pixmap *pixmap,
1038 GP_LineConvert Convert, GP_ProgressCallback *cb)
1040 uint32_t x, y;
1041 int ret = 0;
1042 uint8_t buf[3 * pixmap->w], *addr;
1044 for (y = 0; y < pixmap->h; y++) {
1046 addr = GP_PIXEL_ADDR(pixmap, 0, y);
1048 if (Convert) {
1049 Convert(addr, buf, pixmap->w);
1050 addr = buf;
1053 for (x = 0; x < pixmap->w; x++) {
1054 ret |= write_ascii_byte(io, addr[2]);
1055 ret |= write_ascii_byte(io, addr[1]);
1056 ret |= write_ascii_byte(io, addr[0]);
1058 if (ret)
1059 return errno;
1061 addr+=3;
1064 if (GP_ProgressCallbackReport(cb, y, pixmap->h, pixmap->w)) {
1065 GP_DEBUG(1, "Operation aborted");
1066 return ECANCELED;
1069 if (GP_IOPutC(io, '\n'))
1070 return errno;
1073 GP_ProgressCallbackDone(cb);
1074 return 0;
1077 static GP_PixelType ppm_save_pixels[] = {
1078 GP_PIXEL_RGB888,
1079 GP_PIXEL_UNKNOWN,
1082 int GP_WritePPM(const GP_Pixmap *src, GP_IO *io,
1083 GP_ProgressCallback *callback)
1085 GP_Pixel out_pix;
1086 GP_LineConvert Convert;
1087 GP_IO *bio;
1088 int err = 0;
1090 GP_DEBUG(1, "Writing PPM into I/O (%p)", io);
1092 out_pix = GP_LineConvertible(src->pixel_type, ppm_save_pixels);
1094 if (out_pix == GP_PIXEL_UNKNOWN) {
1095 GP_DEBUG(1, "Invalid pixel type '%s'",
1096 GP_PixelTypeName(src->pixel_type));
1097 errno = EINVAL;
1098 return 1;
1101 bio = GP_IOWBuffer(io, 0);
1102 if (!bio)
1103 return 1;
1105 if (GP_IOPrintF(io, "P3\n%u %u\n255\n",
1106 (unsigned int) src->w, (unsigned int) src->h)) {
1107 err = errno;
1108 goto err;
1111 Convert = GP_LineConvertGet(src->pixel_type, out_pix);
1113 if ((err = save_ascii_rgb888(bio, src, Convert, callback)))
1114 goto err;
1116 return GP_IOClose(bio);
1117 err:
1118 GP_IOClose(bio);
1119 errno = err;
1120 return 1;
1123 int GP_ReadPNMEx(GP_IO *io, GP_Pixmap **img, GP_DataStorage *storage,
1124 GP_ProgressCallback *callback)
1126 struct pnm_header header;
1127 DECLARE_BUFFER(buf, io);
1128 int err, ret = 1;
1130 err = load_header(&buf, &header);
1131 if (err) {
1132 errno = err;
1133 return 1;
1136 fill_meta_data(&header, storage);
1138 if (!img)
1139 return 0;
1141 if (is_bitmap(header.magic))
1142 ret = read_bitmap(&buf, &header, img, callback);
1144 if (is_graymap(header.magic))
1145 ret = read_graymap(&buf, &header, img, callback);
1147 if (is_pixmap(header.magic))
1148 ret = read_pixmap(&buf, &header, img, callback);
1150 return ret;
1153 static GP_PixelType pnm_save_pixels[] = {
1154 GP_PIXEL_G1,
1155 GP_PIXEL_G2,
1156 GP_PIXEL_G4,
1157 GP_PIXEL_G8,
1158 GP_PIXEL_RGB888,
1159 GP_PIXEL_UNKNOWN,
1162 int GP_WritePNM(const GP_Pixmap *src, GP_IO *io,
1163 GP_ProgressCallback *callback)
1165 switch (src->pixel_type) {
1166 case GP_PIXEL_G1:
1167 case GP_PIXEL_G2:
1168 case GP_PIXEL_G4:
1169 case GP_PIXEL_G8:
1170 return GP_WritePGM(src, io, callback);
1171 case GP_PIXEL_RGB888:
1172 return GP_WritePPM(src, io, callback);
1173 default:
1174 if (GP_LineConvertible(src->pixel_type, ppm_save_pixels))
1175 return GP_WritePPM(src, io, callback);
1177 errno = EINVAL;
1178 return 1;
1182 GP_Pixmap *GP_ReadPBM(GP_IO *io, GP_ProgressCallback *callback)
1184 return GP_LoaderReadImage(&GP_PBM, io, callback);
1187 GP_Pixmap *GP_LoadPBM(const char *src_path, GP_ProgressCallback *callback)
1189 return GP_LoaderLoadImage(&GP_PBM, src_path, callback);
1192 int GP_SavePBM(const GP_Pixmap *src, const char *dst_path,
1193 GP_ProgressCallback *callback)
1195 return GP_LoaderSaveImage(&GP_PBM, src, dst_path, callback);
1198 GP_Pixmap *GP_ReadPGM(GP_IO *io, GP_ProgressCallback *callback)
1200 return GP_LoaderReadImage(&GP_PGM, io, callback);
1203 GP_Pixmap *GP_LoadPGM(const char *src_path, GP_ProgressCallback *callback)
1205 return GP_LoaderLoadImage(&GP_PGM, src_path, callback);
1208 int GP_SavePGM(const GP_Pixmap *src, const char *dst_path,
1209 GP_ProgressCallback *callback)
1211 return GP_LoaderSaveImage(&GP_PGM, src, dst_path, callback);
1214 GP_Pixmap *GP_ReadPPM(GP_IO *io, GP_ProgressCallback *callback)
1216 return GP_LoaderReadImage(&GP_PPM, io, callback);
1219 GP_Pixmap *GP_LoadPPM(const char *src_path, GP_ProgressCallback *callback)
1221 return GP_LoaderLoadImage(&GP_PPM, src_path, callback);
1224 int GP_SavePPM(const GP_Pixmap *src, const char *dst_path,
1225 GP_ProgressCallback *callback)
1227 return GP_LoaderSaveImage(&GP_PPM, src, dst_path, callback);
1230 GP_Pixmap *GP_ReadPNM(GP_IO *io, GP_ProgressCallback *callback)
1232 return GP_LoaderReadImage(&GP_PNM, io, callback);
1235 GP_Pixmap *GP_LoadPNM(const char *src_path, GP_ProgressCallback *callback)
1237 return GP_LoaderLoadImage(&GP_PNM, src_path, callback);
1240 int GP_SavePNM(const GP_Pixmap *src, const char *dst_path,
1241 GP_ProgressCallback *callback)
1243 return GP_LoaderSaveImage(&GP_PNM, src, dst_path, callback);
1246 struct GP_Loader GP_PBM = {
1247 .Read = GP_ReadPBMEx,
1248 .Write = GP_WritePBM,
1249 .save_ptypes = pbm_save_pixels,
1250 .Match = GP_MatchPBM,
1252 .fmt_name = "Netpbm portable Bitmap",
1253 .extensions = {"pbm", NULL},
1256 struct GP_Loader GP_PGM = {
1257 .Read = GP_ReadPGMEx,
1258 .Write = GP_WritePGM,
1259 .save_ptypes = pgm_save_pixels,
1260 .Match = GP_MatchPGM,
1262 .fmt_name = "Netpbm portable Graymap",
1263 .extensions = {"pgm", NULL},
1266 struct GP_Loader GP_PPM = {
1267 .Read = GP_ReadPPMEx,
1268 .Write = GP_WritePPM,
1269 .save_ptypes = ppm_save_pixels,
1270 .Match = GP_MatchPPM,
1272 .fmt_name = "Netpbm portable Pixmap",
1273 .extensions = {"ppm", NULL},
1276 struct GP_Loader GP_PNM = {
1277 .Read = GP_ReadPNMEx,
1278 .Write = GP_WritePNM,
1279 .save_ptypes = pnm_save_pixels,
1281 * Avoid double Match
1282 * This format is covered by PBM, PGM and PPM
1284 .Match = NULL,
1286 .fmt_name = "Netpbm portable Anymap",
1287 .extensions = {"pnm", NULL},