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 *****************************************************************************/
25 PNG image support using libpng.
35 #include "../../config.h"
36 #include "core/GP_Debug.h"
44 #include "core/GP_BitSwap.h"
46 int GP_OpenPNG(const char *src_path
, FILE **f
)
51 *f
= fopen(src_path
, "r");
55 GP_DEBUG(1, "Failed to open '%s' : %s",
56 src_path
, strerror(errno
));
60 if (fread(sig
, 1, 8, *f
) <= 0) {
62 GP_DEBUG(1, "Failed to read '%s' : %s",
63 src_path
, strerror(errno
));
67 if (png_sig_cmp(sig
, 0, 8)) {
68 GP_DEBUG(1, "Invalid file header, '%s' not a PNG image?",
74 GP_DEBUG(1, "Found PNG signature in '%s'", src_path
);
84 static const char *interlace_type_name(int interlace
)
87 case PNG_INTERLACE_NONE
:
89 case PNG_INTERLACE_ADAM7
:
96 GP_Context
*GP_ReadPNG(FILE *f
, GP_ProgressCallback
*callback
)
99 png_infop png_info
= NULL
;
101 int depth
, color_type
, interlace_type
;
102 GP_PixelType pixel_type
= GP_PIXEL_UNKNOWN
;
106 png
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
109 GP_DEBUG(1, "Failed to allocate PNG read buffer");
114 png_info
= png_create_info_struct(png
);
116 if (png_info
== NULL
) {
117 GP_DEBUG(1, "Failed to allocate PNG info buffer");
122 if (setjmp(png_jmpbuf(png
))) {
123 GP_DEBUG(1, "Failed to read PNG file :(");
124 //TODO: should we get better error description from libpng?
130 png_set_sig_bytes(png
, 8);
131 png_read_info(png
, png_info
);
133 png_get_IHDR(png
, png_info
, &w
, &h
, &depth
,
134 &color_type
, &interlace_type
, NULL
, NULL
);
136 GP_DEBUG(2, "Have %s%s interlace %s PNG%s size %ux%u depth %i",
137 interlace_type_name(interlace_type
),
138 color_type
& PNG_COLOR_MASK_PALETTE
? " pallete " : "",
139 color_type
& PNG_COLOR_MASK_COLOR
? "color" : "gray",
140 color_type
& PNG_COLOR_MASK_ALPHA
? " with alpha channel" : "",
141 (unsigned int)w
, (unsigned int)h
, depth
);
143 switch (color_type
) {
144 case PNG_COLOR_TYPE_GRAY
:
147 pixel_type
= GP_PIXEL_G1
;
150 pixel_type
= GP_PIXEL_G2
;
153 pixel_type
= GP_PIXEL_G4
;
156 pixel_type
= GP_PIXEL_G8
;
160 case PNG_COLOR_TYPE_RGB
:
166 pixel_type
= GP_PIXEL_RGB888
;
170 case PNG_COLOR_TYPE_PALETTE
:
171 /* Grayscale with BPP < 8 is usually saved as palette */
172 if (png_get_channels(png
, png_info
) == 1) {
175 png_set_packswap(png
);
176 pixel_type
= GP_PIXEL_G1
;
181 /* Convert everything else to RGB888 */
182 //TODO: add palette matching to G2 G4 and G8
183 png_set_palette_to_rgb(png
);
185 pixel_type
= GP_PIXEL_RGB888
;
189 if (pixel_type
== GP_PIXEL_UNKNOWN
) {
190 GP_DEBUG(1, "Unimplemented png format");
195 res
= GP_ContextAlloc(w
, h
, pixel_type
);
202 if (color_type
== PNG_COLOR_TYPE_GRAY
)
203 png_set_packswap(png
);
207 /* start the actuall reading */
208 for (y
= 0; y
< h
; y
++) {
209 png_bytep row
= GP_PIXEL_ADDR(res
, 0, y
);
210 png_read_rows(png
, &row
, NULL
, 1);
212 if (GP_ProgressCallbackReport(callback
, y
, h
, w
)) {
213 GP_DEBUG(1, "Operation aborted");
220 png_destroy_read_struct(&png
, &png_info
, NULL
);
222 GP_ProgressCallbackDone(callback
);
228 png_destroy_read_struct(&png
, png_info
? &png_info
: NULL
, NULL
);
234 GP_Context
*GP_LoadPNG(const char *src_path
, GP_ProgressCallback
*callback
)
239 if (GP_OpenPNG(src_path
, &f
))
242 res
= GP_ReadPNG(f
, callback
);
249 static void load_meta_data(png_structp png
, png_infop png_info
, GP_MetaData
*data
)
253 if (png_get_gAMA(png
, png_info
, &gamma
))
254 GP_MetaDataCreateInt(data
, "gamma", gamma
* 100000);
256 png_uint_32 res_x
, res_y
;
259 if (png_get_pHYs(png
, png_info
, &res_x
, &res_y
, &unit
)) {
260 GP_MetaDataCreateInt(data
, "res_x", res_x
);
261 GP_MetaDataCreateInt(data
, "res_y", res_y
);
263 const char *unit_name
;
265 if (unit
== PNG_RESOLUTION_METER
)
268 unit_name
= "unknown";
270 GP_MetaDataCreateString(data
, "res_unit", unit_name
, 0);
273 double width
, height
;
275 if (png_get_sCAL(png
, png_info
, &unit
, &width
, &height
)) {
276 GP_MetaDataCreateInt(data
, "width", width
* 1000);
277 GP_MetaDataCreateInt(data
, "height", height
* 1000);
278 GP_MetaDataCreateInt(data
, "unit", unit
);
282 int GP_ReadPNGMetaData(FILE *f
, GP_MetaData
*data
)
285 png_infop png_info
= NULL
;
288 png
= png_create_read_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
291 GP_DEBUG(1, "Failed to allocate PNG read buffer");
296 png_info
= png_create_info_struct(png
);
298 if (png_info
== NULL
) {
299 GP_DEBUG(1, "Failed to allocate PNG info buffer");
304 if (setjmp(png_jmpbuf(png
))) {
305 GP_DEBUG(1, "Failed to read PNG file :(");
306 //TODO: should we get better error description from libpng?
312 png_set_sig_bytes(png
, 8);
313 png_read_info(png
, png_info
);
315 load_meta_data(png
, png_info
, data
);
319 png_destroy_read_struct(&png
, png_info
? &png_info
: NULL
, NULL
);
325 int GP_LoadPNGMetaData(const char *src_path
, GP_MetaData
*data
)
330 if (GP_OpenPNG(src_path
, &f
))
333 ret
= GP_ReadPNGMetaData(f
, data
);
341 * Maps gfxprim Pixel Type to the PNG format
343 static int prepare_png_header(const GP_Context
*src
, png_structp png
,
344 png_infop png_info
, int *bit_endian_flag
)
346 int bit_depth
, color_type
;
348 switch (src
->pixel_type
) {
349 case GP_PIXEL_BGR888
:
350 case GP_PIXEL_RGB888
:
352 color_type
= PNG_COLOR_TYPE_RGB
;
356 color_type
= PNG_COLOR_TYPE_GRAY
;
360 color_type
= PNG_COLOR_TYPE_GRAY
;
364 color_type
= PNG_COLOR_TYPE_GRAY
;
368 color_type
= PNG_COLOR_TYPE_GRAY
;
375 /* If pointers weren't passed, just return it is okay */
376 if (png
== NULL
|| png_info
== NULL
)
379 png_set_IHDR(png
, png_info
, src
->w
, src
->h
, bit_depth
, color_type
,
380 PNG_INTERLACE_NONE
, PNG_COMPRESSION_TYPE_DEFAULT
,
381 PNG_FILTER_TYPE_DEFAULT
);
383 /* start the actuall writing */
384 png_write_info(png
, png_info
);
386 //png_set_packing(png);
388 /* prepare for format conversion */
389 switch (src
->pixel_type
) {
390 case GP_PIXEL_RGB888
:
396 *bit_endian_flag
= !src
->bit_endian
;
405 static int write_png_data(const GP_Context
*src
, png_structp png
,
406 GP_ProgressCallback
*callback
, int bit_endian_flag
)
408 /* Look if we need to swap data when writing */
409 if (bit_endian_flag
) {
410 switch (src
->pixel_type
) {
414 png_set_packswap(png
);
424 for (y
= 0; y
< src
->h
; y
++) {
425 png_bytep row
= GP_PIXEL_ADDR(src
, 0, y
);
426 png_write_row(png
, row
);
428 if (GP_ProgressCallbackReport(callback
, y
, src
->h
, src
->w
)) {
429 GP_DEBUG(1, "Operation aborted");
437 int GP_SavePNG(const GP_Context
*src
, const char *dst_path
,
438 GP_ProgressCallback
*callback
)
442 png_infop png_info
= NULL
;
445 GP_DEBUG(1, "Saving PNG Image '%s'", dst_path
);
447 if (prepare_png_header(src
, NULL
, NULL
, NULL
)) {
448 GP_DEBUG(1, "Can't save png with %s pixel type",
449 GP_PixelTypeName(src
->pixel_type
));
453 f
= fopen(dst_path
, "wb");
457 GP_DEBUG(1, "Failed to open '%s' for writing: %s",
458 dst_path
, strerror(errno
));
462 png
= png_create_write_struct(PNG_LIBPNG_VER_STRING
, NULL
, NULL
, NULL
);
465 GP_DEBUG(1, "Failed to allocate PNG write buffer");
470 png_info
= png_create_info_struct(png
);
472 if (png_info
== NULL
) {
473 GP_DEBUG(1, "Failed to allocate PNG info buffer");
478 if (setjmp(png_jmpbuf(png
))) {
479 GP_DEBUG(1, "Failed to write PNG file :(");
480 //TODO: should we get better error description from libpng?
487 int bit_endian_flag
= 0;
488 /* Fill png header and prepare for data */
489 prepare_png_header(src
, png
, png_info
, &bit_endian_flag
);
491 /* Write bitmap buffer */
492 if ((err
= write_png_data(src
, png
, callback
, bit_endian_flag
)))
495 png_write_end(png
, png_info
);
496 png_destroy_write_struct(&png
, &png_info
);
500 GP_DEBUG(1, "Failed to close file '%s': %s",
501 dst_path
, strerror(errno
));
505 GP_ProgressCallbackDone(callback
);
509 png_destroy_write_struct(&png
, png_info
== NULL
? NULL
: &png_info
);
521 int GP_OpenPNG(const char GP_UNUSED(*src_path
),
528 GP_Context
*GP_ReadPNG(FILE GP_UNUSED(*f
),
529 GP_ProgressCallback
GP_UNUSED(*callback
))
535 GP_Context
*GP_LoadPNG(const char GP_UNUSED(*src_path
),
536 GP_ProgressCallback
GP_UNUSED(*callback
))
542 int GP_ReadPNGMetaData(FILE GP_UNUSED(*f
), GP_MetaData
GP_UNUSED(*data
))
548 int GP_LoadPNGMetaData(const char GP_UNUSED(*src_path
), GP_MetaData
GP_UNUSED(*data
))
554 int GP_SavePNG(const GP_Context
GP_UNUSED(*src
),
555 const char GP_UNUSED(*dst_path
),
556 GP_ProgressCallback
GP_UNUSED(*callback
))
562 #endif /* HAVE_LIBPNG */