From bc8d47acfab8a5bd1681fdfac07954f68d5354db Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:10:02 -0400 Subject: [PATCH 01/14] Add support for linking jp2 files into jpx --- src/bin/jp2/CMakeLists.txt | 2 +- src/bin/jp2/opj_merge.c | 106 +++++++++++ src/lib/openjp2/CMakeLists.txt | 2 + src/lib/openjp2/jp2.c | 64 ++----- src/lib/openjp2/jp2.h | 42 +++++ src/lib/openjp2/jpx.c | 310 +++++++++++++++++++++++++++++++++ src/lib/openjp2/jpx.h | 163 +++++++++++++++++ src/lib/openjp2/openjpeg.c | 46 +++++ src/lib/openjp2/opj_includes.h | 2 + 9 files changed, 685 insertions(+), 52 deletions(-) create mode 100644 src/bin/jp2/opj_merge.c create mode 100644 src/lib/openjp2/jpx.c create mode 100644 src/lib/openjp2/jpx.h diff --git a/src/bin/jp2/CMakeLists.txt b/src/bin/jp2/CMakeLists.txt index 26156bcbf..9cf10bb64 100644 --- a/src/bin/jp2/CMakeLists.txt +++ b/src/bin/jp2/CMakeLists.txt @@ -42,7 +42,7 @@ if(WIN32) endif() # Loop over all executables: -foreach(exe opj_decompress opj_compress opj_dump) +foreach(exe opj_decompress opj_compress opj_dump opj_merge) add_executable(${exe} ${exe}.c ${common_SRCS}) target_compile_options(${exe} PRIVATE ${OPENJP2_COMPILE_OPTIONS}) target_link_libraries(${exe} ${OPENJPEG_LIBRARY_NAME} diff --git a/src/bin/jp2/opj_merge.c b/src/bin/jp2/opj_merge.c new file mode 100644 index 000000000..265a79bf9 --- /dev/null +++ b/src/bin/jp2/opj_merge.c @@ -0,0 +1,106 @@ +/* + * The copyright in this software is being made available under the 2-clauses + * BSD License, included below. This software may be subject to other third + * party and contributor rights, including patent rights, and no such rights + * are granted under this license. + * + * Copyright (c) 2010, Mathieu Malaterre, GDCM + * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France + * Copyright (c) 2012, CS Systemes d'Information, France + * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +int main(int argc, char **argv) { + /** Create jpx encoder */ + opj_codec_t* codec = opj_create_compress(OPJ_CODEC_JPX); + /* set encoding parameters to default values */ + opj_cparameters_t parameters; + /** Output file */ + opj_stream_t *outfile = NULL; + /** Creation status */ + OPJ_BOOL bSuccess = OPJ_FALSE; + /** Program return code */ + int ret = 1; + + if (!codec) { + fprintf(stderr, "Failed to initialize the jpx codec.\n"); + return ret; + } + + opj_set_default_encoder_parameters(¶meters); + + // Creating references to other jpx files doesn't require image data. + // so pass NULL for the image parameter and hope for the best. + if (! opj_setup_encoder(codec, ¶meters, 1)) { + fprintf(stderr, "Failed to setup encoder: opj_setup_encoder\n"); + goto fin; + } + + // Use extra options to specify the list of files to be merged into the jpx file. + { + const char* options[2] = { "img/2024_03_27__13_21_29_129__SDO_AIA_AIA_304.jp2", NULL }; + if (!opj_encoder_set_extra_options(codec, options)) { + fprintf(stderr, "Failed to set list of jp2 files to include: opj_encoder_set_extra_options\n"); + goto fin; + } + } + + // /* open a byte stream for writing */ + outfile = opj_stream_create_default_file_stream("sample.jpx", OPJ_FALSE); + if (!outfile) { + fprintf(stderr, "Failed to allocate memory for the output file"); + goto fin; + } + + bSuccess = opj_start_compress(codec, 1, outfile); + if (!bSuccess) { + fprintf(stderr, "Failed to create the jpx image: opj_start_compress\n"); + goto fin; + } + + bSuccess = bSuccess && opj_encode(codec, outfile); + if (!bSuccess) { + fprintf(stderr, "Failed to encode the image: opj_encode\n"); + } + + bSuccess = bSuccess && opj_end_compress(codec, outfile); + if (!bSuccess) { + fprintf(stderr, "Failed to encode the image: opj_end_compress\n"); + goto fin; + } + + ret = 0; + +fin: + if (codec) { + opj_destroy_codec(codec); + } + if (outfile) { + opj_stream_destroy(outfile); + } + return ret; +} \ No newline at end of file diff --git a/src/lib/openjp2/CMakeLists.txt b/src/lib/openjp2/CMakeLists.txt index fd62335f5..421fc7600 100644 --- a/src/lib/openjp2/CMakeLists.txt +++ b/src/lib/openjp2/CMakeLists.txt @@ -28,6 +28,8 @@ set(OPENJPEG_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/j2k.h ${CMAKE_CURRENT_SOURCE_DIR}/jp2.c ${CMAKE_CURRENT_SOURCE_DIR}/jp2.h + ${CMAKE_CURRENT_SOURCE_DIR}/jpx.c + ${CMAKE_CURRENT_SOURCE_DIR}/jpx.h ${CMAKE_CURRENT_SOURCE_DIR}/mct.c ${CMAKE_CURRENT_SOURCE_DIR}/mct.h ${CMAKE_CURRENT_SOURCE_DIR}/mqc.c diff --git a/src/lib/openjp2/jp2.c b/src/lib/openjp2/jp2.c index 6015190e1..9dcc87782 100644 --- a/src/lib/openjp2/jp2.c +++ b/src/lib/openjp2/jp2.c @@ -14,6 +14,7 @@ * Copyright (c) 2010-2011, Kaori Hagihara * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR * Copyright (c) 2012, CS Systemes d'Information, France + * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -133,19 +134,6 @@ static OPJ_BYTE * opj_jp2_write_cdef(opj_jp2_t *jp2, static OPJ_BYTE * opj_jp2_write_colr(opj_jp2_t *jp2, OPJ_UINT32 * p_nb_bytes_written); -/** - * Writes a FTYP box - File type box - * - * @param cio the stream to write data to. - * @param jp2 the jpeg2000 file codec. - * @param p_manager the user event manager. - * - * @return true if writing was successful. - */ -static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2, - opj_stream_private_t *cio, - opj_event_mgr_t * p_manager); - /** * Reads a a FTYP box - File type box * @@ -253,19 +241,6 @@ static OPJ_BOOL opj_jp2_read_jp(opj_jp2_t *jp2, OPJ_UINT32 p_header_size, opj_event_mgr_t * p_manager); -/** - * Writes a jpeg2000 file signature box. - * - * @param cio the stream to write data to. - * @param jp2 the jpeg2000 file codec. - * @param p_manager the user event manager. - * - * @return true if writing was successful. - */ -static OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2, - opj_stream_private_t *cio, - opj_event_mgr_t * p_manager); - /** Apply collected palette data @param image Image. @@ -356,20 +331,6 @@ static OPJ_BOOL opj_jp2_read_header_procedure(opj_jp2_t *jp2, opj_stream_private_t *stream, opj_event_mgr_t * p_manager); -/** - * Executes the given procedures on the given codec. - * - * @param p_procedure_list the list of procedures to execute - * @param jp2 the jpeg2000 file codec to execute the procedures on. - * @param stream the stream to execute the procedures on. - * @param p_manager the user manager. - * - * @return true if all the procedures were successfully executed. - */ -static OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2, - opj_procedure_list_t * p_procedure_list, - opj_stream_private_t *stream, - opj_event_mgr_t * p_manager); /** * Reads a box header. The box is the way data is packed inside a jpeg2000 file structure. @@ -1754,9 +1715,9 @@ static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2, return l_result; } -static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2, - opj_stream_private_t *cio, - opj_event_mgr_t * p_manager) +OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager) { OPJ_UINT32 i; OPJ_UINT32 l_ftyp_size; @@ -1792,6 +1753,7 @@ static OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2, for (i = 0; i < jp2->numcl; i++) { opj_write_bytes(l_current_data_ptr, jp2->cl[i], 4); /* CL */ + l_current_data_ptr += 4; } l_result = (opj_stream_write_data(cio, l_ftyp_data, l_ftyp_size, @@ -1844,9 +1806,9 @@ static OPJ_BOOL opj_jp2_write_jp2c(opj_jp2_t *jp2, return OPJ_TRUE; } -static OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2, - opj_stream_private_t *cio, - opj_event_mgr_t * p_manager) +OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager) { /* 12 bytes will be read */ OPJ_BYTE l_signature_data [12]; @@ -2414,11 +2376,11 @@ static OPJ_BOOL opj_jp2_read_header_procedure(opj_jp2_t *jp2, * * @return true if all the procedures were successfully executed. */ -static OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2, - opj_procedure_list_t * p_procedure_list, - opj_stream_private_t *stream, - opj_event_mgr_t * p_manager - ) +OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2, + opj_procedure_list_t * p_procedure_list, + opj_stream_private_t *stream, + opj_event_mgr_t * p_manager + ) { OPJ_BOOL(** l_procedure)(opj_jp2_t * jp2, opj_stream_private_t *, diff --git a/src/lib/openjp2/jp2.h b/src/lib/openjp2/jp2.h index 173f25119..900a32490 100644 --- a/src/lib/openjp2/jp2.h +++ b/src/lib/openjp2/jp2.h @@ -10,6 +10,7 @@ * Copyright (c) 2005, Herve Drolon, FreeImage Team * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR * Copyright (c) 2012, CS Systemes d'Information, France + * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -512,6 +513,47 @@ opj_codestream_info_v2_t* jp2_get_cstr_info(opj_jp2_t* p_jp2); */ opj_codestream_index_t* jp2_get_cstr_index(opj_jp2_t* p_jp2); +/** + * Writes a jpeg2000 file signature box. + * + * @param cio the stream to write data to. + * @param jp2 the jpeg2000 file codec. + * @param p_manager the user event manager. + * + * @return true if writing was successful. + */ +OPJ_BOOL opj_jp2_write_jp(opj_jp2_t *jp2, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager); + +/** + * Writes a FTYP box - File type box + * + * @param cio the stream to write data to. + * @param jp2 the jpeg2000 file codec. + * @param p_manager the user event manager. + * + * @return true if writing was successful. + */ +OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager); + + +/** + * Executes the given procedures on the given codec. + * + * @param p_procedure_list the list of procedures to execute + * @param jp2 the jpeg2000 file codec to execute the procedures on. + * @param stream the stream to execute the procedures on. + * @param p_manager the user manager. + * + * @return true if all the procedures were successfully executed. + */ +OPJ_BOOL opj_jp2_exec(opj_jp2_t * jp2, + opj_procedure_list_t * p_procedure_list, + opj_stream_private_t *stream, + opj_event_mgr_t * p_manager); /*@}*/ diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c new file mode 100644 index 000000000..dde8a099c --- /dev/null +++ b/src/lib/openjp2/jpx.c @@ -0,0 +1,310 @@ +/* + * The copyright in this software is being made available under the 2-clauses + * BSD License, included below. This software may be subject to other third + * party and contributor rights, including patent rights, and no such rights + * are granted under this license. + * + * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "opj_includes.h" + +/** + * Executes the given procedures on the given codec. + * + * @param p_procedure_list the list of procedures to execute + * @param jpx the jpeg2000 file codec to execute the procedures on. + * @param stream the stream to execute the procedures on. + * @param p_manager the user manager. + * + * @return true if all the procedures were successfully executed. + */ +static OPJ_BOOL opj_jpx_exec(opj_jpx_t * jpx, + opj_procedure_list_t * p_procedure_list, + opj_stream_private_t *stream, + opj_event_mgr_t * p_manager); + +OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, + opj_stream_private_t *stream, + opj_event_mgr_t * p_manager) +{ + puts("Called opj_jpx_encode"); + return OPJ_TRUE; +} + +OPJ_BOOL opj_jpx_end_compress(opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager + ) +{ + puts("Called opj_jpx_end_compress"); + return OPJ_TRUE; +} + +static OPJ_BOOL opj_jpx_setup_header_writing(opj_jpx_t *jpx, + opj_event_mgr_t * p_manager) +{ + /* preconditions */ + assert(jpx != 00); + assert(p_manager != 00); + + if (! opj_procedure_list_add_procedure(jpx->jp2->m_procedure_list, + (opj_procedure)opj_jp2_write_jp, p_manager)) { + return OPJ_FALSE; + } + if (! opj_procedure_list_add_procedure(jpx->jp2->m_procedure_list, + (opj_procedure)opj_jp2_write_ftyp, p_manager)) { + return OPJ_FALSE; + } + + if (! opj_procedure_list_add_procedure(jpx->jp2->m_procedure_list, + (opj_procedure)opj_jpx_write_rreq, p_manager)) { + return OPJ_FALSE; + } + + /* DEVELOPER CORNER, insert your custom procedures */ + + return OPJ_TRUE; +} + +OPJ_BOOL opj_jpx_start_compress(opj_jpx_t *jpx, + opj_stream_private_t *stream, + opj_image_t * p_image, + opj_event_mgr_t * p_manager + ) +{ + assert(jpx != 00); + assert(stream != 00); + assert(p_manager != 00); + + if (! opj_jpx_setup_header_writing(jpx, p_manager)) { + return OPJ_FALSE; + } + + /* write header */ + if (! opj_jp2_exec(jpx->jp2, jpx->jp2->m_procedure_list, stream, p_manager)) { + return OPJ_FALSE; + } + + if (! opj_stream_flush(stream, p_manager)) { + return OPJ_FALSE; + } + + puts("Called opj_jpx_start_compress. execced"); + return OPJ_TRUE; +} + +OPJ_BOOL opj_jpx_setup_encoder(opj_jpx_t *jpx, + opj_cparameters_t *parameters, + opj_image_t *image, + opj_event_mgr_t * p_manager) +{ + puts("Called opj_jpx_setup_encoder"); + assert(jpx != 00); + assert(parameters != 00); + assert(p_manager != 00); + // Need to set brand in ftyp box to "jpx " + jpx->jp2->brand = JPX_JPX; + // JPX compatibility list should contain "jpx ", "jp2 ", and "jpxb" + jpx->jp2->numcl = 3; + // Allocate memory for the compatibility list. + jpx->jp2->cl = (OPJ_UINT32*) opj_malloc(jpx->jp2->numcl * sizeof(OPJ_UINT32)); + // Return failure if we couldn't allocate memory for the cl + if (!jpx->jp2->cl) { + opj_event_msg(p_manager, EVT_ERROR, + "Not enough memory when setting up the JPX encoder\n"); + return OPJ_FALSE; + } + // Assign the cl values + jpx->jp2->cl[0] = JPX_JPX; + jpx->jp2->cl[1] = JP2_JP2; + jpx->jp2->cl[2] = JPX_JPXB; + return OPJ_TRUE; +} + +OPJ_BOOL opj_jpx_encoder_set_extra_options( + opj_jpx_t *p_jpx, + const char* const* p_options, + opj_event_mgr_t * p_manager) +{ + puts("Called opj_jpx_encoder_set_extra_options"); + p_jpx->files = p_options; + puts("Set the list of files to be processed"); + return OPJ_TRUE; +} + +OPJ_BOOL opj_jpx_set_threads(opj_jpx_t *jpx, OPJ_UINT32 num_threads) +{ + puts("Called opj_jpx_set_threads"); + return OPJ_TRUE; +} + +opj_jpx_t* opj_jpx_create(void) +{ + opj_jpx_t *jpx = (opj_jpx_t*)opj_calloc(1, sizeof(opj_jpx_t)); + /* execution list creation */ + jpx->m_procedure_list = opj_procedure_list_create(); + if (! jpx->m_procedure_list) { + opj_jpx_destroy(jpx); + return 00; + } + + jpx->jp2 = opj_jp2_create(OPJ_FALSE); + if (!jpx->jp2) { + opj_jpx_destroy(jpx); + return 00; + } + puts("Created opj_jpx_t instance with a procedure list"); + return jpx; +} + +void opj_jpx_destroy(opj_jpx_t *jpx) +{ + if (jpx) { + if (jpx->m_procedure_list) { + opj_procedure_list_destroy(jpx->m_procedure_list); + } + if (jpx->jp2) { + opj_jp2_destroy(jpx->jp2); + } + opj_free(jpx); + } +} + +/** + * Writes an RREQ box - Reader Requirements box + * + * @param cio the stream to write data to. + * @param jpx the jpeg2000 file codec. + * @param p_manager the user event manager. + * + * @return true if writing was successful. + */ +OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager) +{ + /** + * The reader requirements box is a set of feature flags that list all + * the reader requirements needed to either display the file, or fully + * understand the file. Some features like reading metadata are needed + * to fully understand the file, but not needed for displaying the file. + */ + + // For this implementation of jpx support, we embed multiple codestreams + // in the file. So we set feature flag 2: Contains multiple composition layers + // jp2 files are only being linked, not embedded, so we set + // flag 15: Fragmented codestream where not all fragments are within the file + // but all are in locally accessible files + // We only need 1 byte to support both features masks + OPJ_UINT8 mask_length = 1; + OPJ_UINT8 fully_understand = 0b00000011; + OPJ_UINT8 display_mask = 0b00000011; + OPJ_UINT16 num_flags = 2; + OPJ_UINT16 flags[2] = {RREQ_FLAG_MULTILAYERED, + RREQ_FLAG_REFERENCE_LOCAL_FILES}; + OPJ_UINT8 flag_masks[2] = {0b00000001, // mask for multilayered + 0b00000010}; // mask for local files + OPJ_UINT16 num_vendor_features = 0; + // Above adds up to 13 bytes. Add 8 bytes for box length and box type. + OPJ_UINT32 box_length = 21; + OPJ_BYTE rreq_data[box_length]; + OPJ_BYTE * rreq_data_ptr = &rreq_data[0]; + OPJ_UINT32 i; + + /* preconditions */ + assert(cio != 00); + assert(jp2 != 00); + assert(p_manager != 00); + + OPJ_UNUSED(jp2); + + /* write box length */ + opj_write_bytes(rreq_data_ptr, box_length, 4); + rreq_data_ptr += 4; + + /* writes box type */ + opj_write_bytes(rreq_data_ptr, JPX_RREQ, 4); + rreq_data_ptr += 4; + + /* write reader requirements */ + /* mask length */ + opj_write_bytes(rreq_data_ptr, mask_length, 1); + rreq_data_ptr += 1; + + /* fully understand aspects mask */ + opj_write_bytes(rreq_data_ptr, fully_understand, 1); + rreq_data_ptr += 1; + + /* display contents mask */ + opj_write_bytes(rreq_data_ptr, display_mask, 1); + rreq_data_ptr += 1; + + /* number of flags */ + opj_write_bytes(rreq_data_ptr, num_flags, 2); + rreq_data_ptr += 2; + + /* Write out each flag followed by its mask */ + for (i = 0; i < num_flags; i++) { + opj_write_bytes(rreq_data_ptr, flags[i], 2); + rreq_data_ptr += 2; + opj_write_bytes(rreq_data_ptr, flag_masks[i], 1); + rreq_data_ptr += 1; + } + + /* Write out vendor features */ + opj_write_bytes(rreq_data_ptr, num_vendor_features, 2); + rreq_data_ptr += 2; + + if (opj_stream_write_data(cio, rreq_data, box_length, p_manager) != box_length) { + return OPJ_FALSE; + } + + puts("All good writing header"); + return OPJ_TRUE; +} + +/** + * Ignored for jpx files. Here for codec compatibility. + * + * @param p_jpx the jpeg2000 codec. + * @param p_tile_index FIXME DOC + * @param p_data FIXME DOC + * @param p_data_size FIXME DOC + * @param p_stream the stream to write data to. + * @param p_manager the user event manager. + */ +OPJ_BOOL opj_jpx_write_tile(opj_jpx_t *p_jpx, + OPJ_UINT32 p_tile_index, + OPJ_BYTE * p_data, + OPJ_UINT32 p_data_size, + opj_stream_private_t *p_stream, + opj_event_mgr_t * p_manager) +{ + assert(p_manager != 00); + opj_event_msg(p_manager, EVT_WARNING, + "JPX encoder does not support write tile. Ignoring write tile request.\n"); + return OPJ_TRUE; +} \ No newline at end of file diff --git a/src/lib/openjp2/jpx.h b/src/lib/openjp2/jpx.h new file mode 100644 index 000000000..ec2e83e17 --- /dev/null +++ b/src/lib/openjp2/jpx.h @@ -0,0 +1,163 @@ +/* + * The copyright in this software is being made available under the 2-clauses + * BSD License, included below. This software may be subject to other third + * party and contributor rights, including patent rights, and no such rights + * are granted under this license. + * + * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define JPX_JPX 0x6a707820 /**< JPX ftyp brand */ +#define JPX_JPXB 0x6a707862 /**< JPX baseline */ +#define JPX_RREQ 0x72726571 /**< Reader Requirements box */ + +/** ----------- Feature Flags ----------- **/ +#define RREQ_FLAG_MULTILAYERED 2 +#define RREQ_FLAG_REFERENCE_LOCAL_FILES 15 + + +typedef struct opj_jpx { + /** JPX depends on jp2 codec for writing out header data */ + opj_jp2_t* jp2; + /** List of files to be embedded/linked in the jpx file */ + const char *const *files; + /** list of execution procedures */ + struct opj_procedure_list * m_procedure_list; +} opj_jpx_t; + +/** +Encode an image into a JPEG-2000 jpx file +@param jp2 JP2 compressor handle +@param stream Output buffer stream +@param p_manager event manager +@return Returns true if successful, returns false otherwise +*/ +OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, + opj_stream_private_t *stream, + opj_event_mgr_t * p_manager); + +/** + * Ends the compression procedures. + */ +OPJ_BOOL opj_jpx_end_compress(opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager + ); + +/** + * Starts a compression scheme, i.e. validates the codec parameters, writes the header. + * + * @param jp2 the jpeg2000 file codec. + * @param stream the stream object. + * @param p_image FIXME DOC + * @param p_manager FIXME DOC + * + * @return true if the codec is valid. + */ +OPJ_BOOL opj_jpx_start_compress(opj_jpx_t *jpx, + opj_stream_private_t *stream, + opj_image_t * p_image, + opj_event_mgr_t * p_manager); + +/** + * Setup the encoder parameters using the current image and using user parameters. + * Coding parameters are returned in jp2->j2k->cp. + * + * @param jp2 JP2 compressor handle + * @param parameters compression parameters + * @param image input filled image + * @param p_manager FIXME DOC + * @return OPJ_TRUE if successful, OPJ_FALSE otherwise +*/ +OPJ_BOOL opj_jpx_setup_encoder(opj_jpx_t *jpx, + opj_cparameters_t *parameters, + opj_image_t *image, + opj_event_mgr_t * p_manager); + +/** + * Specify extra options for the encoder. + * + * @param p_jp2 the jpeg2000 codec. + * @param p_options options + * @param p_manager the user event manager + * + * @see opj_encoder_set_extra_options() for more details. + */ +OPJ_BOOL opj_jpx_encoder_set_extra_options( + opj_jpx_t *p_jp2, + const char* const* p_options, + opj_event_mgr_t * p_manager); + +/** + * Does nothing at the moment. + * + * @param jp2 JP2 decompressor handle + * @param num_threads Number of threads. + * @return OPJ_TRUE in case of success. + */ +OPJ_BOOL opj_jpx_set_threads(opj_jpx_t *jpx, OPJ_UINT32 num_threads); + +/** + * Creates a jpx file compressor. + * + * @return an empty jpeg2000 file codec. + */ +opj_jpx_t* opj_jpx_create(void); + +/** + * Destroy a JPX handle + * @param jpx JPX handle to destroy + */ +void opj_jpx_destroy(opj_jpx_t *jpx); + +/** + * Writes an RREQ box - Reader Requirements box + * + * @param cio the stream to write data to. + * @param jp2 the jpeg2000 file codec. + * @param p_manager the user event manager. + * + * @return true if writing was successful. + */ +OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager); + +/** + * Ignored for jpx files. Here for codec compatibility. + * + * @param p_jpx the jpeg2000 codec. + * @param p_tile_index FIXME DOC + * @param p_data FIXME DOC + * @param p_data_size FIXME DOC + * @param p_stream the stream to write data to. + * @param p_manager the user event manager. + */ +OPJ_BOOL opj_jpx_write_tile(opj_jpx_t *p_jpx, + OPJ_UINT32 p_tile_index, + OPJ_BYTE * p_data, + OPJ_UINT32 p_data_size, + opj_stream_private_t *p_stream, + opj_event_mgr_t * p_manager); \ No newline at end of file diff --git a/src/lib/openjp2/openjpeg.c b/src/lib/openjp2/openjpeg.c index 382d8f4f0..a46629cbe 100644 --- a/src/lib/openjp2/openjpeg.c +++ b/src/lib/openjp2/openjpeg.c @@ -7,6 +7,7 @@ * Copyright (c) 2005, Herve Drolon, FreeImage Team * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR * Copyright (c) 2012, CS Systemes d'Information, France + * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -755,6 +756,51 @@ opj_codec_t* OPJ_CALLCONV opj_create_compress(OPJ_CODEC_FORMAT p_format) return 00; } + break; + case OPJ_CODEC_JPX: + /* get a JP2 decoder handle */ + l_codec->m_codec_data.m_compression.opj_encode = (OPJ_BOOL(*)(void *, + struct opj_stream_private *, + struct opj_event_mgr *)) opj_jpx_encode; + + l_codec->m_codec_data.m_compression.opj_end_compress = (OPJ_BOOL(*)(void *, + struct opj_stream_private *, + struct opj_event_mgr *)) opj_jpx_end_compress; + + l_codec->m_codec_data.m_compression.opj_start_compress = (OPJ_BOOL(*)(void *, + struct opj_stream_private *, + struct opj_image *, + struct opj_event_mgr *)) opj_jpx_start_compress; + + l_codec->m_codec_data.m_compression.opj_write_tile = (OPJ_BOOL(*)(void *, + OPJ_UINT32, + OPJ_BYTE*, + OPJ_UINT32, + struct opj_stream_private *, + struct opj_event_mgr *)) opj_jpx_write_tile; + + l_codec->m_codec_data.m_compression.opj_destroy = (void (*)( + void *)) opj_jpx_destroy; + + l_codec->m_codec_data.m_compression.opj_setup_encoder = (OPJ_BOOL(*)(void *, + opj_cparameters_t *, + struct opj_image *, + struct opj_event_mgr *)) opj_jpx_setup_encoder; + + l_codec->m_codec_data.m_compression.opj_encoder_set_extra_options = (OPJ_BOOL( + *)(void *, + const char* const*, + struct opj_event_mgr *)) opj_jpx_encoder_set_extra_options; + + l_codec->opj_set_threads = + (OPJ_BOOL(*)(void * p_codec, OPJ_UINT32 num_threads)) opj_jpx_set_threads; + + l_codec->m_codec = opj_jpx_create(); + if (! l_codec->m_codec) { + opj_free(l_codec); + return 00; + } + break; case OPJ_CODEC_UNKNOWN: diff --git a/src/lib/openjp2/opj_includes.h b/src/lib/openjp2/opj_includes.h index 13613ce52..20d920237 100644 --- a/src/lib/openjp2/opj_includes.h +++ b/src/lib/openjp2/opj_includes.h @@ -7,6 +7,7 @@ * Copyright (c) 2005, Herve Drolon, FreeImage Team * Copyright (c) 2008, 2011-2012, Centre National d'Etudes Spatiales (CNES), FR * Copyright (c) 2012, CS Systemes d'Information, France + * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -234,6 +235,7 @@ typedef unsigned int OPJ_BITFIELD; #include "invert.h" #include "j2k.h" #include "jp2.h" +#include "jpx.h" #include "mqc.h" #include "bio.h" From 22fbfdcef6edd5fc98a577c4c0ab73ae4b2b8fce Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:22:16 -0400 Subject: [PATCH 02/14] Write out fragment tables --- src/bin/jp2/opj_merge.c | 6 +- src/lib/openjp2/jpx.c | 201 +++++++++++++++++++++++++++++++++++++--- src/lib/openjp2/jpx.h | 20 ++++ 3 files changed, 211 insertions(+), 16 deletions(-) diff --git a/src/bin/jp2/opj_merge.c b/src/bin/jp2/opj_merge.c index 265a79bf9..f908b873e 100644 --- a/src/bin/jp2/opj_merge.c +++ b/src/bin/jp2/opj_merge.c @@ -62,7 +62,11 @@ int main(int argc, char **argv) { // Use extra options to specify the list of files to be merged into the jpx file. { - const char* options[2] = { "img/2024_03_27__13_21_29_129__SDO_AIA_AIA_304.jp2", NULL }; + const char* options[2] = { + "img/2024_03_27__13_21_29_129__SDO_AIA_AIA_304.jp2", + "img/2023_08_04__00_01_04_843__SDO_AIA_AIA_193.jp2", + NULL + }; if (!opj_encoder_set_extra_options(codec, options)) { fprintf(stderr, "Failed to set list of jp2 files to include: opj_encoder_set_extra_options\n"); goto fin; diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index dde8a099c..fa6f4fb0f 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -32,25 +32,77 @@ #include "opj_includes.h" /** - * Executes the given procedures on the given codec. + * Writes out a fragment table. * - * @param p_procedure_list the list of procedures to execute - * @param jpx the jpeg2000 file codec to execute the procedures on. - * @param stream the stream to execute the procedures on. - * @param p_manager the user manager. + * @param jp2file Path to jp2 file being embedded + * @param cio the stream to write data to. + * @param jpx the jpx file codec. + * @param p_manager user event manager. + * + * @return true if writing was successful. +*/ +static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, + opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager); + +/** + * Searches a given jp2 file for codestream information. * - * @return true if all the procedures were successfully executed. + * @param[in] jp2file JPEG2000 file to search through for codestream info. + * @param[in] p_manager user event manager. + * @param[out] codestream_offset Byte offset from start of file where the codestream is located + * @param[out] codestream_length Length of the codestream + * @returns OPJ_TRUE if successful */ -static OPJ_BOOL opj_jpx_exec(opj_jpx_t * jpx, - opj_procedure_list_t * p_procedure_list, - opj_stream_private_t *stream, - opj_event_mgr_t * p_manager); +static OPJ_BOOL opj_jpx_find_codestream(const char* jp2file, + opj_event_mgr_t * p_manager, + OPJ_UINT32* codestream_offset, + OPJ_UINT32* codestream_length); +/** Write out fragment tables for the linked jp2 files */ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, opj_stream_private_t *stream, opj_event_mgr_t * p_manager) { - puts("Called opj_jpx_encode"); + OPJ_UINT32 index = 0; + + assert(jpx != 00); + assert(jpx->files != 00); + assert(stream != 00); + assert(p_manager != 00); + + /** + * Iterate over each jp2 file that we're linking to, and add a fragment + * table box for each one. + */ + for (index = 0; index < jpx->file_count; index += 1) { + const char* jp2_fname = jpx->files[index]; + // Current file index is 1-based. + jpx->current_file_index = index + 1; + // Write out the fragment table for this jp2 file. + if (!opj_jpx_write_ftbl(jp2_fname, jpx, stream, p_manager)) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to write fragment tables\n"); + return OPJ_FALSE; + } + } + puts("Added fragment tables"); + + // /** + // * Iterate over each file again, this time inserting any + // * associations (xml boxes) + // */ + // for (index = 0; index < jpx->file_count; index += 1) { + // const char* jp2_fname = jpx->files[index]; + // opj_jpx_write_ftbl(jp2_fname, jpx, stream, p_manager); + // } + + /** Flush data to output stream */ + if (! opj_stream_flush(stream, p_manager)) { + return OPJ_FALSE; + } + return OPJ_TRUE; } @@ -108,11 +160,11 @@ OPJ_BOOL opj_jpx_start_compress(opj_jpx_t *jpx, return OPJ_FALSE; } + /** Flush header to output stream */ if (! opj_stream_flush(stream, p_manager)) { return OPJ_FALSE; } - puts("Called opj_jpx_start_compress. execced"); return OPJ_TRUE; } @@ -121,10 +173,10 @@ OPJ_BOOL opj_jpx_setup_encoder(opj_jpx_t *jpx, opj_image_t *image, opj_event_mgr_t * p_manager) { - puts("Called opj_jpx_setup_encoder"); assert(jpx != 00); assert(parameters != 00); assert(p_manager != 00); + // Need to set brand in ftyp box to "jpx " jpx->jp2->brand = JPX_JPX; // JPX compatibility list should contain "jpx ", "jp2 ", and "jpxb" @@ -149,9 +201,14 @@ OPJ_BOOL opj_jpx_encoder_set_extra_options( const char* const* p_options, opj_event_mgr_t * p_manager) { - puts("Called opj_jpx_encoder_set_extra_options"); + OPJ_UINT32 i = 0; + assert(p_jpx != 00); + assert(p_options != 00); + assert(p_manager != 00); p_jpx->files = p_options; - puts("Set the list of files to be processed"); + // Count the number of files given in the null terminated list. + while (p_jpx->files[i] != NULL) { i++; } + p_jpx->file_count = i; return OPJ_TRUE; } @@ -307,4 +364,118 @@ OPJ_BOOL opj_jpx_write_tile(opj_jpx_t *p_jpx, opj_event_msg(p_manager, EVT_WARNING, "JPX encoder does not support write tile. Ignoring write tile request.\n"); return OPJ_TRUE; +} + +static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, + opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager) +{ + /** A fragment table consists of a fragment table box + a fragment list box. */ + /** A 1-length fragment list box is 24 bytes */ + /** So the whole table box should be 32 bytes */ + OPJ_BYTE ftbl[32]; + OPJ_BYTE* ftbl_ptr = &ftbl[0]; + // FIXME: the specification states offset can be a uint64. + // opj_write_bytes only writes uint32s. + OPJ_UINT32 codestream_offset; + OPJ_UINT32 codestream_length; + OPJ_UINT16 file_index = jpx->current_file_index; + OPJ_BOOL bSuccess = opj_jpx_find_codestream(jp2file, p_manager, &codestream_offset, &codestream_length); + if (!bSuccess) { + opj_event_msg(p_manager, EVT_ERROR, "Failed to find codestream information in %s\n", jp2file); + return OPJ_FALSE; + } + + /** Write box size */ + opj_write_bytes(ftbl_ptr, 32, 4); + ftbl_ptr += 4; + /** Write box type */ + opj_write_bytes(ftbl_ptr, JPX_FTBL, 4); + ftbl_ptr += 4; + /** Write out fragment list */ + /** Fragment list box size */ + opj_write_bytes(ftbl_ptr, 24, 4); + ftbl_ptr += 4; + /** Fragment list box type */ + opj_write_bytes(ftbl_ptr, JPX_FLST, 4); + ftbl_ptr += 4; + /** Fragment list contents */ + // Number of fragments + opj_write_bytes(ftbl_ptr, 1, 2); + ftbl_ptr += 2; + /* Codestream offset */ + opj_write_bytes(ftbl_ptr, 0, 4); + ftbl_ptr += 4; + opj_write_bytes(ftbl_ptr, codestream_offset, 4); + ftbl_ptr += 4; + /* Codestream length */ + opj_write_bytes(ftbl_ptr, codestream_length, 4); + ftbl_ptr += 4; + /* Data Reference index */ + opj_write_bytes(ftbl_ptr, file_index, 2); + + if (opj_stream_write_data(cio, ftbl, 32, p_manager) != 32) { + return OPJ_FALSE; + } + + return OPJ_TRUE; +} + +static OPJ_BOOL opj_jpx_find_codestream(const char* jp2file, + opj_event_mgr_t * p_manager, + OPJ_UINT32* codestream_offset, + OPJ_UINT32* codestream_length) +{ + OPJ_BYTE l_data_header [8]; + OPJ_UINT32 box_length = UINT32_MAX; + OPJ_UINT32 box_type = 0; + OPJ_OFF_T stream_position = 0; + assert(jp2file != 00); + assert(codestream_offset != 00); + assert(codestream_length != 00); + + // Create read stream for the jp2 file. + opj_stream_t* stream = opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); + if (!stream) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to open %s for reading\n", jp2file); + return OPJ_FALSE; + } + + /* Iterate over the boxes in the jp2 file until we find the codestream */ + /* Search for box_length 0, this indicates the codestream location. */ + while (box_length != 0 && box_type != JP2_JP2C) { + // Seek to the next box in the stream. + if (!opj_stream_seek(stream, stream_position, p_manager)) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to seek while reading %s\n", jp2file); + opj_stream_destroy(stream); + return OPJ_FALSE; + } + + // Read the box header. + if (opj_stream_read_data(stream, l_data_header, 8, p_manager) != 8) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to read box information from %s\n", jp2file); + opj_stream_destroy(stream); + return OPJ_FALSE; + } + + // Parse the box header + opj_read_bytes(l_data_header, &box_length, 4); + opj_read_bytes(l_data_header + 4, &box_type, 4); + + // Update the stream location + stream_position += box_length; + } + + // If the loop exists, then the stream should be at the codestream position. + + *codestream_offset = opj_stream_tell(stream); + // Assume the codestream runs until the end of the file. + *codestream_length = opj_stream_get_number_byte_left(stream); + + opj_stream_destroy(stream); + return OPJ_TRUE; } \ No newline at end of file diff --git a/src/lib/openjp2/jpx.h b/src/lib/openjp2/jpx.h index ec2e83e17..db803b86f 100644 --- a/src/lib/openjp2/jpx.h +++ b/src/lib/openjp2/jpx.h @@ -32,6 +32,9 @@ #define JPX_JPX 0x6a707820 /**< JPX ftyp brand */ #define JPX_JPXB 0x6a707862 /**< JPX baseline */ #define JPX_RREQ 0x72726571 /**< Reader Requirements box */ +#define JPX_FTBL 0x6674626c /**< Fragment table box */ +#define JPX_FLST 0x666c7374 /**< Fragment list box */ + /** ----------- Feature Flags ----------- **/ #define RREQ_FLAG_MULTILAYERED 2 @@ -43,8 +46,12 @@ typedef struct opj_jpx { opj_jp2_t* jp2; /** List of files to be embedded/linked in the jpx file */ const char *const *files; + /** Number of files to be linked */ + OPJ_UINT32 file_count; /** list of execution procedures */ struct opj_procedure_list * m_procedure_list; + /** The current file being processed */ + OPJ_UINT32 current_file_index; } opj_jpx_t; /** @@ -132,6 +139,19 @@ opj_jpx_t* opj_jpx_create(void); */ void opj_jpx_destroy(opj_jpx_t *jpx); +/** + * Writes an RREQ box - Reader Requirements box + * + * @param cio the stream to write data to. + * @param jp2 the jpeg2000 file codec. + * @param p_manager the user event manager. + * + * @return true if writing was successful. + */ +OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager); + /** * Writes an RREQ box - Reader Requirements box * From e61da7729553110d47b65500f52a06fd29b1df54 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:49:00 -0400 Subject: [PATCH 03/14] Write association box --- .gitignore | 1 + src/lib/openjp2/jp2.h | 2 +- src/lib/openjp2/jpx.c | 380 +++++++++++++++++++++++++++++++++--------- src/lib/openjp2/jpx.h | 3 + 4 files changed, 308 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index a6f4eff73..1d8dda6fb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ scripts/opjstyle* # Ignore directories made by `make`. /bin/ +/build/ \ No newline at end of file diff --git a/src/lib/openjp2/jp2.h b/src/lib/openjp2/jp2.h index 900a32490..00e0627f2 100644 --- a/src/lib/openjp2/jp2.h +++ b/src/lib/openjp2/jp2.h @@ -60,11 +60,11 @@ #define JP2_DTBL 0x6474626c /**< Data Reference box */ #define JP2_BPCC 0x62706363 /**< Bits per component box */ #define JP2_JP2 0x6a703220 /**< File type fields */ +#define JP2_XML 0x786d6c20 /**< XML box */ /* For the future */ /* #define JP2_RES 0x72657320 */ /**< Resolution box (super-box) */ /* #define JP2_JP2I 0x6a703269 */ /**< Intellectual property box */ -/* #define JP2_XML 0x786d6c20 */ /**< XML box */ /* #define JP2_UUID 0x75756994 */ /**< UUID box */ /* #define JP2_UINF 0x75696e66 */ /**< UUID info box (super-box) */ /* #define JP2_ULST 0x756c7374 */ /**< UUID list box */ diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index fa6f4fb0f..eba23c1e4 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -31,6 +31,15 @@ #include "opj_includes.h" +/** + * Container for a box. + */ +typedef struct box { + OPJ_UINT32 length; + OPJ_UINT32 type; + OPJ_BYTE* contents; +} box_t; + /** * Writes out a fragment table. * @@ -46,6 +55,44 @@ static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, opj_stream_private_t *cio, opj_event_mgr_t * p_manager); +/** + * Writes out an association table. + * + * @param jp2file Path to jp2 file being embedded + * @param cio the stream to write data to. + * @param jpx the jpx file codec. + * @param p_manager user event manager. + * + * @return true if writing was successful. +*/ +static OPJ_BOOL opj_jpx_write_asoc(const char* jp2file, + opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager); + +/** + * Reads a box from the given stream. + * Assumes that the stream is currently pointing to a box header. + * The resulting box must be destroyed with opj_jpx_destroy_box. + * @param[in] l_stream stream to read from + * @param[in] p_manager user event manager + * @param[out] box Box struct to store box data. + */ +static OPJ_BOOL opj_jpx_read_box(opj_stream_private_t *l_stream, + opj_event_mgr_t * p_manager, + box_t* box); + +/** Frees memory allocated for the given box */ +static void opj_jpx_destroy_box(box_t box); + +/** + * Writes data via a cursor. + * Each call, cursor will be moved forward by the number of bytes written. + */ +static void opj_jpx_cursor_write(OPJ_BYTE** p_cursor, + OPJ_UINT32 p_value, + OPJ_UINT32 p_nb_bytes); + /** * Searches a given jp2 file for codestream information. * @@ -83,26 +130,34 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, // Write out the fragment table for this jp2 file. if (!opj_jpx_write_ftbl(jp2_fname, jpx, stream, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to write fragment tables\n"); + "Failed to write fragment table boxes\n"); return OPJ_FALSE; } } - puts("Added fragment tables"); - // /** - // * Iterate over each file again, this time inserting any - // * associations (xml boxes) - // */ - // for (index = 0; index < jpx->file_count; index += 1) { - // const char* jp2_fname = jpx->files[index]; - // opj_jpx_write_ftbl(jp2_fname, jpx, stream, p_manager); - // } + /** + * Iterate over each file again, this time inserting any + * associations (xml boxes) + */ + for (index = 0; index < jpx->file_count; index += 1) { + const char* jp2_fname = jpx->files[index]; + // Current file index is 1-based. + jpx->current_file_index = index + 1; + + // Write out associations for this jp2 file. + if (!opj_jpx_write_asoc(jp2_fname, jpx, stream, p_manager)) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to write association boxes\n"); + return OPJ_FALSE; + } + } /** Flush data to output stream */ if (! opj_stream_flush(stream, p_manager)) { return OPJ_FALSE; } + puts("Encode successful!"); return OPJ_TRUE; } @@ -288,7 +343,7 @@ OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, // Above adds up to 13 bytes. Add 8 bytes for box length and box type. OPJ_UINT32 box_length = 21; OPJ_BYTE rreq_data[box_length]; - OPJ_BYTE * rreq_data_ptr = &rreq_data[0]; + OPJ_BYTE * cursor = &rreq_data[0]; OPJ_UINT32 i; /* preconditions */ @@ -299,41 +354,32 @@ OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, OPJ_UNUSED(jp2); /* write box length */ - opj_write_bytes(rreq_data_ptr, box_length, 4); - rreq_data_ptr += 4; + opj_jpx_cursor_write(&cursor, box_length, 4); /* writes box type */ - opj_write_bytes(rreq_data_ptr, JPX_RREQ, 4); - rreq_data_ptr += 4; + opj_jpx_cursor_write(&cursor, JPX_RREQ, 4); /* write reader requirements */ /* mask length */ - opj_write_bytes(rreq_data_ptr, mask_length, 1); - rreq_data_ptr += 1; + opj_jpx_cursor_write(&cursor, mask_length, 1); /* fully understand aspects mask */ - opj_write_bytes(rreq_data_ptr, fully_understand, 1); - rreq_data_ptr += 1; + opj_jpx_cursor_write(&cursor, fully_understand, 1); /* display contents mask */ - opj_write_bytes(rreq_data_ptr, display_mask, 1); - rreq_data_ptr += 1; + opj_jpx_cursor_write(&cursor, display_mask, 1); /* number of flags */ - opj_write_bytes(rreq_data_ptr, num_flags, 2); - rreq_data_ptr += 2; + opj_jpx_cursor_write(&cursor, num_flags, 2); /* Write out each flag followed by its mask */ for (i = 0; i < num_flags; i++) { - opj_write_bytes(rreq_data_ptr, flags[i], 2); - rreq_data_ptr += 2; - opj_write_bytes(rreq_data_ptr, flag_masks[i], 1); - rreq_data_ptr += 1; + opj_jpx_cursor_write(&cursor, flags[i], 2); + opj_jpx_cursor_write(&cursor, flag_masks[i], 1); } /* Write out vendor features */ - opj_write_bytes(rreq_data_ptr, num_vendor_features, 2); - rreq_data_ptr += 2; + opj_jpx_cursor_write(&cursor, num_vendor_features, 2); if (opj_stream_write_data(cio, rreq_data, box_length, p_manager) != box_length) { return OPJ_FALSE; @@ -375,7 +421,7 @@ static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, /** A 1-length fragment list box is 24 bytes */ /** So the whole table box should be 32 bytes */ OPJ_BYTE ftbl[32]; - OPJ_BYTE* ftbl_ptr = &ftbl[0]; + OPJ_BYTE* cursor = &ftbl[0]; // FIXME: the specification states offset can be a uint64. // opj_write_bytes only writes uint32s. OPJ_UINT32 codestream_offset; @@ -388,32 +434,24 @@ static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, } /** Write box size */ - opj_write_bytes(ftbl_ptr, 32, 4); - ftbl_ptr += 4; + opj_jpx_cursor_write(&cursor, 32, 4); /** Write box type */ - opj_write_bytes(ftbl_ptr, JPX_FTBL, 4); - ftbl_ptr += 4; + opj_jpx_cursor_write(&cursor, JPX_FTBL, 4); /** Write out fragment list */ /** Fragment list box size */ - opj_write_bytes(ftbl_ptr, 24, 4); - ftbl_ptr += 4; + opj_jpx_cursor_write(&cursor, 24, 4); /** Fragment list box type */ - opj_write_bytes(ftbl_ptr, JPX_FLST, 4); - ftbl_ptr += 4; + opj_jpx_cursor_write(&cursor, JPX_FLST, 4); /** Fragment list contents */ // Number of fragments - opj_write_bytes(ftbl_ptr, 1, 2); - ftbl_ptr += 2; + opj_jpx_cursor_write(&cursor, 1, 2); /* Codestream offset */ - opj_write_bytes(ftbl_ptr, 0, 4); - ftbl_ptr += 4; - opj_write_bytes(ftbl_ptr, codestream_offset, 4); - ftbl_ptr += 4; + opj_jpx_cursor_write(&cursor, 0, 4); + opj_jpx_cursor_write(&cursor, codestream_offset, 4); /* Codestream length */ - opj_write_bytes(ftbl_ptr, codestream_length, 4); - ftbl_ptr += 4; + opj_jpx_cursor_write(&cursor, codestream_length, 4); /* Data Reference index */ - opj_write_bytes(ftbl_ptr, file_index, 2); + opj_jpx_cursor_write(&cursor, file_index, 2); if (opj_stream_write_data(cio, ftbl, 32, p_manager) != 32) { return OPJ_FALSE; @@ -422,60 +460,248 @@ static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, return OPJ_TRUE; } +/** + * Does a forward search for the given box type and + * sets the stream position to the header of that box. + * On success, the stream will be pointing to the box length. + * + * If the box is not found, then the stream position is + * undefined. If this function returns false, you should use + * opj_stream_seek to place the stream position back to a known location. + * + * @param p_stream The stream to read + * @param p_manager user event manager + * @param box_type The box type to search for within the stream + * @returns OPJ_TRUE if the box is found. + */ +static OPJ_BOOL opj_jpx_find_box(opj_stream_t* p_stream, + opj_event_mgr_t * p_manager, + OPJ_UINT32 box_type) +{ + OPJ_BYTE l_data_header [8]; + OPJ_OFF_T stream_position = 0; + OPJ_UINT32 lbox = 0; + OPJ_UINT32 tbox = 0; + opj_stream_private_t* l_stream = (opj_stream_private_t*) p_stream; + + + /* Iterate over the boxes in the jp2 file until we find the desired box */ + while (tbox != JP2_JP2C) { + // Seek to the next box in the stream. + if (!opj_stream_seek(l_stream, stream_position, p_manager)) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to seek while reading stream\n"); + return OPJ_FALSE; + } + + // Read the box header. + if (opj_stream_read_data(l_stream, l_data_header, 8, p_manager) != 8) { + // This probably means we reached the end of the file without + // finding the desired box. + opj_event_msg(p_manager, EVT_WARNING, + "Failed to read box information\n"); + return OPJ_FALSE; + } + + // Parse the box header + opj_read_bytes(l_data_header, &lbox, 4); + opj_read_bytes(l_data_header + 4, &tbox, 4); + + // If we found the box + if (tbox == box_type) { + // Set the stream back to the top of the box + if (!opj_stream_seek(l_stream, stream_position, p_manager)) { + // If the seek fails, return false. + opj_event_msg(p_manager, EVT_ERROR, + "Failed to seek while reading stream\n"); + return OPJ_FALSE; + } + + // And return success + return OPJ_TRUE; + } + + // Update the stream location + stream_position += lbox; + } + + // We reached the codestream without finding the box. + return OPJ_FALSE; +} + static OPJ_BOOL opj_jpx_find_codestream(const char* jp2file, opj_event_mgr_t * p_manager, OPJ_UINT32* codestream_offset, OPJ_UINT32* codestream_length) { - OPJ_BYTE l_data_header [8]; - OPJ_UINT32 box_length = UINT32_MAX; - OPJ_UINT32 box_type = 0; + OPJ_BOOL b_found_codestream = OPJ_FALSE; OPJ_OFF_T stream_position = 0; + assert(jp2file != 00); assert(codestream_offset != 00); assert(codestream_length != 00); // Create read stream for the jp2 file. - opj_stream_t* stream = opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); + const opj_stream_private_t* stream = (const opj_stream_private_t*) opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); if (!stream) { opj_event_msg(p_manager, EVT_ERROR, "Failed to open %s for reading\n", jp2file); + opj_stream_destroy((opj_stream_t*) stream); return OPJ_FALSE; } - /* Iterate over the boxes in the jp2 file until we find the codestream */ - /* Search for box_length 0, this indicates the codestream location. */ - while (box_length != 0 && box_type != JP2_JP2C) { - // Seek to the next box in the stream. - if (!opj_stream_seek(stream, stream_position, p_manager)) { - opj_event_msg(p_manager, EVT_ERROR, - "Failed to seek while reading %s\n", jp2file); - opj_stream_destroy(stream); - return OPJ_FALSE; - } + // Find the codestream section of the file. + b_found_codestream = opj_jpx_find_box((opj_stream_t*) stream, p_manager, JP2_JP2C); + if (!b_found_codestream) { + opj_event_msg(p_manager, EVT_ERROR, + "Couldn't find jp2 codestream in %s\n", jp2file); + opj_stream_destroy((opj_stream_t*) stream); + return OPJ_FALSE; + } - // Read the box header. - if (opj_stream_read_data(stream, l_data_header, 8, p_manager) != 8) { - opj_event_msg(p_manager, EVT_ERROR, - "Failed to read box information from %s\n", jp2file); - opj_stream_destroy(stream); - return OPJ_FALSE; + // If the codestream was found, then the stream is pointing to the codestream header. + // The actual codestream starts at stream position + 8 and the length of the codestream + // is the remaining bytes - 8. + stream_position = opj_stream_tell(stream); + + *codestream_offset = stream_position + 8; + // Assume the codestream runs until the end of the file. + *codestream_length = opj_stream_get_number_byte_left(stream) - 8; + + opj_stream_destroy((opj_stream_t*) stream); + return OPJ_TRUE; +} + +static OPJ_BOOL opj_jpx_write_asoc(const char* jp2file, + opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager) +{ + // For the current implementation, only xml boxes are associated with the + // embedded jp2 files. If there's no xml box, then the association is + // skipped + OPJ_BOOL b_has_xml_box = OPJ_FALSE; + + // Create read stream for the jp2 file. + opj_stream_t* p_stream = opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); + opj_stream_private_t* l_stream = (opj_stream_private_t*) p_stream; + if (!p_stream) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to open %s for reading\n", jp2file); + return OPJ_FALSE; + } + + b_has_xml_box = opj_jpx_find_box(p_stream, p_manager, JP2_XML); + // If there is an xml box, then embed it in an association table. + if (b_has_xml_box) { + // Read the xml box into memory + box_t xmlbox; + if (opj_jpx_read_box(l_stream, p_manager, &xmlbox)) { + // Association will contain the xmlbox (xmlbox.length), a number list box (16), and the asoc box header (8) + OPJ_UINT32 asoc_length = xmlbox.length + 16 + 8; + // Allocate memory for the asoc box + OPJ_BYTE* asoc = opj_malloc(asoc_length); + OPJ_BYTE* cursor = asoc; + OPJ_UINT32 associate_number; + OPJ_UINT64 i = 0; + if (asoc) { + /* Write asoc header */ + opj_jpx_cursor_write(&cursor, asoc_length, 4); + /* Write asoc box type */ + opj_jpx_cursor_write(&cursor, JPX_ASOC, 4); + /* Write number list box length */ + opj_jpx_cursor_write(&cursor, 16, 4); + /* Write number list type */ + opj_jpx_cursor_write(&cursor, JPX_NLST, 4); + /* Write codestream association */ + associate_number = 0x01000000; + // current_file_index is 1-based, so need to subtract one since + // associate numbers are 0 based, not 1 based like others. + associate_number |= (jpx->current_file_index - 1); + opj_jpx_cursor_write(&cursor, associate_number, 4); + /* Write compositing layer association */ + associate_number = 0x02000000; + associate_number |= (jpx->current_file_index - 1); + opj_jpx_cursor_write(&cursor, associate_number, 4); + /* Write xml box header */ + opj_jpx_cursor_write(&cursor, xmlbox.length, 4); + opj_jpx_cursor_write(&cursor, JP2_XML, 4); + /* Write xml box contents */ + /* FIXME: is there a better way to dump the buffer than 1 byte at a time?) */ + for (i = 0; i < (xmlbox.length - 8); i++) { + opj_jpx_cursor_write(&cursor, xmlbox.contents[i], 1); + } + + /* Dump contents to stream */ + if (opj_stream_write_data(cio, asoc, asoc_length, p_manager) != asoc_length) { + opj_event_msg(p_manager, EVT_WARNING, + "Failed to write association contents to stream. Stream may be corrupted.\n"); + } else { + puts("Association written successfully."); + } + + opj_free(asoc); + } else { + opj_event_msg(p_manager, EVT_WARNING, + "Failed to allocate memory for association box. Skipping.\n", jp2file); + } + + opj_jpx_destroy_box(xmlbox); + } else { + opj_event_msg(p_manager, EVT_WARNING, + "Failed to read xmlbox from %s. Skipping.\n", jp2file); } + } - // Parse the box header - opj_read_bytes(l_data_header, &box_length, 4); - opj_read_bytes(l_data_header + 4, &box_type, 4); + opj_stream_destroy(p_stream); + return OPJ_TRUE; +} - // Update the stream location - stream_position += box_length; +static OPJ_BOOL opj_jpx_read_box(opj_stream_private_t *l_stream, + opj_event_mgr_t * p_manager, + box_t* box) +{ + OPJ_BYTE box_header[8]; + + assert(l_stream != 00); + assert(p_manager != 00); + + if (opj_stream_read_data(l_stream, box_header, 8, p_manager) != 8) { + opj_event_msg(p_manager, EVT_ERROR, "Failed to read box header\n"); + return OPJ_FALSE; } - // If the loop exists, then the stream should be at the codestream position. + opj_read_bytes(box_header, &box->length, 4); + opj_read_bytes(box_header + 4, &box->type, 4); - *codestream_offset = opj_stream_tell(stream); - // Assume the codestream runs until the end of the file. - *codestream_length = opj_stream_get_number_byte_left(stream); + box->contents = opj_malloc(box->length); + if (!box->contents) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to allocate memory for the box\n"); + return OPJ_FALSE; + } + + if (opj_stream_read_data(l_stream, box->contents, box->length, p_manager) != box->length) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to read all the box contents into memory\n"); + opj_free(box->contents); + return OPJ_FALSE; + } - opj_stream_destroy(stream); return OPJ_TRUE; +} + +static void opj_jpx_destroy_box(box_t box) +{ + if (box.contents) { + opj_free(box.contents); + } +} + +static void opj_jpx_cursor_write(OPJ_BYTE** p_cursor, + OPJ_UINT32 p_value, + OPJ_UINT32 p_nb_bytes) +{ + opj_write_bytes(*p_cursor, p_value, p_nb_bytes); + *p_cursor += p_nb_bytes; } \ No newline at end of file diff --git a/src/lib/openjp2/jpx.h b/src/lib/openjp2/jpx.h index db803b86f..fd52693bf 100644 --- a/src/lib/openjp2/jpx.h +++ b/src/lib/openjp2/jpx.h @@ -34,6 +34,9 @@ #define JPX_RREQ 0x72726571 /**< Reader Requirements box */ #define JPX_FTBL 0x6674626c /**< Fragment table box */ #define JPX_FLST 0x666c7374 /**< Fragment list box */ +#define JPX_ASOC 0x61736f63 /**< Association box */ +#define JPX_ASOC 0x61736f63 /**< Association box */ +#define JPX_NLST 0x6e6c7374 /**< Number List box */ /** ----------- Feature Flags ----------- **/ From ac2c29643e1a263e22b9ee9b14a7bf24d68f9589 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Wed, 27 Mar 2024 17:36:07 -0400 Subject: [PATCH 04/14] Add data reference table --- src/lib/openjp2/jpx.c | 139 ++++++++++++++++++++++++++++++++++++++++++ src/lib/openjp2/jpx.h | 2 + 2 files changed, 141 insertions(+) diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index eba23c1e4..63dd58219 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -29,6 +29,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include "opj_includes.h" /** @@ -70,6 +72,30 @@ static OPJ_BOOL opj_jpx_write_asoc(const char* jp2file, opj_stream_private_t *cio, opj_event_mgr_t * p_manager); +/** + * Writes out the data reference table. + * + * @param jp2file Path to jp2 file being embedded + * @param cio the stream to write data to. + * @param jpx the jpx file codec. + * @param p_manager user event manager. + * + * @return true if writing was successful. + */ +static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager); + +/** + * Computes the size of the Data Reference box based on the jpx encoder parameters. + */ +static OPJ_UINT32 opj_jpx_compute_dtbl_size(opj_jpx_t *jpx); + +/** + * Computes the size of the url box to hold the given file path + */ +static OPJ_UINT32 opj_jpx_compute_urlbox_size(const char* filepath); + /** * Reads a box from the given stream. * Assumes that the stream is currently pointing to a box header. @@ -152,6 +178,15 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, } } + /** + * Final step. Create the data reference table for the jp2 files. + */ + if (!opj_jpx_write_dtbl(jpx, stream, p_manager)) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to write data reference table\n"); + return OPJ_FALSE; + } + /** Flush data to output stream */ if (! opj_stream_flush(stream, p_manager)) { return OPJ_FALSE; @@ -704,4 +739,108 @@ static void opj_jpx_cursor_write(OPJ_BYTE** p_cursor, { opj_write_bytes(*p_cursor, p_value, p_nb_bytes); *p_cursor += p_nb_bytes; +} + +static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, + opj_stream_private_t *cio, + opj_event_mgr_t * p_manager) +{ + OPJ_UINT64 i = 0; + OPJ_BYTE* dtbl = 00; + OPJ_BYTE* cursor = 00; + // Compute the size of the data table. + OPJ_UINT32 dtbl_size = opj_jpx_compute_dtbl_size(jpx); + if (dtbl_size == UINT32_MAX) { + opj_event_msg(p_manager, EVT_ERROR, "Failed to compute the data reference table size\n"); + return OPJ_FALSE; + } + + dtbl = opj_malloc(dtbl_size); + if (!dtbl) { + opj_event_msg(p_manager, EVT_ERROR, "Failed to allocate memory for the data reference table\n"); + return OPJ_FALSE; + } + + cursor = dtbl; + /* Write box header */ + opj_jpx_cursor_write(&cursor, dtbl_size, 4); + opj_jpx_cursor_write(&cursor, JPX_DTBL, 4); + /* Write number of references */ + opj_jpx_cursor_write(&cursor, jpx->file_count, 2); + /* Write data reference boxes */ + for (i = 0; i < jpx->file_count; i++) { + /* Write box header */ + const char* jp2file = jpx->files[i]; + char path_buf[PATH_MAX]; + if (realpath(jp2file, path_buf) == NULL) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to get absolute path of file %s\n", jp2file); + opj_free(dtbl); + return OPJ_FALSE; + } + + OPJ_UINT32 box_size = opj_jpx_compute_urlbox_size(jpx->files[i]); + opj_jpx_cursor_write(&cursor, box_size, 4); + opj_jpx_cursor_write(&cursor, JPX_URL, 4); + /** Version and Flags are defined to be 0. */ + opj_jpx_cursor_write(&cursor, 0, 4); + /** Write the local file path url */ + strcpy((char*) cursor, "file://"); + cursor += strlen("file://"); + strcpy((char*) cursor, path_buf); + cursor += strlen(path_buf); + /* write null terminator */ + opj_jpx_cursor_write(&cursor, 0, 1); + } + + if (opj_stream_write_data(cio, dtbl, dtbl_size, p_manager) != dtbl_size) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to write data reference table to output stream\n"); + opj_free(dtbl); + return OPJ_FALSE; + } + + opj_free(dtbl); + return OPJ_TRUE; +} + +static OPJ_UINT32 opj_jpx_compute_dtbl_size(opj_jpx_t *jpx) +{ + OPJ_UINT32 counter = 0; + // The data reference table is made up of a data reference box + // followed by a number of data entry url boxes. + // Each data entry url box is the size of a box header + 4 bytes of 0 + // + the length of a null terminated utf8 string. + // To compute the size of the dtbl, we have to compute the length of + // all the strings that will be embedded. + OPJ_UINT32 i = 0; + for (i = 0; i < jpx->file_count; i++) { + const char* jp2file = jpx->files[i]; + OPJ_UINT32 urlbox_size = opj_jpx_compute_urlbox_size(jp2file); + if (urlbox_size == UINT32_MAX) { + return UINT32_MAX; + } + counter += urlbox_size; + } + + /* Add the data reference box size */ + // header size (8) + num_reference (2) + counter += 8 + 2; + return counter; +} + +static OPJ_UINT32 opj_jpx_compute_urlbox_size(const char* filepath) +{ + OPJ_UINT32 counter = 0; + char path_buf[PATH_MAX]; + // FIXME: Need realpath equivalent for windows. + if (realpath(filepath, path_buf) == NULL) { + return UINT32_MAX; + } + /* Count the size of a data url box */ + // header size (8) + version size (1) + flag size (3) + counter += 8 + 1 + 3; + // + variable string size + counter += strlen(path_buf) + strlen("file://") + 1; + return counter; } \ No newline at end of file diff --git a/src/lib/openjp2/jpx.h b/src/lib/openjp2/jpx.h index fd52693bf..0235275c1 100644 --- a/src/lib/openjp2/jpx.h +++ b/src/lib/openjp2/jpx.h @@ -37,6 +37,8 @@ #define JPX_ASOC 0x61736f63 /**< Association box */ #define JPX_ASOC 0x61736f63 /**< Association box */ #define JPX_NLST 0x6e6c7374 /**< Number List box */ +#define JPX_URL 0x75726c20 /**< URL Box */ +#define JPX_DTBL 0x6474626c /**< Data Reference Box */ /** ----------- Feature Flags ----------- **/ From 14221f02a062c52b1a04e3d008e0e801a06a998f Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:10:08 -0400 Subject: [PATCH 05/14] Implement opj_merge --- src/bin/common/format_defs.h | 1 + src/bin/jp2/opj_merge.c | 124 ++++++++++++++++++++++++++++++++--- src/lib/openjp2/openjpeg.h | 1 + 3 files changed, 116 insertions(+), 10 deletions(-) diff --git a/src/bin/common/format_defs.h b/src/bin/common/format_defs.h index 1985b54f7..2d39c0045 100644 --- a/src/bin/common/format_defs.h +++ b/src/bin/common/format_defs.h @@ -41,6 +41,7 @@ #define J2K_CFMT 0 #define JP2_CFMT 1 #define JPT_CFMT 2 +#define JPX_CFMT 3 #define PXM_DFMT 10 #define PGX_DFMT 11 diff --git a/src/bin/jp2/opj_merge.c b/src/bin/jp2/opj_merge.c index f908b873e..a0dd39881 100644 --- a/src/bin/jp2/opj_merge.c +++ b/src/bin/jp2/opj_merge.c @@ -32,9 +32,108 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include +#include +#include +#include +#include "openjpeg.h" +#include "opj_getopt.h" +#include "format_defs.h" + +void merge_help_display() +{ + fprintf(stdout, + "\nThis is the opj_merge utility from the OpenJPEG project.\n" + "It creates a jpx with references to multiple local jp2 files.\n" + "It has been compiled against openjp2 library v%s.\n\n", opj_version()); + fprintf(stdout, "Parameters:\n"); + fprintf(stdout, "-----------\n"); + fprintf(stdout, "Required Parameters (except with -h):\n"); + fprintf(stdout, "-i \n"); + fprintf(stdout, " Input file, may be passed multiple times to\n"); + fprintf(stdout, " indicate all jp2 files that will be merged.\n"); + fprintf(stdout, "-o \n"); + fprintf(stdout, " Output file (accepted extensions are jpx).\n"); + fprintf(stdout, "-h\n"); + fprintf(stdout, " Display this help information.\n"); +} + +static int get_file_format(char *filename) +{ + unsigned int i; + static const char *extension[] = { + "pgx", "pnm", "pgm", "ppm", "pbm", "pam", "bmp", "tif", "tiff", "raw", "yuv", "rawl", "tga", "png", "j2k", "jp2", "j2c", "jpc", "jpx" + }; + static const int format[] = { + PGX_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, BMP_DFMT, TIF_DFMT, TIF_DFMT, RAW_DFMT, RAW_DFMT, RAWL_DFMT, TGA_DFMT, PNG_DFMT, J2K_CFMT, JP2_CFMT, J2K_CFMT, J2K_CFMT, JPX_CFMT + }; + char * ext = strrchr(filename, '.'); + if (ext == NULL) { + return -1; + } + ext++; + for (i = 0; i < sizeof(format) / sizeof(*format); i++) { + if (strcasecmp(ext, extension[i]) == 0) { + return format[i]; + } + } + return -1; +} + +/** + * Parse incoming command line arguments and store the + * result in args + * @param[in] argc Program argc + * @param[in] argv Program argv + * @param[out] outfile ptr to const char* which will get the output file. + * @param[inout] input_file_list array instance, should be length argc to ensure it's big enough. + * @param[out] infile_count Number of input files given. + */ +static void parse_cmdline(int argc, + char** argv, + char** outfile, + const char** input_file_list, + OPJ_UINT32* infile_count) +{ + const char optlist[] = "i:o:"; + OPJ_UINT32 num_files = 0; + int c; + while ((c = opj_getopt(argc, argv, optlist)) != -1) { + switch (c) { + case 'i': // Input file + // Store the input file in the array + input_file_list[num_files++] = opj_optarg; + break; + case 'o': + *outfile = opj_optarg; + if (get_file_format(*outfile) != JPX_CFMT) { + fprintf(stderr, "Unknown output file %s [output must be *.jpx]\n", *outfile); + exit(1); + } + break; + case 'h': + merge_help_display(); + exit(0); + } + } + + *infile_count = num_files; + + if (num_files == 0) { + fprintf(stderr, "No input files given!\n"); + exit(1); + } + + if (*outfile == NULL) { + fprintf(stderr, "No output file given!\n"); + exit(1); + } + + // Null terminate the list of input files. + input_file_list[num_files] = NULL; +} int main(int argc, char **argv) { + OPJ_UINT64 i = 0; /** Create jpx encoder */ opj_codec_t* codec = opj_create_compress(OPJ_CODEC_JPX); /* set encoding parameters to default values */ @@ -43,9 +142,19 @@ int main(int argc, char **argv) { opj_stream_t *outfile = NULL; /** Creation status */ OPJ_BOOL bSuccess = OPJ_FALSE; + OPJ_UINT32 file_count; + const char* input_files[argc]; + char* output_file; /** Program return code */ int ret = 1; + parse_cmdline(argc, argv, &output_file, input_files, &file_count); + printf("Merging\n"); + for(i = 0; i < file_count; i++) { + printf(" - %s\n", input_files[i]); + } + printf("Into %s\n", output_file); + if (!codec) { fprintf(stderr, "Failed to initialize the jpx codec.\n"); return ret; @@ -55,32 +164,27 @@ int main(int argc, char **argv) { // Creating references to other jpx files doesn't require image data. // so pass NULL for the image parameter and hope for the best. - if (! opj_setup_encoder(codec, ¶meters, 1)) { + if (! opj_setup_encoder(codec, ¶meters, OPJ_NO_IMAGE_DATA)) { fprintf(stderr, "Failed to setup encoder: opj_setup_encoder\n"); goto fin; } // Use extra options to specify the list of files to be merged into the jpx file. { - const char* options[2] = { - "img/2024_03_27__13_21_29_129__SDO_AIA_AIA_304.jp2", - "img/2023_08_04__00_01_04_843__SDO_AIA_AIA_193.jp2", - NULL - }; - if (!opj_encoder_set_extra_options(codec, options)) { + if (!opj_encoder_set_extra_options(codec, input_files)) { fprintf(stderr, "Failed to set list of jp2 files to include: opj_encoder_set_extra_options\n"); goto fin; } } // /* open a byte stream for writing */ - outfile = opj_stream_create_default_file_stream("sample.jpx", OPJ_FALSE); + outfile = opj_stream_create_default_file_stream(output_file, OPJ_FALSE); if (!outfile) { fprintf(stderr, "Failed to allocate memory for the output file"); goto fin; } - bSuccess = opj_start_compress(codec, 1, outfile); + bSuccess = opj_start_compress(codec, OPJ_NO_IMAGE_DATA, outfile); if (!bSuccess) { fprintf(stderr, "Failed to create the jpx image: opj_start_compress\n"); goto fin; diff --git a/src/lib/openjp2/openjpeg.h b/src/lib/openjp2/openjpeg.h index 113481bbb..efcb9c241 100644 --- a/src/lib/openjp2/openjpeg.h +++ b/src/lib/openjp2/openjpeg.h @@ -271,6 +271,7 @@ typedef size_t OPJ_SIZE_T; #define OPJ_CINEMA_24_COMP 1041666 /** Maximum size per color component for 2K & 4K @ 24fps */ #define OPJ_CINEMA_48_COMP 520833 /** Maximum size per color component for 2K @ 48fps */ +#define OPJ_NO_IMAGE_DATA ((opj_image_t*) 1) /* ========================================================== enum definitions From b15ca48c36ae55e9a9a611aac4e723f71b19dc90 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:53:54 -0400 Subject: [PATCH 06/14] Fix ubuntu warnings/errors --- src/bin/jp2/opj_merge.c | 7 +++- src/lib/openjp2/jpx.c | 84 +++++++++++++++++++++++++++-------------- src/lib/openjp2/jpx.h | 2 +- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/src/bin/jp2/opj_merge.c b/src/bin/jp2/opj_merge.c index a0dd39881..dbae09b71 100644 --- a/src/bin/jp2/opj_merge.c +++ b/src/bin/jp2/opj_merge.c @@ -39,7 +39,12 @@ #include "opj_getopt.h" #include "format_defs.h" -void merge_help_display() +/** + * Prints usage information for opj_merge. + */ +void merge_help_display(void); + +void merge_help_display(void) { fprintf(stdout, "\nThis is the opj_merge utility from the OpenJPEG project.\n" diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index 63dd58219..19be136f2 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -145,14 +145,18 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, assert(stream != 00); assert(p_manager != 00); - /** + /* * Iterate over each jp2 file that we're linking to, and add a fragment * table box for each one. */ + /* The jpx spec only allows a u16 for the number of references. */ + /* We should never have more than that. */ + assert(jpx->file_count < UINT16_MAX); for (index = 0; index < jpx->file_count; index += 1) { const char* jp2_fname = jpx->files[index]; + // Current file index is 1-based. - jpx->current_file_index = index + 1; + jpx->current_file_index = ((OPJ_UINT16)index) + 1; // Write out the fragment table for this jp2 file. if (!opj_jpx_write_ftbl(jp2_fname, jpx, stream, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, @@ -168,7 +172,7 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, for (index = 0; index < jpx->file_count; index += 1) { const char* jp2_fname = jpx->files[index]; // Current file index is 1-based. - jpx->current_file_index = index + 1; + jpx->current_file_index = (OPJ_UINT16)index + 1; // Write out associations for this jp2 file. if (!opj_jpx_write_asoc(jp2_fname, jpx, stream, p_manager)) { @@ -201,7 +205,9 @@ OPJ_BOOL opj_jpx_end_compress(opj_jpx_t *jpx, opj_event_mgr_t * p_manager ) { - puts("Called opj_jpx_end_compress"); + OPJ_UNUSED(jpx); + OPJ_UNUSED(cio); + OPJ_UNUSED(p_manager); return OPJ_TRUE; } @@ -241,6 +247,8 @@ OPJ_BOOL opj_jpx_start_compress(opj_jpx_t *jpx, assert(stream != 00); assert(p_manager != 00); + OPJ_UNUSED(p_image); + if (! opj_jpx_setup_header_writing(jpx, p_manager)) { return OPJ_FALSE; } @@ -267,6 +275,8 @@ OPJ_BOOL opj_jpx_setup_encoder(opj_jpx_t *jpx, assert(parameters != 00); assert(p_manager != 00); + OPJ_UNUSED(image); + // Need to set brand in ftyp box to "jpx " jpx->jp2->brand = JPX_JPX; // JPX compatibility list should contain "jpx ", "jp2 ", and "jpxb" @@ -292,6 +302,7 @@ OPJ_BOOL opj_jpx_encoder_set_extra_options( opj_event_mgr_t * p_manager) { OPJ_UINT32 i = 0; + OPJ_UNUSED(p_manager); assert(p_jpx != 00); assert(p_options != 00); assert(p_manager != 00); @@ -304,7 +315,8 @@ OPJ_BOOL opj_jpx_encoder_set_extra_options( OPJ_BOOL opj_jpx_set_threads(opj_jpx_t *jpx, OPJ_UINT32 num_threads) { - puts("Called opj_jpx_set_threads"); + OPJ_UNUSED(jpx); + OPJ_UNUSED(num_threads); return OPJ_TRUE; } @@ -442,6 +454,11 @@ OPJ_BOOL opj_jpx_write_tile(opj_jpx_t *p_jpx, opj_event_mgr_t * p_manager) { assert(p_manager != 00); + OPJ_UNUSED(p_jpx); + OPJ_UNUSED(p_tile_index); + OPJ_UNUSED(p_data); + OPJ_UNUSED(p_data_size); + OPJ_UNUSED(p_stream); opj_event_msg(p_manager, EVT_WARNING, "JPX encoder does not support write tile. Ignoring write tile request.\n"); return OPJ_TRUE; @@ -571,13 +588,15 @@ static OPJ_BOOL opj_jpx_find_codestream(const char* jp2file, { OPJ_BOOL b_found_codestream = OPJ_FALSE; OPJ_OFF_T stream_position = 0; + OPJ_OFF_T bytes_remaining = 0; + opj_stream_private_t* stream; assert(jp2file != 00); assert(codestream_offset != 00); assert(codestream_length != 00); - // Create read stream for the jp2 file. - const opj_stream_private_t* stream = (const opj_stream_private_t*) opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); + /* Create read stream for the jp2 file. */ + stream = (opj_stream_private_t*) opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); if (!stream) { opj_event_msg(p_manager, EVT_ERROR, "Failed to open %s for reading\n", jp2file); @@ -594,14 +613,17 @@ static OPJ_BOOL opj_jpx_find_codestream(const char* jp2file, return OPJ_FALSE; } - // If the codestream was found, then the stream is pointing to the codestream header. - // The actual codestream starts at stream position + 8 and the length of the codestream - // is the remaining bytes - 8. + /* If the codestream was found, then the stream is pointing to the codestream header. */ + /* The actual codestream starts at stream position + 8 and the length of the codestream */ + /* is the remaining bytes - 8. */ stream_position = opj_stream_tell(stream); - *codestream_offset = stream_position + 8; - // Assume the codestream runs until the end of the file. - *codestream_length = opj_stream_get_number_byte_left(stream) - 8; + assert(stream_position < (UINT32_MAX - 8)); + *codestream_offset = (OPJ_UINT32)stream_position + 8; + /* Assume the codestream runs until the end of the file. */ + bytes_remaining = opj_stream_get_number_byte_left(stream); + assert(bytes_remaining < UINT32_MAX); + *codestream_length = (OPJ_UINT32)bytes_remaining - 8; opj_stream_destroy((opj_stream_t*) stream); return OPJ_TRUE; @@ -779,18 +801,20 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, return OPJ_FALSE; } - OPJ_UINT32 box_size = opj_jpx_compute_urlbox_size(jpx->files[i]); - opj_jpx_cursor_write(&cursor, box_size, 4); - opj_jpx_cursor_write(&cursor, JPX_URL, 4); - /** Version and Flags are defined to be 0. */ - opj_jpx_cursor_write(&cursor, 0, 4); - /** Write the local file path url */ - strcpy((char*) cursor, "file://"); - cursor += strlen("file://"); - strcpy((char*) cursor, path_buf); - cursor += strlen(path_buf); - /* write null terminator */ - opj_jpx_cursor_write(&cursor, 0, 1); + { + OPJ_UINT32 box_size = opj_jpx_compute_urlbox_size(jpx->files[i]); + opj_jpx_cursor_write(&cursor, box_size, 4); + opj_jpx_cursor_write(&cursor, JPX_URL, 4); + /** Version and Flags are defined to be 0. */ + opj_jpx_cursor_write(&cursor, 0, 4); + /** Write the local file path url */ + strcpy((char*) cursor, "file://"); + cursor += strlen("file://"); + strcpy((char*) cursor, path_buf); + cursor += strlen(path_buf); + /* write null terminator */ + opj_jpx_cursor_write(&cursor, 0, 1); + } } if (opj_stream_write_data(cio, dtbl, dtbl_size, p_manager) != dtbl_size) { @@ -831,16 +855,18 @@ static OPJ_UINT32 opj_jpx_compute_dtbl_size(opj_jpx_t *jpx) static OPJ_UINT32 opj_jpx_compute_urlbox_size(const char* filepath) { - OPJ_UINT32 counter = 0; + size_t counter = 0; char path_buf[PATH_MAX]; // FIXME: Need realpath equivalent for windows. if (realpath(filepath, path_buf) == NULL) { return UINT32_MAX; } /* Count the size of a data url box */ - // header size (8) + version size (1) + flag size (3) + /* header size (8) + version size (1) + flag size (3) */ counter += 8 + 1 + 3; - // + variable string size + /* + variable string size */ counter += strlen(path_buf) + strlen("file://") + 1; - return counter; + + assert(counter <= UINT32_MAX); + return (OPJ_UINT32)counter; } \ No newline at end of file diff --git a/src/lib/openjp2/jpx.h b/src/lib/openjp2/jpx.h index 0235275c1..417bdeca3 100644 --- a/src/lib/openjp2/jpx.h +++ b/src/lib/openjp2/jpx.h @@ -56,7 +56,7 @@ typedef struct opj_jpx { /** list of execution procedures */ struct opj_procedure_list * m_procedure_list; /** The current file being processed */ - OPJ_UINT32 current_file_index; + OPJ_UINT16 current_file_index; } opj_jpx_t; /** From aff6e6b2877d1ec31f23cc4183a13e9482290731 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 11:05:03 -0400 Subject: [PATCH 07/14] Add overflow assert --- src/lib/openjp2/jpx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index 19be136f2..d564451db 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -817,6 +817,11 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, } } + /* Assert that there was no heap buffer overflow on dtbl memory. */ + /* The cursor should be exactly the computed size away from the */ + /* start of the reference table. */ + assert((cursor - dtbl) == dtbl_size); + if (opj_stream_write_data(cio, dtbl, dtbl_size, p_manager) != dtbl_size) { opj_event_msg(p_manager, EVT_ERROR, "Failed to write data reference table to output stream\n"); From a0f4ba4f42316a574667adc1b4f62ce17225fe3c Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:08:25 -0400 Subject: [PATCH 08/14] Add windows support --- src/lib/openjp2/jpx.c | 95 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 18 deletions(-) diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index d564451db..97c99787b 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -32,6 +32,9 @@ #include #include #include "opj_includes.h" +#ifdef WIN32 +#include +#endif /** * Container for a box. @@ -57,6 +60,13 @@ static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, opj_stream_private_t *cio, opj_event_mgr_t * p_manager); +/** + * Returns the absolute path for the given file + * The buffer returned must be free'd with opj_free + * @returns NULL on failure, else the absolute path + */ +static char* opj_jpx_get_absolute_path(const char* relative_path); + /** * Writes out an association table. * @@ -92,7 +102,8 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, static OPJ_UINT32 opj_jpx_compute_dtbl_size(opj_jpx_t *jpx); /** - * Computes the size of the url box to hold the given file path + * Computes the size of the url box to hold the given file path. + * @returns UINT32_MAX on failure, else the url box size */ static OPJ_UINT32 opj_jpx_compute_urlbox_size(const char* filepath); @@ -384,12 +395,12 @@ OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, OPJ_UINT16 num_flags = 2; OPJ_UINT16 flags[2] = {RREQ_FLAG_MULTILAYERED, RREQ_FLAG_REFERENCE_LOCAL_FILES}; - OPJ_UINT8 flag_masks[2] = {0b00000001, // mask for multilayered - 0b00000010}; // mask for local files + OPJ_UINT8 flag_masks[2] = {0b00000001, /* mask for multilayered */ + 0b00000010}; /* mask for local files */ OPJ_UINT16 num_vendor_features = 0; - // Above adds up to 13 bytes. Add 8 bytes for box length and box type. + /* Above adds up to 13 bytes.Add 8 bytes for box length and box type. */ OPJ_UINT32 box_length = 21; - OPJ_BYTE rreq_data[box_length]; + OPJ_BYTE rreq_data[21]; OPJ_BYTE * cursor = &rreq_data[0]; OPJ_UINT32 i; @@ -428,11 +439,15 @@ OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, /* Write out vendor features */ opj_jpx_cursor_write(&cursor, num_vendor_features, 2); + /* Assert there was no overflow. */ + puts("Checking rreq table"); + assert((cursor - rreq_data) == 21); + puts("rreq table is good"); + if (opj_stream_write_data(cio, rreq_data, box_length, p_manager) != box_length) { return OPJ_FALSE; } - puts("All good writing header"); return OPJ_TRUE; } @@ -693,8 +708,6 @@ static OPJ_BOOL opj_jpx_write_asoc(const char* jp2file, if (opj_stream_write_data(cio, asoc, asoc_length, p_manager) != asoc_length) { opj_event_msg(p_manager, EVT_WARNING, "Failed to write association contents to stream. Stream may be corrupted.\n"); - } else { - puts("Association written successfully."); } opj_free(asoc); @@ -793,8 +806,8 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, for (i = 0; i < jpx->file_count; i++) { /* Write box header */ const char* jp2file = jpx->files[i]; - char path_buf[PATH_MAX]; - if (realpath(jp2file, path_buf) == NULL) { + char* absolute_path = opj_jpx_get_absolute_path(jp2file); + if (absolute_path == NULL) { opj_event_msg(p_manager, EVT_ERROR, "Failed to get absolute path of file %s\n", jp2file); opj_free(dtbl); @@ -810,17 +823,21 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, /** Write the local file path url */ strcpy((char*) cursor, "file://"); cursor += strlen("file://"); - strcpy((char*) cursor, path_buf); - cursor += strlen(path_buf); + strcpy((char*) cursor, absolute_path); + cursor += strlen(absolute_path); /* write null terminator */ opj_jpx_cursor_write(&cursor, 0, 1); } + + opj_free(absolute_path); } /* Assert that there was no heap buffer overflow on dtbl memory. */ /* The cursor should be exactly the computed size away from the */ /* start of the reference table. */ + puts("Writing data reference table"); assert((cursor - dtbl) == dtbl_size); + puts("Data reference table is good"); if (opj_stream_write_data(cio, dtbl, dtbl_size, p_manager) != dtbl_size) { opj_event_msg(p_manager, EVT_ERROR, @@ -860,18 +877,60 @@ static OPJ_UINT32 opj_jpx_compute_dtbl_size(opj_jpx_t *jpx) static OPJ_UINT32 opj_jpx_compute_urlbox_size(const char* filepath) { - size_t counter = 0; - char path_buf[PATH_MAX]; - // FIXME: Need realpath equivalent for windows. - if (realpath(filepath, path_buf) == NULL) { + OPJ_SIZE_T counter = 0; + char* absolute_path = opj_jpx_get_absolute_path(filepath); + if (!absolute_path) { return UINT32_MAX; } + /* Count the size of a data url box */ /* header size (8) + version size (1) + flag size (3) */ counter += 8 + 1 + 3; /* + variable string size */ - counter += strlen(path_buf) + strlen("file://") + 1; + counter += strlen(absolute_path) + strlen("file://") + 1; assert(counter <= UINT32_MAX); + opj_free(absolute_path); return (OPJ_UINT32)counter; -} \ No newline at end of file +} + +#ifdef WIN32 +static char* opj_jpx_get_absolute_path(const char* relative_path) +{ + /* Maximum path length for windows */ + DWORD buffer_size = 32767; + DWORD result = 0; + char* outbuf = opj_calloc(buffer_size, 1); + if (!outbuf) { + return NULL; + } + + result = GetFullPathName(relative_path, buffer_size, outbuf, NULL); + /* On failure, GetFullPathName will return either 0 */ + /* Or if the buffer is too small, then it returns the required buffer size. */ + if ((result == 0) || (result > buffer_size)) { + opj_free(outbuf); + return NULL; + } + + return outbuf; +} +#else +static char* opj_jpx_get_absolute_path(const char* relative_path) +{ + char* outbuf = NULL; + char path_buf[PATH_MAX]; + OPJ_SIZE_T path_length; + if (realpath(relative_path, path_buf) == NULL) { + return NULL; + } + + outbuf = opj_malloc(strlen(path_buf) + 1); + if (!outbuf) { + return NULL; + } + + strcpy(outbuf, path_buf); + return outbuf; +} +#endif \ No newline at end of file From ae2fb4865228af86ea623087059d0f8ab4bfd41c Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:21:40 -0400 Subject: [PATCH 09/14] Fix opj_merge for windows --- src/bin/jp2/opj_merge.c | 44 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/src/bin/jp2/opj_merge.c b/src/bin/jp2/opj_merge.c index dbae09b71..8706b9606 100644 --- a/src/bin/jp2/opj_merge.c +++ b/src/bin/jp2/opj_merge.c @@ -1,43 +1,12 @@ -/* - * The copyright in this software is being made available under the 2-clauses - * BSD License, included below. This software may be subject to other third - * party and contributor rights, including patent rights, and no such rights - * are granted under this license. - * - * Copyright (c) 2010, Mathieu Malaterre, GDCM - * Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France - * Copyright (c) 2012, CS Systemes d'Information, France - * Copyright (c) 2024, Daniel Garcia Briseno, ADNET Systems Inc, NASA - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - #include #include #include #include "openjpeg.h" #include "opj_getopt.h" #include "format_defs.h" +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif /** * Prints usage information for opj_merge. @@ -148,10 +117,14 @@ int main(int argc, char **argv) { /** Creation status */ OPJ_BOOL bSuccess = OPJ_FALSE; OPJ_UINT32 file_count; - const char* input_files[argc]; + const char** input_files = calloc(argc, sizeof(const char*)); char* output_file; /** Program return code */ int ret = 1; + if (!input_files) { + fprintf(stderr, "Failed to allocate memory for input file list\n"); + exit(1); + } parse_cmdline(argc, argv, &output_file, input_files, &file_count); printf("Merging\n"); @@ -215,5 +188,6 @@ int main(int argc, char **argv) { if (outfile) { opj_stream_destroy(outfile); } + free(input_files); return ret; } \ No newline at end of file From d37eaa74262565095c2e0877f372482d07e83f54 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:41:49 -0400 Subject: [PATCH 10/14] Insert jp2h box in jpx --- src/bin/jp2/opj_merge.c | 9 +++++-- src/lib/openjp2/jp2.c | 21 ++++------------- src/lib/openjp2/jp2.h | 13 +++++++++++ src/lib/openjp2/jpx.c | 52 +++++++++++++++++++++++++++++++++++------ 4 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/bin/jp2/opj_merge.c b/src/bin/jp2/opj_merge.c index 8706b9606..f5690e2f5 100644 --- a/src/bin/jp2/opj_merge.c +++ b/src/bin/jp2/opj_merge.c @@ -17,8 +17,13 @@ void merge_help_display(void) { fprintf(stdout, "\nThis is the opj_merge utility from the OpenJPEG project.\n" - "It creates a jpx with references to multiple local jp2 files.\n" + "It provides limited support for creating a jpx file with" + "references to multiple local jp2 files.\n" "It has been compiled against openjp2 library v%s.\n\n", opj_version()); + fprintf(stdout, + "At this time, this utility can only insert references into jpx files\n" + "it does not include all jp2 codestreams. All jp2 files must being\n" + "merged must be of the same resolution and color format.\n\n"); fprintf(stdout, "Parameters:\n"); fprintf(stdout, "-----------\n"); fprintf(stdout, "Required Parameters (except with -h):\n"); @@ -68,7 +73,7 @@ static void parse_cmdline(int argc, const char** input_file_list, OPJ_UINT32* infile_count) { - const char optlist[] = "i:o:"; + const char optlist[] = "i:o:h"; OPJ_UINT32 num_files = 0; int c; while ((c = opj_getopt(argc, argv, optlist)) != -1) { diff --git a/src/lib/openjp2/jp2.c b/src/lib/openjp2/jp2.c index 9dcc87782..34052614d 100644 --- a/src/lib/openjp2/jp2.c +++ b/src/lib/openjp2/jp2.c @@ -168,19 +168,6 @@ static OPJ_BOOL opj_jp2_read_jp2h(opj_jp2_t *jp2, OPJ_UINT32 p_header_size, opj_event_mgr_t * p_manager); -/** - * Writes the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box). - * - * @param jp2 the jpeg2000 file codec. - * @param stream the stream to write data to. - * @param p_manager user event manager. - * - * @return true if writing was successful. - */ -static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2, - opj_stream_private_t *stream, - opj_event_mgr_t * p_manager); - /** * Writes the Jpeg2000 codestream Header box - JP2C Header box. This function must be called AFTER the coding has been done. * @@ -1608,10 +1595,10 @@ OPJ_BOOL opj_jp2_decode(opj_jp2_t *jp2, return opj_jp2_apply_color_postprocessing(jp2, p_image, p_manager); } -static OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2, - opj_stream_private_t *stream, - opj_event_mgr_t * p_manager - ) +OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2, + opj_stream_private_t *stream, + opj_event_mgr_t * p_manager + ) { opj_jp2_img_header_writer_handler_t l_writers [4]; opj_jp2_img_header_writer_handler_t * l_current_writer; diff --git a/src/lib/openjp2/jp2.h b/src/lib/openjp2/jp2.h index 00e0627f2..bf14a1e13 100644 --- a/src/lib/openjp2/jp2.h +++ b/src/lib/openjp2/jp2.h @@ -540,6 +540,19 @@ OPJ_BOOL opj_jp2_write_ftyp(opj_jp2_t *jp2, opj_event_mgr_t * p_manager); +/** + * Writes the Jpeg2000 file Header box - JP2 Header box (warning, this is a super box). + * + * @param jp2 the jpeg2000 file codec. + * @param stream the stream to write data to. + * @param p_manager user event manager. + * + * @return true if writing was successful. + */ +OPJ_BOOL opj_jp2_write_jp2h(opj_jp2_t *jp2, + opj_stream_private_t *stream, + opj_event_mgr_t * p_manager); + /** * Executes the given procedures on the given codec. * diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index 97c99787b..66538d573 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -122,6 +122,9 @@ static OPJ_BOOL opj_jpx_read_box(opj_stream_private_t *l_stream, /** Frees memory allocated for the given box */ static void opj_jpx_destroy_box(box_t box); +/** Initializes options for the jp2 encoder. */ +static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, opj_event_mgr_t * p_manager); + /** * Writes data via a cursor. * Each call, cursor will be moved forward by the number of bytes written. @@ -243,6 +246,11 @@ static OPJ_BOOL opj_jpx_setup_header_writing(opj_jpx_t *jpx, return OPJ_FALSE; } + if (! opj_procedure_list_add_procedure(jpx->jp2->m_procedure_list, + (opj_procedure)opj_jp2_write_jp2h, p_manager)) { + return OPJ_FALSE; + } + /* DEVELOPER CORNER, insert your custom procedures */ return OPJ_TRUE; @@ -304,6 +312,7 @@ OPJ_BOOL opj_jpx_setup_encoder(opj_jpx_t *jpx, jpx->jp2->cl[0] = JPX_JPX; jpx->jp2->cl[1] = JP2_JP2; jpx->jp2->cl[2] = JPX_JPXB; + return OPJ_TRUE; } @@ -313,7 +322,6 @@ OPJ_BOOL opj_jpx_encoder_set_extra_options( opj_event_mgr_t * p_manager) { OPJ_UINT32 i = 0; - OPJ_UNUSED(p_manager); assert(p_jpx != 00); assert(p_options != 00); assert(p_manager != 00); @@ -321,6 +329,11 @@ OPJ_BOOL opj_jpx_encoder_set_extra_options( // Count the number of files given in the null terminated list. while (p_jpx->files[i] != NULL) { i++; } p_jpx->file_count = i; + + // Read the first jp2 file to set the remaining jp2 parameters + if (!opj_jpx_init_jp2_options(p_jpx, p_manager)) { + return OPJ_FALSE; + } return OPJ_TRUE; } @@ -341,7 +354,7 @@ opj_jpx_t* opj_jpx_create(void) return 00; } - jpx->jp2 = opj_jp2_create(OPJ_FALSE); + jpx->jp2 = opj_jp2_create(OPJ_TRUE); if (!jpx->jp2) { opj_jpx_destroy(jpx); return 00; @@ -440,9 +453,7 @@ OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, opj_jpx_cursor_write(&cursor, num_vendor_features, 2); /* Assert there was no overflow. */ - puts("Checking rreq table"); assert((cursor - rreq_data) == 21); - puts("rreq table is good"); if (opj_stream_write_data(cio, rreq_data, box_length, p_manager) != box_length) { return OPJ_FALSE; @@ -835,9 +846,7 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, /* Assert that there was no heap buffer overflow on dtbl memory. */ /* The cursor should be exactly the computed size away from the */ /* start of the reference table. */ - puts("Writing data reference table"); assert((cursor - dtbl) == dtbl_size); - puts("Data reference table is good"); if (opj_stream_write_data(cio, dtbl, dtbl_size, p_manager) != dtbl_size) { opj_event_msg(p_manager, EVT_ERROR, @@ -894,7 +903,36 @@ static OPJ_UINT32 opj_jpx_compute_urlbox_size(const char* filepath) return (OPJ_UINT32)counter; } -#ifdef WIN32 +static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, opj_event_mgr_t* p_manager) +{ + opj_stream_t* p_stream = NULL; + opj_image_t* tmp_img = opj_image_create0(); + if (!tmp_img) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to create temporary image buffer\n", jpx->files[0]); + return OPJ_FALSE; + } + + // Read the header options from the first jp2 file + assert(jpx->file_count > 0); + + p_stream = opj_stream_create_default_file_stream(jpx->files[0], OPJ_TRUE); + if (!p_stream) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to open %s for reading\n", jpx->files[0]); + return OPJ_FALSE; + } + if (!opj_jp2_read_header((opj_stream_private_t*) p_stream, jpx->jp2, &tmp_img, p_manager)) { + opj_event_msg(p_manager, EVT_ERROR, + "Failed to read header from %s\n", jpx->files[0]); + return OPJ_FALSE; + } + + opj_image_destroy(tmp_img); + return OPJ_TRUE; +} + +#ifdef _WIN32 static char* opj_jpx_get_absolute_path(const char* relative_path) { /* Maximum path length for windows */ From 5e8c619d5345712402c8ecf9519c7960f60ea762 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:47:07 -0400 Subject: [PATCH 11/14] Update help message --- src/bin/jp2/opj_merge.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/jp2/opj_merge.c b/src/bin/jp2/opj_merge.c index f5690e2f5..7abb12d75 100644 --- a/src/bin/jp2/opj_merge.c +++ b/src/bin/jp2/opj_merge.c @@ -17,13 +17,13 @@ void merge_help_display(void) { fprintf(stdout, "\nThis is the opj_merge utility from the OpenJPEG project.\n" - "It provides limited support for creating a jpx file with" + "It provides limited support for creating a jpx file with\n" "references to multiple local jp2 files.\n" "It has been compiled against openjp2 library v%s.\n\n", opj_version()); fprintf(stdout, - "At this time, this utility can only insert references into jpx files\n" - "it does not include all jp2 codestreams. All jp2 files must being\n" - "merged must be of the same resolution and color format.\n\n"); + "At this time, this utility can only insert references into jpx files.\n" + "It does not include all jp2 codestreams. All jp2 files being\n" + "merged must have the same resolution and color format.\n\n"); fprintf(stdout, "Parameters:\n"); fprintf(stdout, "-----------\n"); fprintf(stdout, "Required Parameters (except with -h):\n"); From e9dea110fbb1e15f7e320fface944ada72e77c09 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:06:07 -0400 Subject: [PATCH 12/14] Update jp2 header info after read --- src/lib/openjp2/jpx.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index 66538d573..83700403e 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -293,26 +293,6 @@ OPJ_BOOL opj_jpx_setup_encoder(opj_jpx_t *jpx, assert(jpx != 00); assert(parameters != 00); assert(p_manager != 00); - - OPJ_UNUSED(image); - - // Need to set brand in ftyp box to "jpx " - jpx->jp2->brand = JPX_JPX; - // JPX compatibility list should contain "jpx ", "jp2 ", and "jpxb" - jpx->jp2->numcl = 3; - // Allocate memory for the compatibility list. - jpx->jp2->cl = (OPJ_UINT32*) opj_malloc(jpx->jp2->numcl * sizeof(OPJ_UINT32)); - // Return failure if we couldn't allocate memory for the cl - if (!jpx->jp2->cl) { - opj_event_msg(p_manager, EVT_ERROR, - "Not enough memory when setting up the JPX encoder\n"); - return OPJ_FALSE; - } - // Assign the cl values - jpx->jp2->cl[0] = JPX_JPX; - jpx->jp2->cl[1] = JP2_JP2; - jpx->jp2->cl[2] = JPX_JPXB; - return OPJ_TRUE; } @@ -928,6 +908,27 @@ static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, opj_event_mgr_t* p_mana return OPJ_FALSE; } + /* Replace jp2 header with jpx information */ + /* Need to set brand in ftyp box to "jpx " */ + jpx->jp2->brand = JPX_JPX; + /* JPX compatibility list should contain "jpx ", "jp2 ", and "jpxb" */ + jpx->jp2->numcl = 3; + /* Allocate memory for the compatibility list. */ + if (jpx->jp2->cl) { + opj_free(jpx->jp2->cl); + } + jpx->jp2->cl = (OPJ_UINT32*) opj_malloc(jpx->jp2->numcl * sizeof(OPJ_UINT32)); + /* Return failure if we couldn't allocate memory for the cl */ + if (!jpx->jp2->cl) { + opj_event_msg(p_manager, EVT_ERROR, + "Not enough memory when setting up the JPX encoder\n"); + return OPJ_FALSE; + } + // Assign the cl values + jpx->jp2->cl[0] = JPX_JPX; + jpx->jp2->cl[1] = JP2_JP2; + jpx->jp2->cl[2] = JPX_JPXB; + opj_image_destroy(tmp_img); return OPJ_TRUE; } From aba1fc0ec1cae5aeb9fee9219367a2b770ff28be Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:16:00 -0400 Subject: [PATCH 13/14] Style updates --- src/lib/openjp2/jpx.c | 70 ++++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index 83700403e..e6338b2e9 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -123,7 +123,8 @@ static OPJ_BOOL opj_jpx_read_box(opj_stream_private_t *l_stream, static void opj_jpx_destroy_box(box_t box); /** Initializes options for the jp2 encoder. */ -static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, opj_event_mgr_t * p_manager); +static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, + opj_event_mgr_t * p_manager); /** * Writes data via a cursor. @@ -174,7 +175,7 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, // Write out the fragment table for this jp2 file. if (!opj_jpx_write_ftbl(jp2_fname, jpx, stream, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to write fragment table boxes\n"); + "Failed to write fragment table boxes\n"); return OPJ_FALSE; } } @@ -191,7 +192,7 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, // Write out associations for this jp2 file. if (!opj_jpx_write_asoc(jp2_fname, jpx, stream, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to write association boxes\n"); + "Failed to write association boxes\n"); return OPJ_FALSE; } } @@ -201,7 +202,7 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, */ if (!opj_jpx_write_dtbl(jpx, stream, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to write data reference table\n"); + "Failed to write data reference table\n"); return OPJ_FALSE; } @@ -307,7 +308,9 @@ OPJ_BOOL opj_jpx_encoder_set_extra_options( assert(p_manager != 00); p_jpx->files = p_options; // Count the number of files given in the null terminated list. - while (p_jpx->files[i] != NULL) { i++; } + while (p_jpx->files[i] != NULL) { + i++; + } p_jpx->file_count = i; // Read the first jp2 file to set the remaining jp2 parameters @@ -387,9 +390,11 @@ OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, OPJ_UINT8 display_mask = 0b00000011; OPJ_UINT16 num_flags = 2; OPJ_UINT16 flags[2] = {RREQ_FLAG_MULTILAYERED, - RREQ_FLAG_REFERENCE_LOCAL_FILES}; + RREQ_FLAG_REFERENCE_LOCAL_FILES + }; OPJ_UINT8 flag_masks[2] = {0b00000001, /* mask for multilayered */ - 0b00000010}; /* mask for local files */ + 0b00000010 + }; /* mask for local files */ OPJ_UINT16 num_vendor_features = 0; /* Above adds up to 13 bytes.Add 8 bytes for box length and box type. */ OPJ_UINT32 box_length = 21; @@ -435,7 +440,8 @@ OPJ_BOOL opj_jpx_write_rreq(opj_jp2_t *jp2, /* Assert there was no overflow. */ assert((cursor - rreq_data) == 21); - if (opj_stream_write_data(cio, rreq_data, box_length, p_manager) != box_length) { + if (opj_stream_write_data(cio, rreq_data, box_length, + p_manager) != box_length) { return OPJ_FALSE; } @@ -466,7 +472,7 @@ OPJ_BOOL opj_jpx_write_tile(opj_jpx_t *p_jpx, OPJ_UNUSED(p_data_size); OPJ_UNUSED(p_stream); opj_event_msg(p_manager, EVT_WARNING, - "JPX encoder does not support write tile. Ignoring write tile request.\n"); + "JPX encoder does not support write tile. Ignoring write tile request.\n"); return OPJ_TRUE; } @@ -485,9 +491,11 @@ static OPJ_BOOL opj_jpx_write_ftbl(const char* jp2file, OPJ_UINT32 codestream_offset; OPJ_UINT32 codestream_length; OPJ_UINT16 file_index = jpx->current_file_index; - OPJ_BOOL bSuccess = opj_jpx_find_codestream(jp2file, p_manager, &codestream_offset, &codestream_length); + OPJ_BOOL bSuccess = opj_jpx_find_codestream(jp2file, p_manager, + &codestream_offset, &codestream_length); if (!bSuccess) { - opj_event_msg(p_manager, EVT_ERROR, "Failed to find codestream information in %s\n", jp2file); + opj_event_msg(p_manager, EVT_ERROR, + "Failed to find codestream information in %s\n", jp2file); return OPJ_FALSE; } @@ -548,7 +556,7 @@ static OPJ_BOOL opj_jpx_find_box(opj_stream_t* p_stream, // Seek to the next box in the stream. if (!opj_stream_seek(l_stream, stream_position, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to seek while reading stream\n"); + "Failed to seek while reading stream\n"); return OPJ_FALSE; } @@ -557,7 +565,7 @@ static OPJ_BOOL opj_jpx_find_box(opj_stream_t* p_stream, // This probably means we reached the end of the file without // finding the desired box. opj_event_msg(p_manager, EVT_WARNING, - "Failed to read box information\n"); + "Failed to read box information\n"); return OPJ_FALSE; } @@ -571,7 +579,7 @@ static OPJ_BOOL opj_jpx_find_box(opj_stream_t* p_stream, if (!opj_stream_seek(l_stream, stream_position, p_manager)) { // If the seek fails, return false. opj_event_msg(p_manager, EVT_ERROR, - "Failed to seek while reading stream\n"); + "Failed to seek while reading stream\n"); return OPJ_FALSE; } @@ -602,7 +610,8 @@ static OPJ_BOOL opj_jpx_find_codestream(const char* jp2file, assert(codestream_length != 00); /* Create read stream for the jp2 file. */ - stream = (opj_stream_private_t*) opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); + stream = (opj_stream_private_t*) opj_stream_create_default_file_stream(jp2file, + OPJ_TRUE); if (!stream) { opj_event_msg(p_manager, EVT_ERROR, "Failed to open %s for reading\n", jp2file); @@ -611,7 +620,8 @@ static OPJ_BOOL opj_jpx_find_codestream(const char* jp2file, } // Find the codestream section of the file. - b_found_codestream = opj_jpx_find_box((opj_stream_t*) stream, p_manager, JP2_JP2C); + b_found_codestream = opj_jpx_find_box((opj_stream_t*) stream, p_manager, + JP2_JP2C); if (!b_found_codestream) { opj_event_msg(p_manager, EVT_ERROR, "Couldn't find jp2 codestream in %s\n", jp2file); @@ -646,7 +656,8 @@ static OPJ_BOOL opj_jpx_write_asoc(const char* jp2file, OPJ_BOOL b_has_xml_box = OPJ_FALSE; // Create read stream for the jp2 file. - opj_stream_t* p_stream = opj_stream_create_default_file_stream(jp2file, OPJ_TRUE); + opj_stream_t* p_stream = opj_stream_create_default_file_stream(jp2file, + OPJ_TRUE); opj_stream_private_t* l_stream = (opj_stream_private_t*) p_stream; if (!p_stream) { opj_event_msg(p_manager, EVT_ERROR, @@ -698,13 +709,13 @@ static OPJ_BOOL opj_jpx_write_asoc(const char* jp2file, /* Dump contents to stream */ if (opj_stream_write_data(cio, asoc, asoc_length, p_manager) != asoc_length) { opj_event_msg(p_manager, EVT_WARNING, - "Failed to write association contents to stream. Stream may be corrupted.\n"); + "Failed to write association contents to stream. Stream may be corrupted.\n"); } opj_free(asoc); } else { opj_event_msg(p_manager, EVT_WARNING, - "Failed to allocate memory for association box. Skipping.\n", jp2file); + "Failed to allocate memory for association box. Skipping.\n", jp2file); } opj_jpx_destroy_box(xmlbox); @@ -738,13 +749,14 @@ static OPJ_BOOL opj_jpx_read_box(opj_stream_private_t *l_stream, box->contents = opj_malloc(box->length); if (!box->contents) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to allocate memory for the box\n"); + "Failed to allocate memory for the box\n"); return OPJ_FALSE; } - if (opj_stream_read_data(l_stream, box->contents, box->length, p_manager) != box->length) { + if (opj_stream_read_data(l_stream, box->contents, box->length, + p_manager) != box->length) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to read all the box contents into memory\n"); + "Failed to read all the box contents into memory\n"); opj_free(box->contents); return OPJ_FALSE; } @@ -777,13 +789,15 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, // Compute the size of the data table. OPJ_UINT32 dtbl_size = opj_jpx_compute_dtbl_size(jpx); if (dtbl_size == UINT32_MAX) { - opj_event_msg(p_manager, EVT_ERROR, "Failed to compute the data reference table size\n"); + opj_event_msg(p_manager, EVT_ERROR, + "Failed to compute the data reference table size\n"); return OPJ_FALSE; } dtbl = opj_malloc(dtbl_size); if (!dtbl) { - opj_event_msg(p_manager, EVT_ERROR, "Failed to allocate memory for the data reference table\n"); + opj_event_msg(p_manager, EVT_ERROR, + "Failed to allocate memory for the data reference table\n"); return OPJ_FALSE; } @@ -830,7 +844,7 @@ static OPJ_BOOL opj_jpx_write_dtbl(opj_jpx_t *jpx, if (opj_stream_write_data(cio, dtbl, dtbl_size, p_manager) != dtbl_size) { opj_event_msg(p_manager, EVT_ERROR, - "Failed to write data reference table to output stream\n"); + "Failed to write data reference table to output stream\n"); opj_free(dtbl); return OPJ_FALSE; } @@ -883,7 +897,8 @@ static OPJ_UINT32 opj_jpx_compute_urlbox_size(const char* filepath) return (OPJ_UINT32)counter; } -static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, opj_event_mgr_t* p_manager) +static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, + opj_event_mgr_t* p_manager) { opj_stream_t* p_stream = NULL; opj_image_t* tmp_img = opj_image_create0(); @@ -902,7 +917,8 @@ static OPJ_BOOL opj_jpx_init_jp2_options(opj_jpx_t* jpx, opj_event_mgr_t* p_mana "Failed to open %s for reading\n", jpx->files[0]); return OPJ_FALSE; } - if (!opj_jp2_read_header((opj_stream_private_t*) p_stream, jpx->jp2, &tmp_img, p_manager)) { + if (!opj_jp2_read_header((opj_stream_private_t*) p_stream, jpx->jp2, &tmp_img, + p_manager)) { opj_event_msg(p_manager, EVT_ERROR, "Failed to read header from %s\n", jpx->files[0]); return OPJ_FALSE; From 003206fc613df876a4da3b7c4ae490881effa291 Mon Sep 17 00:00:00 2001 From: Daniel Garcia Briseno <94071409+dgarciabriseno@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:04:14 -0400 Subject: [PATCH 14/14] Remove debug puts --- src/lib/openjp2/jpx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/openjp2/jpx.c b/src/lib/openjp2/jpx.c index e6338b2e9..46d70c5a1 100644 --- a/src/lib/openjp2/jpx.c +++ b/src/lib/openjp2/jpx.c @@ -211,7 +211,6 @@ OPJ_BOOL opj_jpx_encode(opj_jpx_t *jpx, return OPJ_FALSE; } - puts("Encode successful!"); return OPJ_TRUE; } @@ -342,7 +341,6 @@ opj_jpx_t* opj_jpx_create(void) opj_jpx_destroy(jpx); return 00; } - puts("Created opj_jpx_t instance with a procedure list"); return jpx; }