From 0f21481f8a4be073704ff2f625742097f3f37a89 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Tue, 17 Sep 2024 19:17:01 +1000 Subject: [PATCH 01/20] Added support for NLT marker only, but not tested. --- src/core/codestream/ojph_codestream.cpp | 6 + src/core/codestream/ojph_codestream_local.h | 3 + src/core/codestream/ojph_params.cpp | 251 +++++++++++++++++++- src/core/codestream/ojph_params_local.h | 57 +++++ src/core/codestream/ojph_resolution.cpp | 4 +- src/core/common/ojph_codestream.h | 16 +- src/core/common/ojph_params.h | 40 ++++ src/core/common/ojph_version.h | 2 +- 8 files changed, 363 insertions(+), 16 deletions(-) diff --git a/src/core/codestream/ojph_codestream.cpp b/src/core/codestream/ojph_codestream.cpp index 06f6b56..f2832ac 100644 --- a/src/core/codestream/ojph_codestream.cpp +++ b/src/core/codestream/ojph_codestream.cpp @@ -84,6 +84,12 @@ namespace ojph { return param_qcd(&state->qcd); } + //////////////////////////////////////////////////////////////////////////// + param_nlt codestream::access_nlt() + { + return param_nlt(&state->nlt); + } + //////////////////////////////////////////////////////////////////////////// void codestream::set_planar(bool planar) { diff --git a/src/core/codestream/ojph_codestream_local.h b/src/core/codestream/ojph_codestream_local.h index 8ca8c71..0a95ef8 100644 --- a/src/core/codestream/ojph_codestream_local.h +++ b/src/core/codestream/ojph_codestream_local.h @@ -96,6 +96,8 @@ namespace ojph { } const param_dfs* access_dfs() { if (dfs.exists()) return &dfs; else return NULL; } + const param_nlt* access_nlt() + { return ≮ } mem_fixed_allocator* get_allocator() { return allocator; } mem_elastic_allocator* get_elastic_alloc() { return elastic_alloc; } outfile_base* get_file() { return outfile; } @@ -161,6 +163,7 @@ namespace ojph { param_cap cap; // extended capabilities param_qcd qcd; // quantization default param_tlm tlm; // tile-part lengths + param_nlt nlt; // non-linearity point transformation private: // this is to handle qcc and coc int used_qcc_fields; diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index b6ada17..bd0471f 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -372,6 +372,27 @@ namespace ojph { // //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + void param_nlt::set_type3_transformation(ui16 comp_num, bool enable) + { + state->set_type3_transformation(comp_num, enable); + } + + ////////////////////////////////////////////////////////////////////////// + bool param_nlt::get_type3_transformation(ui16 comp_num, ui8& bit_depth, + bool& is_signed) + { + return state->get_type3_transformation(comp_num, bit_depth, is_signed); + } + + //////////////////////////////////////////////////////////////////////////// + // + // + // + // + // + //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void comment_exchange::set_string(const char* str) { @@ -611,7 +632,7 @@ namespace ojph { if ((Rsiz & 0x4000) == 0) OJPH_ERROR(0x00050044, "Rsiz bit 14 is not set (this is not a JPH file)"); - if ((Rsiz & 0x8000) != 0 && (Rsiz & 0xF5F) != 0) + if ((Rsiz & 0x8000) != 0 && (Rsiz & 0xD5F) != 0) OJPH_WARN(0x00050001, "Rsiz in SIZ has unimplemented fields"); if (file->read(&Xsiz, 4) != 4) OJPH_ERROR(0x00050045, "error reading SIZ marker"); @@ -1214,6 +1235,220 @@ namespace ojph { OJPH_ERROR(0x000500AA, "wrong Sqcc value in QCC marker"); } + ////////////////////////////////////////////////////////////////////////// + // + // + // + // + // + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + void param_nlt::check_validity(const param_siz& siz) + { + if (is_any_enabled() == false) + return; + + bool all_same_bit_depth = true; + bool all_same_signedness = true; + ui32 num_comps = siz.get_num_components(); + + ui32 bit_depth = 0; // unknown yet + bool is_signed = false; // unknown yet + for (ui32 c = 0; c < num_comps; ++c) + { + param_nlt* p = get_comp_object(c); + if (p == NULL || !p->enabled) // comp is not in list or not enabled + { + if (bit_depth == 0) + { // this is the first component which has not type 3 nlt definition + bit_depth = siz.get_bit_depth(c); + is_signed = siz.is_signed(c); + } + else + { // we have seen an undefined component previously + all_same_bit_depth = + all_same_bit_depth && (bit_depth == siz.get_bit_depth(c)); + all_same_signedness = + all_same_signedness && (is_signed != siz.is_signed(c)); + } + } + else + { + p->BDnlt = (ui8)(siz.get_bit_depth(c) - 1); + p->BDnlt |= (ui8)(siz.is_signed(c) ? 0x80 : 0); + } + } + + if (this->enabled) + { + if (bit_depth != 0) // default captures some components + { + this->BDnlt = (ui8)((bit_depth - 1) | (is_signed ? 0x80 : 0)); + if (!all_same_bit_depth || !all_same_signedness) + { + // We cannot use the default for all undefined components, so we + // will keep it and set it to the values of the first undefined + // component, but we will also define that component + + for (ui32 c = 0; c < num_comps; ++c) + { + param_nlt* p = get_comp_object(c); + if (p == NULL) { + // values were defined previously for (p && enabled) + p = add_object(c); + p->enabled = true; + p->BDnlt = (ui8)(siz.get_bit_depth(c) - 1); + p->BDnlt |= (ui8)(siz.is_signed(c) ? 0x80 : 0); + } + } + } + } + else + this->enabled = false; + } + + trim_non_existing_components(num_comps); + } + + ////////////////////////////////////////////////////////////////////////// + void param_nlt::set_type3_transformation(ui16 comp_num, bool enable) + { + param_nlt* p = get_comp_object(comp_num); + if (p == NULL) + p = add_object(comp_num); + p->enabled = enable; + } + + ////////////////////////////////////////////////////////////////////////// + bool param_nlt::get_type3_transformation(ui16 comp_num, ui8& bit_depth, + bool& is_signed) const + { + const param_nlt* p = get_comp_object(comp_num); + p = p ? p : NULL; + if (p->enabled) + { + bit_depth = (p->BDnlt & 0x7F) + 1; + bit_depth = bit_depth <= 38 ? bit_depth : 38; + is_signed = (p->BDnlt & 0x80) == 0x80; + } + return p->enabled; + } + + ////////////////////////////////////////////////////////////////////////// + bool param_nlt::write(outfile_base* file) const + { + if (is_any_enabled() == false) + return true; + + char buf[2]; + bool result = true; + const param_nlt* p = this; + while (p) + { + if (p->enabled) + { + *(ui16*)buf = JP2K_MARKER::NLT; + *(ui16*)buf = swap_byte(*(ui16*)buf); + result &= file->write(&buf, 2) == 2; + *(ui16*)buf = swap_byte(Lnlt); + result &= file->write(&buf, 2) == 2; + *(ui16*)buf = swap_byte(Cnlt); + result &= file->write(&buf, 2) == 2; + result &= file->write(&BDnlt, 1) == 1; + result &= file->write(&Tnlt, 1) == 1; + } + p = p->next; + } + return result; + } + + ////////////////////////////////////////////////////////////////////////// + void param_nlt::read(infile_base* file) + { + ui8 buf[6]; + bool result = true; + + if (result &= file->read(buf, 6) == 6) + OJPH_ERROR(0x00050141, "error reading NLT marker segment"); + + ui16 length = swap_byte(*(ui16*)buf); + if (length != 6 || buf[5] != 3) // wrong length or type + OJPH_ERROR(0x00050142, "Unsupported NLT type %d\n", buf[5]); + + ui16 comp = swap_byte(*(ui16*)(buf + 2)); + param_nlt* p = this; + if (comp != 65535) + { + p = get_comp_object(comp); + if (p == NULL) + p = add_object(comp); + } + p->enabled = true; + p->Cnlt = comp; + p->BDnlt = buf[4]; + } + + ////////////////////////////////////////////////////////////////////////// + param_nlt* param_nlt::get_comp_object(ui32 comp_num) + { + // cast object to constant + const param_nlt* const_p = const_cast(this); + // call using the constant object, then cast to non-const + return const_cast(const_p->get_comp_object(comp_num)); + } + + ////////////////////////////////////////////////////////////////////////// + const param_nlt* param_nlt::get_comp_object(ui32 comp_num) const + { + if (Cnlt == comp_num) + return this; + else { + param_nlt* p = next; + while (p && p->Cnlt != comp_num) + p = p->next; + return p; + } + } + + ////////////////////////////////////////////////////////////////////////// + param_nlt* param_nlt::add_object(ui32 comp_num) + { + assert(Cnlt != comp_num); + param_nlt* p = this; + while (p->next != NULL) { + assert(p->Cnlt != comp_num); + p = p->next; + } + p->next = new param_nlt; + p = p->next; + p->Cnlt = (ui16)comp_num; + p->alloced_next = true; + return p; + } + + ////////////////////////////////////////////////////////////////////////// + bool param_nlt::is_any_enabled() const + { + // check if any field is enabled + const param_nlt* p = this; + while (p && p->enabled == false) + p = p->next; + return (p != NULL); + } + + ////////////////////////////////////////////////////////////////////////// + void param_nlt::trim_non_existing_components(ui32 num_comps) + { + param_nlt* p = this->next; + while (p) { + if (p->enabled == true && p->Cnlt >= num_comps) + p->enabled = false; + p = p->next; + } + } + + ////////////////////////////////////////////////////////////////////////// // // @@ -1239,10 +1474,8 @@ namespace ojph { result &= file->write(&buf, 2) == 2; *(ui32*)buf = swap_byte(Psot); result &= file->write(&buf, 4) == 4; - *(ui8*)buf = TPsot; - result &= file->write(&buf, 1) == 1; - *(ui8*)buf = TNsot; - result &= file->write(&buf, 1) == 1; + result &= file->write(&TPsot, 1) == 1; + result &= file->write(&TNsot, 1) == 1; return result; } @@ -1263,10 +1496,8 @@ namespace ojph { result &= file->write(&buf, 2) == 2; *(ui32*)buf = swap_byte(payload_len + 14); result &= file->write(&buf, 4) == 4; - *(ui8*)buf = TPsot; - result &= file->write(&buf, 1) == 1; - *(ui8*)buf = TNsot; - result &= file->write(&buf, 1) == 1; + result &= file->write(&TPsot, 1) == 1; + result &= file->write(&TNsot, 1) == 1; return result; } @@ -1363,7 +1594,7 @@ namespace ojph { "In any case, this limit means that we have 10922 " "tileparts or more, which is a huge number."); this->num_pairs = num_pairs; - pairs = (Ttlm_Ptlm_pair*)store; + pairs = store; Ltlm = (ui16)(4 + 6 * num_pairs); Ztlm = 0; Stlm = 0x60; diff --git a/src/core/codestream/ojph_params_local.h b/src/core/codestream/ojph_params_local.h index 1958b8e..4064116 100644 --- a/src/core/codestream/ojph_params_local.h +++ b/src/core/codestream/ojph_params_local.h @@ -138,6 +138,7 @@ namespace ojph { COM = 0xFF64, //comment DFS = 0xFF72, //downsampling factor styles ADS = 0xFF73, //arbitrary decomposition styles + NLT = 0xFF76, //non-linearity point transformation ATK = 0xFF79, //arbitrary transformation kernels SOT = 0xFF90, //start of tile-part SOP = 0xFF91, //start of packet @@ -659,6 +660,62 @@ namespace ojph { ui16 comp_idx; }; + /////////////////////////////////////////////////////////////////////////// + // + // + // + // + // + /////////////////////////////////////////////////////////////////////////// + // data structures used by param_nlt + struct param_nlt + { + public: + param_nlt() { + Lnlt = 6; + Cnlt = 65535; // default + BDnlt = 0; + Tnlt = 3; + enabled = false; next = NULL; alloced_next = false; + } + + ~param_nlt() { + if (next && alloced_next) { + delete next; + next = NULL; + } + } + + void check_validity(const param_siz& siz); + void set_type3_transformation(ui16 comp_num, bool enable); + bool get_type3_transformation(ui16 comp_num, ui8& bit_depth, + bool& is_signed) const; + bool write(outfile_base* file) const; + void read(infile_base* file); + + private: + const param_nlt* get_comp_object(ui32 comp_num) const; + param_nlt* get_comp_object(ui32 comp_num); + param_nlt* add_object(ui32 comp_num); + bool is_any_enabled() const; + void trim_non_existing_components(ui32 num_comps); + + private: + ui16 Lnlt; // length of the marker segment excluding marker + ui16 Cnlt; // Component involved in the transformation + ui8 BDnlt; // Decoded image component bit depth parameter + ui8 Tnlt; // Type of non-linearity + bool enabled; // true if this object is used + param_nlt* next; // for chaining NLT markers + bool alloced_next; // true if next was allocated, not just set to an + // existing object + + // The top level param_nlt object is not allocated, but as part of + // codestream, and is used to manage allocated next objects. + // next holds a list of param_nlt objects, which are managed by the top + // param_nlt object. + }; + /////////////////////////////////////////////////////////////////////////// // // diff --git a/src/core/codestream/ojph_resolution.cpp b/src/core/codestream/ojph_resolution.cpp index b82a810..87466e0 100644 --- a/src/core/codestream/ojph_resolution.cpp +++ b/src/core/codestream/ojph_resolution.cpp @@ -245,8 +245,8 @@ namespace ojph { const param_dfs* dfs = codestream->access_dfs(); if (dfs == NULL) { OJPH_ERROR(0x00070011, "There is a problem with codestream " - "marker segments. COD/COC specifies the use of a DFS marker " - "but there are no DFS markers within the main codestream " + "marker segments. COD/COC specifies the use of a DFS marker " + "but there are no DFS markers within the main codestream " "headers"); } else { diff --git a/src/core/common/ojph_codestream.h b/src/core/common/ojph_codestream.h index 5f6dcdb..c28096e 100644 --- a/src/core/common/ojph_codestream.h +++ b/src/core/common/ojph_codestream.h @@ -57,6 +57,7 @@ namespace ojph { class param_siz; class param_cod; class param_qcd; + class param_nlt; class comment_exchange; class mem_fixed_allocator; struct point; @@ -318,7 +319,7 @@ namespace ojph { * @brief Returns the underlying SIZ marker segment object * * @return param_siz This object holds SIZ marker segment information, - * which are related to codestream dimensions, number + * which deals with codestream dimensions, number * of components, bit depth, ... etc. */ param_siz access_siz(); @@ -327,7 +328,7 @@ namespace ojph { * @brief Returns the underlying COD marker segment object * * @return param_cod This object holds COD marker segment information, - * which are related to coding parameters, such as + * which deals with coding parameters, such as * codeblock sizes, progression order, reversible, * ... etc. */ @@ -337,11 +338,20 @@ namespace ojph { * @brief Returns the underlying QCD marker segment object * * @return param_qcd This object holds QCD marker segment information, - * which are related to quantization parameters -- + * which deals with quantization parameters -- * quantization step size for each subband. */ param_qcd access_qcd(); + /** + * @brief Returns the underlying NLT marker segment object + * + * @return param_nlt This object holds NLT marker segment information, + * which deals with non-linearity point transformation + * for each component. + */ + param_nlt access_nlt(); + /** * @brief Query if the codestream extraction is planar or not. * See the documentation for ojph::codestream::set_planar() diff --git a/src/core/common/ojph_params.h b/src/core/common/ojph_params.h index 0dce0ce..5a74160 100644 --- a/src/core/common/ojph_params.h +++ b/src/core/common/ojph_params.h @@ -52,6 +52,7 @@ namespace ojph { struct param_qcd; struct param_qcc; struct param_cap; + struct param_nlt; class codestream; } @@ -131,6 +132,45 @@ namespace ojph { local::param_qcd* state; }; + /** + * @brief non-linearity point transformation object + * (implements NLT marker segment) + * + */ + class OJPH_EXPORT param_nlt + { + public: + param_nlt(local::param_nlt* p) : state(p) {} + + /** + * @brief enables or disables type 3 nonlinearity for a component + * or the default setting + * + * If you think that you need type 3 nonlinearity for all components, + * call this function with comp_num set to 65535 and enable to true. + * + * @param comp_num: component number, or 65535 for the default setting + * @param enable: true to enable nlt type 3 for this component or the + default setting, false to disable nlt type 3. + */ + void set_type3_transformation(ui16 comp_num, bool enable); + + /** + * @brief get the state (enabled or disabled) of type 3 nonlinearity + * for a component or the default setting + * + * @param comp_num: component number, or 65535 for the default setting + * @param bit_depth: returns the bit depth of the component/default + * @param is_signed: returns true if the component/default is signed + * @return true if enabled or false if not. + */ + bool get_type3_transformation(ui16 comp_num, ui8& bit_depth, + bool& is_signed); + + private: + local::param_nlt* state; + }; + //////////////////////////////////////////////////////////////////////////// class OJPH_EXPORT comment_exchange { diff --git a/src/core/common/ojph_version.h b/src/core/common/ojph_version.h index 16a0843..2f3adcc 100644 --- a/src/core/common/ojph_version.h +++ b/src/core/common/ojph_version.h @@ -34,5 +34,5 @@ //***************************************************************************/ #define OPENJPH_VERSION_MAJOR 0 -#define OPENJPH_VERSION_MINOR 16 +#define OPENJPH_VERSION_MINOR 17 #define OPENJPH_VERSION_PATCH 0 From 5d888b5924f58e1ff287169482fbb3bcdc722046 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Tue, 17 Sep 2024 19:18:00 +1000 Subject: [PATCH 02/20] This is for lossless compression. It removes the 1.1f factor. --- src/core/codestream/ojph_params.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index bd0471f..e17c1d5 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -935,17 +935,16 @@ namespace ojph { B += is_employing_color_transform ? 1 : 0; //1 bit for RCT int s = 0; float bibo_l = bibo_gains::get_bibo_gain_l(num_decomps, true); - //we leave some leeway for numerical error by multiplying by 1.1f - ui32 X = (ui32) ceil(log(bibo_l * bibo_l * 1.1f) / M_LN2); + ui32 X = (ui32) ceil(log(bibo_l * bibo_l) / M_LN2); u8_SPqcd[s++] = (ui8)((B + X) << 3); for (ui32 d = num_decomps; d > 0; --d) { float bibo_l = bibo_gains::get_bibo_gain_l(d, true); float bibo_h = bibo_gains::get_bibo_gain_h(d - 1, true); - X = (ui32) ceil(log(bibo_h * bibo_l * 1.1f) / M_LN2); + X = (ui32) ceil(log(bibo_h * bibo_l) / M_LN2); u8_SPqcd[s++] = (ui8)((B + X) << 3); u8_SPqcd[s++] = (ui8)((B + X) << 3); - X = (ui32) ceil(log(bibo_h * bibo_h * 1.1f) / M_LN2); + X = (ui32) ceil(log(bibo_h * bibo_h) / M_LN2); u8_SPqcd[s++] = (ui8)((B + X) << 3); } } From 5f296034f69763704ddd4e19fc47b305e308de37 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Tue, 17 Sep 2024 21:24:59 +1000 Subject: [PATCH 03/20] This should complete all code changed for the library. We need to test now. --- src/core/codestream/ojph_codestream_local.cpp | 59 ++++++++++++------- src/core/codestream/ojph_codestream_local.h | 2 +- src/core/codestream/ojph_params.cpp | 2 +- src/core/codestream/ojph_tile.cpp | 46 ++++++++++++--- src/core/codestream/ojph_tile.h | 1 + src/core/transform/ojph_colour.cpp | 36 ++++++++--- src/core/transform/ojph_colour.h | 4 ++ src/core/transform/ojph_colour_avx2.cpp | 21 +++++++ src/core/transform/ojph_colour_local.h | 17 ++++++ src/core/transform/ojph_colour_sse2.cpp | 18 ++++++ src/core/transform/ojph_colour_wasm.cpp | 18 ++++++ 11 files changed, 182 insertions(+), 42 deletions(-) diff --git a/src/core/codestream/ojph_codestream_local.cpp b/src/core/codestream/ojph_codestream_local.cpp index 8279466..7a114b7 100644 --- a/src/core/codestream/ojph_codestream_local.cpp +++ b/src/core/codestream/ojph_codestream_local.cpp @@ -550,6 +550,7 @@ namespace ojph { cod.update_atk(atk); qcd.check_validity(siz, cod); cap.check_validity(cod, qcd); + nlt.check_validity(siz); if (profile == OJPH_PN_IMF) check_imf_validity(); else if (profile == OJPH_PN_BROADCAST) @@ -632,6 +633,9 @@ namespace ojph { if (!qcd.write(file)) OJPH_ERROR(0x00030026, "Error writing to file"); + if (!nlt.write(file)) + OJPH_ERROR(0x00030027, "Error writing to file"); + char buf[] = " OpenJPH Ver " OJPH_INT_TO_STRING(OPENJPH_VERSION_MAJOR) "." OJPH_INT_TO_STRING(OPENJPH_VERSION_MINOR) "." @@ -642,23 +646,23 @@ namespace ojph { //1 for General use (IS 8859-15:1999 (Latin) values) *(ui16*)(buf + 4) = swap_byte((ui16)(1)); if (file->write(buf, len) != len) - OJPH_ERROR(0x00030027, "Error writing to file"); + OJPH_ERROR(0x00030028, "Error writing to file"); if (comments != NULL) { for (ui32 i = 0; i < num_comments; ++i) { t = swap_byte(JP2K_MARKER::COM); if (file->write(&t, 2) != 2) - OJPH_ERROR(0x00030028, "Error writing to file"); + OJPH_ERROR(0x00030029, "Error writing to file"); t = swap_byte((ui16)(comments[i].len + 4)); if (file->write(&t, 2) != 2) - OJPH_ERROR(0x00030029, "Error writing to file"); + OJPH_ERROR(0x0003002A, "Error writing to file"); //1 for General use (IS 8859-15:1999 (Latin) values) t = swap_byte(comments[i].Rcom); if (file->write(&t, 2) != 2) - OJPH_ERROR(0x0003002A, "Error writing to file"); - if (file->write(comments[i].data, comments[i].len)!=comments[i].len) OJPH_ERROR(0x0003002B, "Error writing to file"); + if (file->write(comments[i].data, comments[i].len)!=comments[i].len) + OJPH_ERROR(0x0003002C, "Error writing to file"); } } } @@ -728,8 +732,8 @@ namespace ojph { ////////////////////////////////////////////////////////////////////////// void codestream::read_headers(infile_base *file) { - ui16 marker_list[19] = { SOC, SIZ, CAP, PRF, CPF, COD, COC, QCD, QCC, - RGN, POC, PPM, TLM, PLM, CRG, COM, DFS, ATK, SOT }; + ui16 marker_list[20] = { SOC, SIZ, CAP, PRF, CPF, COD, COC, QCD, QCC, + RGN, POC, PPM, TLM, PLM, CRG, COM, DFS, ATK, NLT, SOT }; find_marker(file, marker_list, 1); //find SOC find_marker(file, marker_list + 1, 1); //find SIZ siz.read(file); @@ -737,7 +741,7 @@ namespace ojph { int received_markers = 0; //check that COD, & QCD received while (true) { - marker_idx = find_marker(file, marker_list + 2, 17); + marker_idx = find_marker(file, marker_list + 2, 18); if (marker_idx == 0) cap.read(file); else if (marker_idx == 1) @@ -813,6 +817,8 @@ namespace ojph { else if (marker_idx == 15) atk[2].read(file); else if (marker_idx == 16) + nlt.read(file); + else if (marker_idx == 17) break; else OJPH_ERROR(0x00030051, "File ended before finding a tile segment"); @@ -902,19 +908,20 @@ namespace ojph { } bool sod_found = false; - ui16 other_tile_part_markers[6] = { SOT, POC, PPT, PLT, COM, SOD }; + ui16 other_tile_part_markers[7] = { SOT, POC, PPT, PLT, COM, + NLT, SOD }; while (true) { int marker_idx = 0; int result = 0; - marker_idx = find_marker(infile, other_tile_part_markers + 1, 5); + marker_idx = find_marker(infile, other_tile_part_markers + 1, 6); if (marker_idx == 0) result = skip_marker(infile, "POC", - "POC in a tile is not supported yet", + "POC marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 1) result = skip_marker(infile, "PPT", - "PPT in a tile is not supported yet", + "PPT marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 2) //Skipping PLT marker segment;this should not cause any issues @@ -924,6 +931,10 @@ namespace ojph { result = skip_marker(infile, "COM", NULL, OJPH_MSG_LEVEL::NO_MSG, resilient); else if (marker_idx == 4) + result = skip_marker(infile, "NLT", + "NLT marker in tile is not supported yet", + OJPH_MSG_LEVEL::WARN, resilient); + else if (marker_idx == 5) { sod_found = true; break; @@ -961,40 +972,40 @@ namespace ojph { else { //first tile part bool sod_found = false; - ui16 first_tile_part_markers[11] = { SOT, COD, COC, QCD, QCC, RGN, - POC, PPT, PLT, COM, SOD }; + ui16 first_tile_part_markers[12] = { SOT, COD, COC, QCD, QCC, RGN, + POC, PPT, PLT, COM, NLT, SOD }; while (true) { int marker_idx = 0; int result = 0; - marker_idx = find_marker(infile, first_tile_part_markers+1, 10); + marker_idx = find_marker(infile, first_tile_part_markers+1, 11); if (marker_idx == 0) result = skip_marker(infile, "COD", - "COD in a tile is not supported yet", + "COD marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 1) result = skip_marker(infile, "COC", - "COC in a tile is not supported yet", + "COC marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 2) result = skip_marker(infile, "QCD", - "QCD in a tile is not supported yet", + "QCD marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 3) result = skip_marker(infile, "QCC", - "QCC in a tile is not supported yet", + "QCC marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 4) result = skip_marker(infile, "RGN", - "RGN in a tile is not supported yet", + "RGN marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 5) result = skip_marker(infile, "POC", - "POC in a tile is not supported yet", + "POC marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 6) result = skip_marker(infile, "PPT", - "PPT in a tile is not supported yet", + "PPT marker segment in a tile is not supported yet", OJPH_MSG_LEVEL::WARN, resilient); else if (marker_idx == 7) //Skipping PLT marker segment;this should not cause any issues @@ -1004,6 +1015,10 @@ namespace ojph { result = skip_marker(infile, "COM", NULL, OJPH_MSG_LEVEL::NO_MSG, resilient); else if (marker_idx == 9) + result = skip_marker(infile, "NLT", + "PPT marker segment in a tile is not supported yet", + OJPH_MSG_LEVEL::WARN, resilient); + else if (marker_idx == 10) { sod_found = true; break; diff --git a/src/core/codestream/ojph_codestream_local.h b/src/core/codestream/ojph_codestream_local.h index 0a95ef8..e6930d5 100644 --- a/src/core/codestream/ojph_codestream_local.h +++ b/src/core/codestream/ojph_codestream_local.h @@ -96,7 +96,7 @@ namespace ojph { } const param_dfs* access_dfs() { if (dfs.exists()) return &dfs; else return NULL; } - const param_nlt* access_nlt() + const param_nlt* get_nlt() { return ≮ } mem_fixed_allocator* get_allocator() { return allocator; } mem_elastic_allocator* get_elastic_alloc() { return elastic_alloc; } diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index e17c1d5..f75bcd4 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -1324,7 +1324,7 @@ namespace ojph { bool& is_signed) const { const param_nlt* p = get_comp_object(comp_num); - p = p ? p : NULL; + p = p ? p : this; if (p->enabled) { bit_depth = (p->BDnlt & 0x7F) + 1; diff --git a/src/core/codestream/ojph_tile.cpp b/src/core/codestream/ojph_tile.cpp index 3be907d..29377e7 100644 --- a/src/core/codestream/ojph_tile.cpp +++ b/src/core/codestream/ojph_tile.cpp @@ -67,6 +67,7 @@ namespace ojph { allocator->pre_alloc_obj(num_comps); //for line_offsets allocator->pre_alloc_obj(num_comps); //for num_bits allocator->pre_alloc_obj(num_comps); //for is_signed + allocator->pre_alloc_obj(num_comps); //for nlt_type3 allocator->pre_alloc_obj(num_comps); //for cur_line ui32 tilepart_div = codestream->get_tilepart_div(); @@ -142,6 +143,7 @@ namespace ojph { //allocate tiles_comp const param_siz *szp = codestream->get_siz(); + const param_nlt *nlp = codestream->get_nlt(); this->num_bytes = 0; num_comps = szp->get_num_components(); @@ -152,6 +154,7 @@ namespace ojph { line_offsets = allocator->post_alloc_obj(num_comps); num_bits = allocator->post_alloc_obj(num_comps); is_signed = allocator->post_alloc_obj(num_comps); + nlt_type3 = allocator->post_alloc_obj(num_comps); cur_line = allocator->post_alloc_obj(num_comps); profile = codestream->get_profile(); @@ -176,6 +179,8 @@ namespace ojph { ui32 width = 0; for (ui32 i = 0; i < num_comps; ++i) { + ui8 bd; bool is; // used for nlt_type3 + point downsamp = szp->get_downsampling(i); point recon_downsamp = szp->get_recon_downsampling(i); @@ -205,6 +210,13 @@ namespace ojph { num_bits[i] = szp->get_bit_depth(i); is_signed[i] = szp->is_signed(i); + nlt_type3[i] = nlp->get_type3_transformation(i, bd, is); + if (nlt_type3[i] == true && (bd != num_bits[i] || is != is_signed[i])) + OJPH_ERROR(0x000300A1, "Mismatch between Ssiz (bit_depth = %d, " + "is_signed = %s) from SIZ marker segment, and BDnlt " + "(bit_depth = %d, is_signed = %s) from NLT marker segment, " + "for component %d",i, num_bits[i], + is_signed[i] ? "True" : "False", bd, is ? "True" : "False"); cur_line[i] = 0; } @@ -250,8 +262,12 @@ namespace ojph { int shift = 1 << (num_bits[comp_num] - 1); const si32 *sp = line->i32 + line_offsets[comp_num]; si32* dp = tc->i32; - if (is_signed[comp_num]) - memcpy(dp, sp, comp_width * sizeof(si32)); + if (is_signed[comp_num]) { + if (nlt_type3[comp_num]) + cnvrt_si32_to_si32_nlt_type3(sp, dp, shift + 1, comp_width); + else + memcpy(dp, sp, comp_width * sizeof(si32)); + } else cnvrt_si32_to_si32_shftd(sp, dp, -shift, comp_width); } @@ -269,14 +285,18 @@ namespace ojph { } else { + int shift = 1 << (num_bits[comp_num] - 1); ui32 comp_width = comp_rects[comp_num].siz.w; if (reversible) { - int shift = 1 << (num_bits[comp_num] - 1); const si32 *sp = line->i32 + line_offsets[comp_num]; si32 *dp = lines[comp_num].i32; - if (is_signed[comp_num]) - memcpy(dp, sp, comp_width * sizeof(si32)); + if (is_signed[comp_num]) { + if (nlt_type3[comp_num]) + cnvrt_si32_to_si32_nlt_type3(sp, dp, shift + 1, comp_width); + else + memcpy(dp, sp, comp_width * sizeof(si32)); + } else cnvrt_si32_to_si32_shftd(sp, dp, -shift, comp_width); if (comp_num == 2) @@ -333,8 +353,12 @@ namespace ojph { int shift = 1 << (num_bits[comp_num] - 1); const si32 *sp = src_line->i32; si32* dp = tgt_line->i32 + line_offsets[comp_num]; - if (is_signed[comp_num]) - memcpy(dp, sp, comp_width * sizeof(si32)); + if (is_signed[comp_num]) { + if (nlt_type3[comp_num]) + cnvrt_si32_to_si32_nlt_type3(sp, dp, shift + 1, comp_width); + else + memcpy(dp, sp, comp_width * sizeof(si32)); + } else cnvrt_si32_to_si32_shftd(sp, dp, +shift, comp_width); } @@ -373,8 +397,12 @@ namespace ojph { else sp = comps[comp_num].pull_line()->i32; si32* dp = tgt_line->i32 + line_offsets[comp_num]; - if (is_signed[comp_num]) - memcpy(dp, sp, comp_width * sizeof(si32)); + if (is_signed[comp_num]) { + if (nlt_type3[comp_num]) + cnvrt_si32_to_si32_nlt_type3(sp, dp, shift + 1, comp_width); + else + memcpy(dp, sp, comp_width * sizeof(si32)); + } else cnvrt_si32_to_si32_shftd(sp, dp, +shift, comp_width); } diff --git a/src/core/codestream/ojph_tile.h b/src/core/codestream/ojph_tile.h index 056c7c9..4b54242 100644 --- a/src/core/codestream/ojph_tile.h +++ b/src/core/codestream/ojph_tile.h @@ -89,6 +89,7 @@ namespace ojph { ui32 *num_bits; bool *is_signed; ui32 *cur_line; + bool *nlt_type3; int prog_order; private: diff --git a/src/core/transform/ojph_colour.cpp b/src/core/transform/ojph_colour.cpp index fb42a7d..34161d4 100644 --- a/src/core/transform/ojph_colour.cpp +++ b/src/core/transform/ojph_colour.cpp @@ -49,43 +49,47 @@ namespace ojph { void (*cnvrt_si32_to_si32_shftd) (const si32 *sp, si32 *dp, int shift, ui32 width) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + void (*cnvrt_si32_to_si32_nlt_type3) + (const si32* sp, si32* dp, int shift, ui32 width) = NULL; + + ////////////////////////////////////////////////////////////////////////// void (*cnvrt_si32_to_float_shftd) (const si32 *sp, float *dp, float mul, ui32 width) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void (*cnvrt_si32_to_float) (const si32 *sp, float *dp, float mul, ui32 width) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void (*cnvrt_float_to_si32_shftd) (const float *sp, si32 *dp, float mul, ui32 width) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void (*cnvrt_float_to_si32) (const float *sp, si32 *dp, float mul, ui32 width) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void (*rct_forward) (const si32 *r, const si32 *g, const si32 *b, si32 *y, si32 *cb, si32 *cr, ui32 repeat) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void (*rct_backward) (const si32 *y, const si32 *cb, const si32 *cr, si32 *r, si32 *g, si32 *b, ui32 repeat) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void (*ict_forward) (const float *r, const float *g, const float *b, float *y, float *cb, float *cr, ui32 repeat) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// void (*ict_backward) (const float *y, const float *cb, const float *cr, float *r, float *g, float *b, ui32 repeat) = NULL; - //////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// static bool colour_transform_functions_initialized = false; ////////////////////////////////////////////////////////////////////////// @@ -97,6 +101,7 @@ namespace ojph { #if !defined(OJPH_ENABLE_WASM_SIMD) || !defined(OJPH_EMSCRIPTEN) cnvrt_si32_to_si32_shftd = gen_cnvrt_si32_to_si32_shftd; + cnvrt_si32_to_si32_nlt_type3 = gen_cnvrt_si32_to_si32_nlt_type3; cnvrt_si32_to_float_shftd = gen_cnvrt_si32_to_float_shftd; cnvrt_si32_to_float = gen_cnvrt_si32_to_float; cnvrt_float_to_si32_shftd = gen_cnvrt_float_to_si32_shftd; @@ -128,6 +133,7 @@ namespace ojph { cnvrt_float_to_si32_shftd = sse2_cnvrt_float_to_si32_shftd; cnvrt_float_to_si32 = sse2_cnvrt_float_to_si32; cnvrt_si32_to_si32_shftd = sse2_cnvrt_si32_to_si32_shftd; + cnvrt_si32_to_si32_nlt_type3 = sse2_cnvrt_si32_to_si32_nlt_type3; rct_forward = sse2_rct_forward; rct_backward = sse2_rct_backward; } @@ -149,6 +155,7 @@ namespace ojph { if (get_cpu_ext_level() >= X86_CPU_EXT_LEVEL_AVX2) { cnvrt_si32_to_si32_shftd = avx2_cnvrt_si32_to_si32_shftd; + cnvrt_si32_to_si32_nlt_type3 = avx2_cnvrt_si32_to_si32_nlt_type3; rct_forward = avx2_rct_forward; rct_backward = avx2_rct_backward; } @@ -162,6 +169,7 @@ namespace ojph { #else // OJPH_ENABLE_WASM_SIMD cnvrt_si32_to_si32_shftd = wasm_cnvrt_si32_to_si32_shftd; + cnvrt_si32_to_si32_nlt_type3 = wasm_cnvrt_si32_to_si32_nlt_type3; cnvrt_si32_to_float_shftd = wasm_cnvrt_si32_to_float_shftd; cnvrt_si32_to_float = wasm_cnvrt_si32_to_float; cnvrt_float_to_si32_shftd = wasm_cnvrt_float_to_si32_shftd; @@ -200,6 +208,16 @@ namespace ojph { *dp++ = *sp++ + shift; } + ////////////////////////////////////////////////////////////////////////// + void gen_cnvrt_si32_to_si32_nlt_type3(const si32 *sp, si32 *dp, + int shift, ui32 width) + { + for (ui32 i = width; i > 0; --i) { + const si32 v = *sp++; + *dp++ = v > 0 ? v : (- v - shift); + } + } + ////////////////////////////////////////////////////////////////////////// void gen_cnvrt_si32_to_float_shftd(const si32 *sp, float *dp, float mul, ui32 width) diff --git a/src/core/transform/ojph_colour.h b/src/core/transform/ojph_colour.h index 212848b..52df312 100644 --- a/src/core/transform/ojph_colour.h +++ b/src/core/transform/ojph_colour.h @@ -49,6 +49,10 @@ namespace ojph { extern void (*cnvrt_si32_to_si32_shftd) (const si32 *sp, si32 *dp, int shift, ui32 width); + //////////////////////////////////////////////////////////////////////////// + extern void (*cnvrt_si32_to_si32_nlt_type3) + (const si32 *sp, si32 *dp, int shift, ui32 width); + //////////////////////////////////////////////////////////////////////////// extern void (*cnvrt_si32_to_float_shftd) (const si32 *sp, float *dp, float mul, ui32 width); diff --git a/src/core/transform/ojph_colour_avx2.cpp b/src/core/transform/ojph_colour_avx2.cpp index 60e20d6..14e5a35 100644 --- a/src/core/transform/ojph_colour_avx2.cpp +++ b/src/core/transform/ojph_colour_avx2.cpp @@ -59,6 +59,27 @@ namespace ojph { } } + ////////////////////////////////////////////////////////////////////////// + void avx2_cnvrt_si32_to_si32_nlt_type3(const si32* sp, si32* dp, + int shift, ui32 width) + { + __m256i sh = _mm256_set1_epi32(-shift); + __m256i zero = _mm256_setzero_si256(); + for (int i = (width + 7) >> 3; i > 0; --i, sp += 8, dp += 8) + { + __m256i s = _mm256_loadu_si256((__m256i*)sp); + __m256i c = _mm256_cmpgt_epi32(s, zero); // 0xFFFFFFFF for +ve value + __m256i z = _mm256_cmpeq_epi32(s, zero); // 0xFFFFFFFF for 0 + c = _mm256_or_si256(c, z); // 0xFFFFFFFF for +ve and 0 + + __m256i v_m_sh = _mm256_sub_epi32(sh, s); // - shift - value + v_m_sh = _mm256_andnot_si256(c, v_m_sh); // keep only - shift - value + s = _mm256_and_si256(c, s); // keep only +ve or 0 + s = _mm256_or_si256(s, v_m_sh); // combine + _mm256_storeu_si256((__m256i*)dp, s); + } + } + ////////////////////////////////////////////////////////////////////////// void avx2_rct_forward(const si32 *r, const si32 *g, const si32 *b, si32 *y, si32 *cb, si32 *cr, ui32 repeat) diff --git a/src/core/transform/ojph_colour_local.h b/src/core/transform/ojph_colour_local.h index 6ddf890..ae5eba1 100644 --- a/src/core/transform/ojph_colour_local.h +++ b/src/core/transform/ojph_colour_local.h @@ -68,6 +68,10 @@ namespace ojph { void gen_cnvrt_si32_to_si32_shftd(const si32 *sp, si32 *dp, int shift, ui32 width); + ////////////////////////////////////////////////////////////////////////// + void gen_cnvrt_si32_to_si32_nlt_type3(const si32 *sp, si32 *dp, + int shift, ui32 width); + ////////////////////////////////////////////////////////////////////////// void gen_cnvrt_si32_to_float_shftd(const si32 *sp, float *dp, float mul, ui32 width); @@ -160,6 +164,11 @@ namespace ojph { void sse2_cnvrt_si32_to_si32_shftd(const si32 *sp, si32 *dp, int shift, ui32 width); + ////////////////////////////////////////////////////////////////////////// + void sse2_cnvrt_si32_to_si32_nlt_type3(const si32 *sp, si32 *dp, + int shift, ui32 width); + + ////////////////////////////////////////////////////////////////////////// void sse2_rct_forward(const si32 *r, const si32 *g, const si32 *b, si32 *y, si32 *cb, si32 *cr, ui32 repeat); @@ -212,6 +221,10 @@ namespace ojph { void avx2_cnvrt_si32_to_si32_shftd(const si32 *sp, si32 *dp, int shift, ui32 width); + ////////////////////////////////////////////////////////////////////////// + void avx2_cnvrt_si32_to_si32_nlt_type3(const si32 *sp, si32 *dp, + int shift, ui32 width); + ////////////////////////////////////////////////////////////////////////// void avx2_rct_forward(const si32 *r, const si32 *g, const si32 *b, si32 *y, si32 *cb, si32 *cr, ui32 repeat); @@ -248,6 +261,10 @@ namespace ojph { void wasm_cnvrt_si32_to_si32_shftd(const si32 *sp, si32 *dp, int shift, ui32 width); + ////////////////////////////////////////////////////////////////////////// + void wasm_cnvrt_si32_to_si32_nlt_type3(const si32 *sp, si32 *dp, + int shift, ui32 width); + ////////////////////////////////////////////////////////////////////////// void wasm_rct_forward(const si32 *r, const si32 *g, const si32 *b, si32 *y, si32 *cb, si32 *cr, ui32 repeat); diff --git a/src/core/transform/ojph_colour_sse2.cpp b/src/core/transform/ojph_colour_sse2.cpp index 4a3cb14..c50c091 100644 --- a/src/core/transform/ojph_colour_sse2.cpp +++ b/src/core/transform/ojph_colour_sse2.cpp @@ -94,6 +94,24 @@ namespace ojph { } } + ////////////////////////////////////////////////////////////////////////// + void sse2_cnvrt_si32_to_si32_nlt_type3(const si32* sp, si32* dp, + int shift, ui32 width) + { + __m128i sh = _mm_set1_epi32(-shift); + __m128i zero = _mm_setzero_si128(); + for (int i = (width + 3) >> 2; i > 0; --i, sp += 4, dp += 4) + { + __m128i s = _mm_loadu_si128((__m128i*)sp); + __m128i c = _mm_cmplt_epi32(s, zero); // 0xFFFFFFFF for -ve value + __m128i v_m_sh = _mm_sub_epi32(sh, s); // - shift - value + v_m_sh = _mm_and_si128(c, v_m_sh); // keep only - shift - value + s = _mm_andnot_si128(c, s); // keep only +ve or 0 + s = _mm_or_si128(s, v_m_sh); // combine + _mm_storeu_si128((__m128i*)dp, s); + } + } + ////////////////////////////////////////////////////////////////////////// void sse2_rct_forward(const si32 *r, const si32 *g, const si32 *b, si32 *y, si32 *cb, si32 *cr, ui32 repeat) diff --git a/src/core/transform/ojph_colour_wasm.cpp b/src/core/transform/ojph_colour_wasm.cpp index 632a645..bc25d42 100644 --- a/src/core/transform/ojph_colour_wasm.cpp +++ b/src/core/transform/ojph_colour_wasm.cpp @@ -122,6 +122,24 @@ namespace ojph { } } + ////////////////////////////////////////////////////////////////////////// + void wasm_cnvrt_si32_to_si32_nlt_type3(const si32* sp, si32* dp, + int shift, ui32 width) + { + v128_t sh = wasm_f32x4_splat(-shift); + v128_t zero = wasm_f32x4_splat(0); + for (int i = (width + 3) >> 2; i > 0; --i, sp += 4, dp += 4) + { + v128_t s = wasm_v128_load(sp); + v128_t c = wasm_i32x4_lt(s, zero); // 0xFFFFFFFF for -ve value + v128_t v_m_sh = wasm_i32x4_sub(sh, s); // - shift - value + v_m_sh = wasm_v128_and(v_m_sh, c); // keep only - shift - value + s = wasm_v128_andnot(s, c); // keep only +ve or 0 + s = wasm_v128_or(s, v_m_sh); // combine + wasm_v128_store(dp, s); + } + } + ////////////////////////////////////////////////////////////////////////// void wasm_rct_forward(const si32 *r, const si32 *g, const si32 *b, si32 *y, si32 *cb, si32 *cr, ui32 repeat) From 517e1be033cf17d0f8cb8f9685ff24c3c03d0086 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Tue, 17 Sep 2024 21:31:41 +1000 Subject: [PATCH 04/20] Getting rid of some of the warnings --- src/core/codestream/ojph_params.cpp | 12 ++++++------ src/core/codestream/ojph_params_local.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index f75bcd4..da91a54 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -1275,7 +1275,7 @@ namespace ojph { else { p->BDnlt = (ui8)(siz.get_bit_depth(c) - 1); - p->BDnlt |= (ui8)(siz.is_signed(c) ? 0x80 : 0); + p->BDnlt |= (ui8)(siz.is_signed(c) ? 0x80 : (ui8)0); } } @@ -1283,7 +1283,7 @@ namespace ojph { { if (bit_depth != 0) // default captures some components { - this->BDnlt = (ui8)((bit_depth - 1) | (is_signed ? 0x80 : 0)); + this->BDnlt = (ui8)((bit_depth - 1) | (is_signed ? 0x80 : (ui8)0)); if (!all_same_bit_depth || !all_same_signedness) { // We cannot use the default for all undefined components, so we @@ -1298,7 +1298,7 @@ namespace ojph { p = add_object(c); p->enabled = true; p->BDnlt = (ui8)(siz.get_bit_depth(c) - 1); - p->BDnlt |= (ui8)(siz.is_signed(c) ? 0x80 : 0); + p->BDnlt |= siz.is_signed(c) ? 0x80 : (ui8)0; } } } @@ -1311,7 +1311,7 @@ namespace ojph { } ////////////////////////////////////////////////////////////////////////// - void param_nlt::set_type3_transformation(ui16 comp_num, bool enable) + void param_nlt::set_type3_transformation(ui32 comp_num, bool enable) { param_nlt* p = get_comp_object(comp_num); if (p == NULL) @@ -1320,14 +1320,14 @@ namespace ojph { } ////////////////////////////////////////////////////////////////////////// - bool param_nlt::get_type3_transformation(ui16 comp_num, ui8& bit_depth, + bool param_nlt::get_type3_transformation(ui32 comp_num, ui8& bit_depth, bool& is_signed) const { const param_nlt* p = get_comp_object(comp_num); p = p ? p : this; if (p->enabled) { - bit_depth = (p->BDnlt & 0x7F) + 1; + bit_depth = (p->BDnlt & 0x7F) + (ui8)1; bit_depth = bit_depth <= 38 ? bit_depth : 38; is_signed = (p->BDnlt & 0x80) == 0x80; } diff --git a/src/core/codestream/ojph_params_local.h b/src/core/codestream/ojph_params_local.h index 4064116..fa2f690 100644 --- a/src/core/codestream/ojph_params_local.h +++ b/src/core/codestream/ojph_params_local.h @@ -687,8 +687,8 @@ namespace ojph { } void check_validity(const param_siz& siz); - void set_type3_transformation(ui16 comp_num, bool enable); - bool get_type3_transformation(ui16 comp_num, ui8& bit_depth, + void set_type3_transformation(ui32 comp_num, bool enable); + bool get_type3_transformation(ui32 comp_num, ui8& bit_depth, bool& is_signed) const; bool write(outfile_base* file) const; void read(infile_base* file); From 2626fe244ac9f07939f67891b7b0d7063caaa1e8 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Tue, 17 Sep 2024 21:34:51 +1000 Subject: [PATCH 05/20] 2nd attempt to get rid of warnings. --- src/core/codestream/ojph_params.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index da91a54..390b6e4 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -1275,7 +1275,7 @@ namespace ojph { else { p->BDnlt = (ui8)(siz.get_bit_depth(c) - 1); - p->BDnlt |= (ui8)(siz.is_signed(c) ? 0x80 : (ui8)0); + p->BDnlt = (ui8)(p->BDnlt | (siz.is_signed(c) ? 0x80 : 0)); } } @@ -1298,7 +1298,7 @@ namespace ojph { p = add_object(c); p->enabled = true; p->BDnlt = (ui8)(siz.get_bit_depth(c) - 1); - p->BDnlt |= siz.is_signed(c) ? 0x80 : (ui8)0; + p->BDnlt = (ui8)(p->BDnlt | (siz.is_signed(c) ? 0x80 : 0)); } } } @@ -1327,7 +1327,7 @@ namespace ojph { p = p ? p : this; if (p->enabled) { - bit_depth = (p->BDnlt & 0x7F) + (ui8)1; + bit_depth = (ui8)((p->BDnlt & 0x7F) + 1); bit_depth = bit_depth <= 38 ? bit_depth : 38; is_signed = (p->BDnlt & 0x80) == 0x80; } From fcd652c46c16b53d0bcdd8a41b824f9a4e68d2a9 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Tue, 17 Sep 2024 21:39:45 +1000 Subject: [PATCH 06/20] Small bug fix for wasm --- src/core/transform/ojph_colour_wasm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/transform/ojph_colour_wasm.cpp b/src/core/transform/ojph_colour_wasm.cpp index bc25d42..e174d0b 100644 --- a/src/core/transform/ojph_colour_wasm.cpp +++ b/src/core/transform/ojph_colour_wasm.cpp @@ -126,8 +126,8 @@ namespace ojph { void wasm_cnvrt_si32_to_si32_nlt_type3(const si32* sp, si32* dp, int shift, ui32 width) { - v128_t sh = wasm_f32x4_splat(-shift); - v128_t zero = wasm_f32x4_splat(0); + v128_t sh = wasm_i32x4_splat(-shift); + v128_t zero = wasm_i32x4_splat(0); for (int i = (width + 3) >> 2; i > 0; --i, sp += 4, dp += 4) { v128_t s = wasm_v128_load(sp); From 086a5a54eff0a97ce001f31fa4e7308ab6e214af Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Tue, 17 Sep 2024 22:24:47 +1000 Subject: [PATCH 07/20] A bug fix, and addressing the float to double type conversion. --- src/core/codestream/ojph_params.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index 390b6e4..f250c13 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -934,13 +934,13 @@ namespace ojph { ui32 B = bit_depth; B += is_employing_color_transform ? 1 : 0; //1 bit for RCT int s = 0; - float bibo_l = bibo_gains::get_bibo_gain_l(num_decomps, true); + double bibo_l = bibo_gains::get_bibo_gain_l(num_decomps, true); ui32 X = (ui32) ceil(log(bibo_l * bibo_l) / M_LN2); u8_SPqcd[s++] = (ui8)((B + X) << 3); for (ui32 d = num_decomps; d > 0; --d) { - float bibo_l = bibo_gains::get_bibo_gain_l(d, true); - float bibo_h = bibo_gains::get_bibo_gain_h(d - 1, true); + double bibo_l = bibo_gains::get_bibo_gain_l(d, true); + double bibo_h = bibo_gains::get_bibo_gain_h(d - 1, true); X = (ui32) ceil(log(bibo_h * bibo_l) / M_LN2); u8_SPqcd[s++] = (ui8)((B + X) << 3); u8_SPqcd[s++] = (ui8)((B + X) << 3); @@ -1366,9 +1366,8 @@ namespace ojph { void param_nlt::read(infile_base* file) { ui8 buf[6]; - bool result = true; - if (result &= file->read(buf, 6) == 6) + if (file->read(buf, 6) != 6) OJPH_ERROR(0x00050141, "error reading NLT marker segment"); ui16 length = swap_byte(*(ui16*)buf); From 5921b01a9949b767fd270822f7621a6fa206c7ea Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Wed, 18 Sep 2024 20:39:54 +1000 Subject: [PATCH 08/20] Added support for .pfm in ojph_compress and ojph_expand -- not happy with it, but it is good for testing. --- src/apps/common/ojph_img_io.h | 112 ++++++- src/apps/ojph_compress/ojph_compress.cpp | 69 +++++ src/apps/ojph_expand/ojph_expand.cpp | 27 ++ src/apps/others/ojph_img_io.cpp | 376 ++++++++++++++++++----- src/core/codestream/ojph_params.cpp | 14 +- src/core/codestream/ojph_params_local.h | 19 +- src/core/codestream/ojph_tile.cpp | 2 +- src/core/common/ojph_params.h | 6 +- 8 files changed, 541 insertions(+), 84 deletions(-) diff --git a/src/apps/common/ojph_img_io.h b/src/apps/common/ojph_img_io.h index 8e41493..7fddf5b 100644 --- a/src/apps/common/ojph_img_io.h +++ b/src/apps/common/ojph_img_io.h @@ -135,7 +135,7 @@ namespace ojph { ui32 cur_line; si64 start_of_data; - int planar; + bool planar; ui32 bit_depth[3]; bool is_signed[3]; point subsampling[3]; @@ -446,6 +446,67 @@ namespace ojph { size_t buffer_size; }; + //////////////////////////////////////////////////////////////////////////// + // + // + // + // + // + //////////////////////////////////////////////////////////////////////////// + class pfm_in : public image_in_base + { + public: + pfm_in(mem_fixed_allocator *p = NULL) + { + fh = 0; + fname = NULL; + alloc_p = p; + temp_buf = NULL; + temp_buf_byte_size = 0; + bit_depth = 32; + scale = 0.0f; + little_endian = true; + width = height = num_comps = 0; + + cur_line = 0; + start_of_data = 0; + } + virtual ~pfm_in() + { + close(); + if (alloc_p == NULL && temp_buf) + free(temp_buf); + } + + void open(const char* filename); + void finalize_alloc(); + void configure(ui32 bit_depth) { + assert(bit_depth > 0 && bit_depth <= 32); + this->bit_depth = bit_depth; + } + virtual ui32 read(const line_buf* line, ui32 comp_num); + void close() { if(fh) { fclose(fh); fh = NULL; } fname = NULL; } + + size get_size() { assert(fh); return size(width, height); } + ui32 get_width() { assert(fh); return width; } + ui32 get_height() { assert(fh); return height; } + ui32 get_num_components() { assert(fh); return num_comps; } + + private: + FILE *fh; + const char *fname; + mem_fixed_allocator *alloc_p; + float *temp_buf; + ui32 temp_buf_byte_size; + ui32 bit_depth; // this truncates data to bit_depth in the LSB + float scale; + bool little_endian; + ui32 width, height, num_comps; + ui32 cur_line; + si64 start_of_data; + }; + + //////////////////////////////////////////////////////////////////////////// // Accelerators (defined in ojph_img_io_*) typedef void (*conversion_fun)(const line_buf *ln0, const line_buf *ln1, @@ -703,6 +764,55 @@ namespace ojph { ui8* buffer; ui32 buffer_size; }; + + //////////////////////////////////////////////////////////////////////////// + // + // + // + // + // + //////////////////////////////////////////////////////////////////////////// + class pfm_out : public image_out_base + { + public: + pfm_out() + { + fh = NULL; + fname = NULL; + buffer = NULL; + buffer_size = 0; + width = height = num_components = 0; + scale = -1.0f; + bit_depth = 32; + cur_line = 0; + start_of_data = 0; + } + virtual ~pfm_out() + { + close(); + if (buffer) + free(buffer); + } + + void open(char* filename); + void configure(ui32 width, ui32 height, ui32 num_components, + float scale, ui32 bit_depth); + virtual ui32 write(const line_buf* line, ui32 comp_num); + virtual void close() { if(fh) { fclose(fh); fh = NULL; } fname = NULL; } + + private: + FILE *fh; + const char *fname; + float* buffer; + ui32 buffer_size; + ui32 width, height, num_components; + float scale; + ui32 bit_depth; + ui32 cur_line; + si64 start_of_data; + }; + + } #endif // !OJPH_IMG_IO_H diff --git a/src/apps/ojph_compress/ojph_compress.cpp b/src/apps/ojph_compress/ojph_compress.cpp index 0c4aa0e..e1a6702 100644 --- a/src/apps/ojph_compress/ojph_compress.cpp +++ b/src/apps/ojph_compress/ojph_compress.cpp @@ -611,6 +611,7 @@ int main(int argc, char * argv[]) { ojph::codestream codestream; ojph::ppm_in ppm; + ojph::pfm_in pfm; ojph::yuv_in yuv; ojph::raw_in raw; ojph::dpx_in dpx; @@ -736,6 +737,74 @@ int main(int argc, char * argv[]) { base = &ppm; } + else if (is_matching(".pfm", v)) + { + pfm.open(input_filename); + ojph::param_siz siz = codestream.access_siz(); + siz.set_image_extent(ojph::point(image_offset.x + pfm.get_width(), + image_offset.y + pfm.get_height())); + ojph::ui32 num_comps = pfm.get_num_components(); + assert(num_comps == 1 || num_comps == 3); + siz.set_num_components(num_comps); + + pfm.configure(bit_depth[0]); + for (ojph::ui32 c = 0; c < num_comps; ++c) { + ojph::ui32 bd = 32; + if (bit_depth[c] != 0) + bd = bit_depth[c]; + bool is = false; + if (is_signed[c] != -1) + is = is_signed[c] != 0; + ojph::point ds(1, 1); + siz.set_component(c, ds, bd, is); + } + siz.set_image_offset(image_offset); + siz.set_tile_size(tile_size); + siz.set_tile_offset(tile_offset); + + ojph::param_cod cod = codestream.access_cod(); + cod.set_num_decomposition(num_decompositions); + cod.set_block_dims(block_size.w, block_size.h); + if (num_precincts != -1) + cod.set_precinct_size(num_precincts, precinct_size); + cod.set_progression_order(prog_order); + if (employ_color_transform == -1) + cod.set_color_transform(true); + else + cod.set_color_transform(employ_color_transform == 1); + cod.set_reversible(reversible); + if (!reversible && quantization_step != -1.0f) + codestream.access_qcd().set_irrev_quant(quantization_step); + + ojph::param_nlt nlt = codestream.access_nlt(); + if (reversible) + nlt.set_type3_transformation(ojph::param_nlt::ALL_COMPS, true); + else + OJPH_ERROR(0x01000091, "The support for pfm image is not " + "complete; I need to figure how to modify the interface " + "to better support the exchange of floating point data. " + "Feeding float point data is not supported yet, unless it " + "is for lossless compression."); + + codestream.set_planar(false); + if (profile_string[0] != '\0') + codestream.set_profile(profile_string); + codestream.set_tilepart_divisions(tileparts_at_resolutions, + tileparts_at_components); + codestream.request_tlm_marker(tlm_marker); + + if (dims.w != 0 || dims.h != 0) + OJPH_WARN(0x01000092, + "-dims option is not needed and was not used\n"); + if (num_components != 0) + OJPH_WARN(0x01000093, + "-num_comps is not needed and was not used\n"); + if (comp_downsampling[0].x != 0 || comp_downsampling[0].y != 0) + OJPH_WARN(0x01000094, + "-downsamp is not needed and was not used\n"); + + base = &pfm; + } #ifdef OJPH_ENABLE_TIFF_SUPPORT else if (is_matching(".tif", v) || is_matching(".tiff", v)) { diff --git a/src/apps/ojph_expand/ojph_expand.cpp b/src/apps/ojph_expand/ojph_expand.cpp index 7d6f3d5..2adb535 100644 --- a/src/apps/ojph_expand/ojph_expand.cpp +++ b/src/apps/ojph_expand/ojph_expand.cpp @@ -213,6 +213,7 @@ int main(int argc, char *argv[]) { ojph::codestream codestream; ojph::ppm_out ppm; + ojph::pfm_out pfm; #ifdef OJPH_ENABLE_TIFF_SUPPORT ojph::tif_out tif; #endif /* OJPH_ENABLE_TIFF_SUPPORT */ @@ -266,6 +267,32 @@ int main(int argc, char *argv[]) { ppm.open(output_filename); base = &ppm; } + else if (is_matching(".pfm", v)) + { + codestream.set_planar(false); + ojph::param_siz siz = codestream.access_siz(); + + ojph::ui32 num_comps = siz.get_num_components(); + if (num_comps != 3 && num_comps != 1) + OJPH_ERROR(0x0200000C, + "The file has %d color components; this cannot be saved to" + " a .pfm file\n", num_comps); + bool all_same = true; + ojph::point p = siz.get_downsampling(0); + for (ojph::ui32 i = 1; i < siz.get_num_components(); ++i) + { + ojph::point p1 = siz.get_downsampling(i); + all_same = all_same && (p1.x == p.x) && (p1.y == p.y); + } + if (!all_same) + OJPH_ERROR(0x0200000D, + "To save an image to ppm, all the components must have the " + "same downsampling ratio\n"); + pfm.configure(siz.get_recon_width(0), siz.get_recon_height(0), + siz.get_num_components(), -1.0f, siz.get_bit_depth(0)); + pfm.open(output_filename); + base = &pfm; + } #ifdef OJPH_ENABLE_TIFF_SUPPORT else if (is_matching(".tif", v) || is_matching(".tiff", v)) { diff --git a/src/apps/others/ojph_img_io.cpp b/src/apps/others/ojph_img_io.cpp index 82bbe10..93dd453 100644 --- a/src/apps/others/ojph_img_io.cpp +++ b/src/apps/others/ojph_img_io.cpp @@ -247,7 +247,7 @@ namespace ojph { assert(fh == 0); fh = fopen(filename, "rb"); if (fh == 0) - OJPH_ERROR(0x030000001, "Unable to open file %s", filename); + OJPH_ERROR(0x03000001, "Unable to open file %s", filename); fname = filename; // read magic number @@ -255,27 +255,27 @@ namespace ojph { if (fread(t, 1, 2, fh) != 2) { close(); - OJPH_ERROR(0x030000002, "Error reading file %s", filename); + OJPH_ERROR(0x03000002, "Error reading file %s", filename); } // check magic number if (t[0] != 'P' || (t[1] != '5' && t[1] != '6')) { close(); - OJPH_ERROR(0x030000003, "unknown file type for file %s", filename); + OJPH_ERROR(0x03000003, "unknown file type for file %s", filename); } size_t len = strlen(filename); if (t[1] == '5' && strncmp(filename + len - 4, ".pgm", 4) != 0) { close(); - OJPH_ERROR(0x030000004, "wrong file extension, a file with " + OJPH_ERROR(0x03000004, "wrong file extension, a file with " "keyword P5 must have a .pgm extension for file %s", filename); } if (t[1] == '6' && strncmp(filename + len - 4, ".ppm", 4) != 0) { close(); - OJPH_ERROR(0x030000005, "wrong file extension, a file with keyword P6 " + OJPH_ERROR(0x03000005, "wrong file extension, a file with keyword P6 " "must have a .ppm extension for file %s", filename); } @@ -287,7 +287,7 @@ namespace ojph { if (fscanf(fh, "%d %d %d", &width, &height, &max_val) != 3) { close(); - OJPH_ERROR(0x030000006, "error in file format for file %s", filename); + OJPH_ERROR(0x03000006, "error in file format for file %s", filename); } num_ele_per_line = num_comps * width; bytes_per_sample = max_val > 255 ? 2 : 1; @@ -309,7 +309,7 @@ namespace ojph { temp_buf = malloc(temp_buf_byte_size); if (temp_buf == NULL) { // failed to allocate memory if (t) free(t); // the original buffer is still valid - OJPH_ERROR(0x030000007, "error allocating memory"); + OJPH_ERROR(0x03000007, "error allocating memory"); } } else @@ -347,7 +347,7 @@ namespace ojph { if (result != num_ele_per_line) { close(); - OJPH_ERROR(0x030000011, "not enough data in file %s", fname); + OJPH_ERROR(0x03000011, "not enough data in file %s", fname); } if (++cur_line >= height) { @@ -394,17 +394,17 @@ namespace ojph { if (strncmp(".ppm", filename + len - 4, 4) == 0) { filename[len - 2] = 'g'; - OJPH_WARN(0x03000001, "file was renamed %s\n", filename); + OJPH_WARN(0x03000021, "file was renamed %s\n", filename); } if (strncmp(".PPM", filename + len - 4, 4) == 0) { filename[len - 2] = 'G'; - OJPH_WARN(0x03000002, "file was renamed %s\n", filename); + OJPH_WARN(0x03000022, "file was renamed %s\n", filename); } } fh = fopen(filename, "wb"); if (fh == NULL) - OJPH_ERROR(0x030000021, + OJPH_ERROR(0x03000023, "unable to open file %s for writing", filename); fprintf(fh, "P5\n%d %d\n%d\n", width, height, (1 << bit_depth) - 1); @@ -419,22 +419,22 @@ namespace ojph { if (strncmp(".pgm", filename + len - 4, 4) == 0) { filename[len - 2] = 'p'; - OJPH_WARN(0x03000003, "file was renamed %s\n", filename); + OJPH_WARN(0x03000024, "file was renamed %s\n", filename); } if (strncmp(".PGM", filename + len - 4, 4) == 0) { filename[len - 2] = 'P'; - OJPH_WARN(0x03000004, "file was renamed %s\n", filename); + OJPH_WARN(0x03000025, "file was renamed %s\n", filename); } } fh = fopen(filename, "wb"); if (fh == NULL) - OJPH_ERROR(0x030000022, + OJPH_ERROR(0x03000026, "unable to open file %s for writing", filename); int result = //the number of written characters fprintf(fh, "P6\n%d %d\n%d\n", width, height, (1 << bit_depth) - 1); if (result == 0) - OJPH_ERROR(0x030000023, "error writing to file %s", filename); + OJPH_ERROR(0x03000027, "error writing to file %s", filename); buffer_size = width * num_components * bytes_per_sample; buffer = (ui8*)malloc(buffer_size); } @@ -448,7 +448,7 @@ namespace ojph { { assert(fh == NULL); //configure before opening if (num_components != 1 && num_components != 3) - OJPH_ERROR(0x030000031, + OJPH_ERROR(0x03000031, "ppm supports 3 colour components, while pgm supports 1"); this->width = width; this->height = height; @@ -530,12 +530,244 @@ namespace ojph { size_t result = fwrite(buffer, bytes_per_sample, samples_per_line, fh); if (result != samples_per_line) - OJPH_ERROR(0x030000042, "error writing to file %s", fname); + OJPH_ERROR(0x03000041, "error writing to file %s", fname); } return 0; } //////////////////////////////////////////////////////////////////////////// + // + // + // + // + // + //////////////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////////////// + void pfm_in::open(const char *filename) + { + assert(fh == 0); + fh = fopen(filename, "rb"); + if (fh == 0) + OJPH_ERROR(0x03000051, "Unable to open file %s", filename); + fname = filename; + + // read magic number + char t[2]; + if (fread(t, 1, 2, fh) != 2) + { + close(); + OJPH_ERROR(0x03000052, "Error reading file %s", filename); + } + + // check magic number + if (t[0] != 'P' || (t[1] != 'F' && t[1] != 'f')) + { + close(); + OJPH_ERROR(0x03000053, "Unknown file type for file %s", filename); + } + + // set number of components based on file-type + num_comps = t[1] == 'f' ? 1 : 3; + eat_white_spaces(fh); + + // read width, height and max value in header + if (fscanf(fh, "%d %d", &width, &height) != 2) + { + close(); + OJPH_ERROR(0x03000054, + "Error reading width and height in file %s", filename); + } + eat_white_spaces(fh); + + // little or big-endian + if (fscanf(fh, "%f", &scale) != 1) + { + close(); + OJPH_ERROR(0x03000055, "Error reading scale in file %s", filename); + } + little_endian = scale < 0.0f; + scale = std::abs(scale); + + fgetc(fh); + start_of_data = ojph_ftell(fh); + + // alloc. linebuffer to hold a line of image data, if more than 1 comp. + if (temp_buf_byte_size < num_comps * width * sizeof(float)) + { + if (alloc_p == NULL) + { + temp_buf_byte_size = num_comps * width * sizeof(float); + void* t = temp_buf; + if (temp_buf) + temp_buf = (float*)realloc(temp_buf, temp_buf_byte_size); + else + temp_buf = (float*)malloc(temp_buf_byte_size); + if (temp_buf == NULL) { // failed to allocate memory + if (t) free(t); // the original buffer is still valid + OJPH_ERROR(0x03000056, "Error allocating memory"); + } + } + else + { + assert(temp_buf_byte_size == 0); //cannot reallocate the buffer + temp_buf_byte_size = num_comps * width * sizeof(float); + alloc_p->pre_alloc_data(temp_buf_byte_size, 0); + } + } + cur_line = 0; + } + + ///////////////////////////////////////////////////////////////////////////// + void pfm_in::finalize_alloc() + { + if (alloc_p == NULL) + return; + temp_buf = alloc_p->post_alloc_data(num_comps * width, 0); + } + + ///////////////////////////////////////////////////////////////////////////// + ui32 pfm_in::read(const line_buf* line, ui32 comp_num) + { + assert(temp_buf_byte_size != 0 ); + assert(fh != 0 && comp_num < num_comps); + assert(line->size >= width); + + if (comp_num == 0) + { + si64 loc = start_of_data; + loc += (height - 1 - cur_line) * num_comps * width * sizeof(float); + if (ojph_fseek(fh, loc, SEEK_SET) != 0) + { + close(); + OJPH_ERROR(0x03000061, "Error seeking in file %s", fname); + } + size_t result = fread(temp_buf, sizeof(float), num_comps * width, fh); + if (result != num_comps * width) + { + close(); + OJPH_ERROR(0x03000062, "Not enough data in file %s", fname); + } + if (++cur_line >= height) + cur_line = 0; + } + + if (little_endian) + { + ui32 shift = 32 - bit_depth; + const float* sp = temp_buf + comp_num; + float* dp = line->f32; + if (shift) + for (ui32 i = width; i > 0; --i, sp += num_comps) + { + ui32 v = *(ui32*)sp; + v >>= shift; + *dp++ = *(float*)&v; + } + else + for (ui32 i = width; i > 0; --i, sp += num_comps) + *dp++ = *sp; + } + else { + ui32 shift = 32 - bit_depth; + const float* sp = temp_buf + comp_num; + float* dp = line->f32; + if (shift) + for (ui32 i = width; i > 0; --i, sp += num_comps) { + ui32 v = be2le(*(ui32*)sp); + v >>= shift; + *dp++ = *(float*)&v; + } + else + for (ui32 i = width; i > 0; --i, sp += num_comps) { + ui32 v = be2le(*(ui32*)sp); + *dp++ = *(float*)&v; + } + } + + return width; + } + + //////////////////////////////////////////////////////////////////////////// + // + // + // + // + // + //////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////// + void pfm_out::open(char* filename) + { + assert(fh == NULL && buffer == NULL); + fh = fopen(filename, "wb"); + if (fh == NULL) + OJPH_ERROR(0x03000071, + "Unable to open file %s for writing", filename); + int result = //the number of written characters + fprintf(fh, "P%c\n%d %d\n%f\n", + num_components > 1 ? 'F' : 'f', width, height, scale); + if (result == 0) + OJPH_ERROR(0x03000072, "error writing to file %s", filename); + buffer_size = width * num_components * sizeof(float); + buffer = (float*)malloc(buffer_size); + fname = filename; + cur_line = 0; + start_of_data = ojph_ftell(fh); + } + + //////////////////////////////////////////////////////////////////////////// + void pfm_out::configure(ui32 width, ui32 height, ui32 num_components, + float scale, ui32 bit_depth) + { + assert(fh == NULL); //configure before opening + if (num_components != 1 && num_components != 3) + OJPH_ERROR(0x03000081, + "pfm supports 1 or 3 colour components, not %d", num_components); + this->width = width; + this->height = height; + this->num_components = num_components; + this->scale = scale < 0.0f ? scale : -scale; + this->bit_depth = bit_depth; + } + + //////////////////////////////////////////////////////////////////////////// + ui32 pfm_out::write(const line_buf* line, ui32 comp_num) + { + assert(fh); + + ui32 shift = 32 - bit_depth; + float* dp = buffer + comp_num; + const float* sp = line->f32; + + if (shift) + for (ui32 i = width; i > 0; --i, dp += num_components, ++sp) + { + ui32 v = *(ui32*)sp; + v <<= shift; + *dp = *(float*)&v; + } + else + for (ui32 i = width; i > 0; --i, dp += num_components) + *dp = *sp++; + + if (comp_num == num_components - 1) + { + size_t samples_per_line = num_components * width; + si64 loc = start_of_data; + loc += (height - 1 - cur_line)* samples_per_line * sizeof(float); + if (ojph_fseek(fh, loc, SEEK_SET) != 0) + OJPH_ERROR(0x03000082, "Error seeking in file %s", fname); + size_t result = fwrite(buffer, sizeof(float), samples_per_line, fh); + if (result != samples_per_line) + OJPH_ERROR(0x03000083, "error writing to file %s", fname); + ++cur_line; + } + + return 0; + } + + //////////////////////////////////////////////////////////////////////////// // // // @@ -548,7 +780,7 @@ namespace ojph { { tiff_handle = NULL; if ((tiff_handle = TIFFOpen(filename, "r")) == NULL) - OJPH_ERROR(0x0300000B1, "Unable to open file %s", filename); + OJPH_ERROR(0x03000091, "Unable to open file %s", filename); fname = filename; ui32 tiff_width = 0; @@ -588,7 +820,7 @@ namespace ojph { // allocate linebuffer to hold a line of image data line_buffer = malloc(bytes_per_line); if (NULL == line_buffer) - OJPH_ERROR(0x0300000B2, "Unable to allocate %d bytes for line_buffer[] " + OJPH_ERROR(0x03000092, "Unable to allocate %d bytes for line_buffer[] " "for file %s", bytes_per_line, filename); cur_line = 0; @@ -596,7 +828,7 @@ namespace ojph { // Error on known incompatilbe input formats if( tiff_bits_per_sample != 8 && tiff_bits_per_sample != 16 ) { - OJPH_ERROR(0x0300000B3, "\nTIFF IO is currently limited" + OJPH_ERROR(0x03000093, "\nTIFF IO is currently limited" " to files with TIFFTAG_BITSPERSAMPLE=8 and TIFFTAG_BITSPERSAMPLE=16 \n" "input file = %s has TIFFTAG_BITSPERSAMPLE=%d", filename, tiff_bits_per_sample); @@ -604,14 +836,14 @@ namespace ojph { if( TIFFIsTiled( tiff_handle ) ) { - OJPH_ERROR(0x0300000B4, "\nTIFF IO is currently limited to TIF files " + OJPH_ERROR(0x03000094, "\nTIFF IO is currently limited to TIF files " "without tiles. \nInput file %s has been detected as tiled", filename); } if(PHOTOMETRIC_RGB != tiff_photometric && PHOTOMETRIC_MINISBLACK != tiff_photometric ) { - OJPH_ERROR(0x0300000B5, "\nTIFF IO is currently limited to " + OJPH_ERROR(0x03000095, "\nTIFF IO is currently limited to " "TIFFTAG_PHOTOMETRIC=PHOTOMETRIC_MINISBLACK=%d and " "PHOTOMETRIC_RGB=%d. \nInput file %s has been detected " "TIFFTAG_PHOTOMETRIC=%d", @@ -620,7 +852,7 @@ namespace ojph { if( tiff_samples_per_pixel > 4 ) { - OJPH_ERROR(0x0300000B6, "\nTIFF IO is currently limited to " + OJPH_ERROR(0x03000096, "\nTIFF IO is currently limited to " "TIFFTAG_SAMPLESPERPIXEL=4 \nInput file %s has been detected with " "TIFFTAG_SAMPLESPERPIXEL=%d", filename, tiff_samples_per_pixel); @@ -642,7 +874,7 @@ namespace ojph { line_buffer_for_planar_support_uint8 = (uint8_t*)calloc(width, sizeof(uint8_t)); if (NULL == line_buffer_for_planar_support_uint8) - OJPH_ERROR(0x0300000B7, "Unable to allocate %d bytes for " + OJPH_ERROR(0x03000097, "Unable to allocate %d bytes for " "line_buffer_for_planar_support_uint8[] for file %s", width * sizeof(uint8_t), filename); } @@ -652,7 +884,7 @@ namespace ojph { line_buffer_for_planar_support_uint16 = (uint16_t*)calloc(width, sizeof(uint16_t)); if (NULL == line_buffer_for_planar_support_uint16) - OJPH_ERROR(0x0300000B8, "Unable to allocate %d bytes for " + OJPH_ERROR(0x03000098, "Unable to allocate %d bytes for " "line_buffer_for_planar_support_uint16[] for file %s", width * sizeof(uint16_t), filename); } @@ -664,7 +896,7 @@ namespace ojph { void tif_in::set_bit_depth(ui32 num_bit_depths, ui32* bit_depth) { if (num_bit_depths < 1) - OJPH_ERROR(0x030000B9, "one or more bit_depths must be provided"); + OJPH_ERROR(0x030000A1, "one or more bit_depths must be provided"); ui32 last_bd_idx = 0; for (ui32 i = 0; i < 4; ++i) { @@ -673,7 +905,7 @@ namespace ojph { if (bd > 32 || bd < 1) { - OJPH_ERROR(0x0300000BA, + OJPH_ERROR(0x030000A2, "bit_depth = %d, this must be an integer from 1-32", bd); } this->bit_depth[i] = bd; @@ -809,20 +1041,20 @@ namespace ojph { } if (max_bitdepth > 16) { - OJPH_WARN(0x0300000C2, "TIFF output is currently limited to files " + OJPH_WARN(0x030000B1, "TIFF output is currently limited to files " "with max_bitdepth = 16, the source codestream has max_bitdepth=%d" ", the decoded data will be truncated to 16 bits", max_bitdepth); } if (num_components > 4) { - OJPH_ERROR(0x0300000C3, "TIFF IO is currently limited to files with " + OJPH_ERROR(0x030000B2, "TIFF IO is currently limited to files with " "num_components=1 to 4"); } assert(tiff_handle == NULL && buffer == NULL); if ((tiff_handle = TIFFOpen(filename, "w")) == NULL) { - OJPH_ERROR(0x0300000C1, "unable to open file %s for writing", filename); + OJPH_ERROR(0x030000B3, "unable to open file %s for writing", filename); } buffer_size = width * num_components * bytes_per_sample; @@ -1014,7 +1246,7 @@ namespace ojph { { int result = TIFFWriteScanline(tiff_handle, buffer, cur_line++); if (result != 1) - OJPH_ERROR(0x0300000C4, "error writing to file %s", fname); + OJPH_ERROR(0x030000C1, "error writing to file %s", fname); } return 0; } @@ -1034,7 +1266,7 @@ namespace ojph { assert(fh == NULL); fh = fopen(filename, "rb"); if (fh == 0) - OJPH_ERROR(0x03000051, "Unable to open file %s", filename); + OJPH_ERROR(0x030000D1, "Unable to open file %s", filename); //need to extract info from filename @@ -1062,7 +1294,7 @@ namespace ojph { if (result != width[comp_num]) { close(); - OJPH_ERROR(0x03000061, "not enough data in file %s", fname); + OJPH_ERROR(0x030000E1, "not enough data in file %s", fname); } if (bytes_per_sample[comp_num] == 1) @@ -1088,11 +1320,11 @@ namespace ojph { ui32 num_downsamplings, const point *subsampling) { if (num_components != 1 && num_components !=3) - OJPH_ERROR(0x03000071, "yuv_in support 1 or 3 components"); + OJPH_ERROR(0x030000F1, "yuv_in support 1 or 3 components"); this->num_com = num_components; if (num_downsamplings < 1) - OJPH_ERROR(0x03000072, "one or more downsampling must be provided"); + OJPH_ERROR(0x030000F2, "one or more downsampling must be provided"); ui32 last_downsamp_idx = 0; for (ui32 i = 0; i < num_components; ++i) @@ -1114,7 +1346,7 @@ namespace ojph { void yuv_in::set_bit_depth(ui32 num_bit_depths, ui32* bit_depth) { if (num_bit_depths < 1) - OJPH_ERROR(0x03000081, "one or more bit_depths must be provided"); + OJPH_ERROR(0x03000101, "one or more bit_depths must be provided"); ui32 last_bd_idx = 0; for (ui32 i = 0; i < 3; ++i) { @@ -1156,7 +1388,7 @@ namespace ojph { assert(fh == NULL); //configure before open fh = fopen(filename, "wb"); if (fh == 0) - OJPH_ERROR(0x03000091, "Unable to open file %s", filename); + OJPH_ERROR(0x03000111, "Unable to open file %s", filename); fname = filename; } @@ -1199,7 +1431,7 @@ namespace ojph { *dp++ = (ui16)val; } if (fwrite(buffer, 2, w, fh) != w) - OJPH_ERROR(0x030000A1, "unable to write to file %s", fname); + OJPH_ERROR(0x03000121, "unable to write to file %s", fname); } else { @@ -1213,7 +1445,7 @@ namespace ojph { *dp++ = (ui8)val; } if (fwrite(buffer, 1, w, fh) != w) - OJPH_ERROR(0x030000A2, "unable to write to file %s", fname); + OJPH_ERROR(0x03000122, "unable to write to file %s", fname); } return w; @@ -1233,7 +1465,7 @@ namespace ojph { assert(fh == NULL); fh = fopen(filename, "rb"); if (fh == NULL) - OJPH_ERROR(0x030000C1, "Unable to open file %s", filename); + OJPH_ERROR(0x03000131, "Unable to open file %s", filename); cur_line = 0; bytes_per_sample = (bit_depth + 7) >> 3; @@ -1251,7 +1483,7 @@ namespace ojph { if (result != width) { close(); - OJPH_ERROR(0x030000C2, "not enough data in file %s", fname); + OJPH_ERROR(0x03000132, "not enough data in file %s", fname); } if (bytes_per_sample > 3) @@ -1360,7 +1592,7 @@ namespace ojph { assert(fh == NULL); //configure before open fh = fopen(filename, "wb"); if (fh == 0) - OJPH_ERROR(0x03000091, "Unable to open file %s", filename); + OJPH_ERROR(0x03000141, "Unable to open file %s", filename); fname = filename; } @@ -1404,7 +1636,7 @@ namespace ojph { *dp++ = (ui32)val; } if (fwrite(buffer, bytes_per_sample, width, fh) != width) - OJPH_ERROR(0x030000B1, "unable to write to file %s", fname); + OJPH_ERROR(0x03000151, "unable to write to file %s", fname); } else if (bytes_per_sample > 2) { @@ -1420,7 +1652,7 @@ namespace ojph { dp = (ui32*)((ui8*)dp + 3); } if (fwrite(buffer, bytes_per_sample, width, fh) != width) - OJPH_ERROR(0x030000B2, "unable to write to file %s", fname); + OJPH_ERROR(0x03000152, "unable to write to file %s", fname); } else if (bytes_per_sample > 1) { @@ -1434,7 +1666,7 @@ namespace ojph { *dp++ = (ui16)val; } if (fwrite(buffer, bytes_per_sample, width, fh) != width) - OJPH_ERROR(0x030000B3, "unable to write to file %s", fname); + OJPH_ERROR(0x03000153, "unable to write to file %s", fname); } else { @@ -1448,7 +1680,7 @@ namespace ojph { *dp++ = (ui8)val; } if (fwrite(buffer, bytes_per_sample, width, fh) != width) - OJPH_ERROR(0x030000B4, "unable to write to file %s", fname); + OJPH_ERROR(0x03000154, "unable to write to file %s", fname); } return width; @@ -1470,7 +1702,7 @@ namespace ojph { assert(file_handle == 0); file_handle = fopen(filename, "rb"); if (0 == file_handle) - OJPH_ERROR(0x0300000D1, "Unable to open file %s", filename); + OJPH_ERROR(0x03000161, "Unable to open file %s", filename); fname = filename; // read magic number @@ -1478,7 +1710,7 @@ namespace ojph { if (fread(&magic_number, sizeof(ui32), 1, file_handle) != 1) { close(); - OJPH_ERROR(0x0300000D2, "Error reading file %s", filename); + OJPH_ERROR(0x03000162, "Error reading file %s", filename); } // check magic number @@ -1497,7 +1729,7 @@ namespace ojph { else { close(); - OJPH_ERROR(0x0300000D3, "Error reading file %s - this does not appear " + OJPH_ERROR(0x03000163, "Error reading file %s - this does not appear " "to be a valid DPX file. It has magic number = 0x%08X. The magic " "number of a DPX file is 0x%08X.", filename, magic_number, dpx_magic_number); @@ -1508,7 +1740,7 @@ namespace ojph { != 1) { close(); - OJPH_ERROR(0x0300000D4, "Error reading file %s", filename); + OJPH_ERROR(0x03000164, "Error reading file %s", filename); } if (is_byte_swapping_necessary) offset_to_image_data_in_bytes = be2le(offset_to_image_data_in_bytes); @@ -1516,14 +1748,14 @@ namespace ojph { if (fread(version, sizeof(uint8_t), 8, file_handle) != 8) { close(); - OJPH_ERROR(0x0300000D5, "Error reading file %s", filename); + OJPH_ERROR(0x03000165, "Error reading file %s", filename); } // read image file size in bytes if (fread(&total_image_file_size_in_bytes, sizeof(ui32), 1, file_handle) != 1) { close(); - OJPH_ERROR(0x0300000D6, "Error reading file %s", filename); + OJPH_ERROR(0x03000166, "Error reading file %s", filename); } if (is_byte_swapping_necessary) total_image_file_size_in_bytes = be2le(total_image_file_size_in_bytes); @@ -1532,14 +1764,14 @@ namespace ojph { if (fseek(file_handle,768, SEEK_SET) != 0) { close(); - OJPH_ERROR(0x0300000D7, "Error reading file %s", filename); + OJPH_ERROR(0x03000167, "Error reading file %s", filename); } // read image_orientation if (fread(&image_orientation, sizeof(uint16_t), 1, file_handle) != 1) { close(); - OJPH_ERROR(0x0300000D8, "Error reading file %s", filename); + OJPH_ERROR(0x03000168, "Error reading file %s", filename); } if (is_byte_swapping_necessary) image_orientation = be2le(image_orientation); @@ -1549,7 +1781,7 @@ namespace ojph { != 1) { close(); - OJPH_ERROR(0x0300000D9, "Error reading file %s", filename); + OJPH_ERROR(0x03000169, "Error reading file %s", filename); } if (is_byte_swapping_necessary) number_of_image_elements = be2le(number_of_image_elements); @@ -1558,7 +1790,7 @@ namespace ojph { if (fread(&pixels_per_line, sizeof(ui32), 1, file_handle) != 1) { close(); - OJPH_ERROR(0x0300000DA, "Error reading file %s", filename); + OJPH_ERROR(0x0300016A, "Error reading file %s", filename); } if (is_byte_swapping_necessary) pixels_per_line = be2le(pixels_per_line); @@ -1567,7 +1799,7 @@ namespace ojph { if (fread(&lines_per_image_element, sizeof(ui32), 1, file_handle) != 1) { close(); - OJPH_ERROR(0x0300000DB, "Error reading file %s", filename); + OJPH_ERROR(0x0300016B, "Error reading file %s", filename); } if (is_byte_swapping_necessary) lines_per_image_element = be2le(lines_per_image_element); @@ -1576,7 +1808,7 @@ namespace ojph { if (fseek(file_handle, 780, SEEK_SET) != 0) { close(); - OJPH_ERROR(0x0300000DC, "Error reading file %s", filename); + OJPH_ERROR(0x0300016C, "Error reading file %s", filename); } // read data sign for image element @@ -1584,7 +1816,7 @@ namespace ojph { != 1) { close(); - OJPH_ERROR(0x0300000DE, "Error reading file %s", filename); + OJPH_ERROR(0x0300016E, "Error reading file %s", filename); } if (is_byte_swapping_necessary) data_sign_for_image_element_1 = be2le(data_sign_for_image_element_1); @@ -1593,7 +1825,7 @@ namespace ojph { if (fseek(file_handle, 800, SEEK_SET) != 0) { close(); - OJPH_ERROR(0x0300000DF, "Error reading file %s", filename); + OJPH_ERROR(0x0300016F, "Error reading file %s", filename); } // read descriptor @@ -1601,7 +1833,7 @@ namespace ojph { != 1) { close(); - OJPH_ERROR(0x0300000E0, "Error reading file %s", filename); + OJPH_ERROR(0x03000170, "Error reading file %s", filename); } // read transfer characteristic @@ -1609,7 +1841,7 @@ namespace ojph { 1, file_handle) != 1) { close(); - OJPH_ERROR(0x0300000E1, "Error reading file %s", filename); + OJPH_ERROR(0x03000171, "Error reading file %s", filename); } // read colorimetric specification @@ -1617,7 +1849,7 @@ namespace ojph { 1, file_handle) != 1) { close(); - OJPH_ERROR(0x0300000E2, "Error reading file %s", filename); + OJPH_ERROR(0x03000172, "Error reading file %s", filename); } // read bit depth @@ -1625,7 +1857,7 @@ namespace ojph { != 1) { close(); - OJPH_ERROR(0x0300000E3, "Error reading file %s", filename); + OJPH_ERROR(0x03000173, "Error reading file %s", filename); } // read packing @@ -1633,7 +1865,7 @@ namespace ojph { != 1) { close(); - OJPH_ERROR(0x0300000E4, "Error reading file %s", filename); + OJPH_ERROR(0x03000174, "Error reading file %s", filename); } if (is_byte_swapping_necessary) packing_for_image_element_1 = be2le(packing_for_image_element_1); @@ -1643,7 +1875,7 @@ namespace ojph { != 1) { close(); - OJPH_ERROR(0x0300000E5, "Error reading file %s", filename); + OJPH_ERROR(0x03000175, "Error reading file %s", filename); } if (is_byte_swapping_necessary) encoding_for_image_element_1 = be2le(encoding_for_image_element_1); @@ -1653,7 +1885,7 @@ namespace ojph { file_handle) != 1) { close(); - OJPH_ERROR(0x0300000E6, "Error reading file %s", filename); + OJPH_ERROR(0x03000176, "Error reading file %s", filename); } if (is_byte_swapping_necessary) offset_to_data_for_image_element_1 = @@ -1663,7 +1895,7 @@ namespace ojph { if (fseek(file_handle, (long)offset_to_image_data_in_bytes, SEEK_SET) != 0) { close(); - OJPH_ERROR(0x0300000E7, "Error reading file %s", filename); + OJPH_ERROR(0x03000177, "Error reading file %s", filename); } // set ojph properties @@ -1689,7 +1921,7 @@ namespace ojph { // allocate linebuffer to hold a line of image data from the file line_buffer = malloc(number_of_32_bit_words_per_line * sizeof(ui32) ); if (NULL == line_buffer) - OJPH_ERROR(0x0300000E8, "Unable to allocate %d bytes for line_buffer[] " + OJPH_ERROR(0x03000178, "Unable to allocate %d bytes for line_buffer[] " "for file %s", number_of_32_bit_words_per_line * sizeof(ui32), filename); @@ -1697,7 +1929,7 @@ namespace ojph { line_buffer_16bit_samples = (ui16*) malloc(width * num_comps * sizeof(ui16)); if (NULL == line_buffer_16bit_samples) - OJPH_ERROR(0x0300000E9, "Unable to allocate %d bytes for " + OJPH_ERROR(0x03000179, "Unable to allocate %d bytes for " "line_buffer_16bit_samples[] for file %s", width * num_comps * sizeof(ui16), filename); @@ -1719,7 +1951,7 @@ namespace ojph { file_handle) != number_of_32_bit_words_per_line) { close(); - OJPH_ERROR(0x0300000F1, "Error reading file %s", fname); + OJPH_ERROR(0x03000181, "Error reading file %s", fname); } if (true == is_byte_swapping_necessary) @@ -1773,7 +2005,7 @@ namespace ojph { } else { - OJPH_ERROR(0x0300000F2, "file %s uses DPX image formats that are not " + OJPH_ERROR(0x03000182, "file %s uses DPX image formats that are not " "yet supported by this software\n bitdepth_for_image_element_1 = " "%d\n num_comps=%d\npacking_for_image_element_1=%d\n " "descriptor_for_image_element_1=%d", fname, diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index f250c13..0ef3fd2 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -373,13 +373,13 @@ namespace ojph { //////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// - void param_nlt::set_type3_transformation(ui16 comp_num, bool enable) + void param_nlt::set_type3_transformation(ui32 comp_num, bool enable) { state->set_type3_transformation(comp_num, enable); } ////////////////////////////////////////////////////////////////////////// - bool param_nlt::get_type3_transformation(ui16 comp_num, ui8& bit_depth, + bool param_nlt::get_type3_transformation(ui32 comp_num, ui8& bit_depth, bool& is_signed) { return state->get_type3_transformation(comp_num, bit_depth, is_signed); @@ -1243,7 +1243,7 @@ namespace ojph { ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// - void param_nlt::check_validity(const param_siz& siz) + void param_nlt::check_validity(param_siz& siz) { if (is_any_enabled() == false) return; @@ -1269,7 +1269,7 @@ namespace ojph { all_same_bit_depth = all_same_bit_depth && (bit_depth == siz.get_bit_depth(c)); all_same_signedness = - all_same_signedness && (is_signed != siz.is_signed(c)); + all_same_signedness && (is_signed == siz.is_signed(c)); } } else @@ -1308,6 +1308,10 @@ namespace ojph { } trim_non_existing_components(num_comps); + + if (is_any_enabled() == false) + return; + siz.set_Rsiz_flag(param_siz::RSIZ_EXT_FLAG | param_siz::RSIZ_NLT_FLAG); } ////////////////////////////////////////////////////////////////////////// @@ -1376,7 +1380,7 @@ namespace ojph { ui16 comp = swap_byte(*(ui16*)(buf + 2)); param_nlt* p = this; - if (comp != 65535) + if (comp != special_comp_num::ALL_COMPS) { p = get_comp_object(comp); if (p == NULL) diff --git a/src/core/codestream/ojph_params_local.h b/src/core/codestream/ojph_params_local.h index fa2f690..225ad99 100644 --- a/src/core/codestream/ojph_params_local.h +++ b/src/core/codestream/ojph_params_local.h @@ -166,13 +166,20 @@ namespace ojph { { friend ::ojph::param_siz; + public: + enum : ui16 { + RSIZ_NLT_FLAG = 0x200, + RSIZ_HT_FLAG = 0x4000, + RSIZ_EXT_FLAG = 0x8000, + }; + public: param_siz() { memset(this, 0, sizeof(param_siz)); cptr = store; old_Csiz = 4; - Rsiz = 0x4000; //for jph, bit 14 of Rsiz is 1 + Rsiz = RSIZ_HT_FLAG; } ~param_siz() @@ -274,6 +281,11 @@ namespace ojph { bool is_ws_kern_support_needed() { return ws_kern_support_needed; } bool is_dfs_support_needed() { return dfs_support_needed; } + void set_Rsiz_flag(ui16 flag) + { Rsiz |= flag; } + void reset_Rsiz_flag(ui16 flag) + { Rsiz &= ~flag; } + private: ui16 Lsiz; ui16 Rsiz; @@ -670,10 +682,11 @@ namespace ojph { // data structures used by param_nlt struct param_nlt { + using special_comp_num = ojph::param_nlt::special_comp_num; public: param_nlt() { Lnlt = 6; - Cnlt = 65535; // default + Cnlt = special_comp_num::ALL_COMPS; // default BDnlt = 0; Tnlt = 3; enabled = false; next = NULL; alloced_next = false; @@ -686,7 +699,7 @@ namespace ojph { } } - void check_validity(const param_siz& siz); + void check_validity(param_siz& siz); void set_type3_transformation(ui32 comp_num, bool enable); bool get_type3_transformation(ui32 comp_num, ui8& bit_depth, bool& is_signed) const; diff --git a/src/core/codestream/ojph_tile.cpp b/src/core/codestream/ojph_tile.cpp index 29377e7..281e156 100644 --- a/src/core/codestream/ojph_tile.cpp +++ b/src/core/codestream/ojph_tile.cpp @@ -269,7 +269,7 @@ namespace ojph { memcpy(dp, sp, comp_width * sizeof(si32)); } else - cnvrt_si32_to_si32_shftd(sp, dp, -shift, comp_width); + cnvrt_si32_to_si32_shftd(sp, dp, -shift, comp_width); } else { diff --git a/src/core/common/ojph_params.h b/src/core/common/ojph_params.h index 5a74160..602fd99 100644 --- a/src/core/common/ojph_params.h +++ b/src/core/common/ojph_params.h @@ -139,6 +139,8 @@ namespace ojph { */ class OJPH_EXPORT param_nlt { + public: + enum special_comp_num : ui16 { ALL_COMPS = 65535 }; public: param_nlt(local::param_nlt* p) : state(p) {} @@ -153,7 +155,7 @@ namespace ojph { * @param enable: true to enable nlt type 3 for this component or the default setting, false to disable nlt type 3. */ - void set_type3_transformation(ui16 comp_num, bool enable); + void set_type3_transformation(ui32 comp_num, bool enable); /** * @brief get the state (enabled or disabled) of type 3 nonlinearity @@ -164,7 +166,7 @@ namespace ojph { * @param is_signed: returns true if the component/default is signed * @return true if enabled or false if not. */ - bool get_type3_transformation(ui16 comp_num, ui8& bit_depth, + bool get_type3_transformation(ui32 comp_num, ui8& bit_depth, bool& is_signed); private: From 2a36ea4ab1a8456292b5798828d2148ae42db9ff Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 13:16:18 +1000 Subject: [PATCH 09/20] Bug fixes, pfm with different bit_depth/signedness are supported now. --- src/apps/common/ojph_img_io.h | 17 ++++--- src/apps/ojph_compress/ojph_compress.cpp | 33 ++++++++++-- src/apps/ojph_expand/ojph_expand.cpp | 5 +- src/apps/others/ojph_img_io.cpp | 11 ++-- src/core/codestream/ojph_params.cpp | 64 +++++++++++++++--------- src/core/codestream/ojph_params_local.h | 1 + 6 files changed, 89 insertions(+), 42 deletions(-) diff --git a/src/apps/common/ojph_img_io.h b/src/apps/common/ojph_img_io.h index 7fddf5b..5f6488f 100644 --- a/src/apps/common/ojph_img_io.h +++ b/src/apps/common/ojph_img_io.h @@ -463,7 +463,7 @@ namespace ojph { alloc_p = p; temp_buf = NULL; temp_buf_byte_size = 0; - bit_depth = 32; + bit_depth[0] = bit_depth[1] = bit_depth[2] = 32; scale = 0.0f; little_endian = true; width = height = num_comps = 0; @@ -480,9 +480,10 @@ namespace ojph { void open(const char* filename); void finalize_alloc(); - void configure(ui32 bit_depth) { - assert(bit_depth > 0 && bit_depth <= 32); - this->bit_depth = bit_depth; + void configure(ui32* bit_depth) { + assert(num_comps != 0); + for (ui32 c = 0; c < num_comps; ++c) + this->bit_depth[c] = bit_depth[c]; } virtual ui32 read(const line_buf* line, ui32 comp_num); void close() { if(fh) { fclose(fh); fh = NULL; } fname = NULL; } @@ -498,7 +499,7 @@ namespace ojph { mem_fixed_allocator *alloc_p; float *temp_buf; ui32 temp_buf_byte_size; - ui32 bit_depth; // this truncates data to bit_depth in the LSB + ui32 bit_depth[3]; // this truncates data to bit_depth in the LSB float scale; bool little_endian; ui32 width, height, num_comps; @@ -783,7 +784,7 @@ namespace ojph { buffer_size = 0; width = height = num_components = 0; scale = -1.0f; - bit_depth = 32; + bit_depth[0] = bit_depth[1] = bit_depth[2] = 32; cur_line = 0; start_of_data = 0; } @@ -796,7 +797,7 @@ namespace ojph { void open(char* filename); void configure(ui32 width, ui32 height, ui32 num_components, - float scale, ui32 bit_depth); + float scale, ui32* bit_depth); virtual ui32 write(const line_buf* line, ui32 comp_num); virtual void close() { if(fh) { fclose(fh); fh = NULL; } fname = NULL; } @@ -807,7 +808,7 @@ namespace ojph { ui32 buffer_size; ui32 width, height, num_components; float scale; - ui32 bit_depth; + ui32 bit_depth[3]; ui32 cur_line; si64 start_of_data; }; diff --git a/src/apps/ojph_compress/ojph_compress.cpp b/src/apps/ojph_compress/ojph_compress.cpp index e1a6702..0ba6a9e 100644 --- a/src/apps/ojph_compress/ojph_compress.cpp +++ b/src/apps/ojph_compress/ojph_compress.cpp @@ -747,7 +747,28 @@ int main(int argc, char * argv[]) { assert(num_comps == 1 || num_comps == 3); siz.set_num_components(num_comps); - pfm.configure(bit_depth[0]); + if (bit_depth[0] != 0) // one was set + if (num_bit_depths < num_comps) // but if not enough, repeat + for (ojph::ui32 c = num_bit_depths; c < num_comps; ++c) + bit_depth[c] = bit_depth[num_bit_depths - 1]; + if (is_signed[0] != -1) // one was set + if (num_is_signed < num_comps) // but if not enough, repeat + for (ojph::ui32 c = num_is_signed; c < num_comps; ++c) + is_signed[c] = is_signed[num_is_signed - 1]; + + bool all_the_same = true; + if (num_comps == 3) + { + all_the_same = all_the_same + && bit_depth[0] == bit_depth[1] + && bit_depth[1] == bit_depth[2]; + all_the_same = all_the_same + && is_signed[0] == is_signed[1] + && is_signed[1] == is_signed[2]; + } + + pfm.configure(bit_depth); + ojph::point ds(1, 1); for (ojph::ui32 c = 0; c < num_comps; ++c) { ojph::ui32 bd = 32; if (bit_depth[c] != 0) @@ -755,7 +776,6 @@ int main(int argc, char * argv[]) { bool is = false; if (is_signed[c] != -1) is = is_signed[c] != 0; - ojph::point ds(1, 1); siz.set_component(c, ds, bd, is); } siz.set_image_offset(image_offset); @@ -777,8 +797,13 @@ int main(int argc, char * argv[]) { codestream.access_qcd().set_irrev_quant(quantization_step); ojph::param_nlt nlt = codestream.access_nlt(); - if (reversible) - nlt.set_type3_transformation(ojph::param_nlt::ALL_COMPS, true); + if (reversible) { + if (all_the_same) + nlt.set_type3_transformation(ojph::param_nlt::ALL_COMPS, true); + else + for (ojph::ui32 c = 0; c < num_comps; ++c) + nlt.set_type3_transformation(c, true); + } else OJPH_ERROR(0x01000091, "The support for pfm image is not " "complete; I need to figure how to modify the interface " diff --git a/src/apps/ojph_expand/ojph_expand.cpp b/src/apps/ojph_expand/ojph_expand.cpp index 2adb535..c394038 100644 --- a/src/apps/ojph_expand/ojph_expand.cpp +++ b/src/apps/ojph_expand/ojph_expand.cpp @@ -288,8 +288,11 @@ int main(int argc, char *argv[]) { OJPH_ERROR(0x0200000D, "To save an image to ppm, all the components must have the " "same downsampling ratio\n"); + ojph::ui32 bit_depth[3]; + for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c) + bit_depth[c] = siz.get_bit_depth(c); pfm.configure(siz.get_recon_width(0), siz.get_recon_height(0), - siz.get_num_components(), -1.0f, siz.get_bit_depth(0)); + siz.get_num_components(), -1.0f, bit_depth); pfm.open(output_filename); base = &pfm; } diff --git a/src/apps/others/ojph_img_io.cpp b/src/apps/others/ojph_img_io.cpp index 93dd453..7dcdd4b 100644 --- a/src/apps/others/ojph_img_io.cpp +++ b/src/apps/others/ojph_img_io.cpp @@ -654,7 +654,7 @@ namespace ojph { if (little_endian) { - ui32 shift = 32 - bit_depth; + ui32 shift = 32 - bit_depth[comp_num]; const float* sp = temp_buf + comp_num; float* dp = line->f32; if (shift) @@ -669,7 +669,7 @@ namespace ojph { *dp++ = *sp; } else { - ui32 shift = 32 - bit_depth; + ui32 shift = 32 - bit_depth[comp_num]; const float* sp = temp_buf + comp_num; float* dp = line->f32; if (shift) @@ -718,7 +718,7 @@ namespace ojph { //////////////////////////////////////////////////////////////////////////// void pfm_out::configure(ui32 width, ui32 height, ui32 num_components, - float scale, ui32 bit_depth) + float scale, ui32* bit_depth) { assert(fh == NULL); //configure before opening if (num_components != 1 && num_components != 3) @@ -728,7 +728,8 @@ namespace ojph { this->height = height; this->num_components = num_components; this->scale = scale < 0.0f ? scale : -scale; - this->bit_depth = bit_depth; + for (ui32 c = 0; c < num_components; ++c) + this->bit_depth[c] = bit_depth[c]; } //////////////////////////////////////////////////////////////////////////// @@ -736,7 +737,7 @@ namespace ojph { { assert(fh); - ui32 shift = 32 - bit_depth; + ui32 shift = 32 - bit_depth[comp_num]; float* dp = buffer + comp_num; const float* sp = line->f32; diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index 0ef3fd2..dd4692c 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -1248,10 +1248,13 @@ namespace ojph { if (is_any_enabled() == false) return; - bool all_same_bit_depth = true; - bool all_same_signedness = true; + bool all_same = true; ui32 num_comps = siz.get_num_components(); + // first stage; find out if all components captured by the default + // entry (ALL_COMPS) has the same bit_depth/signedness, + // while doing this, set the BDnlt for components not captured but the + // default entry (ALL_COMPS) ui32 bit_depth = 0; // unknown yet bool is_signed = false; // unknown yet for (ui32 c = 0; c < num_comps; ++c) @@ -1266,10 +1269,8 @@ namespace ojph { } else { // we have seen an undefined component previously - all_same_bit_depth = - all_same_bit_depth && (bit_depth == siz.get_bit_depth(c)); - all_same_signedness = - all_same_signedness && (is_signed == siz.is_signed(c)); + all_same = all_same && (bit_depth == siz.get_bit_depth(c)); + all_same = all_same && (is_signed == siz.is_signed(c)); } } else @@ -1279,26 +1280,41 @@ namespace ojph { } } + // If the default entry is enabled/used, then if the components captured + // by it are not the same, we need to create entries for these + // components if (this->enabled) { if (bit_depth != 0) // default captures some components { + // captures at least one of the componets in the default entry this->BDnlt = (ui8)((bit_depth - 1) | (is_signed ? 0x80 : (ui8)0)); - if (!all_same_bit_depth || !all_same_signedness) + + if (!all_same) { - // We cannot use the default for all undefined components, so we - // will keep it and set it to the values of the first undefined - // component, but we will also define that component + // We cannot use the default for all components in it, so we + // will keep the first one, and we will also define other + // components on their own. for (ui32 c = 0; c < num_comps; ++c) { - param_nlt* p = get_comp_object(c); - if (p == NULL) { - // values were defined previously for (p && enabled) - p = add_object(c); - p->enabled = true; - p->BDnlt = (ui8)(siz.get_bit_depth(c) - 1); - p->BDnlt = (ui8)(p->BDnlt | (siz.is_signed(c) ? 0x80 : 0)); + ui16 bd = siz.get_bit_depth(c); + bool is = siz.is_signed(c); + if (bd != bit_depth || is != is_signed) + { + // this component has different bit_depth/signedness than the + // default (ALL_COMPS) entry + param_nlt* p = get_comp_object(c); + if (p == NULL || !p->enabled) + { + // this component is captured by the default (ALL_COMPS) + // entry (because it is either not in the list, or + // not enabled + if (p == NULL) + p = add_object(c); + p->enabled = true; + p->BDnlt = (ui8)((bd - 1) | (is ? 0x80 : 0)); + } } } } @@ -1354,12 +1370,12 @@ namespace ojph { *(ui16*)buf = JP2K_MARKER::NLT; *(ui16*)buf = swap_byte(*(ui16*)buf); result &= file->write(&buf, 2) == 2; - *(ui16*)buf = swap_byte(Lnlt); + *(ui16*)buf = swap_byte(p->Lnlt); result &= file->write(&buf, 2) == 2; - *(ui16*)buf = swap_byte(Cnlt); + *(ui16*)buf = swap_byte(p->Cnlt); result &= file->write(&buf, 2) == 2; - result &= file->write(&BDnlt, 1) == 1; - result &= file->write(&Tnlt, 1) == 1; + result &= file->write(&p->BDnlt, 1) == 1; + result &= file->write(&p->Tnlt, 1) == 1; } p = p->next; } @@ -1423,9 +1439,9 @@ namespace ojph { p = p->next; } p->next = new param_nlt; + p->alloced_next = true; p = p->next; p->Cnlt = (ui16)comp_num; - p->alloced_next = true; return p; } @@ -1444,8 +1460,8 @@ namespace ojph { { param_nlt* p = this->next; while (p) { - if (p->enabled == true && p->Cnlt >= num_comps) - p->enabled = false; + if (p->enabled == true && p->Cnlt >= num_comps) + p->enabled = false; p = p->next; } } diff --git a/src/core/codestream/ojph_params_local.h b/src/core/codestream/ojph_params_local.h index 225ad99..8415e07 100644 --- a/src/core/codestream/ojph_params_local.h +++ b/src/core/codestream/ojph_params_local.h @@ -695,6 +695,7 @@ namespace ojph { ~param_nlt() { if (next && alloced_next) { delete next; + alloced_next = false; next = NULL; } } From 27c9a2e6ef637b58c47d5cc85f0e0ad02c053690 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 13:56:00 +1000 Subject: [PATCH 10/20] This fixes the one component case. --- src/apps/ojph_compress/ojph_compress.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/apps/ojph_compress/ojph_compress.cpp b/src/apps/ojph_compress/ojph_compress.cpp index 0ba6a9e..753eeaf 100644 --- a/src/apps/ojph_compress/ojph_compress.cpp +++ b/src/apps/ojph_compress/ojph_compress.cpp @@ -788,10 +788,20 @@ int main(int argc, char * argv[]) { if (num_precincts != -1) cod.set_precinct_size(num_precincts, precinct_size); cod.set_progression_order(prog_order); - if (employ_color_transform == -1) - cod.set_color_transform(true); + if (num_comps == 1) + { + if (employ_color_transform != -1) + OJPH_WARN(0x01000016, + "-colour_trans option is not needed and was not used; " + "this is because the image has one component only\n"); + } else - cod.set_color_transform(employ_color_transform == 1); + { + if (employ_color_transform == -1) + cod.set_color_transform(true); + else + cod.set_color_transform(employ_color_transform == 1); + } cod.set_reversible(reversible); if (!reversible && quantization_step != -1.0f) codestream.access_qcd().set_irrev_quant(quantization_step); From 0c6dfd3054ee211cb5ed81626f2a6d1ee41fb232 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 15:31:25 +1000 Subject: [PATCH 11/20] Removes compilation warnings. --- src/apps/common/ojph_img_io.h | 4 ++-- src/core/codestream/ojph_params.cpp | 2 +- src/core/codestream/ojph_params_local.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/apps/common/ojph_img_io.h b/src/apps/common/ojph_img_io.h index 5f6488f..27ad9f7 100644 --- a/src/apps/common/ojph_img_io.h +++ b/src/apps/common/ojph_img_io.h @@ -498,7 +498,7 @@ namespace ojph { const char *fname; mem_fixed_allocator *alloc_p; float *temp_buf; - ui32 temp_buf_byte_size; + size_t temp_buf_byte_size; ui32 bit_depth[3]; // this truncates data to bit_depth in the LSB float scale; bool little_endian; @@ -621,7 +621,7 @@ namespace ojph { ui32 width, height, num_components; ui32 bit_depth, bytes_per_sample; ui8* buffer; - ui32 buffer_size; + size_t buffer_size; ui32 cur_line, samples_per_line, bytes_per_line; conversion_fun converter; const line_buf *lptr[3]; diff --git a/src/core/codestream/ojph_params.cpp b/src/core/codestream/ojph_params.cpp index dd4692c..2bd3987 100644 --- a/src/core/codestream/ojph_params.cpp +++ b/src/core/codestream/ojph_params.cpp @@ -1298,7 +1298,7 @@ namespace ojph { for (ui32 c = 0; c < num_comps; ++c) { - ui16 bd = siz.get_bit_depth(c); + ui32 bd = siz.get_bit_depth(c); bool is = siz.is_signed(c); if (bd != bit_depth || is != is_signed) { diff --git a/src/core/codestream/ojph_params_local.h b/src/core/codestream/ojph_params_local.h index 8415e07..ac8bb77 100644 --- a/src/core/codestream/ojph_params_local.h +++ b/src/core/codestream/ojph_params_local.h @@ -284,7 +284,7 @@ namespace ojph { void set_Rsiz_flag(ui16 flag) { Rsiz |= flag; } void reset_Rsiz_flag(ui16 flag) - { Rsiz &= ~flag; } + { Rsiz = (ui16)(Rsiz & ~flag); } private: ui16 Lsiz; From 9f8011ce790b1d014ebcd7e0a6842730bf14e739 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 20:19:18 +1000 Subject: [PATCH 12/20] This fixes a bug in the block decoder code. The bug can happen when the data is around 16bits/sample of more, coded losslessly. --- src/core/codestream/ojph_codeblock.cpp | 4 ++-- src/core/coding/ojph_block_decoder.cpp | 14 +++++++------- src/core/coding/ojph_block_decoder_avx2.cpp | 14 +++++++------- src/core/coding/ojph_block_decoder_ssse3.cpp | 14 +++++++------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/core/codestream/ojph_codeblock.cpp b/src/core/codestream/ojph_codeblock.cpp index 9a63ca1..0915951 100644 --- a/src/core/codestream/ojph_codeblock.cpp +++ b/src/core/codestream/ojph_codeblock.cpp @@ -152,11 +152,11 @@ namespace ojph { if (result == false) { if (resilient == true) { - OJPH_INFO(0x000300A1, "Error decoding a codeblock"); + OJPH_INFO(0x000300A1, "Error decoding a codeblock."); zero_block = true; } else - OJPH_ERROR(0x000300A1, "Error decoding a codeblock"); + OJPH_ERROR(0x000300A1, "Error decoding a codeblock."); } } else diff --git a/src/core/coding/ojph_block_decoder.cpp b/src/core/coding/ojph_block_decoder.cpp index 5be5430..259371b 100644 --- a/src/core/coding/ojph_block_decoder.cpp +++ b/src/core/coding/ojph_block_decoder.cpp @@ -753,14 +753,14 @@ namespace ojph { { OJPH_WARN(0x00010001, "A malformed codeblock that has more than " "one coding pass, but zero length for " - "2nd and potential 3rd pass"); + "2nd and potential 3rd pass."); num_passes = 1; } if (num_passes > 3) { OJPH_WARN(0x00010002, "We do not support more than 3 coding passes; " - "This codeblocks has %d passes", + "This codeblocks has %d passes.", num_passes); return false; } @@ -772,7 +772,7 @@ namespace ojph { insufficient_precision = true; OJPH_WARN(0x00010003, "32 bits are not enough to decode this " "codeblock. This message will not be " - "displayed again"); + "displayed again."); } return false; } @@ -783,7 +783,7 @@ namespace ojph { OJPH_WARN(0x00010004, "Not enough precision to decode the cleanup " "pass. The code can be modified to support " "this case. This message will not be " - "displayed again"); + "displayed again."); } return false; // 32 bits are not enough to decode this } @@ -796,7 +796,7 @@ namespace ojph { OJPH_WARN(0x00010005, "Not enough precision to decode the SgnProp " "nor MagRef passes; both will be skipped. " "This message will not be displayed " - "again"); + "again."); } } } @@ -806,7 +806,7 @@ namespace ojph { if (lengths1 < 2) { - OJPH_WARN(0x00010006, "Wrong codeblock length"); + OJPH_WARN(0x00010006, "Wrong codeblock length."); return false; } @@ -1079,7 +1079,7 @@ namespace ojph { // quad 0 length len = uvlc_entry & 0x7; // quad 0 suffix length uvlc_entry >>= 3; - ui16 u_q = (ui16)((uvlc_entry & 7) + (tmp & ~(0xFU << len))); //u_q + ui16 u_q = (ui16)((uvlc_entry & 7) + (tmp & ~(0xFFU << len))); sp[1] = u_q; u_q = (ui16)((uvlc_entry >> 3) + (tmp >> len)); // u_q sp[3] = u_q; diff --git a/src/core/coding/ojph_block_decoder_avx2.cpp b/src/core/coding/ojph_block_decoder_avx2.cpp index e7270a7..156ba1a 100644 --- a/src/core/coding/ojph_block_decoder_avx2.cpp +++ b/src/core/coding/ojph_block_decoder_avx2.cpp @@ -1077,14 +1077,14 @@ namespace ojph { { OJPH_WARN(0x00010001, "A malformed codeblock that has more than " "one coding pass, but zero length for " - "2nd and potential 3rd pass"); + "2nd and potential 3rd pass."); num_passes = 1; } if (num_passes > 3) { OJPH_WARN(0x00010002, "We do not support more than 3 coding passes; " - "This codeblocks has %d passes", + "This codeblocks has %d passes.", num_passes); return false; } @@ -1096,7 +1096,7 @@ namespace ojph { insufficient_precision = true; OJPH_WARN(0x00010003, "32 bits are not enough to decode this " "codeblock. This message will not be " - "displayed again"); + "displayed again."); } return false; } @@ -1107,7 +1107,7 @@ namespace ojph { OJPH_WARN(0x00010004, "Not enough precision to decode the cleanup " "pass. The code can be modified to support " "this case. This message will not be " - "displayed again"); + "displayed again."); } return false; // 32 bits are not enough to decode this } @@ -1120,7 +1120,7 @@ namespace ojph { OJPH_WARN(0x00010005, "Not enough precision to decode the SgnProp " "nor MagRef passes; both will be skipped. " "This message will not be displayed " - "again"); + "again."); } } } @@ -1130,7 +1130,7 @@ namespace ojph { if (lengths1 < 2) { - OJPH_WARN(0x00010006, "Wrong codeblock length"); + OJPH_WARN(0x00010006, "Wrong codeblock length."); return false; } @@ -1407,7 +1407,7 @@ namespace ojph { // quad 0 length len = uvlc_entry & 0x7; // quad 0 suffix length uvlc_entry >>= 3; - ui16 u_q = (ui16)((uvlc_entry & 7) + (tmp & ~(0xFU << len))); //u_q + ui16 u_q = (ui16)((uvlc_entry & 7) + (tmp & ~(0xFFU << len))); sp[1] = u_q; u_q = (ui16)((uvlc_entry >> 3) + (tmp >> len)); // u_q sp[3] = u_q; diff --git a/src/core/coding/ojph_block_decoder_ssse3.cpp b/src/core/coding/ojph_block_decoder_ssse3.cpp index 99ae38c..9fa5800 100644 --- a/src/core/coding/ojph_block_decoder_ssse3.cpp +++ b/src/core/coding/ojph_block_decoder_ssse3.cpp @@ -1033,14 +1033,14 @@ namespace ojph { { OJPH_WARN(0x00010001, "A malformed codeblock that has more than " "one coding pass, but zero length for " - "2nd and potential 3rd pass"); + "2nd and potential 3rd pass."); num_passes = 1; } if (num_passes > 3) { OJPH_WARN(0x00010002, "We do not support more than 3 coding passes; " - "This codeblocks has %d passes", + "This codeblocks has %d passes.", num_passes); return false; } @@ -1052,7 +1052,7 @@ namespace ojph { insufficient_precision = true; OJPH_WARN(0x00010003, "32 bits are not enough to decode this " "codeblock. This message will not be " - "displayed again"); + "displayed again."); } return false; } @@ -1063,7 +1063,7 @@ namespace ojph { OJPH_WARN(0x00010004, "Not enough precision to decode the cleanup " "pass. The code can be modified to support " "this case. This message will not be " - "displayed again"); + "displayed again."); } return false; // 32 bits are not enough to decode this } @@ -1076,7 +1076,7 @@ namespace ojph { OJPH_WARN(0x00010005, "Not enough precision to decode the SgnProp " "nor MagRef passes; both will be skipped. " "This message will not be displayed " - "again"); + "again."); } } } @@ -1086,7 +1086,7 @@ namespace ojph { if (lengths1 < 2) { - OJPH_WARN(0x00010006, "Wrong codeblock length"); + OJPH_WARN(0x00010006, "Wrong codeblock length."); return false; } @@ -1361,7 +1361,7 @@ namespace ojph { // quad 0 length len = uvlc_entry & 0x7; // quad 0 suffix length uvlc_entry >>= 3; - ui16 u_q = (ui16)((uvlc_entry & 7) + (tmp & ~(0xFU << len))); //u_q + ui16 u_q = (ui16)((uvlc_entry & 7) + (tmp & ~(0xFFU << len))); sp[1] = u_q; u_q = (ui16)((uvlc_entry >> 3) + (tmp >> len)); // u_q sp[3] = u_q; From 7b0f5c89c0d3274c2370247541f62244e337be5d Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 22:06:08 +1000 Subject: [PATCH 13/20] Improvement for pfm_in, and a bug fix for handling signed pfm data. --- src/apps/others/ojph_img_io.cpp | 7 ++++--- src/core/transform/ojph_colour.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/apps/others/ojph_img_io.cpp b/src/apps/others/ojph_img_io.cpp index 7dcdd4b..d3211d7 100644 --- a/src/apps/others/ojph_img_io.cpp +++ b/src/apps/others/ojph_img_io.cpp @@ -660,7 +660,7 @@ namespace ojph { if (shift) for (ui32 i = width; i > 0; --i, sp += num_comps) { - ui32 v = *(ui32*)sp; + si32 v = *(si32*)sp; v >>= shift; *dp++ = *(float*)&v; } @@ -675,8 +675,9 @@ namespace ojph { if (shift) for (ui32 i = width; i > 0; --i, sp += num_comps) { ui32 v = be2le(*(ui32*)sp); - v >>= shift; - *dp++ = *(float*)&v; + si32 u = *(si32*)&v; + u >>= shift; + *dp++ = *(float*)&u; } else for (ui32 i = width; i > 0; --i, sp += num_comps) { diff --git a/src/core/transform/ojph_colour.cpp b/src/core/transform/ojph_colour.cpp index 34161d4..ca96d2d 100644 --- a/src/core/transform/ojph_colour.cpp +++ b/src/core/transform/ojph_colour.cpp @@ -214,7 +214,7 @@ namespace ojph { { for (ui32 i = width; i > 0; --i) { const si32 v = *sp++; - *dp++ = v > 0 ? v : (- v - shift); + *dp++ = v >= 0 ? v : (- v - shift); } } From b6ddad9ff89ab93161b9ea1cfccc8d2503699184 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 22:37:27 +1000 Subject: [PATCH 14/20] This should address "strict-aliasing". Cosmetic change to a wasm file. Also addresses CodeQL. --- src/apps/others/ojph_img_io.cpp | 80 ++++++++++++++----------- src/core/transform/ojph_colour_wasm.cpp | 4 +- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/apps/others/ojph_img_io.cpp b/src/apps/others/ojph_img_io.cpp index d3211d7..572dd5e 100644 --- a/src/apps/others/ojph_img_io.cpp +++ b/src/apps/others/ojph_img_io.cpp @@ -593,11 +593,11 @@ namespace ojph { start_of_data = ojph_ftell(fh); // alloc. linebuffer to hold a line of image data, if more than 1 comp. - if (temp_buf_byte_size < num_comps * width * sizeof(float)) + if (temp_buf_byte_size < num_comps * (size_t)width * sizeof(float)) { if (alloc_p == NULL) { - temp_buf_byte_size = num_comps * width * sizeof(float); + temp_buf_byte_size = num_comps * (size_t)width * sizeof(float); void* t = temp_buf; if (temp_buf) temp_buf = (float*)realloc(temp_buf, temp_buf_byte_size); @@ -611,7 +611,7 @@ namespace ojph { else { assert(temp_buf_byte_size == 0); //cannot reallocate the buffer - temp_buf_byte_size = num_comps * width * sizeof(float); + temp_buf_byte_size = num_comps * (size_t)width * sizeof(float); alloc_p->pre_alloc_data(temp_buf_byte_size, 0); } } @@ -623,7 +623,7 @@ namespace ojph { { if (alloc_p == NULL) return; - temp_buf = alloc_p->post_alloc_data(num_comps * width, 0); + temp_buf = alloc_p->post_alloc_data(num_comps * (size_t)width, 0); } ///////////////////////////////////////////////////////////////////////////// @@ -636,13 +636,14 @@ namespace ojph { if (comp_num == 0) { si64 loc = start_of_data; - loc += (height - 1 - cur_line) * num_comps * width * sizeof(float); + loc += (size_t)(height-1 - cur_line) * num_comps * width * sizeof(float); if (ojph_fseek(fh, loc, SEEK_SET) != 0) { close(); OJPH_ERROR(0x03000061, "Error seeking in file %s", fname); } - size_t result = fread(temp_buf, sizeof(float), num_comps * width, fh); + size_t result = + fread(temp_buf, sizeof(float), num_comps * (size_t)width, fh); if (result != num_comps * width) { close(); @@ -652,38 +653,42 @@ namespace ojph { cur_line = 0; } + union { + si32* s; + ui32* u; + float* f; + } sp, dp; + if (little_endian) { ui32 shift = 32 - bit_depth[comp_num]; - const float* sp = temp_buf + comp_num; - float* dp = line->f32; + sp.f = temp_buf + comp_num; + dp.f = line->f32; if (shift) - for (ui32 i = width; i > 0; --i, sp += num_comps) + for (ui32 i = width; i > 0; --i, sp.f += num_comps) { - si32 v = *(si32*)sp; - v >>= shift; - *dp++ = *(float*)&v; + si32 s = *sp.s; + s >>= shift; + *dp.s++ = s; } else - for (ui32 i = width; i > 0; --i, sp += num_comps) - *dp++ = *sp; + for (ui32 i = width; i > 0; --i, sp.f += num_comps) + *dp.f++ = *sp.f; } else { ui32 shift = 32 - bit_depth[comp_num]; - const float* sp = temp_buf + comp_num; - float* dp = line->f32; + sp.f = temp_buf + comp_num; + dp.f = line->f32; if (shift) - for (ui32 i = width; i > 0; --i, sp += num_comps) { - ui32 v = be2le(*(ui32*)sp); - si32 u = *(si32*)&v; - u >>= shift; - *dp++ = *(float*)&u; + for (ui32 i = width; i > 0; --i, sp.f += num_comps) { + ui32 u = be2le(*sp.u); + si32 s = *(si32*)&u; + s >>= shift; + *dp.s++ = s; } else - for (ui32 i = width; i > 0; --i, sp += num_comps) { - ui32 v = be2le(*(ui32*)sp); - *dp++ = *(float*)&v; - } + for (ui32 i = width; i > 0; --i, sp.f += num_comps) + *dp.u++ = be2le(*sp.u); } return width; @@ -710,7 +715,7 @@ namespace ojph { num_components > 1 ? 'F' : 'f', width, height, scale); if (result == 0) OJPH_ERROR(0x03000072, "error writing to file %s", filename); - buffer_size = width * num_components * sizeof(float); + buffer_size = (size_t)width * num_components * sizeof(float); buffer = (float*)malloc(buffer_size); fname = filename; cur_line = 0; @@ -739,23 +744,28 @@ namespace ojph { assert(fh); ui32 shift = 32 - bit_depth[comp_num]; - float* dp = buffer + comp_num; - const float* sp = line->f32; + union { + ui32* u; + float* f; + } sp, dp; + + dp.f = buffer + comp_num; + sp.f = line->f32; if (shift) - for (ui32 i = width; i > 0; --i, dp += num_components, ++sp) + for (ui32 i = width; i > 0; --i, dp.f += num_components, ++sp.f) { - ui32 v = *(ui32*)sp; - v <<= shift; - *dp = *(float*)&v; + ui32 u = *sp.u; + u <<= shift; + *dp.u = u; } else - for (ui32 i = width; i > 0; --i, dp += num_components) - *dp = *sp++; + for (ui32 i = width; i > 0; --i, dp.f += num_components) + *dp.f = *sp.f++; if (comp_num == num_components - 1) { - size_t samples_per_line = num_components * width; + size_t samples_per_line = num_components * (size_t)width; si64 loc = start_of_data; loc += (height - 1 - cur_line)* samples_per_line * sizeof(float); if (ojph_fseek(fh, loc, SEEK_SET) != 0) diff --git a/src/core/transform/ojph_colour_wasm.cpp b/src/core/transform/ojph_colour_wasm.cpp index e174d0b..57b84c7 100644 --- a/src/core/transform/ojph_colour_wasm.cpp +++ b/src/core/transform/ojph_colour_wasm.cpp @@ -133,8 +133,8 @@ namespace ojph { v128_t s = wasm_v128_load(sp); v128_t c = wasm_i32x4_lt(s, zero); // 0xFFFFFFFF for -ve value v128_t v_m_sh = wasm_i32x4_sub(sh, s); // - shift - value - v_m_sh = wasm_v128_and(v_m_sh, c); // keep only - shift - value - s = wasm_v128_andnot(s, c); // keep only +ve or 0 + v_m_sh = wasm_v128_and(c, v_m_sh); // keep only - shift - value + s = wasm_v128_andnot(c, s); // keep only +ve or 0 s = wasm_v128_or(s, v_m_sh); // combine wasm_v128_store(dp, s); } From 1580ae79669fe86aa88c26ed42e775a2ecbd6aa8 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 22:41:48 +1000 Subject: [PATCH 15/20] This address one more warning. --- src/apps/common/ojph_img_io.h | 2 +- src/apps/others/ojph_img_io.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/apps/common/ojph_img_io.h b/src/apps/common/ojph_img_io.h index 27ad9f7..401ad65 100644 --- a/src/apps/common/ojph_img_io.h +++ b/src/apps/common/ojph_img_io.h @@ -805,7 +805,7 @@ namespace ojph { FILE *fh; const char *fname; float* buffer; - ui32 buffer_size; + size_t buffer_size; ui32 width, height, num_components; float scale; ui32 bit_depth[3]; diff --git a/src/apps/others/ojph_img_io.cpp b/src/apps/others/ojph_img_io.cpp index 572dd5e..a7a91b7 100644 --- a/src/apps/others/ojph_img_io.cpp +++ b/src/apps/others/ojph_img_io.cpp @@ -643,7 +643,7 @@ namespace ojph { OJPH_ERROR(0x03000061, "Error seeking in file %s", fname); } size_t result = - fread(temp_buf, sizeof(float), num_comps * (size_t)width, fh); + fread(temp_buf, sizeof(float), (size_t)num_comps * (size_t)width, fh); if (result != num_comps * width) { close(); From 9e97f99d3e97afb62b6f84f334e9a5a9134ac8f4 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 22:48:46 +1000 Subject: [PATCH 16/20] Hopefully, this will remove warnings. --- src/apps/others/ojph_img_io.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/apps/others/ojph_img_io.cpp b/src/apps/others/ojph_img_io.cpp index a7a91b7..d812025 100644 --- a/src/apps/others/ojph_img_io.cpp +++ b/src/apps/others/ojph_img_io.cpp @@ -636,7 +636,8 @@ namespace ojph { if (comp_num == 0) { si64 loc = start_of_data; - loc += (size_t)(height-1 - cur_line) * num_comps * width * sizeof(float); + loc += (size_t)(height-1 - cur_line) * (size_t)num_comps + * (size_t)width * sizeof(float); if (ojph_fseek(fh, loc, SEEK_SET) != 0) { close(); @@ -644,7 +645,7 @@ namespace ojph { } size_t result = fread(temp_buf, sizeof(float), (size_t)num_comps * (size_t)width, fh); - if (result != num_comps * width) + if (result != (size_t)num_comps * (size_t)width) { close(); OJPH_ERROR(0x03000062, "Not enough data in file %s", fname); From bcc4d3bd394fd97b50b82677b96d3aa346f50647 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Fri, 20 Sep 2024 23:24:36 +1000 Subject: [PATCH 17/20] Added usage text for ojph_compress. Also , the end user must now specify -bit_depth for .pfm files -- this temporary. --- src/apps/ojph_compress/ojph_compress.cpp | 41 +++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/apps/ojph_compress/ojph_compress.cpp b/src/apps/ojph_compress/ojph_compress.cpp index 753eeaf..90c8a05 100644 --- a/src/apps/ojph_compress/ojph_compress.cpp +++ b/src/apps/ojph_compress/ojph_compress.cpp @@ -526,9 +526,9 @@ int main(int argc, char * argv[]) { std::cout << "\nThe following arguments are necessary:\n" #ifdef OJPH_ENABLE_TIFF_SUPPORT - " -i input file name (either pgm, ppm, tif(f), or raw(yuv))\n" + " -i input file name (either pgm, ppm, pfm, tif(f), or raw(yuv))\n" #else - " -i input file name (either pgm, ppm, or raw(yuv))\n" + " -i input file name (either pgm, ppm, pfm, or raw(yuv))\n" #endif // !OJPH_ENABLE_TIFF_SUPPORT " -o output file name\n\n" @@ -587,7 +587,28 @@ int main(int argc, char * argv[]) { " component; for example: 12,10,10\n" " -downsamp {x,y},{x,y},...,{x,y} a list of x,y points, one for each\n" " component; for example {1,1},{2,2},{2,2}\n\n" - ; + "\n" + + ".pfm files receive special treatment. Currently, lossy compression\n" + "with these files is not supported, only lossless. When these files are\n" + "used, the NLT segment marker is automatically inserted into the\n" + "codestream. For these files the following arguments can be useful\n" + " -signed a comma - separated list of true or false parameters, one\n" + " for each component; for example: true,false,false.\n" + " The sign only affects how values are treated; for negative\n" + " values the standard requires a special non-linear\n" + " transformation. When signed is false, no transformation\n" + " is employed, as we assume all values are 0 or positive.\n" + " When signed is true, the aforementioned transformation is\n" + " employed on negative values only.\n" + " -bit_depth a comma-separated list of bit depth values, one per \n" + " component; for example: 12,10,10.\n" + " Floating value numbers are treated as integers, and they\n" + " are shifted to the right, keeping only the specificed\n" + " number of bits. Note that a bit depth of 28 upwards is not\n" + " supported.\n" + + "\n"; return -1; } if (!get_arguments(argc, argv, input_filename, output_filename, @@ -747,6 +768,10 @@ int main(int argc, char * argv[]) { assert(num_comps == 1 || num_comps == 3); siz.set_num_components(num_comps); + if (bit_depth[0] == 0) + OJPH_ERROR(0x01000091, + "-bit_depth must be specified (this is temporary only).\n"); + if (bit_depth[0] != 0) // one was set if (num_bit_depths < num_comps) // but if not enough, repeat for (ojph::ui32 c = num_bit_depths; c < num_comps; ++c) @@ -791,7 +816,7 @@ int main(int argc, char * argv[]) { if (num_comps == 1) { if (employ_color_transform != -1) - OJPH_WARN(0x01000016, + OJPH_WARN(0x01000092, "-colour_trans option is not needed and was not used; " "this is because the image has one component only\n"); } @@ -815,7 +840,7 @@ int main(int argc, char * argv[]) { nlt.set_type3_transformation(c, true); } else - OJPH_ERROR(0x01000091, "The support for pfm image is not " + OJPH_ERROR(0x01000093, "The support for pfm image is not " "complete; I need to figure how to modify the interface " "to better support the exchange of floating point data. " "Feeding float point data is not supported yet, unless it " @@ -829,13 +854,13 @@ int main(int argc, char * argv[]) { codestream.request_tlm_marker(tlm_marker); if (dims.w != 0 || dims.h != 0) - OJPH_WARN(0x01000092, + OJPH_WARN(0x01000094, "-dims option is not needed and was not used\n"); if (num_components != 0) - OJPH_WARN(0x01000093, + OJPH_WARN(0x01000095, "-num_comps is not needed and was not used\n"); if (comp_downsampling[0].x != 0 || comp_downsampling[0].y != 0) - OJPH_WARN(0x01000094, + OJPH_WARN(0x01000096, "-downsamp is not needed and was not used\n"); base = &pfm; From 11307b641838a551e0e9c76ed067b49ed2226c71 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Sun, 22 Sep 2024 13:45:58 +1000 Subject: [PATCH 18/20] Change default signedness of .pfm to true. --- src/apps/ojph_compress/ojph_compress.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/ojph_compress/ojph_compress.cpp b/src/apps/ojph_compress/ojph_compress.cpp index 90c8a05..d87e38b 100644 --- a/src/apps/ojph_compress/ojph_compress.cpp +++ b/src/apps/ojph_compress/ojph_compress.cpp @@ -798,7 +798,7 @@ int main(int argc, char * argv[]) { ojph::ui32 bd = 32; if (bit_depth[c] != 0) bd = bit_depth[c]; - bool is = false; + bool is = true; if (is_signed[c] != -1) is = is_signed[c] != 0; siz.set_component(c, ds, bd, is); From 57c7af3b757f2ca6ba7d4dca527fb33f1d11a7d5 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Sun, 22 Sep 2024 14:13:18 +1000 Subject: [PATCH 19/20] This adds some codestream checking in ojph_expand. --- src/apps/ojph_expand/ojph_expand.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/apps/ojph_expand/ojph_expand.cpp b/src/apps/ojph_expand/ojph_expand.cpp index c394038..3d3b981 100644 --- a/src/apps/ojph_expand/ojph_expand.cpp +++ b/src/apps/ojph_expand/ojph_expand.cpp @@ -271,6 +271,8 @@ int main(int argc, char *argv[]) { { codestream.set_planar(false); ojph::param_siz siz = codestream.access_siz(); + ojph::param_cod cod = codestream.access_cod(); + ojph::param_nlt nlt = codestream.access_nlt(); ojph::ui32 num_comps = siz.get_num_components(); if (num_comps != 3 && num_comps != 1) @@ -289,8 +291,30 @@ int main(int argc, char *argv[]) { "To save an image to ppm, all the components must have the " "same downsampling ratio\n"); ojph::ui32 bit_depth[3]; - for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c) - bit_depth[c] = siz.get_bit_depth(c); + for (ojph::ui32 c = 0; c < siz.get_num_components(); ++c) { + ojph::ui8 bd = 0; + bool is = true; + bool result = nlt.get_type3_transformation(c, bd, is); + if (result == false) + OJPH_ERROR(0x0200000E, + "This codestream is not supported; it does not have an " + "NLT segment marker for this component (or no default NLT " + "settings) .\n"); + if (bd != siz.get_bit_depth(c) || is != siz.is_signed(c)) + OJPH_ERROR(0x0200000F, + "There is discrepancy in component %d configuration between " + "SIZ marker segment, which specifies bit_depth = %d and " + "signedness = %s, and NLT marker segment, which specifies " + "bit_depth = %d and signedness = %s.\n", c, + siz.get_bit_depth(c), is != siz.is_signed(c) ? "True" : "False", + bd, is ? "True" : "False"); + bit_depth[c] = bd; + } + if (!cod.is_reversible()) + OJPH_ERROR(0x02000010, + "This codestream is lossy (not reversible), and we currently " + "only support reversible codestreams for .pfm target files. " + "This is only temporary and will be changed at some point.\n"); pfm.configure(siz.get_recon_width(0), siz.get_recon_height(0), siz.get_num_components(), -1.0f, bit_depth); pfm.open(output_filename); From 796fb6bd3cfeed1276f814b0d59df04a229830b7 Mon Sep 17 00:00:00 2001 From: Aous Naman Date: Sun, 22 Sep 2024 14:42:03 +1000 Subject: [PATCH 20/20] Spelling mistake fix. --- src/apps/ojph_compress/ojph_compress.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/ojph_compress/ojph_compress.cpp b/src/apps/ojph_compress/ojph_compress.cpp index d87e38b..e7c047a 100644 --- a/src/apps/ojph_compress/ojph_compress.cpp +++ b/src/apps/ojph_compress/ojph_compress.cpp @@ -604,7 +604,7 @@ int main(int argc, char * argv[]) { " -bit_depth a comma-separated list of bit depth values, one per \n" " component; for example: 12,10,10.\n" " Floating value numbers are treated as integers, and they\n" - " are shifted to the right, keeping only the specificed\n" + " are shifted to the right, keeping only the specified\n" " number of bits. Note that a bit depth of 28 upwards is not\n" " supported.\n"