From 68cb0aaee3de6371b81a4613476d9b33e43e95b1 Mon Sep 17 00:00:00 2001 From: Glenn Randers-Pehrson Date: Thu, 13 Jul 2017 11:19:53 -0500 Subject: [PATCH] [libpng16] Implement eXIf chunk support --- ANNOUNCE | 1 + CHANGES | 1 + libpng-manual.txt | 16 +++++++++++++++ libpng.3 | 20 ++++++++++++++++++ png.c | 10 +++++++++ png.h | 16 ++++++++++----- pngget.c | 18 +++++++++++++++++ pnginfo.h | 5 +++++ pngpriv.h | 6 ++++++ pngrutil.c | 38 +++++++++++++++++++++++++++++++++++ pngset.c | 33 ++++++++++++++++++++++++++++++ pngstruct.h | 3 +++ pngtest.c | 16 +++++++++++++++ pngwutil.c | 31 ++++++++++++++++++++++++++++ scripts/pnglibconf.dfa | 2 ++ scripts/pnglibconf.h.prebuilt | 3 +++ scripts/symbols.def | 2 ++ 17 files changed, 216 insertions(+), 5 deletions(-) diff --git a/ANNOUNCE b/ANNOUNCE index 4896a9682..0448062a5 100644 --- a/ANNOUNCE +++ b/ANNOUNCE @@ -59,6 +59,7 @@ Version 1.6.31beta04 [July 11, 2017] overflow' check that is on by default with -Wall -Wextra. Version 1.6.31beta05 [July 11, 2017] + Added eXIf chunk support. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/CHANGES b/CHANGES index 3e07b17d4..18e90d601 100644 --- a/CHANGES +++ b/CHANGES @@ -5889,6 +5889,7 @@ Version 1.6.31beta04 [July 11, 2017] overflow' check that is on by default with -Wall -Wextra. Version 1.6.31beta05 [July 11, 2017] + Added eXIf chunk support. Send comments/corrections/commendations to png-mng-implement at lists.sf.net (subscription required; visit diff --git a/libpng-manual.txt b/libpng-manual.txt index 2903f92b0..a6251f96b 100644 --- a/libpng-manual.txt +++ b/libpng-manual.txt @@ -1453,6 +1453,11 @@ png_set_rgb_to_gray()). the single transparent color for non-paletted images (PNG_INFO_tRNS) + png_get_eXIf(png_ptr, info_ptr, &exif); + (PNG_INFO_eXIf) + + exif - Exif profile (array of png_byte) + png_get_hIST(png_ptr, info_ptr, &hist); (PNG_INFO_hIST) @@ -2498,6 +2503,7 @@ your application instead of by libpng, you can use PNG_INFO_gAMA, PNG_INFO_sBIT, PNG_INFO_cHRM, PNG_INFO_PLTE, PNG_INFO_tRNS, PNG_INFO_bKGD, + PNG_INFO_eXIf, PNG_INFO_hIST, PNG_INFO_pHYs, PNG_INFO_oFFs, PNG_INFO_tIME, PNG_INFO_pCAL, PNG_INFO_sRGB, @@ -3097,6 +3103,11 @@ width, height, bit_depth, and color_type must be the same in each call. single transparent color for non-paletted images (PNG_INFO_tRNS) + png_set_eXIf(png_ptr, info_ptr, exif); + + hist - Exif profile (array of + png_byte) (PNG_INFO_eXIf) + png_set_hIST(png_ptr, info_ptr, hist); hist - histogram of palette (array of @@ -5205,6 +5216,11 @@ is an error. Previously this requirement of the PNG specification was not enforced, and the palette was always limited to 256 entries. An over-length PLTE chunk found in an input PNG is silently truncated. +Starting with libpng-1.6.31, the eXIf chunk is supported. Libpng does not +attempt to decode the Exif profile; it simply returns a byte array +containing the profile to the calling application which must do its own +decoding. + XIII. Detecting libpng The png_get_io_ptr() function has been present since libpng-0.88, has never diff --git a/libpng.3 b/libpng.3 index 9dec52b51..62298f887 100644 --- a/libpng.3 +++ b/libpng.3 @@ -97,6 +97,8 @@ libpng \- Portable Network Graphics (PNG) Reference Library 1.6.31beta05 \fBpng_byte png_get_header_version (png_const_structp \fIpng_ptr\fP\fB);\fP +\fBpng_uint_32 png_get_eXIf (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fI*exif\fP\fB);\fP + \fBpng_uint_32 png_get_hIST (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fI*hist\fP\fB);\fP \fBpng_uint_32 png_get_iCCP (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_charpp \fP\fIname\fP\fB, int \fP\fI*compression_type\fP\fB, png_bytepp \fP\fIprofile\fP\fB, png_uint_32 \fI*proflen\fP\fB);\fP @@ -347,6 +349,8 @@ libpng \- Portable Network Graphics (PNG) Reference Library 1.6.31beta05 \fBvoid png_set_gray_to_rgb (png_structp \fIpng_ptr\fP\fB);\fP +\fBvoid png_set_eXIf (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fIexif\fP\fB);\fP + \fBvoid png_set_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fIhist\fP\fB);\fP \fBvoid png_set_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_const_charp \fP\fIname\fP\fB, int \fP\fIcompression_type\fP\fB, png_const_bytep \fP\fIprofile\fP\fB, png_uint_32 \fIproflen\fP\fB);\fP @@ -1963,6 +1967,11 @@ png_set_rgb_to_gray()). the single transparent color for non-paletted images (PNG_INFO_tRNS) + png_get_eXIf(png_ptr, info_ptr, &exif); + (PNG_INFO_eXIf) + + exif - Exif profile (array of png_byte) + png_get_hIST(png_ptr, info_ptr, &hist); (PNG_INFO_hIST) @@ -3008,6 +3017,7 @@ your application instead of by libpng, you can use PNG_INFO_gAMA, PNG_INFO_sBIT, PNG_INFO_cHRM, PNG_INFO_PLTE, PNG_INFO_tRNS, PNG_INFO_bKGD, + PNG_INFO_eXIf, PNG_INFO_hIST, PNG_INFO_pHYs, PNG_INFO_oFFs, PNG_INFO_tIME, PNG_INFO_pCAL, PNG_INFO_sRGB, @@ -3607,6 +3617,11 @@ width, height, bit_depth, and color_type must be the same in each call. single transparent color for non-paletted images (PNG_INFO_tRNS) + png_set_eXIf(png_ptr, info_ptr, exif); + + hist - Exif profile (array of + png_byte) (PNG_INFO_eXIf) + png_set_hIST(png_ptr, info_ptr, hist); hist - histogram of palette (array of @@ -5715,6 +5730,11 @@ is an error. Previously this requirement of the PNG specification was not enforced, and the palette was always limited to 256 entries. An over-length PLTE chunk found in an input PNG is silently truncated. +Starting with libpng-1.6.31, the eXIf chunk is supported. Libpng does not +attempt to decode the Exif profile; it simply returns a byte array +containing the profile to the calling application which must do its own +decoding. + .SH XIII. Detecting libpng The png_get_io_ptr() function has been present since libpng-0.88, has never diff --git a/png.c b/png.c index 3a7d6f08e..2081d73f6 100644 --- a/png.c +++ b/png.c @@ -615,6 +615,16 @@ png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, } #endif +#ifdef PNG_eXIf_SUPPORTED + /* Free any eXIf entry */ + if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0) + { + png_free(png_ptr, info_ptr->exif); + info_ptr->exif = NULL; + info_ptr->valid &= ~PNG_INFO_eXIf; + } +#endif + #ifdef PNG_hIST_SUPPORTED /* Free any hIST entry */ if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0) diff --git a/png.h b/png.h index 62d6ac6ee..92f9bd188 100644 --- a/png.h +++ b/png.h @@ -776,6 +776,7 @@ typedef png_unknown_chunk * * png_unknown_chunkpp; #define PNG_INFO_sPLT 0x2000U /* ESR, 1.0.6 */ #define PNG_INFO_sCAL 0x4000U /* ESR, 1.0.6 */ #define PNG_INFO_IDAT 0x8000U /* ESR, 1.0.6 */ +#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */ /* This is used for the transformation routines, as some of them * change these values for the row. It also should enable using @@ -1788,7 +1789,8 @@ PNG_EXPORT(99, void, png_data_freer, (png_const_structrp png_ptr, #define PNG_FREE_PLTE 0x1000U #define PNG_FREE_TRNS 0x2000U #define PNG_FREE_TEXT 0x4000U -#define PNG_FREE_ALL 0x7fffU +#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */ +#define PNG_FREE_ALL 0xffffU #define PNG_FREE_MUL 0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ #ifdef PNG_USER_MEM_SUPPORTED @@ -2007,6 +2009,13 @@ PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, png_fixed_point int_blue_Z)) #endif +#ifdef PNG_eXIf_SUPPORTED +PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *exif)); +PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr, + png_inforp info_ptr, const png_bytep exif)); +#endif + #ifdef PNG_gAMA_SUPPORTED PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, png_const_inforp info_ptr, double *file_gamma)) @@ -2025,9 +2034,6 @@ PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, #ifdef PNG_hIST_SUPPORTED PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, png_inforp info_ptr, png_uint_16p *hist)); -#endif - -#ifdef PNG_hIST_SUPPORTED PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, png_inforp info_ptr, png_const_uint_16p hist)); #endif @@ -3253,7 +3259,7 @@ PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, * one to use is one more than this.) */ #ifdef PNG_EXPORT_LAST_ORDINAL - PNG_EXPORT_LAST_ORDINAL(245); + PNG_EXPORT_LAST_ORDINAL(247); #endif #ifdef __cplusplus diff --git a/pngget.c b/pngget.c index 141c39333..ace9e6351 100644 --- a/pngget.c +++ b/pngget.c @@ -773,6 +773,24 @@ png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr, } #endif +#ifdef PNG_eXIf_SUPPORTED +png_uint_32 PNGAPI +png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytep *exif) +{ + png_debug1(1, "in %s retrieval function", "eXIf"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_eXIf) != 0 && exif != NULL) + { + *exif = info_ptr->exif; + return (PNG_INFO_eXIf); + } + + return (0); +} +#endif + #ifdef PNG_hIST_SUPPORTED png_uint_32 PNGAPI png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr, diff --git a/pnginfo.h b/pnginfo.h index 361ed8be7..6e6d46a62 100644 --- a/pnginfo.h +++ b/pnginfo.h @@ -185,6 +185,11 @@ defined(PNG_READ_BACKGROUND_SUPPORTED) png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */ #endif +#ifdef PNG_eXIf_SUPPORTED + int num_exif; + png_bytep exif; +#endif + #ifdef PNG_hIST_SUPPORTED /* The hIST chunk contains the relative frequency or importance of the * various palette entries, so that a viewer can intelligently select a diff --git a/pngpriv.h b/pngpriv.h index 663608b0f..7c273bd5e 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -842,6 +842,7 @@ #define png_PLTE PNG_U32( 80, 76, 84, 69) #define png_bKGD PNG_U32( 98, 75, 71, 68) #define png_cHRM PNG_U32( 99, 72, 82, 77) +#define png_eXIf PNG_U32(101, 88, 73, 102) /* registered July 2017 */ #define png_fRAc PNG_U32(102, 82, 65, 99) /* registered, not defined */ #define png_gAMA PNG_U32(103, 65, 77, 65) #define png_gIFg PNG_U32(103, 73, 70, 103) @@ -1441,6 +1442,11 @@ PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); #endif +#ifdef PNG_READ_eXIf_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + #ifdef PNG_READ_gAMA_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); diff --git a/pngrutil.c b/pngrutil.c index bf3ef627e..9cde64845 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -2009,6 +2009,44 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #endif +#ifdef PNG_READ_eXIf_SUPPORTED +void /* PRIVATE */ +png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + unsigned int i; + png_bytep eXIf_buf; + + png_debug(1, "in png_handle_eXIf"); + + if ((png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_eXIf) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + eXIf_buf = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, length)); + + for (i = 0; i < length; i++) + { + png_byte buf[1]; + png_crc_read(png_ptr, buf, 1); + eXIf_buf[i] = buf[0]; + } + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + info_ptr->num_exif = length; + + png_set_eXIf(png_ptr, info_ptr, eXIf_buf); +} +#endif + #ifdef PNG_READ_hIST_SUPPORTED void /* PRIVATE */ png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) diff --git a/pngset.c b/pngset.c index 2bb575d38..998473557 100644 --- a/pngset.c +++ b/pngset.c @@ -134,6 +134,39 @@ png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, #endif /* cHRM */ +#ifdef PNG_eXIf_SUPPORTED +void PNGAPI +png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, + const png_bytep eXIf_buf) +{ + int i; + + png_debug1(1, "in %s storage function", "eXIf"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_free_data(png_ptr, info_ptr, PNG_FREE_EXIF, 0); + + info_ptr->exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr, + info_ptr->num_exif)); + + if (info_ptr->exif == NULL) + { + png_warning(png_ptr, "Insufficient memory for eXIf chunk data"); + + return; + } + + info_ptr->free_me |= PNG_FREE_EXIF; + + for (i = 0; i < info_ptr->num_exif; i++) + info_ptr->exif[i] = eXIf_buf[i]; + + info_ptr->valid |= PNG_INFO_eXIf; +} +#endif /* eXIf */ + #ifdef PNG_gAMA_SUPPORTED void PNGFAPI png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, diff --git a/pngstruct.h b/pngstruct.h index 749d7e35b..1e1c5f8f3 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -479,5 +479,8 @@ struct png_struct_def png_colorspace colorspace; #endif #endif + +/* New member added in libpng-1.6.30 */ + int num_exif; }; #endif /* PNGSTRUCT_H */ diff --git a/pngtest.c b/pngtest.c index 01e4143ea..77e25e124 100644 --- a/pngtest.c +++ b/pngtest.c @@ -1192,6 +1192,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } } #endif +#ifdef PNG_eXIf_SUPPORTED + { + png_bytep exif; + + if (png_get_eXIf(read_ptr, read_info_ptr, &exif) != 0) + png_set_eXIf(write_ptr, write_info_ptr, exif); + } +#endif #ifdef PNG_hIST_SUPPORTED { png_uint_16p hist; @@ -1530,6 +1538,14 @@ test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) } } #endif +#ifdef PNG_eXIf_SUPPORTED + { + png_bytep exif; + + if (png_get_eXIf(read_ptr, end_info_ptr, &exif) != 0) + png_set_eXIf(write_ptr, write_end_info_ptr, exif); + } +#endif #ifdef PNG_tIME_SUPPORTED { png_timep mod_time; diff --git a/pngwutil.c b/pngwutil.c index dd800586b..348bb524d 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1473,6 +1473,37 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) } #endif +#ifdef PNG_WRITE_eXIf_SUPPORTED +/* Write the Exif data */ +void /* PRIVATE */ +png_write_eXIf(png_structrp png_ptr, png_bytep exif, int num_exif) +{ + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_eXIf"); + + if (num_exif > (int)png_ptr->num_exif) + { + png_debug2(3, "num_exif = %d, png_ptr->num_exif = %d", num_exif, + png_ptr->num_exif); + + png_warning(png_ptr, "Invalid number of exif bytes specified"); + return; + } + + png_write_chunk_header(png_ptr, png_eXIf, (png_uint_32)(num_exif)); + + for (i = 0; i < num_exif; i++) + { + buf[i] = exif[i]; + png_write_chunk_data(png_ptr, buf, (png_size_t)1); + } + + png_write_chunk_end(png_ptr); +} +#endif + #ifdef PNG_WRITE_hIST_SUPPORTED /* Write the histogram */ void /* PRIVATE */ diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 4d4e13b16..cd7aaafd9 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -741,6 +741,8 @@ setting IDAT_READ_SIZE default PNG_ZBUF_SIZE # Ancillary chunks chunk bKGD chunk cHRM enables COLORSPACE +# enable eXIf only after chunk is approved +chunk eXIf chunk gAMA enables GAMMA chunk hIST chunk iCCP enables COLORSPACE, GAMMA diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index a9013953f..eb7048f5f 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -84,6 +84,7 @@ #define PNG_READ_USER_TRANSFORM_SUPPORTED #define PNG_READ_bKGD_SUPPORTED #define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_eXIf_SUPPORTED #define PNG_READ_gAMA_SUPPORTED #define PNG_READ_hIST_SUPPORTED #define PNG_READ_iCCP_SUPPORTED @@ -153,6 +154,7 @@ #define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #define PNG_WRITE_bKGD_SUPPORTED #define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_eXIf_SUPPORTED #define PNG_WRITE_gAMA_SUPPORTED #define PNG_WRITE_hIST_SUPPORTED #define PNG_WRITE_iCCP_SUPPORTED @@ -170,6 +172,7 @@ #define PNG_WRITE_zTXt_SUPPORTED #define PNG_bKGD_SUPPORTED #define PNG_cHRM_SUPPORTED +#define PNG_eXIf_SUPPORTED #define PNG_gAMA_SUPPORTED #define PNG_hIST_SUPPORTED #define PNG_iCCP_SUPPORTED diff --git a/scripts/symbols.def b/scripts/symbols.def index 89782c9cd..063650917 100644 --- a/scripts/symbols.def +++ b/scripts/symbols.def @@ -250,3 +250,5 @@ EXPORTS png_get_palette_max @243 png_set_option @244 png_image_write_to_memory @245 + png_get_eXIf @246 + png_set_eXIf @247