diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c index 6e9cf8ce..08525289 100644 --- a/src/lib/openjp2/j2k.c +++ b/src/lib/openjp2/j2k.c @@ -10442,6 +10442,9 @@ opj_codestream_info_v2_t* j2k_get_cstr_info(opj_j2k_t* p_j2k) if (!cstr_info) { return NULL; } + + cstr_info->nbasoc = 0; + cstr_info->asoc_info = 00; cstr_info->nbcomps = p_j2k->m_private_image->numcomps; diff --git a/src/lib/openjp2/jp2.c b/src/lib/openjp2/jp2.c index c79ea731..48fa5dca 100644 --- a/src/lib/openjp2/jp2.c +++ b/src/lib/openjp2/jp2.c @@ -111,6 +111,11 @@ static OPJ_BOOL opj_jp2_read_cdef(opj_jp2_t * jp2, static void opj_jp2_apply_cdef(opj_image_t *image, opj_jp2_color_t *color, opj_event_mgr_t *); +/** + * Destroy list of ASOC entities + */ + static void opj_jp2_asoc_destroy( opj_jp2_asoc_t *p_asoc, OPJ_UINT32 num ); + /** * Writes the Channel Definition box. * @@ -159,7 +164,23 @@ static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2, static OPJ_BOOL opj_jp2_read_ftyp(opj_jp2_t *jp2, OPJ_BYTE * p_header_data, OPJ_UINT32 p_header_size, - opj_event_mgr_t * p_manager); + opj_event_mgr_t * p_manager); + +/** + * Reads a ASOC box - Associated data box. + * Also reads contained label (LBL) and XML boxes + * + * @param p_header_data the data contained in the ASOC box. + * @param jp2 the jpeg2000 file codec. + * @param p_header_size the size of the data contained in the ASOC box. + * @param p_manager the user event manager. + * + * @return true if the ASOC box is valid. + */ + static OPJ_BOOL opj_jp2_read_asoc( opj_jp2_t *jp2, + OPJ_BYTE * p_header_data, + OPJ_UINT32 p_header_size, + opj_event_mgr_t * p_manager); static OPJ_BOOL opj_jp2_skip_jp2c(opj_jp2_t *jp2, opj_stream_private_t *stream, @@ -425,7 +446,8 @@ static const opj_jp2_header_handler_t * opj_jp2_find_handler(OPJ_UINT32 p_id); static const opj_jp2_header_handler_t jp2_header [] = { {JP2_JP, opj_jp2_read_jp}, {JP2_FTYP, opj_jp2_read_ftyp}, - {JP2_JP2H, opj_jp2_read_jp2h} + {JP2_JP2H, opj_jp2_read_jp2h}, + {JP2_ASOC, opj_jp2_read_asoc} }; static const opj_jp2_header_handler_t jp2_img_header [] = { @@ -2636,6 +2658,104 @@ static OPJ_BOOL opj_jp2_read_ftyp(opj_jp2_t *jp2, jp2->jp2_state |= JP2_STATE_FILE_TYPE; return OPJ_TRUE; +} + +static OPJ_BOOL opj_jp2_read_asoc( opj_jp2_t *jp2, + OPJ_BYTE * p_header_data, + OPJ_UINT32 p_header_size, + opj_event_mgr_t * p_manager) +{ + OPJ_UINT32 label_tag; + OPJ_UINT32 asoc_tag; + OPJ_UINT32 asoc_size; + opj_jp2_asoc_t *asoc; + + /* preconditions */ + assert(jp2 != 00); + assert(p_header_data != 00); + assert(p_manager != 00); + + opj_read_bytes(p_header_data,&asoc_size,4); + p_header_data += 4; + p_header_size -= 4; + + opj_read_bytes(p_header_data,&label_tag,4); + p_header_data += 4; + p_header_size -= 4; + asoc_size -= 4; + + if (label_tag != JP2_LBL) { + /* TODO: Verify that ASOC must have a following label ? */ + opj_event_msg(p_manager, EVT_WARNING, "ASOC data does not have a label (LBL)\n"); + return OPJ_TRUE; // No error if we could not parse + } + + if ( jp2->numasoc == 0 ) { + jp2->numasoc = 1; + jp2->asoc = opj_malloc(sizeof(opj_jp2_asoc_t)); + } + else { + (jp2->numasoc)++; + jp2->asoc = opj_realloc(jp2->asoc, jp2->numasoc * sizeof(opj_jp2_asoc_t)); + } + asoc = &(jp2->asoc[jp2->numasoc-1]); + asoc->level = jp2->numasoc-1; /* TODO: This is not correct if a parent asoc contains multiple child asocs! */ + asoc->label_length = asoc_size; + asoc->label = opj_malloc(asoc_size); + memcpy(asoc->label, p_header_data, asoc_size); + asoc->xml_buf = 00; + asoc->xml_len = 0; + + p_header_data += asoc_size; + p_header_size -= asoc_size; + + opj_read_bytes(p_header_data,&asoc_tag,4); + p_header_data += 4; + p_header_size -= 4; + + switch (asoc_tag) { + case JP2_ASOC: { + /* Start of nested ASOC tags. Parse this level. */ + if (!opj_jp2_read_asoc( jp2, p_header_data, p_header_size, p_manager )) { + return OPJ_FALSE; + } + } + break; + + case JP2_XML: { + asoc->xml_len = p_header_size; + asoc->xml_buf = opj_malloc(p_header_size); + memcpy( asoc->xml_buf, p_header_data, p_header_size ); + } + break; + + default: { + /* Copy the unknown data for external handling. + NOTE: This is not tested, but does the same as if an XML tag was found.*/ + asoc->xml_len = p_header_size; + asoc->xml_buf = opj_malloc(p_header_size); + memcpy( asoc->xml_buf, p_header_data, p_header_size ); + } + } + + return OPJ_TRUE; +} + +static void opj_jp2_asoc_destroy( opj_jp2_asoc_t *p_asoc, OPJ_UINT32 num ) +{ + OPJ_UINT32 i; + opj_jp2_asoc_t *asoc; + for (i=0; ilabel ); + asoc->label = 00; + asoc->label_length = 0; + + opj_free( asoc->xml_buf ); + asoc->xml_buf = 00; + asoc->xml_len = 0; + } } static OPJ_BOOL opj_jp2_skip_jp2c(opj_jp2_t *jp2, @@ -3068,6 +3188,12 @@ void opj_jp2_destroy(opj_jp2_t *jp2) if (jp2->m_procedure_list) { opj_procedure_list_destroy(jp2->m_procedure_list); jp2->m_procedure_list = 00; + } + + if ( jp2->numasoc ) { + opj_jp2_asoc_destroy( jp2->asoc, jp2->numasoc ); + jp2->asoc = 00; + jp2->numasoc = 0; } opj_free(jp2); @@ -3204,7 +3330,12 @@ opj_jp2_t* opj_jp2_create(OPJ_BOOL p_is_decoder) if (! jp2->m_procedure_list) { opj_jp2_destroy(jp2); return 00; - } + } + + /* Association data */ + jp2->asoc = 00; + jp2->numasoc = 0; + } return jp2; @@ -3226,8 +3357,31 @@ opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2) } opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2) -{ - return j2k_get_cstr_info(p_jp2->j2k); +{ + opj_codestream_info_v2_t* p_info = j2k_get_cstr_info(p_jp2->j2k); + jp2_copy_asoc_data( p_jp2, p_info ); + return p_info; +} + +OPJ_BOOL jp2_copy_asoc_data( opj_jp2_t* p_jp2, opj_codestream_info_v2_t* p_info ) +{ + OPJ_UINT32 i; + opj_jp2_asoc_t *asoc, *to_asoc; + p_info->nbasoc = p_jp2->numasoc; + p_info->asoc_info = opj_malloc(p_info->nbasoc * sizeof(opj_jp2_asoc_t)); + for (i=0; inbasoc; i++) { + asoc = &(p_jp2->asoc[i]); + to_asoc = &(p_info->asoc_info[i]); + to_asoc->level = asoc->level; + to_asoc->label_length = asoc->label_length; + to_asoc->xml_len = asoc->xml_len; + to_asoc->label = opj_malloc( to_asoc->label_length ); + memcpy(to_asoc->label, asoc->label, to_asoc->label_length); + to_asoc->xml_buf = opj_malloc( to_asoc->xml_len); + memcpy(to_asoc->xml_buf, asoc->xml_buf, to_asoc->xml_len); + } + + return OPJ_TRUE; } OPJ_BOOL opj_jp2_set_decoded_resolution_factor(opj_jp2_t *p_jp2, diff --git a/src/lib/openjp2/jp2.h b/src/lib/openjp2/jp2.h index 34abd511..8d620d59 100644 --- a/src/lib/openjp2/jp2.h +++ b/src/lib/openjp2/jp2.h @@ -58,7 +58,10 @@ #define JP2_CDEF 0x63646566 /**< Channel Definition box */ #define JP2_DTBL 0x6474626c /**< Data Reference box */ #define JP2_BPCC 0x62706363 /**< Bits per component box */ -#define JP2_JP2 0x6a703220 /**< File type fields */ +#define JP2_JP2 0x6a703220 /**< File type fields */ +#define JP2_ASOC 0x61736f63 /**< Associated data */ +#define JP2_LBL 0x6c626c20 /**< Association label */ +#define JP2_XML 0x786d6c20 /**< XML data */ /* For the future */ /* #define JP2_RES 0x72657320 */ /**< Resolution box (super-box) */ @@ -184,7 +187,10 @@ typedef struct opj_jp2 { OPJ_UINT32 jp2_state; OPJ_UINT32 jp2_img_state; - opj_jp2_color_t color; + opj_jp2_color_t color; + + opj_jp2_asoc_t *asoc; + OPJ_UINT32 numasoc; OPJ_BOOL ignore_pclr_cmap_cdef; OPJ_BYTE has_jp2h; @@ -478,7 +484,12 @@ void jp2_dump(opj_jp2_t* p_jp2, OPJ_INT32 flag, FILE* out_stream); * *@return the codestream information extract from the jpg2000 codec */ -opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2); +opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2); + +/** + * Copy associated data + */ +OPJ_BOOL jp2_copy_asoc_data( opj_jp2_t* p_jp2, opj_codestream_info_v2_t* p_info ); /** * Get the codestream index from a JPEG2000 codec. diff --git a/src/lib/openjp2/openjpeg.c b/src/lib/openjp2/openjpeg.c index 7b123034..2c35ec4d 100644 --- a/src/lib/openjp2/openjpeg.c +++ b/src/lib/openjp2/openjpeg.c @@ -37,6 +37,7 @@ #include "opj_includes.h" +static void opj_asoc_destroy( opj_jp2_asoc_t *p_asoc, OPJ_UINT32 num ); /* ---------------------------------------------------------------------- */ /* Functions to set the message handlers */ @@ -950,6 +951,26 @@ void OPJ_CALLCONV opj_dump_codec(opj_codec_t *p_codec, /* TODO return error */ /* fprintf(stderr, "[ERROR] Input parameter of the dump_codec function are incorrect.\n"); */ return; +} + +void OPJ_CALLCONV opj_dump_associated_data( + opj_codestream_info_v2_t* cstr_info, + FILE* output_stream ) +{ + OPJ_UINT32 i; + if ( cstr_info->asoc_info ) { + fprintf(output_stream, "\n\nAssociated data: {\n"); + for (i=0; inbasoc; i++) { + fprintf(output_stream, "\tlabel=%s, xml/data=", (char*) cstr_info->asoc_info[i].label); + if (cstr_info->asoc_info[i].xml_buf) { + fprintf(output_stream, "%s\n", (char*) cstr_info->asoc_info[i].xml_buf); + } + else { + fprintf(output_stream, "NULL\n"); + } + } + fprintf(output_stream, "}\n"); + } } opj_codestream_info_v2_t* OPJ_CALLCONV opj_get_cstr_info(opj_codec_t *p_codec) @@ -963,6 +984,23 @@ opj_codestream_info_v2_t* OPJ_CALLCONV opj_get_cstr_info(opj_codec_t *p_codec) return NULL; } +void opj_asoc_destroy( opj_jp2_asoc_t *p_asoc, OPJ_UINT32 num ) +{ + OPJ_UINT32 i; + opj_jp2_asoc_t *asoc; + for (i=0; ilabel ); + asoc->label = 00; + asoc->label_length = 0; + + opj_free( asoc->xml_buf ); + asoc->xml_buf = 00; + asoc->xml_len = 0; + } +} + void OPJ_CALLCONV opj_destroy_cstr_info(opj_codestream_info_v2_t **cstr_info) { if (cstr_info) { @@ -975,6 +1013,12 @@ void OPJ_CALLCONV opj_destroy_cstr_info(opj_codestream_info_v2_t **cstr_info) /* FIXME not used for the moment*/ } + if ((*cstr_info)->nbasoc) { + opj_asoc_destroy((*cstr_info)->asoc_info, (*cstr_info)->nbasoc); + (*cstr_info)->asoc_info = 00; + (*cstr_info)->nbasoc = 0; + } + opj_free((*cstr_info)); (*cstr_info) = NULL; } @@ -1049,7 +1093,12 @@ opj_stream_t* OPJ_CALLCONV opj_stream_create_file_stream( return l_stream; } - + +OPJ_OFF_T opj_stream_skip_api(opj_stream_t * p_stream, OPJ_OFF_T p_size) +{ + opj_stream_private_t* l_stream = (opj_stream_private_t*) p_stream; + return l_stream->m_opj_skip(l_stream, p_size, NULL); +} void* OPJ_CALLCONV opj_image_data_alloc(OPJ_SIZE_T size) { diff --git a/src/lib/openjp2/openjpeg.h b/src/lib/openjp2/openjpeg.h index 6412e1b8..2c15d28b 100644 --- a/src/lib/openjp2/openjpeg.h +++ b/src/lib/openjp2/openjpeg.h @@ -916,6 +916,22 @@ typedef struct opj_tile_v2_info { } opj_tile_info_v2_t; +/** +Collector for association box (ASOC data), defined by a level, label and optionally XML data. +E.g. georeferencing with GML uses this (http://docs.opengeospatial.org/is/08-085r4/08-085r4.html). +In this case the first asoc is labelled 'gml.data' and has no XML data. The second asoc is named +'gml.root-instance' and contains XML formatted geo-information. +*/ +typedef struct opj_jp2_asoc +{ + OPJ_UINT32 level; + OPJ_BYTE *label; + OPJ_UINT32 label_length; + OPJ_BYTE *xml_buf; + OPJ_UINT32 xml_len; + +} opj_jp2_asoc_t; + /** * Information structure about the codestream (FIXME should be expand and enhance) */ @@ -943,6 +959,12 @@ typedef struct opj_codestream_info_v2 { /** information regarding tiles inside image */ opj_tile_info_v2_t *tile_info; /* FIXME not used for the moment */ + /** Number of associated data boxes*/ + OPJ_UINT32 nbasoc; + + /** Associated data, e.g. GML geoinformation */ + opj_jp2_asoc_t *asoc_info; + } opj_codestream_info_v2_t; @@ -1178,13 +1200,22 @@ OPJ_API void OPJ_CALLCONV opj_stream_set_skip_function(opj_stream_t* p_stream, opj_stream_skip_fn p_function); /** - * Sets the given function to be used as a seek function, the stream is then seekable, - * using SEEK_SET behavior. + * Sets the given function to be used as a seek function, the stream is then seekable. * @param p_stream the stream to modify * @param p_function the function to use a skip function. */ OPJ_API void OPJ_CALLCONV opj_stream_set_seek_function(opj_stream_t* p_stream, - opj_stream_seek_fn p_function); + opj_stream_seek_fn p_function); + + /** + * Skips a number of bytes from the stream. + * @param p_stream the stream to skip data from. + * @param p_size the number of bytes to skip. + * @param p_event_mgr the user event manager to be notified of special events. + * @return the number of bytes skipped, or -1 if an error occurred. + */ +OPJ_API OPJ_OFF_T OPJ_CALLCONV opj_stream_skip_api(opj_stream_t * p_stream, OPJ_OFF_T p_size); + /** * Sets the given data to be used as a user data for the stream. @@ -1314,9 +1345,6 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec, * number, or "ALL_CPUS". If OPJ_NUM_THREADS is set and this function is called, * this function will override the behaviour of the environment variable. * - * Currently this function must be called after opj_setup_decoder() and - * before opj_read_header(). - * * Note: currently only has effect on the decompressor. * * @param p_codec decompressor handler @@ -1609,7 +1637,11 @@ OPJ_API void OPJ_CALLCONV opj_dump_codec(opj_codec_t *p_codec, * */ OPJ_API opj_codestream_info_v2_t* OPJ_CALLCONV opj_get_cstr_info( - opj_codec_t *p_codec); + opj_codec_t *p_codec); + +OPJ_API void OPJ_CALLCONV opj_dump_associated_data( + opj_codestream_info_v2_t* cstr_info, + FILE* output_stream ); /** * Get the codestream index from the codec