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-2014 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 PNM portable bitmap header
26 --------------------------
30 a magic number value of 'P' and one of
31 '1' - PBM Bitmap ASCII
34 '4' - PBM Bitmap Binary
37 whitespace (blanks, TABs, CRs, LFs).
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
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"
71 static char *pnm_names
[] = {
80 static const char *pnm_magic_name(char magic
)
82 return pnm_names
[magic
- '1'];
89 static int is_bitmap(char magic
)
91 return magic
== '1' || magic
== '4';
94 int GP_MatchPBM(const void *buf
)
98 return b
[0] == 'P' && is_bitmap(b
[1]);
105 static int is_graymap(char magic
)
107 return magic
== '2' || magic
== '5';
110 int GP_MatchPGM(const void *buf
)
114 return b
[0] == 'P' && is_graymap(b
[1]);
121 static int is_pixmap(char magic
)
123 return magic
== '3' || magic
== '6';
126 int GP_MatchPPM(const void *buf
)
130 return b
[0] == 'P' && is_pixmap(b
[1]);
133 static int magic_is_valid(char magic
)
143 int GP_MatchPNM(const void *buf
)
147 return b
[0] == 'P' && magic_is_valid(b
[1]);
160 * Simple buffer implementation on the top of the GP_IO
164 unsigned int buf_end
;
165 unsigned int buf_pos
;
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
)
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
));
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
;
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
,
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
;
221 if (h1
== EOF
|| h2
== EOF
) {
222 GP_DEBUG(1, "Failed to read header");
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
: ' ');
261 GP_WARN("Ignoring character 0x%02x (%c)",
262 c
, isprint(c
) ? c
: ' ');
282 if (is_bitmap(header
->magic
))
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
);
303 GP_DEBUG(1, "Unexpected end of file when reading header");
310 static int get_ascii_int(struct buf
*buf
, int *val
)
312 int c
, in_number
= 0;
321 GP_DEBUG(1, "Unexpected end of file");
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
)
354 GP_IOPutC(io
, '0' + byte
/100);
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
)
373 for (y
= 0; y
< pixmap
->h
; y
++) {
374 for (x
= 0; x
< pixmap
->w
; x
++) {
376 if ((err
= get_ascii_int(buf
, &val
)))
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");
388 GP_ProgressCallbackDone(cb
);
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
)
402 for (y
= 0; y
< pixmap
->h
; y
++) {
403 for (x
= 0; x
< pixmap
->w
; x
+=8) {
405 if ((val
= getb(buf
)) == EOF
)
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");
418 GP_ProgressCallbackDone(cb
);
422 static int load_ascii_g1(struct buf
*buf
, GP_Pixmap
*pixmap
,
423 GP_ProgressCallback
*cb
)
428 for (y
= 0; y
< pixmap
->h
; y
++) {
429 for (x
= 0; x
< pixmap
->w
; x
++) {
431 if ((err
= get_ascii_int(buf
, &val
)))
435 GP_WARN("Value too large for 1BPP (%i)", val
);
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");
448 GP_ProgressCallbackDone(cb
);
452 static int load_ascii_g2(struct buf
*buf
, GP_Pixmap
*pixmap
,
453 GP_ProgressCallback
*cb
)
458 for (y
= 0; y
< pixmap
->h
; y
++) {
459 for (x
= 0; x
< pixmap
->w
; x
++) {
461 if ((err
= get_ascii_int(buf
, &val
)))
465 GP_WARN("Value too large for 2BPP (%i)", val
);
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");
478 GP_ProgressCallbackDone(cb
);
482 static int load_ascii_g4(struct buf
*buf
, GP_Pixmap
*pixmap
,
483 GP_ProgressCallback
*cb
)
488 for (y
= 0; y
< pixmap
->h
; y
++) {
489 for (x
= 0; x
< pixmap
->w
; x
++) {
491 if ((err
= get_ascii_int(buf
, &val
)))
495 GP_WARN("Value too large for 4BPP (%i)", val
);
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");
508 GP_ProgressCallbackDone(cb
);
512 static int load_ascii_g8(struct buf
*buf
, GP_Pixmap
*pixmap
,
513 GP_ProgressCallback
*cb
)
518 for (y
= 0; y
< pixmap
->h
; y
++) {
519 for (x
= 0; x
< pixmap
->w
; x
++) {
521 if ((err
= get_ascii_int(buf
, &val
)))
525 GP_WARN("Value too large for 8BPP (%i)", val
);
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");
538 GP_ProgressCallbackDone(cb
);
542 static int load_bin_g8(struct buf
*buf
, GP_Pixmap
*pixmap
,
543 GP_ProgressCallback
*cb
)
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
))
553 if (GP_ProgressCallbackReport(cb
, y
, pixmap
->h
, pixmap
->w
)) {
554 GP_DEBUG(1, "Operation aborted");
559 GP_ProgressCallbackDone(cb
);
563 static int load_ascii_rgb888(struct buf
*buf
, GP_Pixmap
*pixmap
,
564 GP_ProgressCallback
*cb
)
569 for (y
= 0; y
< pixmap
->h
; y
++) {
570 for (x
= 0; x
< pixmap
->w
; x
++) {
572 if ((err
= get_ascii_int(buf
, &r
)))
576 GP_WARN("R value too large (%i)", r
);
580 if ((err
= get_ascii_int(buf
, &g
)))
584 GP_WARN("G value too large (%i)", r
);
588 if ((err
= get_ascii_int(buf
, &b
)))
592 GP_WARN("G value too large (%i)", r
);
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");
606 GP_ProgressCallbackDone(cb
);
610 static int load_bin_rgb888(struct buf
*buf
, GP_Pixmap
*pixmap
,
611 GP_ProgressCallback
*cb
)
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))
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");
630 GP_ProgressCallbackDone(cb
);
634 static int save_ascii(GP_IO
*io
, const GP_Pixmap
*pixmap
,
635 GP_ProgressCallback
*cb
, int inv
)
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
);
647 if (write_ascii_byte(io
, val
)) {
649 GP_DEBUG(1, "Failed to write data");
654 if (GP_ProgressCallbackReport(cb
, y
, pixmap
->h
, pixmap
->w
)) {
655 GP_DEBUG(1, "Operation aborted");
659 if (GP_IOPutC(io
, '\n'))
663 GP_ProgressCallbackDone(cb
);
667 static int read_bitmap(struct buf
*buf
, struct pnm_header
*header
,
668 GP_Pixmap
**img
, GP_ProgressCallback
*callback
)
673 if (!is_bitmap(header
->magic
)) {
674 GP_DEBUG(1, "Invalid Bitmap magic P%c", header
->magic
);
679 ret
= GP_PixmapAlloc(header
->w
, header
->h
, GP_PIXEL_G1
);
686 if (header
->magic
== '1')
687 err
= load_ascii_g1_inv(buf
, ret
, callback
);
689 err
= load_raw_g1_inv(buf
, ret
, callback
);
703 static void fill_meta_data(struct pnm_header
*header
, GP_DataStorage
*storage
)
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
);
721 err
= load_header(&buf
, &header
);
727 fill_meta_data(&header
, storage
);
732 return read_bitmap(&buf
, &header
, img
, callback
);
735 static GP_PixelType pbm_save_pixels
[] = {
740 int GP_WritePBM(const GP_Pixmap
*src
, GP_IO
*io
,
741 GP_ProgressCallback
*callback
)
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
));
755 bio
= GP_IOWBuffer(io
, 0);
759 if (GP_IOPrintF(io
, "P1\n%u %u\n",
760 (unsigned int) src
->w
, (unsigned int) src
->h
)) {
765 if ((err
= save_ascii(bio
, src
, callback
, 1)))
768 return GP_IOClose(bio
);
775 static GP_Pixel
depth_to_pixel(int depth
)
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
)
796 switch (header
->depth
) {
798 err
= load_ascii_g1(buf
, ret
, callback
);
801 err
= load_ascii_g2(buf
, ret
, callback
);
804 err
= load_ascii_g4(buf
, ret
, callback
);
807 err
= load_ascii_g8(buf
, ret
, callback
);
814 static int load_bin_graymap(struct buf
*buf
, struct pnm_header
*header
,
815 GP_Pixmap
*ret
, GP_ProgressCallback
*callback
)
819 switch (header
->depth
) {
821 err
= load_bin_g8(buf
, ret
, callback
);
828 static int read_graymap(struct buf
*buf
, struct pnm_header
*header
,
829 GP_Pixmap
**img
, GP_ProgressCallback
*callback
)
832 GP_PixelType pixel_type
;
835 if (!is_graymap(header
->magic
)) {
836 GP_DEBUG(1, "Invalid graymap magic P%c", header
->magic
);
841 if ((pixel_type
= depth_to_pixel(header
->depth
)) == GP_PIXEL_UNKNOWN
) {
842 GP_DEBUG(1, "Invalid number of grays %u", header
->depth
);
847 ret
= GP_PixmapAlloc(header
->w
, header
->h
, pixel_type
);
854 if (header
->magic
== '5')
855 err
= load_bin_graymap(buf
, header
, ret
, callback
);
857 err
= load_ascii_graymap(buf
, header
, ret
, callback
);
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
);
878 err
= load_header(&buf
, &header
);
884 fill_meta_data(&header
, storage
);
889 return read_graymap(&buf
, &header
, img
, callback
);
892 static int pixel_to_depth(GP_Pixel pixel
)
908 static GP_PixelType pgm_save_pixels
[] = {
916 int GP_WritePGM(const GP_Pixmap
*src
, GP_IO
*io
,
917 GP_ProgressCallback
*callback
)
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
));
931 bio
= GP_IOWBuffer(io
, 0);
935 if (GP_IOPrintF(io
, "P2\n%u %u\n%u\n",
936 (unsigned int) src
->w
, (unsigned int) src
->h
, depth
)) {
941 if ((err
= save_ascii(bio
, src
, callback
, 0)))
944 return GP_IOClose(bio
);
951 static int read_pixmap(struct buf
*buf
, struct pnm_header
*header
,
952 GP_Pixmap
**img
, GP_ProgressCallback
*callback
)
957 if (!is_pixmap(header
->magic
)) {
958 GP_DEBUG(1, "Invalid Pixmap magic P%c", header
->magic
);
963 if (header
->depth
!= 255) {
964 GP_DEBUG(1, "Unsupported depth %"PRIu32
, header
->depth
);
969 ret
= GP_PixmapAlloc(header
->w
, header
->h
, GP_PIXEL_RGB888
);
976 switch (header
->magic
) {
978 err
= load_ascii_rgb888(buf
, ret
, callback
);
981 err
= load_bin_rgb888(buf
, ret
, callback
);
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
);
1004 err
= load_header(&buf
, &header
);
1010 fill_meta_data(&header
, storage
);
1015 return read_pixmap(&buf
, &header
, img
, callback
);
1018 static int write_binary_ppm(FILE *f
, GP_Pixmap
*src
)
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)
1037 static int save_ascii_rgb888(GP_IO
*io
, const GP_Pixmap
*pixmap
,
1038 GP_LineConvert Convert
, GP_ProgressCallback
*cb
)
1042 uint8_t buf
[3 * pixmap
->w
], *addr
;
1044 for (y
= 0; y
< pixmap
->h
; y
++) {
1046 addr
= GP_PIXEL_ADDR(pixmap
, 0, y
);
1049 Convert(addr
, buf
, pixmap
->w
);
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]);
1064 if (GP_ProgressCallbackReport(cb
, y
, pixmap
->h
, pixmap
->w
)) {
1065 GP_DEBUG(1, "Operation aborted");
1069 if (GP_IOPutC(io
, '\n'))
1073 GP_ProgressCallbackDone(cb
);
1077 static GP_PixelType ppm_save_pixels
[] = {
1082 int GP_WritePPM(const GP_Pixmap
*src
, GP_IO
*io
,
1083 GP_ProgressCallback
*callback
)
1086 GP_LineConvert Convert
;
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
));
1101 bio
= GP_IOWBuffer(io
, 0);
1105 if (GP_IOPrintF(io
, "P3\n%u %u\n255\n",
1106 (unsigned int) src
->w
, (unsigned int) src
->h
)) {
1111 Convert
= GP_LineConvertGet(src
->pixel_type
, out_pix
);
1113 if ((err
= save_ascii_rgb888(bio
, src
, Convert
, callback
)))
1116 return GP_IOClose(bio
);
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
);
1130 err
= load_header(&buf
, &header
);
1136 fill_meta_data(&header
, storage
);
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
);
1153 static GP_PixelType pnm_save_pixels
[] = {
1162 int GP_WritePNM(const GP_Pixmap
*src
, GP_IO
*io
,
1163 GP_ProgressCallback
*callback
)
1165 switch (src
->pixel_type
) {
1170 return GP_WritePGM(src
, io
, callback
);
1171 case GP_PIXEL_RGB888
:
1172 return GP_WritePPM(src
, io
, callback
);
1174 if (GP_LineConvertible(src
->pixel_type
, ppm_save_pixels
))
1175 return GP_WritePPM(src
, io
, callback
);
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
1286 .fmt_name
= "Netpbm portable Anymap",
1287 .extensions
= {"pnm", NULL
},