From 0f528e95788863608aa1772f5370659edf618793 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 17 Mar 2023 18:16:35 +0100 Subject: [PATCH 1/2] opj_jp2_read_header(): move setting color_space here instead in opj_jp2_decode()/get_tile() (fixes #570) --- src/lib/openjp2/jp2.c | 128 ++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 81 deletions(-) diff --git a/src/lib/openjp2/jp2.c b/src/lib/openjp2/jp2.c index ec2022722..25f379af8 100644 --- a/src/lib/openjp2/jp2.c +++ b/src/lib/openjp2/jp2.c @@ -1594,22 +1594,10 @@ static OPJ_BOOL opj_jp2_read_colr(opj_jp2_t *jp2, return OPJ_TRUE; } -OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2, - opj_stream_private_t *p_stream, - opj_image_t* p_image, - opj_event_mgr_t * p_manager) +static OPJ_BOOL opj_jp2_apply_color_postprocessing(opj_jp2_t *jp2, + opj_image_t* p_image, + opj_event_mgr_t * p_manager) { - if (!p_image) { - return OPJ_FALSE; - } - - /* J2K decoding */ - if (! opj_j2k_decode(jp2->j2k, p_stream, p_image, p_manager)) { - opj_event_msg(p_manager, EVT_ERROR, - "Failed to decode the codestream in the JP2 file\n"); - return OPJ_FALSE; - } - if (jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) { /* Bypass all JP2 component transforms */ return OPJ_TRUE; @@ -1620,21 +1608,6 @@ OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2, return OPJ_FALSE; } - /* Set Image Color Space */ - if (jp2->enumcs == 16) { - p_image->color_space = OPJ_CLRSPC_SRGB; - } else if (jp2->enumcs == 17) { - p_image->color_space = OPJ_CLRSPC_GRAY; - } else if (jp2->enumcs == 18) { - p_image->color_space = OPJ_CLRSPC_SYCC; - } else if (jp2->enumcs == 24) { - p_image->color_space = OPJ_CLRSPC_EYCC; - } else if (jp2->enumcs == 12) { - p_image->color_space = OPJ_CLRSPC_CMYK; - } else { - p_image->color_space = OPJ_CLRSPC_UNKNOWN; - } - if (jp2->color.jp2_pclr) { /* Part 1, I.5.3.4: Either both or none : */ if (!jp2->color.jp2_pclr->cmap) { @@ -1661,6 +1634,25 @@ OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2, return OPJ_TRUE; } +OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2, + opj_stream_private_t *p_stream, + opj_image_t* p_image, + opj_event_mgr_t * p_manager) +{ + if (!p_image) { + return OPJ_FALSE; + } + + /* J2K decoding */ + if (! opj_j2k_decode(jp2->j2k, p_stream, p_image, p_manager)) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to decode the codestream in the JP2 file\n"); + return OPJ_FALSE; + } + + return opj_jp2_apply_color_postprocessing(jp2, p_image, p_manager); +} + static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2, opj_stream_private_t *stream, opj_event_mgr_t * p_manager @@ -2843,6 +2835,8 @@ OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream, opj_event_mgr_t * p_manager ) { + int ret; + /* preconditions */ assert(jp2 != 00); assert(p_stream != 00); @@ -2876,10 +2870,28 @@ OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream, return OPJ_FALSE; } - return opj_j2k_read_header(p_stream, - jp2->j2k, - p_image, - p_manager); + ret = opj_j2k_read_header(p_stream, + jp2->j2k, + p_image, + p_manager); + + if (p_image && *p_image) { + /* Set Image Color Space */ + if (jp2->enumcs == 16) { + (*p_image)->color_space = OPJ_CLRSPC_SRGB; + } else if (jp2->enumcs == 17) { + (*p_image)->color_space = OPJ_CLRSPC_GRAY; + } else if (jp2->enumcs == 18) { + (*p_image)->color_space = OPJ_CLRSPC_SYCC; + } else if (jp2->enumcs == 24) { + (*p_image)->color_space = OPJ_CLRSPC_EYCC; + } else if (jp2->enumcs == 12) { + (*p_image)->color_space = OPJ_CLRSPC_CMYK; + } else { + (*p_image)->color_space = OPJ_CLRSPC_UNKNOWN; + } + } + return ret; } static OPJ_BOOL opj_jp2_setup_encoding_validation(opj_jp2_t *jp2, @@ -3123,53 +3135,7 @@ OPJ_BOOL opj_jp2_get_tile(opj_jp2_t *p_jp2, return OPJ_FALSE; } - if (p_jp2->j2k->m_specific_param.m_decoder.m_numcomps_to_decode) { - /* Bypass all JP2 component transforms */ - return OPJ_TRUE; - } - - if (!opj_jp2_check_color(p_image, &(p_jp2->color), p_manager)) { - return OPJ_FALSE; - } - - /* Set Image Color Space */ - if (p_jp2->enumcs == 16) { - p_image->color_space = OPJ_CLRSPC_SRGB; - } else if (p_jp2->enumcs == 17) { - p_image->color_space = OPJ_CLRSPC_GRAY; - } else if (p_jp2->enumcs == 18) { - p_image->color_space = OPJ_CLRSPC_SYCC; - } else if (p_jp2->enumcs == 24) { - p_image->color_space = OPJ_CLRSPC_EYCC; - } else if (p_jp2->enumcs == 12) { - p_image->color_space = OPJ_CLRSPC_CMYK; - } else { - p_image->color_space = OPJ_CLRSPC_UNKNOWN; - } - - if (p_jp2->color.jp2_pclr) { - /* Part 1, I.5.3.4: Either both or none : */ - if (!p_jp2->color.jp2_pclr->cmap) { - opj_jp2_free_pclr(&(p_jp2->color)); - } else { - if (!opj_jp2_apply_pclr(p_image, &(p_jp2->color), p_manager)) { - return OPJ_FALSE; - } - } - } - - /* Apply the color space if needed */ - if (p_jp2->color.jp2_cdef) { - opj_jp2_apply_cdef(p_image, &(p_jp2->color), p_manager); - } - - if (p_jp2->color.icc_profile_buf) { - p_image->icc_profile_buf = p_jp2->color.icc_profile_buf; - p_image->icc_profile_len = p_jp2->color.icc_profile_len; - p_jp2->color.icc_profile_buf = NULL; - } - - return OPJ_TRUE; + return opj_jp2_apply_color_postprocessing(p_jp2, p_image, p_manager); } /* ----------------------------------------------------------------------- */ From 59ec1f0ae6edb19f45ab06792aa8902759af6954 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 26 Mar 2023 14:03:59 +0200 Subject: [PATCH 2/2] opj_jp2_read_header(): move setting icc_profile here instead in opj_jp2_decode()/get_tile() + add unit test (fixes #570) --- src/lib/openjp2/jp2.c | 12 ++-- tests/unit/CMakeLists.txt | 4 ++ tests/unit/testjp2.c | 144 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 6 deletions(-) create mode 100644 tests/unit/testjp2.c diff --git a/src/lib/openjp2/jp2.c b/src/lib/openjp2/jp2.c index 25f379af8..6015190e1 100644 --- a/src/lib/openjp2/jp2.c +++ b/src/lib/openjp2/jp2.c @@ -1623,12 +1623,6 @@ static OPJ_BOOL opj_jp2_apply_color_postprocessing(opj_jp2_t *jp2, if (jp2->color.jp2_cdef) { opj_jp2_apply_cdef(p_image, &(jp2->color), p_manager); } - - if (jp2->color.icc_profile_buf) { - p_image->icc_profile_buf = jp2->color.icc_profile_buf; - p_image->icc_profile_len = jp2->color.icc_profile_len; - jp2->color.icc_profile_buf = NULL; - } } return OPJ_TRUE; @@ -2890,6 +2884,12 @@ OPJ_BOOL opj_jp2_read_header(opj_stream_private_t *p_stream, } else { (*p_image)->color_space = OPJ_CLRSPC_UNKNOWN; } + + if (jp2->color.icc_profile_buf) { + (*p_image)->icc_profile_buf = jp2->color.icc_profile_buf; + (*p_image)->icc_profile_len = jp2->color.icc_profile_len; + jp2->color.icc_profile_buf = NULL; + } } return ret; } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 772b1a30f..652643e61 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -15,3 +15,7 @@ foreach(ut ${unit_test}) target_link_libraries(${ut} openjp2) add_test(NAME ${ut} COMMAND ${ut}) endforeach() + +add_executable(testjp2 testjp2.c) +target_link_libraries(testjp2 openjp2) +add_test(NAME testjp2 COMMAND testjp2 ${OPJ_DATA_ROOT}) diff --git a/tests/unit/testjp2.c b/tests/unit/testjp2.c new file mode 100644 index 000000000..0e7a85b37 --- /dev/null +++ b/tests/unit/testjp2.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023, Even Rouault + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "openjpeg.h" + +static void test_colorspace(const char* pszDirectory) +{ + char szFile[2048]; + opj_image_t* image = NULL; + opj_stream_t *l_stream = NULL; /* Stream */ + opj_codec_t* l_codec = NULL; /* Handle to a decompressor */ + opj_dparameters_t parameters; /* decompression parameters */ + + snprintf(szFile, sizeof(szFile), "%s/input/conformance/file1.jp2", + pszDirectory); + l_stream = opj_stream_create_default_file_stream(szFile, 1); + if (!l_stream) { + fprintf(stderr, "ERROR -> failed to create the stream from the file %s\n", + szFile); + exit(1); + } + l_codec = opj_create_decompress(OPJ_CODEC_JP2); + + /* Setup the decoder */ + opj_set_default_decoder_parameters(¶meters); + if (!opj_setup_decoder(l_codec, ¶meters)) { + fprintf(stderr, "ERROR -> opj_decompress: failed to setup the decoder\n"); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + exit(1); + } + + /* Read the main header of the codestream and if necessary the JP2 boxes*/ + if (! opj_read_header(l_stream, l_codec, &image)) { + fprintf(stderr, "ERROR -> opj_decompress: failed to read the header\n"); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + exit(1); + } + + /* Check that color_space is set after opj_read_header() */ + if (image->color_space != OPJ_CLRSPC_SRGB) { + fprintf(stderr, "ERROR -> image->color_space (=%d) != OPJ_CLRSPC_SRGB\n", + image->color_space); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + exit(1); + } + + opj_destroy_codec(l_codec); + opj_stream_destroy(l_stream); + opj_image_destroy(image); +} + +static void test_iccprofile(const char* pszDirectory) +{ + char szFile[2048]; + opj_image_t* image = NULL; + opj_stream_t *l_stream = NULL; /* Stream */ + opj_codec_t* l_codec = NULL; /* Handle to a decompressor */ + opj_dparameters_t parameters; /* decompression parameters */ + + snprintf(szFile, sizeof(szFile), "%s/input/nonregression/relax.jp2", + pszDirectory); + l_stream = opj_stream_create_default_file_stream(szFile, 1); + if (!l_stream) { + fprintf(stderr, "ERROR -> failed to create the stream from the file %s\n", + szFile); + exit(1); + } + l_codec = opj_create_decompress(OPJ_CODEC_JP2); + + /* Setup the decoder */ + opj_set_default_decoder_parameters(¶meters); + if (!opj_setup_decoder(l_codec, ¶meters)) { + fprintf(stderr, "ERROR -> opj_decompress: failed to setup the decoder\n"); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + exit(1); + } + + /* Read the main header of the codestream and if necessary the JP2 boxes*/ + if (! opj_read_header(l_stream, l_codec, &image)) { + fprintf(stderr, "ERROR -> opj_decompress: failed to read the header\n"); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + exit(1); + } + + /* Check that icc_profile_len is set after opj_read_header() */ + if (image->icc_profile_len != 278) { + fprintf(stderr, "ERROR -> image->icc_profile_len (=%d) != 278\n", + image->icc_profile_len); + opj_stream_destroy(l_stream); + opj_destroy_codec(l_codec); + opj_image_destroy(image); + exit(1); + } + + opj_destroy_codec(l_codec); + opj_stream_destroy(l_stream); + opj_image_destroy(image); +} + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + fprintf(stderr, "usage: testjp2 /path/to/opj_data_root\n"); + exit(1); + } + + test_colorspace(argv[1]); + test_iccprofile(argv[1]); + + return 0; +}