From 65925ad4b2cbed934d5d850fe764dc46c4becbcb Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Sat, 22 Jun 2024 00:35:55 -0400 Subject: [PATCH] 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). Reviewed-by: John Bowler Reviewed-by: Chris Blume Reviewed-by: Cosmin Truta Signed-off-by: Cosmin Truta --- AUTHORS.md | 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.md b/AUTHORS.md index 544341694a..f30a4ee192 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -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 5d3c706c80..ba6894c8a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -811,6 +811,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_1(png_const_structrp png_ptr, png_const_inforp info_ptr, diff --git a/pnginfo.h b/pnginfo.h index 1f98dedc42..7df4f5b845 100644 --- a/pnginfo.h +++ b/pnginfo.h @@ -101,6 +101,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 ffab19c08c..c5fa70523f 100644 --- a/pngpread.c +++ b/pngpread.c @@ -294,6 +294,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 b59084e7eb..79de6e9e92 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -835,6 +835,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) @@ -1131,6 +1132,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); @@ -1474,6 +1481,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 07a39df6e2..e29218b394 100644 --- a/pngread.c +++ b/pngread.c @@ -174,6 +174,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 d31dc21dae..6dd1cf5f17 100644 --- a/pngrutil.c +++ b/pngrutil.c @@ -2032,6 +2032,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 ef449d667f..c5590a9a57 100644 --- a/pngset.c +++ b/pngset.c @@ -134,6 +134,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_1(png_const_structrp png_ptr, png_inforp info_ptr, diff --git a/pngtest.c b/pngtest.c index 9bdf6274d0..6490319459 100644 --- a/pngtest.c +++ b/pngtest.c @@ -1206,6 +1206,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 8727ea47de..f659afdf52 100644 --- a/pngwrite.c +++ b/pngwrite.c @@ -237,6 +237,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 61596f68fe..b8f8d170d1 100644 --- a/pngwutil.c +++ b/pngwutil.c @@ -1471,6 +1471,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 da79e6878c..3de27b5928 100644 --- a/scripts/pnglibconf.dfa +++ b/scripts/pnglibconf.dfa @@ -844,6 +844,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 45defca3ee..adfbf264fc 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 @@ -157,6 +158,7 @@ #define PNG_WRITE_USER_TRANSFORM_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 @@ -175,6 +177,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 3e9448c643..c084d26730 100644 --- a/scripts/symbols.def +++ b/scripts/symbols.def @@ -248,3 +248,5 @@ EXPORTS png_image_write_to_memory @245 png_get_eXIf_1 @248 png_set_eXIf_1 @249 + png_get_cICP @250 + png_set_cICP @251