summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2024-06-25 20:29:30 +0200
committerEven Rouault <even.rouault@spatialys.com>2024-07-01 11:36:37 +0200
commit954c6e3cb9d79aaa08c6666373d2bfa04f89ead1 (patch)
tree046ff1d04b434acdd8a320281d21a908a8ecf93e /src/lib
parent5005a350a78d1918e98e970457a8316a23c50e19 (diff)
Use TLM markers to optimize opj_get_decoded_tile()
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/openjp2/j2k.c343
-rw-r--r--src/lib/openjp2/j2k.h20
2 files changed, 301 insertions, 62 deletions
diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c
index 7cdeffd7..55ef0191 100644
--- a/src/lib/openjp2/j2k.c
+++ b/src/lib/openjp2/j2k.c
@@ -2484,6 +2484,11 @@ static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k,
++l_current_tile_param;
}
+ /*Allocate and initialize some elements of codestrem index*/
+ if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) {
+ return OPJ_FALSE;
+ }
+
p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MH;
opj_image_comp_header_update(l_image, l_cp);
@@ -3657,21 +3662,29 @@ static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k,
opj_event_mgr_t * p_manager
)
{
- OPJ_UINT32 l_Ztlm, l_Stlm, l_ST, l_SP, l_tot_num_tp_remaining, l_quotient,
- l_Ptlm_size;
+ OPJ_UINT32 l_Ztlm, l_Stlm, l_ST, l_SP,
+ l_Ptlm_size, l_entry_size, l_num_tileparts;
+ OPJ_UINT32 i;
+ opj_j2k_tlm_tile_part_info_t* l_tile_part_infos;
+ opj_j2k_tlm_info_t* l_tlm;
+
/* preconditions */
assert(p_header_data != 00);
assert(p_j2k != 00);
assert(p_manager != 00);
- OPJ_UNUSED(p_j2k);
+ l_tlm = &(p_j2k->m_specific_param.m_decoder.m_tlm);
if (p_header_size < 2) {
- opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
+ opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker.\n");
return OPJ_FALSE;
}
p_header_size -= 2;
+ if (l_tlm->m_is_invalid) {
+ return OPJ_TRUE;
+ }
+
opj_read_bytes(p_header_data, &l_Ztlm,
1); /* Ztlm */
++p_header_data;
@@ -3680,27 +3693,83 @@ static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k,
++p_header_data;
l_ST = ((l_Stlm >> 4) & 0x3);
+ if (l_ST == 3) {
+ l_tlm->m_is_invalid = OPJ_TRUE;
+ opj_event_msg(p_manager, EVT_WARNING,
+ "opj_j2k_read_tlm(): ST = 3 is invalid.\n");
+ return OPJ_TRUE;
+ }
l_SP = (l_Stlm >> 6) & 0x1;
l_Ptlm_size = (l_SP + 1) * 2;
- l_quotient = l_Ptlm_size + l_ST;
+ l_entry_size = l_Ptlm_size + l_ST;
+
+ if ((p_header_size % l_entry_size) != 0) {
+ l_tlm->m_is_invalid = OPJ_TRUE;
+ opj_event_msg(p_manager, EVT_WARNING,
+ "opj_j2k_read_tlm(): TLM marker not of expected size.\n");
+ return OPJ_TRUE;
+ }
- l_tot_num_tp_remaining = p_header_size % l_quotient;
+ l_num_tileparts = p_header_size / l_entry_size;
+ if (l_num_tileparts == 0) {
+ /* not totally sure if this is valid... */
+ return OPJ_TRUE;
+ }
- if (l_tot_num_tp_remaining != 0) {
- opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n");
- return OPJ_FALSE;
+ /* Highly unlikely, unless there are gazillions of TLM markers */
+ if (l_tlm->m_entries_count > UINT32_MAX - l_num_tileparts ||
+ l_tlm->m_entries_count + l_num_tileparts > UINT32_MAX / sizeof(
+ opj_j2k_tlm_tile_part_info_t)) {
+ l_tlm->m_is_invalid = OPJ_TRUE;
+ opj_event_msg(p_manager, EVT_WARNING,
+ "opj_j2k_read_tlm(): too many TLM markers.\n");
+ return OPJ_TRUE;
}
- /* FIXME Do not care of this at the moment since only local variables are set here */
- /*
- for
- (i = 0; i < l_tot_num_tp; ++i)
- {
- opj_read_bytes(p_header_data,&l_Ttlm_i,l_ST); // Ttlm_i
+
+ l_tile_part_infos = (opj_j2k_tlm_tile_part_info_t*)opj_realloc(
+ l_tlm->m_tile_part_infos,
+ (l_tlm->m_entries_count + l_num_tileparts) * sizeof(
+ opj_j2k_tlm_tile_part_info_t));
+ if (!l_tile_part_infos) {
+ l_tlm->m_is_invalid = OPJ_TRUE;
+ opj_event_msg(p_manager, EVT_WARNING,
+ "opj_j2k_read_tlm(): cannot allocate m_tile_part_infos.\n");
+ return OPJ_TRUE;
+ }
+
+ l_tlm->m_tile_part_infos = l_tile_part_infos;
+
+ for (i = 0; i < l_num_tileparts; ++ i) {
+ OPJ_UINT32 l_tile_index;
+ OPJ_UINT32 l_length;
+
+ /* Read Ttlm_i */
+ if (l_ST == 0) {
+ l_tile_index = l_tlm->m_entries_count;
+ } else {
+ opj_read_bytes(p_header_data, &l_tile_index, l_ST);
p_header_data += l_ST;
- opj_read_bytes(p_header_data,&l_Ptlm_i,l_Ptlm_size); // Ptlm_i
- p_header_data += l_Ptlm_size;
- }*/
+ }
+
+ if (l_tile_index >= p_j2k->m_cp.tw * p_j2k->m_cp.th) {
+ l_tlm->m_is_invalid = OPJ_TRUE;
+ opj_event_msg(p_manager, EVT_WARNING,
+ "opj_j2k_read_tlm(): invalid tile number %d\n",
+ l_tile_index);
+ return OPJ_TRUE;
+ }
+
+ /* Read Ptlm_i */
+ opj_read_bytes(p_header_data, &l_length, l_Ptlm_size);
+ p_header_data += l_Ptlm_size;
+
+ l_tile_part_infos[l_tlm->m_entries_count].m_tile_index =
+ (OPJ_UINT16)l_tile_index;
+ l_tile_part_infos[l_tlm->m_entries_count].m_length = l_length;
+ ++l_tlm->m_entries_count;
+ }
+
return OPJ_TRUE;
}
@@ -4583,14 +4652,26 @@ static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
}
/* Index */
- if (p_j2k->cstr_index) {
+ {
assert(p_j2k->cstr_index->tile_index != 00);
p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tileno =
p_j2k->m_current_tile_number;
p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_tpsno =
l_current_part;
- if (l_num_parts != 0) {
+ if (!p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid &&
+ l_num_parts >
+ p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps) {
+ opj_event_msg(p_manager, EVT_WARNING,
+ "SOT marker for tile %u declares more tile-parts than found in TLM marker.",
+ p_j2k->m_current_tile_number);
+ p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid = OPJ_TRUE;
+ }
+
+ if (!p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid) {
+ /* do nothing */
+ } else if (l_num_parts != 0) {
+
p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps =
l_num_parts;
p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps =
@@ -4661,33 +4742,6 @@ static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k,
}
- /* FIXME move this onto a separate method to call before reading any SOT, remove part about main_end header, use a index struct inside p_j2k */
- /* if (p_j2k->cstr_info) {
- if (l_tcp->first) {
- if (tileno == 0) {
- p_j2k->cstr_info->main_head_end = p_stream_tell(p_stream) - 13;
- }
-
- p_j2k->cstr_info->tile[tileno].tileno = tileno;
- p_j2k->cstr_info->tile[tileno].start_pos = p_stream_tell(p_stream) - 12;
- p_j2k->cstr_info->tile[tileno].end_pos = p_j2k->cstr_info->tile[tileno].start_pos + totlen - 1;
- p_j2k->cstr_info->tile[tileno].num_tps = numparts;
-
- if (numparts) {
- p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(numparts * sizeof(opj_tp_info_t));
- }
- else {
- p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(10 * sizeof(opj_tp_info_t)); // Fixme (10)
- }
- }
- else {
- p_j2k->cstr_info->tile[tileno].end_pos += totlen;
- }
-
- p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos = p_stream_tell(p_stream) - 12;
- p_j2k->cstr_info->tile[tileno].tp[partno].tp_end_pos =
- p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos + totlen - 1;
- }*/
return OPJ_TRUE;
}
@@ -5023,7 +5077,7 @@ static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
/* Index */
l_cstr_index = p_j2k->cstr_index;
- if (l_cstr_index) {
+ {
OPJ_OFF_T l_current_pos = opj_stream_tell(p_stream) - 2;
OPJ_UINT32 l_current_tile_part =
@@ -8472,13 +8526,6 @@ OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream,
/* Copy codestream image information to the output image */
opj_copy_image_header(p_j2k->m_private_image, *p_image);
- /*Allocate and initialize some elements of codestrem index*/
- if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) {
- opj_image_destroy(*p_image);
- *p_image = NULL;
- return OPJ_FALSE;
- }
-
return OPJ_TRUE;
}
@@ -8830,6 +8877,87 @@ static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t *p_j2k,
return l_is_valid;
}
+/** Fill p_j2k->cstr_index->tp_index[].start_pos/end_pos fields from TLM marker segments */
+static void opj_j2k_build_tp_index_from_tlm(opj_j2k_t* p_j2k,
+ opj_event_mgr_t * p_manager)
+{
+ opj_j2k_tlm_info_t* l_tlm;
+ OPJ_UINT32 i;
+ OPJ_OFF_T l_cur_offset;
+
+ assert(p_j2k->cstr_index->main_head_end > 0);
+ assert(p_j2k->cstr_index->nb_of_tiles > 0);
+ assert(p_j2k->cstr_index->tile_index != NULL);
+
+ l_tlm = &(p_j2k->m_specific_param.m_decoder.m_tlm);
+
+ if (l_tlm->m_entries_count == 0) {
+ l_tlm->m_is_invalid = OPJ_TRUE;
+ return;
+ }
+
+ if (l_tlm->m_is_invalid) {
+ return;
+ }
+
+ /* Initial pass to count the number of tile-parts per tile */
+ for (i = 0; i < l_tlm->m_entries_count; ++i) {
+ OPJ_UINT32 l_tile_index_no = l_tlm->m_tile_part_infos[i].m_tile_index;
+ assert(l_tile_index_no < p_j2k->cstr_index->nb_of_tiles);
+ p_j2k->cstr_index->tile_index[l_tile_index_no].tileno = l_tile_index_no;
+ ++p_j2k->cstr_index->tile_index[l_tile_index_no].current_nb_tps;
+ }
+
+ /* Now check that all tiles have at least one tile-part */
+ for (i = 0; i < p_j2k->cstr_index->nb_of_tiles; ++i) {
+ if (p_j2k->cstr_index->tile_index[i].current_nb_tps == 0) {
+ opj_event_msg(p_manager, EVT_ERROR,
+ "opj_j2k_build_tp_index_from_tlm(): tile %d has no "
+ "registered tile-part in TLM marker segments.\n", i);
+ goto error;
+ }
+ }
+
+ /* Final pass to fill p_j2k->cstr_index */
+ l_cur_offset = p_j2k->cstr_index->main_head_end;
+ for (i = 0; i < l_tlm->m_entries_count; ++i) {
+ OPJ_UINT32 l_tile_index_no = l_tlm->m_tile_part_infos[i].m_tile_index;
+ opj_tile_index_t* l_tile_index = &
+ (p_j2k->cstr_index->tile_index[l_tile_index_no]);
+ if (!l_tile_index->tp_index) {
+ l_tile_index->tp_index = (opj_tp_index_t *) opj_calloc(
+ l_tile_index->current_nb_tps, sizeof(opj_tp_index_t));
+ if (! l_tile_index->tp_index) {
+ opj_event_msg(p_manager, EVT_ERROR,
+ "opj_j2k_build_tp_index_from_tlm(): tile index allocation failed\n");
+ goto error;
+ }
+ }
+
+ assert(l_tile_index->nb_tps < l_tile_index->current_nb_tps);
+ l_tile_index->tp_index[l_tile_index->nb_tps].start_pos = l_cur_offset;
+ /* We don't know how to set the tp_index[].end_header field, but this is not really needed */
+ /* If there would be no markers between SOT and SOD, that would be : */
+ /* l_tile_index->tp_index[l_tile_index->nb_tps].end_header = l_cur_offset + 12; */
+ l_tile_index->tp_index[l_tile_index->nb_tps].end_pos = l_cur_offset +
+ l_tlm->m_tile_part_infos[i].m_length;
+ ++l_tile_index->nb_tps;
+
+ l_cur_offset += l_tlm->m_tile_part_infos[i].m_length;
+ }
+
+ return;
+
+error:
+ l_tlm->m_is_invalid = OPJ_TRUE;
+ for (i = 0; i < l_tlm->m_entries_count; ++i) {
+ OPJ_UINT32 l_tile_index = l_tlm->m_tile_part_infos[i].m_tile_index;
+ p_j2k->cstr_index->tile_index[l_tile_index].current_nb_tps = 0;
+ opj_free(p_j2k->cstr_index->tile_index[l_tile_index].tp_index);
+ p_j2k->cstr_index->tile_index[l_tile_index].tp_index = NULL;
+ }
+}
+
static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
opj_stream_private_t *p_stream,
opj_event_mgr_t * p_manager)
@@ -9009,6 +9137,9 @@ static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k,
/* Position of the last element if the main header */
p_j2k->cstr_index->main_head_end = (OPJ_UINT32) opj_stream_tell(p_stream) - 2;
+ /* Build tile-part index from TLM information */
+ opj_j2k_build_tp_index_from_tlm(p_j2k, p_manager);
+
/* Next step: read a tile-part header */
p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT;
@@ -9232,6 +9363,9 @@ void opj_j2k_destroy(opj_j2k_t *p_j2k)
p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = 00;
p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0;
+ opj_free(p_j2k->m_specific_param.m_decoder.m_tlm.m_tile_part_infos);
+ p_j2k->m_specific_param.m_decoder.m_tlm.m_tile_part_infos = NULL;
+
} else {
if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) {
@@ -9730,6 +9864,60 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k,
if (! opj_j2k_read_sod(p_j2k, p_stream, p_manager)) {
return OPJ_FALSE;
}
+
+ /* Check if we can use the TLM index to access the next tile-part */
+ if (!p_j2k->m_specific_param.m_decoder.m_can_decode &&
+ p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec >= 0 &&
+ p_j2k->m_current_tile_number == (OPJ_UINT32)
+ p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec &&
+ !p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid &&
+ opj_stream_has_seek(p_stream)) {
+ l_tcp = p_j2k->m_cp.tcps + p_j2k->m_current_tile_number;
+ if (l_tcp->m_nb_tile_parts ==
+ p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps &&
+ (OPJ_UINT32)l_tcp->m_current_tile_part_number + 1 < l_tcp->m_nb_tile_parts) {
+ const OPJ_OFF_T next_tp_sot_pos = p_j2k->cstr_index->tile_index[
+ p_j2k->m_current_tile_number].tp_index[l_tcp->m_current_tile_part_number +
+ 1].start_pos;
+
+ if (next_tp_sot_pos != opj_stream_tell(p_stream)) {
+#if 0
+ opj_event_msg(p_manager, EVT_INFO,
+ "opj_j2k_read_tile_header(tile=%u): seek to tile part %u at %" PRId64 "\n",
+ p_j2k->m_current_tile_number,
+ l_tcp->m_current_tile_part_number + 1,
+ next_tp_sot_pos);
+#endif
+
+ if (!(opj_stream_read_seek(p_stream,
+ next_tp_sot_pos,
+ p_manager))) {
+ opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
+ return OPJ_FALSE;
+ }
+ }
+
+ /* Try to read 2 bytes (the marker ID) from stream and copy them into the buffer */
+ if (opj_stream_read_data(p_stream,
+ p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+ opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+ return OPJ_FALSE;
+ }
+
+ /* Read 2 bytes from the buffer as the marker ID */
+ opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data,
+ &l_current_marker,
+ 2);
+
+ if (l_current_marker != J2K_MS_SOT) {
+ opj_event_msg(p_manager, EVT_ERROR, "Did not get expected SOT marker\n");
+ return OPJ_FALSE;
+ }
+
+ continue;
+ }
+ }
+
if (p_j2k->m_specific_param.m_decoder.m_can_decode &&
!p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked) {
/* Issue 254 */
@@ -11313,6 +11501,17 @@ static void opj_j2k_dump_MH_index(opj_j2k_t* p_j2k, FILE* out_stream)
OPJ_UINT32 l_acc_nb_of_tile_part = 0;
for (it_tile = 0; it_tile < cstr_index->nb_of_tiles ; it_tile++) {
l_acc_nb_of_tile_part += cstr_index->tile_index[it_tile].nb_tps;
+
+ /* To avoid regenerating expected opj_dump results from the test */
+ /* suite when there is a TLM marker present */
+ if (cstr_index->tile_index[it_tile].nb_tps &&
+ cstr_index->tile_index[it_tile].tp_index &&
+ cstr_index->tile_index[it_tile].tp_index[0].start_pos > 0 &&
+ cstr_index->tile_index[it_tile].tp_index[0].end_header == 0 &&
+ getenv("OJP_DO_NOT_DISPLAY_TILE_INDEX_IF_TLM") != NULL) {
+ l_acc_nb_of_tile_part = 0;
+ break;
+ }
}
if (l_acc_nb_of_tile_part) {
@@ -11830,12 +12029,6 @@ static OPJ_BOOL opj_j2k_decode_one_tile(opj_j2k_t *p_j2k,
OPJ_UINT32 l_nb_tiles;
OPJ_UINT32 i;
- /*Allocate and initialize some elements of codestrem index if not already done*/
- if (!p_j2k->cstr_index->tile_index) {
- if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) {
- return OPJ_FALSE;
- }
- }
/* Move into the codestream to the first SOT used to decode the desired tile */
l_tile_no_to_dec = (OPJ_UINT32)
p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec;
@@ -11850,12 +12043,38 @@ static OPJ_BOOL opj_j2k_decode_one_tile(opj_j2k_t *p_j2k,
return OPJ_FALSE;
}
} else {
+ OPJ_OFF_T sot_pos =
+ p_j2k->cstr_index->tile_index[l_tile_no_to_dec].tp_index[0].start_pos;
+ OPJ_UINT32 l_marker;
+
+#if 0
+ opj_event_msg(p_manager, EVT_INFO,
+ "opj_j2k_decode_one_tile(%u): seek to %" PRId64 "\n",
+ l_tile_no_to_dec,
+ sot_pos);
+#endif
if (!(opj_stream_read_seek(p_stream,
- p_j2k->cstr_index->tile_index[l_tile_no_to_dec].tp_index[0].start_pos + 2,
+ sot_pos,
p_manager))) {
opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n");
return OPJ_FALSE;
}
+
+ /* Try to read 2 bytes (the marker ID) from stream and copy them into the buffer */
+ if (opj_stream_read_data(p_stream,
+ p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) {
+ opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n");
+ return OPJ_FALSE;
+ }
+
+ /* Read 2 bytes from the buffer as the marker ID */
+ opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, &l_marker,
+ 2);
+
+ if (l_marker != J2K_MS_SOT) {
+ opj_event_msg(p_manager, EVT_ERROR, "Did not get expected SOT marker\n");
+ return OPJ_FALSE;
+ }
}
/* Special case if we have previously read the EOC marker (if the previous tile getted is the last ) */
if (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_EOC) {
diff --git a/src/lib/openjp2/j2k.h b/src/lib/openjp2/j2k.h
index e0b9688a..9ca5f547 100644
--- a/src/lib/openjp2/j2k.h
+++ b/src/lib/openjp2/j2k.h
@@ -466,6 +466,24 @@ typedef struct opj_cp {
/* <<UniPG */
} opj_cp_t;
+/** Entry of a TLM marker segment */
+typedef struct opj_j2k_tlm_tile_part_info {
+ /** Tile index of the tile part. Ttlmi field */
+ OPJ_UINT16 m_tile_index;
+ /** Length in bytes, from the beginning of the SOT marker to the end of
+ * the bit stream data for that tile-part. Ptlmi field */
+ OPJ_UINT32 m_length;
+} opj_j2k_tlm_tile_part_info_t;
+
+/** Information got from the concatenation of TLM marker semgnets. */
+typedef struct opj_j2k_tlm_info {
+ /** Number of entries in m_tile_part_infos. */
+ OPJ_UINT32 m_entries_count;
+ /** Array of m_entries_count values. */
+ opj_j2k_tlm_tile_part_info_t* m_tile_part_infos;
+
+ OPJ_BOOL m_is_invalid;
+} opj_j2k_tlm_info_t;
typedef struct opj_j2k_dec {
/** locate in which part of the codestream the decoder is (main header, tile header, end) */
@@ -499,6 +517,8 @@ typedef struct opj_j2k_dec {
OPJ_UINT32 m_numcomps_to_decode;
OPJ_UINT32 *m_comps_indices_to_decode;
+ opj_j2k_tlm_info_t m_tlm;
+
/** to tell that a tile can be decoded. */
OPJ_BITFIELD m_can_decode : 1;
OPJ_BITFIELD m_discard_tiles : 1;