From 75748d93ce7b6084381d369ec79d1587b5793f69 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Sat, 22 Jun 2024 00:35:55 -0400 Subject: [PATCH] [libpng16] Add support for reading and writing the cICP chunk This chunk was added in the third edition of the PNG specification and contains Coding Independent Code Points (related to color space description). It is fairly simple as it only contains four fields of one byte each: Colour Primaries, Transfer Function, Matrix Coefficients, Video Full Range Flag. The test file originally comes from the related WPT test case: https://github.com/web-platform-tests/wpt/blob/master/png/support/cicp-display-p3.png Note that I reencoded the file to make it match libpng's default encoding parameters (it only modifies the IDAT chunk). This is a cherry-pick of commit 65925ad4b2cbed934d5d850fe764dc46c4becbcb from branch 'libpng18'. Reviewed-by: John Bowler Reviewed-by: Chris Blume Reviewed-by: Cosmin Truta Signed-off-by: Cosmin Truta --- AUTHORS | 1 + CMakeLists.txt | 4 ++++ cicp-display-p3_reencoded.png | Bin 0 -> 142 bytes png.h | 14 ++++++++++++- pngget.c | 25 +++++++++++++++++++++++ pnginfo.h | 8 ++++++++ pngpread.c | 8 ++++++++ pngpriv.h | 12 +++++++++++ pngread.c | 4 ++++ pngrutil.c | 37 ++++++++++++++++++++++++++++++++++ pngset.c | 20 ++++++++++++++++++ pngtest.c | 17 ++++++++++++++++ pngwrite.c | 10 +++++++++ pngwutil.c | 20 ++++++++++++++++++ scripts/pnglibconf.dfa | 1 + scripts/pnglibconf.h.prebuilt | 3 +++ scripts/symbols.def | 2 ++ 17 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 cicp-display-p3_reencoded.png diff --git a/AUTHORS b/AUTHORS index 544341694..f30a4ee19 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,6 +17,7 @@ Authors, for copyright and licensing purposes. * James Yu * John Bowler * Kevin Bracey + * Lucas Chollet * Magnus Holmgren * Mandar Sahastrabuddhe * Mans Rullgard diff --git a/CMakeLists.txt b/CMakeLists.txt index d62485412..5c6866f42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -814,6 +814,10 @@ if(PNG_TESTS AND PNG_SHARED) COMMAND pngtest FILES "${PNGTEST_PNG}") + png_add_test(NAME pngtest-cicp + COMMAND pngtest + FILES "${CMAKE_CURRENT_SOURCE_DIR}/cicp-display-p3_reencoded.png") + add_executable(pngvalid ${pngvalid_sources}) target_link_libraries(pngvalid PRIVATE png_shared) diff --git a/cicp-display-p3_reencoded.png b/cicp-display-p3_reencoded.png new file mode 100644 index 0000000000000000000000000000000000000000..47830bd6b358903bcb42d251a7efa8d727d30001 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^DL`z*!3-qlzV7P*Qak}ZA+A9B|NsBSX)}T3oQ)he zfqa%^Pv-z0UIxZI=Ev`Wd|gi$$B>BDw`UD`ftr{XEc@pget2GNLcf~95Vo7yD_DZ$=K#dHZu6{1-oD!Mvalid & PNG_INFO_cICP) != 0 && + colour_primaries != NULL && transfer_function != NULL && + matrix_coefficients != NULL && video_full_range_flag != NULL) + { + *colour_primaries = info_ptr->cicp_colour_primaries; + *transfer_function = info_ptr->cicp_transfer_function; + *matrix_coefficients = info_ptr->cicp_matrix_coefficients; + *video_full_range_flag = info_ptr->cicp_video_full_range_flag; + return (PNG_INFO_cICP); + } + + return (0); +} +#endif + #ifdef PNG_eXIf_SUPPORTED png_uint_32 PNGAPI png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, diff --git a/pnginfo.h b/pnginfo.h index ea4f11500..e85420c1a 100644 --- a/pnginfo.h +++ b/pnginfo.h @@ -100,6 +100,14 @@ struct png_info_def png_colorspace colorspace; #endif +#ifdef PNG_cICP_SUPPORTED + /* cICP chunk data */ + png_byte cicp_colour_primaries; + png_byte cicp_transfer_function; + png_byte cicp_matrix_coefficients; + png_byte cicp_video_full_range_flag; +#endif + #ifdef PNG_iCCP_SUPPORTED /* iCCP chunk data. */ png_charp iccp_name; /* profile name */ diff --git a/pngpread.c b/pngpread.c index 322b0cf9f..efa03f978 100644 --- a/pngpread.c +++ b/pngpread.c @@ -308,6 +308,14 @@ png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); } +#endif +#ifdef PNG_READ_cICP_SUPPORTED + else if (png_ptr->chunk_name == png_cICP) + { + PNG_PUSH_SAVE_BUFFER_IF_FULL + png_handle_cICP(png_ptr, info_ptr, png_ptr->push_length); + } + #endif #ifdef PNG_READ_eXIf_SUPPORTED else if (png_ptr->chunk_name == png_eXIf) diff --git a/pngpriv.h b/pngpriv.h index 62615bee8..84f77c350 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -834,6 +834,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_cICP PNG_U32( 99, 73, 67, 80) #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) @@ -1130,6 +1131,12 @@ PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr, /* The xy value must have been previously validated */ #endif +#ifdef PNG_WRITE_cICP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_cICP,(png_structrp png_ptr, + png_byte colour_primaries, png_byte transfer_function, + png_byte matrix_coefficients, png_byte video_full_range_flag), PNG_EMPTY); +#endif + #ifdef PNG_WRITE_sRGB_SUPPORTED PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, int intent),PNG_EMPTY); @@ -1473,6 +1480,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_cICP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_cICP,(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); diff --git a/pngread.c b/pngread.c index b10b42651..317951f3d 100644 --- a/pngread.c +++ b/pngread.c @@ -173,6 +173,10 @@ png_read_info(png_structrp png_ptr, png_inforp info_ptr) else if (chunk_name == png_cHRM) png_handle_cHRM(png_ptr, info_ptr, length); #endif +#ifdef PNG_READ_cICP_SUPPORTED + else if (chunk_name == png_cICP) + png_handle_cICP(png_ptr, info_ptr, length); +#endif #ifdef PNG_READ_eXIf_SUPPORTED else if (chunk_name == png_eXIf) diff --git a/pngrutil.c b/pngrutil.c index 44b3a6869..282d976c8 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -2046,6 +2046,43 @@ png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) } #endif +#ifdef PNG_READ_cICP_SUPPORTED +void /* PRIVATE */ +png_handle_cICP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +{ + png_byte buf[4]; + + png_debug(1, "in png_handle_cICP"); + + 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_cICP) != 0) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + if ((png_ptr->mode & PNG_HAVE_IDAT) != 0) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 4) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_crc_read(png_ptr, buf, 4); + + if (png_crc_finish(png_ptr, 0) != 0) + return; + + png_set_cICP(png_ptr, info_ptr, buf[0], buf[1], buf[2], buf[3]); +} +#endif + #ifdef PNG_READ_eXIf_SUPPORTED void /* PRIVATE */ png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) diff --git a/pngset.c b/pngset.c index b530f65d1..c4f286733 100644 --- a/pngset.c +++ b/pngset.c @@ -133,6 +133,26 @@ png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, #endif /* cHRM */ +#ifdef PNG_cICP_SUPPORTED +void PNGAPI +png_set_cICP(png_const_structrp png_ptr, + png_inforp info_ptr, png_byte colour_primaries, + png_byte transfer_function, png_byte matrix_coefficients, + png_byte video_full_range_flag) +{ + png_debug1(1, "in %s storage function", "cICP"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->cicp_colour_primaries = colour_primaries; + info_ptr->cicp_transfer_function = transfer_function; + info_ptr->cicp_matrix_coefficients = matrix_coefficients; + info_ptr->cicp_video_full_range_flag = video_full_range_flag; + info_ptr->valid |= PNG_INFO_cICP; +} +#endif /* cICP */ + #ifdef PNG_eXIf_SUPPORTED void PNGAPI png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr, diff --git a/pngtest.c b/pngtest.c index bcf0f30b5..60da60825 100644 --- a/pngtest.c +++ b/pngtest.c @@ -1205,6 +1205,23 @@ test_one_file(const char *inname, const char *outname) png_set_bKGD(write_ptr, write_info_ptr, background); } #endif +#ifdef PNG_cICP_SUPPORTED + { + png_byte colour_primaries; + png_byte transfer_function; + png_byte matrix_coefficients; + png_byte video_full_range_flag; + + if (png_get_cICP(read_ptr, read_info_ptr, &colour_primaries, + &transfer_function, &matrix_coefficients, + &video_full_range_flag) != 0) +#ifdef PNG_WRITE_cICP_SUPPORTED + png_set_cICP(write_ptr, write_info_ptr, colour_primaries, + transfer_function, matrix_coefficients, + video_full_range_flag); +#endif + } +#endif #ifdef PNG_READ_eXIf_SUPPORTED { png_bytep exif = NULL; diff --git a/pngwrite.c b/pngwrite.c index 0ed6a551a..8b72304f4 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -236,6 +236,16 @@ png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); #endif +#ifdef PNG_WRITE_cICP_SUPPORTED + if ((info_ptr->valid & PNG_INFO_cICP) != 0) + { + png_write_cICP(png_ptr, info_ptr->cicp_colour_primaries, + info_ptr->cicp_transfer_function, + info_ptr->cicp_matrix_coefficients, + info_ptr->cicp_video_full_range_flag); + } +#endif + #ifdef PNG_WRITE_eXIf_SUPPORTED if ((info_ptr->valid & PNG_INFO_eXIf) != 0) { diff --git a/pngwutil.c b/pngwutil.c index abe7b07f9..11f343daa 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1488,6 +1488,26 @@ png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) } #endif +#ifdef PNG_WRITE_cICP_SUPPORTED +/* Write the cICP data */ +void /* PRIVATE */ +png_write_cICP(png_structrp png_ptr, + png_byte colour_primaries, png_byte transfer_function, + png_byte matrix_coefficients, png_byte video_full_range_flag) +{ + png_debug(1, "in png_write_cICP"); + + png_write_chunk_header(png_ptr, png_cICP, 4); + + png_write_chunk_data(png_ptr, &colour_primaries, 1); + png_write_chunk_data(png_ptr, &transfer_function, 1); + png_write_chunk_data(png_ptr, &matrix_coefficients, 1); + png_write_chunk_data(png_ptr, &video_full_range_flag, 1); + + png_write_chunk_end(png_ptr); +} +#endif + #ifdef PNG_WRITE_eXIf_SUPPORTED /* Write the Exif data */ void /* PRIVATE */ diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa index 0648f0721..0a2345447 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -846,6 +846,7 @@ setting IDAT_READ_SIZE default PNG_ZBUF_SIZE # Ancillary chunks chunk bKGD chunk cHRM enables COLORSPACE +chunk cICP chunk eXIf chunk gAMA enables GAMMA chunk hIST diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt index 6c3e43ecd..613d7e91f 100644 --- a/scripts/pnglibconf.h.prebuilt +++ b/scripts/pnglibconf.h.prebuilt @@ -88,6 +88,7 @@ #define PNG_READ_USER_TRANSFORM_SUPPORTED #define PNG_READ_bKGD_SUPPORTED #define PNG_READ_cHRM_SUPPORTED +#define PNG_READ_cICP_SUPPORTED #define PNG_READ_eXIf_SUPPORTED #define PNG_READ_gAMA_SUPPORTED #define PNG_READ_hIST_SUPPORTED @@ -158,6 +159,7 @@ #define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED #define PNG_WRITE_bKGD_SUPPORTED #define PNG_WRITE_cHRM_SUPPORTED +#define PNG_WRITE_cICP_SUPPORTED #define PNG_WRITE_eXIf_SUPPORTED #define PNG_WRITE_gAMA_SUPPORTED #define PNG_WRITE_hIST_SUPPORTED @@ -176,6 +178,7 @@ #define PNG_WRITE_zTXt_SUPPORTED #define PNG_bKGD_SUPPORTED #define PNG_cHRM_SUPPORTED +#define PNG_cICP_SUPPORTED #define PNG_eXIf_SUPPORTED #define PNG_gAMA_SUPPORTED #define PNG_hIST_SUPPORTED diff --git a/scripts/symbols.def b/scripts/symbols.def index 82494bbf9..305bd1a28 100644 --- a/scripts/symbols.def +++ b/scripts/symbols.def @@ -253,3 +253,5 @@ EXPORTS png_set_eXIf @247 png_get_eXIf_1 @248 png_set_eXIf_1 @249 + png_get_cICP @250 + png_set_cICP @251