From 4914fb68b27844e23ab6f3293a314f58e2983f56 Mon Sep 17 00:00:00 2001 From: Pierre-Anthony Lemieux Date: Mon, 29 Jun 2026 15:41:53 -0700 Subject: [PATCH 1/3] Add mix rev test --- mixed_rev_irrev_4comp.j2c | Bin 0 -> 259 bytes tests/CMakeLists.txt | 13 +++ tests/test_qcd_qcc_markers.cpp | 143 +++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 mixed_rev_irrev_4comp.j2c create mode 100644 tests/test_qcd_qcc_markers.cpp diff --git a/mixed_rev_irrev_4comp.j2c b/mixed_rev_irrev_4comp.j2c new file mode 100644 index 0000000000000000000000000000000000000000..dfacd42f7e65cc6ee2913099f690b3cf3678e758 GIT binary patch literal 259 zcmezG|38qy$bkU}Km-WGfCG>L!Vtj1&d3Ny{{t8}7?^+(O8 + +#include "ojph_mem.h" +#include "ojph_file.h" +#include "ojph_codestream.h" +#include "ojph_params.h" +#include "gtest/gtest.h" + +/////////////////////////////////////////////////////////////////////////////// +// Test encoding a 4-component image where 3 components use the irreversible +// (9/7) wavelet and 1 component uses the reversible (5/3) wavelet. +// This exercises COC and QCC marker generation for mixed coding modes. +// Verifies that encoding succeeds, headers can be read back, and the +// per-component coding settings are preserved. +TEST(TestQcdQccMarkers, FourCompMixedReversibility) { + const ojph::ui32 width = 64; + const ojph::ui32 height = 64; + const ojph::ui32 num_comps = 4; + const ojph::ui32 bit_depth = 8; + const ojph::ui32 num_decomps = 5; + + // encode + { + ojph::codestream codestream; + + ojph::param_siz siz = codestream.access_siz(); + siz.set_image_extent(ojph::point(width, height)); + siz.set_num_components(num_comps); + for (ojph::ui32 c = 0; c < num_comps; ++c) + siz.set_component(c, ojph::point(1, 1), bit_depth, false); + siz.set_image_offset(ojph::point(0, 0)); + siz.set_tile_size(ojph::size(width, height)); + siz.set_tile_offset(ojph::point(0, 0)); + + ojph::param_cod cod = codestream.access_cod(); + cod.set_num_decomposition(num_decomps); + cod.set_block_dims(64, 64); + cod.set_color_transform(false); + cod.set_reversible(false); + + ojph::param_coc coc3 = cod.get_coc(3); + coc3.set_reversible(true); + + codestream.access_qcd().set_irrev_quant(0.01f); + codestream.set_planar(true); + + ojph::j2c_outfile j2c_file; + j2c_file.open("mixed_rev_irrev_4comp.j2c"); + codestream.write_headers(&j2c_file); + + ojph::ui32 next_comp; + ojph::line_buf* cur_line = codestream.exchange(NULL, next_comp); + + for (ojph::ui32 c = 0; c < num_comps; ++c) + { + for (ojph::ui32 y = 0; y < height; ++y) + { + ASSERT_EQ(next_comp, c); + if (cur_line->flags & ojph::line_buf::LFT_INTEGER) + memset(cur_line->i32, 0, + sizeof(ojph::si32) * cur_line->size); + else + memset(cur_line->f32, 0, + sizeof(float) * cur_line->size); + cur_line = codestream.exchange(cur_line, next_comp); + } + } + + codestream.flush(); + codestream.close(); + } + + // read back headers and verify per-component settings + { + ojph::codestream codestream; + ojph::j2c_infile j2c_file; + j2c_file.open("mixed_rev_irrev_4comp.j2c"); + codestream.read_headers(&j2c_file); + + ojph::param_siz siz = codestream.access_siz(); + EXPECT_EQ(siz.get_num_components(), num_comps); + for (ojph::ui32 c = 0; c < num_comps; ++c) + EXPECT_EQ(siz.get_bit_depth(c), bit_depth); + + ojph::param_cod cod = codestream.access_cod(); + EXPECT_FALSE(cod.is_reversible()); + + for (ojph::ui32 c = 0; c < 3; ++c) { + ojph::param_coc coc = cod.get_coc(c); + EXPECT_FALSE(coc.is_reversible()) + << "Component " << c << " should be irreversible"; + } + + ojph::param_coc coc3 = cod.get_coc(3); + EXPECT_TRUE(coc3.is_reversible()) + << "Component 3 should be reversible"; + + codestream.close(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From a1b5725a9c654373c5394dfbd12fd2e7ab911e3e Mon Sep 17 00:00:00 2001 From: Pierre-Anthony Lemieux Date: Mon, 29 Jun 2026 20:58:38 -0700 Subject: [PATCH 2/3] Improve unit test and fix get_coc() --- src/core/codestream/ojph_params_local.h | 20 ++++++++++-- tests/test_qcd_qcc_markers.cpp | 42 ++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/core/codestream/ojph_params_local.h b/src/core/codestream/ojph_params_local.h index fd5e2256..91efb94f 100644 --- a/src/core/codestream/ojph_params_local.h +++ b/src/core/codestream/ojph_params_local.h @@ -627,10 +627,24 @@ namespace ojph { void init(param_cod* top_cod, ui16 comp_idx) { type = top_cod ? COC_MAIN : COD_MAIN; - Lcod = 0; - Scod = 0; + if (top_cod) + { + // a freshly materialized COC inherits the COD's current + // settings, matching the fallback used by the const get_coc() + // when no COC marker exists for a component + Lcod = top_cod->Lcod; + Scod = top_cod->Scod; + SGCod = top_cod->SGCod; + SPcod = top_cod->SPcod; + atk = top_cod->atk; + } + else + { + Lcod = 0; + Scod = 0; + atk = NULL; + } next = NULL; - atk = NULL; this->top_cod = top_cod; this->comp_idx = comp_idx; } diff --git a/tests/test_qcd_qcc_markers.cpp b/tests/test_qcd_qcc_markers.cpp index bfc5836c..8271c7f5 100644 --- a/tests/test_qcd_qcc_markers.cpp +++ b/tests/test_qcd_qcc_markers.cpp @@ -36,6 +36,7 @@ //***************************************************************************/ #include +#include #include "ojph_mem.h" #include "ojph_file.h" @@ -56,6 +57,10 @@ TEST(TestQcdQccMarkers, FourCompMixedReversibility) { const ojph::ui32 bit_depth = 8; const ojph::ui32 num_decomps = 5; + // samples fed into component 3 during encoding, kept around so they can + // be compared against the decoded output further down + std::vector comp3_orig(width * height); + // encode { ojph::codestream codestream; @@ -93,7 +98,17 @@ TEST(TestQcdQccMarkers, FourCompMixedReversibility) { for (ojph::ui32 y = 0; y < height; ++y) { ASSERT_EQ(next_comp, c); - if (cur_line->flags & ojph::line_buf::LFT_INTEGER) + if (c == 3) + { + // non-constant pattern so a roundtrip mismatch would be caught + for (ojph::ui32 x = 0; x < width; ++x) + { + ojph::si32 val = (ojph::si32)((x + y * width) % 256); + cur_line->i32[x] = val; + comp3_orig[y * width + x] = val; + } + } + else if (cur_line->flags & ojph::line_buf::LFT_INTEGER) memset(cur_line->i32, 0, sizeof(ojph::si32) * cur_line->size); else @@ -132,6 +147,31 @@ TEST(TestQcdQccMarkers, FourCompMixedReversibility) { EXPECT_TRUE(coc3.is_reversible()) << "Component 3 should be reversible"; + // decode all components and verify component 3 (reversible) samples + // are identical to what was encoded + codestream.restrict_input_resolution(0, 0); + codestream.set_planar(true); + codestream.create(); + + for (ojph::ui32 c = 0; c < num_comps; ++c) + { + ojph::ui32 comp_height = siz.get_recon_height(c); + ojph::ui32 comp_width = siz.get_recon_width(c); + for (ojph::ui32 y = 0; y < comp_height; ++y) + { + ojph::ui32 comp_num; + ojph::line_buf* line = codestream.pull(comp_num); + ASSERT_EQ(comp_num, c); + if (c == 3) + { + ASSERT_EQ(comp_width, width); + for (ojph::ui32 x = 0; x < width; ++x) + EXPECT_EQ(line->i32[x], comp3_orig[y * width + x]) + << "Component 3 sample mismatch at (" << x << ", " << y << ")"; + } + } + } + codestream.close(); } } From d18c9ef58a119b5999c974df6694ddc474604bc0 Mon Sep 17 00:00:00 2001 From: Pierre-Anthony Lemieux Date: Mon, 29 Jun 2026 21:05:52 -0700 Subject: [PATCH 3/3] Fix copyright and file names --- tests/CMakeLists.txt | 8 ++++---- tests/{test_qcd_qcc_markers.cpp => test_mixed_coc.cpp} | 8 +++----- 2 files changed, 7 insertions(+), 9 deletions(-) rename tests/{test_qcd_qcc_markers.cpp => test_mixed_coc.cpp} (96%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9e561a3c..1b6252b4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -42,19 +42,19 @@ target_link_libraries( # configure QCD/QCC marker tests (library API tests) add_executable( - test_qcd_qcc_markers - test_qcd_qcc_markers.cpp + test_mixed_coc + test_mixed_coc.cpp ) target_link_libraries( - test_qcd_qcc_markers + test_mixed_coc openjph GTest::gtest_main ) include(GoogleTest) gtest_add_tests(TARGET test_executables) -gtest_add_tests(TARGET test_qcd_qcc_markers) +gtest_add_tests(TARGET test_mixed_coc) if (MSVC) add_custom_command(TARGET test_executables POST_BUILD diff --git a/tests/test_qcd_qcc_markers.cpp b/tests/test_mixed_coc.cpp similarity index 96% rename from tests/test_qcd_qcc_markers.cpp rename to tests/test_mixed_coc.cpp index 8271c7f5..73bc5a95 100644 --- a/tests/test_qcd_qcc_markers.cpp +++ b/tests/test_mixed_coc.cpp @@ -2,9 +2,7 @@ // This software is released under the 2-Clause BSD license, included // below. // -// Copyright (c) 2019, Aous Naman -// Copyright (c) 2019, Kakadu Software Pty Ltd, Australia -// Copyright (c) 2019, The University of New South Wales, Australia +// Copyright (c) Pierre-Anthony Lemieux // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are @@ -30,7 +28,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //***************************************************************************/ // This file is part of the OpenJPH software implementation. -// File: test_qcd_qcc_markers.cpp +// File: test_mixed_coc.cpp // Author: Pierre-Anthony Lemieux // Date: 29 June 2026 //***************************************************************************/ @@ -50,7 +48,7 @@ // This exercises COC and QCC marker generation for mixed coding modes. // Verifies that encoding succeeds, headers can be read back, and the // per-component coding settings are preserved. -TEST(TestQcdQccMarkers, FourCompMixedReversibility) { +TEST(TestMixedCOC, FourCompMixedReversibility) { const ojph::ui32 width = 64; const ojph::ui32 height = 64; const ojph::ui32 num_comps = 4;