From 37b81c9c801c125b4e595153b065b05b69d02744 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 17 Oct 2023 12:25:01 +0200 Subject: [PATCH 001/129] rename heif-convert to heif-dec --- examples/CMakeLists.txt | 18 +++++++++--------- examples/{heif-convert.1 => heif-dec.1} | 0 examples/{heif_convert.cc => heif_dec.cc} | 0 3 files changed, 9 insertions(+), 9 deletions(-) rename examples/{heif-convert.1 => heif-dec.1} (100%) rename examples/{heif_convert.cc => heif_dec.cc} (100%) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0a519e76c4..c255afa5a0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,19 +19,19 @@ install(TARGETS heif-info RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES heif-info.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) -add_executable(heif-convert ${getopt_sources} +add_executable(heif-dec ${getopt_sources} encoder.cc encoder.h encoder_y4m.cc encoder_y4m.h - heif_convert.cc + heif_dec.cc ../libheif/exif.cc ../libheif/exif.cc common.cc common.h) -target_link_libraries(heif-convert PRIVATE heif) -install(TARGETS heif-convert RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -install(FILES heif-convert.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +target_link_libraries(heif-dec PRIVATE heif) +install(TARGETS heif-dec RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(FILES heif-dec.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) add_executable(heif-enc ${getopt_sources} @@ -83,10 +83,10 @@ int main() { add_definitions(-DHAVE_JPEG_WRITE_ICC_PROFILE=1) endif () - target_link_libraries(heif-convert PRIVATE JPEG::JPEG) + target_link_libraries(heif-dec PRIVATE JPEG::JPEG) target_link_libraries(heif-enc PRIVATE JPEG::JPEG) - target_sources(heif-convert PRIVATE encoder_jpeg.cc encoder_jpeg.h) + target_sources(heif-dec PRIVATE encoder_jpeg.cc encoder_jpeg.h) target_sources(heif-enc PRIVATE decoder.h decoder_jpeg.cc decoder_jpeg.h) endif () @@ -95,10 +95,10 @@ find_package(PNG) if (TARGET PNG::PNG) add_definitions(-DHAVE_LIBPNG=1) - target_link_libraries(heif-convert PRIVATE PNG::PNG) + target_link_libraries(heif-dec PRIVATE PNG::PNG) target_link_libraries(heif-enc PRIVATE PNG::PNG) - target_sources(heif-convert PRIVATE encoder_png.cc encoder_png.h) + target_sources(heif-dec PRIVATE encoder_png.cc encoder_png.h) target_sources(heif-enc PRIVATE decoder_png.cc decoder_png.h) add_executable(heif-thumbnailer ${getopt_sources} diff --git a/examples/heif-convert.1 b/examples/heif-dec.1 similarity index 100% rename from examples/heif-convert.1 rename to examples/heif-dec.1 diff --git a/examples/heif_convert.cc b/examples/heif_dec.cc similarity index 100% rename from examples/heif_convert.cc rename to examples/heif_dec.cc From 100d1725269d9af42b6a803d5784eb804b46ea63 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 17 Oct 2023 12:25:52 +0200 Subject: [PATCH 002/129] install link from 'heif-convert' to new filename 'heif-dec' --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a61145b324..10745569c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -260,6 +260,9 @@ configure_file(libheif.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libheif.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libheif.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-dec${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-convert${CMAKE_EXECUTABLE_SUFFIX})") + + # --- option(WITH_EXAMPLES "Build examples" ON) From fd1d633a273d01931811e4f21c78e39d7bdca7de Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 17 Oct 2023 12:28:54 +0200 Subject: [PATCH 003/129] show correct program name in help --- examples/heif_dec.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/heif_dec.cc b/examples/heif_dec.cc index f474901798..e47cc228b0 100644 --- a/examples/heif_dec.cc +++ b/examples/heif_dec.cc @@ -67,9 +67,9 @@ static void show_help(const char* argv0) { - std::cerr << " heif-convert libheif version: " << heif_get_version() << "\n" - << "-------------------------------------------\n" - "Usage: heif-convert [options] [output-image]\n" + std::cerr << " " << argv0 << " libheif version: " << heif_get_version() << "\n" + << "---------------------------------------\n" + "Usage: " << argv0 << " [options] [output-image]\n" "\n" "The program determines the output file format from the output filename suffix.\n" "These suffixes are recognized: jpg, jpeg, png, y4m. If no output filename is specified, 'jpg' is used.\n" From 8055af74671a987cb4ad7f2621848540a7dc6c7f Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 17 Oct 2023 14:09:35 +0200 Subject: [PATCH 004/129] cmake: move creating the heif-convert symlink to after installing heif-dec (#996) --- CMakeLists.txt | 2 -- examples/CMakeLists.txt | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 10745569c8..394a484968 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -260,8 +260,6 @@ configure_file(libheif.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libheif.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libheif.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-dec${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-convert${CMAKE_EXECUTABLE_SUFFIX})") - # --- diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c255afa5a0..7d981814b1 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -33,6 +33,9 @@ target_link_libraries(heif-dec PRIVATE heif) install(TARGETS heif-dec RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES heif-dec.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +# create symbolic link from the old name `heif-convert` to `heif-dec` +install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-dec${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-convert${CMAKE_EXECUTABLE_SUFFIX})") + add_executable(heif-enc ${getopt_sources} heif_enc.cc From 87815a5bc0f3fbab8ec7877061afe605510307a5 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 18 Oct 2023 11:06:42 +0200 Subject: [PATCH 005/129] On Windows, copy heic-dec to heic-convert instead of using symlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Miloš Komarčević <4973094+kmilos@users.noreply.github.com> --- examples/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7d981814b1..86556dd19b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -34,7 +34,11 @@ install(TARGETS heif-dec RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES heif-dec.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # create symbolic link from the old name `heif-convert` to `heif-dec` -install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-dec${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-convert${CMAKE_EXECUTABLE_SUFFIX})") +if(NOT WIN32) + install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-dec${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-convert${CMAKE_EXECUTABLE_SUFFIX})") +else() + install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-dec${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/heif-convert${CMAKE_EXECUTABLE_SUFFIX})") +endif() add_executable(heif-enc ${getopt_sources} From c4401efee9ffc0557c7215d42a487fceb4dc4f05 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 18 Oct 2023 11:10:11 +0200 Subject: [PATCH 006/129] rename heif-convert to heif-dec in manpage --- README.md | 4 ++-- examples/heif-dec.1 | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c2bc757a4f..076102572b 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,7 @@ This is `libheif` running in JavaScript in your browser. ## Example programs Some example programs are provided in the `examples` directory. -The program `heif-convert` converts all images stored in an HEIF/AVIF file to JPEG or PNG. +The program `heif-dec` converts all images stored in an HEIF/AVIF file to JPEG or PNG. `heif-enc` lets you convert JPEG files to HEIF/AVIF. The program `heif-info` is a simple, minimal decoder that dumps the file structure to the console. @@ -313,7 +313,7 @@ For example convert `example.heic` to JPEGs and one of the JPEGs back to HEIF: ```sh cd examples/ -./heif-convert example.heic example.jpeg +./heif-dec example.heic example.jpeg ./heif-enc example-1.jpeg -o example.heif ``` diff --git a/examples/heif-dec.1 b/examples/heif-dec.1 index 171acbbc78..6bc57858bd 100644 --- a/examples/heif-dec.1 +++ b/examples/heif-dec.1 @@ -1,13 +1,13 @@ .TH HEIF-THUMBNAILER 1 .SH NAME -heif-convert \- convert HEIC/HEIF image +heif-dec \- decode HEIC/HEIF image .SH SYNOPSIS -.B heif-convert +.B heif-dec [\fB\-q\fR \fIQUALITY\fR] .IR filename .IR output[.jpg|.png|.y4m] .SH DESCRIPTION -.B heif-convert +.B heif-dec Convert HEIC/HEIF image to a different image format. .SH OPTIONS .TP From 7b7c0f2fc3efadcfbee27d1e52c66ee5f0be498f Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 18 Oct 2023 11:10:55 +0200 Subject: [PATCH 007/129] rename heif-convert to heif-dec in CI script --- scripts/run-ci.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/run-ci.sh b/scripts/run-ci.sh index f28e753055..b2a5b267e5 100755 --- a/scripts/run-ci.sh +++ b/scripts/run-ci.sh @@ -163,19 +163,19 @@ if [ -z "$EMSCRIPTEN_VERSION" ] && [ -z "$CHECK_LICENSES" ] && [ -z "$TARBALL" ] ${BIN_WRAPPER} ./examples/heif-enc${BIN_SUFFIX} --list-encoders echo "List available decoders" - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} --list-decoders + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} --list-decoders echo "Dumping information of sample file ..." ${BIN_WRAPPER} ./examples/heif-info${BIN_SUFFIX} --dump-boxes examples/example.heic if [ ! -z "$WITH_GRAPHICS" ] && [ ! -z "$WITH_HEIF_DECODER" ]; then echo "Converting sample HEIF file to JPEG ..." - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} examples/example.heic example.jpg + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} examples/example.heic example.jpg echo "Checking first generated file ..." [ -s "example-1.jpg" ] || exit 1 echo "Checking second generated file ..." [ -s "example-2.jpg" ] || exit 1 echo "Converting sample HEIF file to PNG ..." - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} examples/example.heic example.png + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} examples/example.heic example.png echo "Checking first generated file ..." [ -s "example-1.png" ] || exit 1 echo "Checking second generated file ..." @@ -183,11 +183,11 @@ if [ -z "$EMSCRIPTEN_VERSION" ] && [ -z "$CHECK_LICENSES" ] && [ -z "$TARBALL" ] fi if [ ! -z "$WITH_GRAPHICS" ] && [ ! -z "$WITH_AVIF_DECODER" ]; then echo "Converting sample AVIF file to JPEG ..." - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} examples/example.avif example.jpg + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} examples/example.avif example.jpg echo "Checking generated file ..." [ -s "example.jpg" ] || exit 1 echo "Converting sample AVIF file to PNG ..." - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} examples/example.avif example.png + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} examples/example.avif example.png echo "Checking generated file ..." [ -s "example.png" ] || exit 1 fi @@ -197,14 +197,14 @@ if [ -z "$EMSCRIPTEN_VERSION" ] && [ -z "$CHECK_LICENSES" ] && [ -z "$TARBALL" ] echo "Checking generated file ..." [ -s "output-single.heic" ] || exit 1 echo "Converting back generated heif to JPEG ..." - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} output-single.heic output-single.jpg + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} output-single.heic output-single.jpg echo "Checking generated file ..." [ -s "output-single.jpg" ] || exit 1 echo "Converting multiple JPEG files to heif ..." ${BIN_WRAPPER} ./examples/heif-enc${BIN_SUFFIX} -o output-multi.heic --verbose --verbose --verbose --thumb 320x240 example-1.jpg example-2.jpg echo "Checking generated file ..." [ -s "output-multi.heic" ] || exit 1 - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} output-multi.heic output-multi.jpg + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} output-multi.heic output-multi.jpg echo "Checking first generated file ..." [ -s "output-multi-1.jpg" ] || exit 1 echo "Checking second generated file ..." @@ -216,7 +216,7 @@ if [ -z "$EMSCRIPTEN_VERSION" ] && [ -z "$CHECK_LICENSES" ] && [ -z "$TARBALL" ] echo "Checking generated file ..." [ -s "output-jpeg.avif" ] || exit 1 echo "Converting back generated AVIF to JPEG ..." - ${BIN_WRAPPER} ./examples/heif-convert${BIN_SUFFIX} output-jpeg.avif output-jpeg.jpg + ${BIN_WRAPPER} ./examples/heif-dec${BIN_SUFFIX} output-jpeg.avif output-jpeg.jpg echo "Checking generated file ..." [ -s "output-jpeg.jpg" ] || exit 1 fi From 30b696bd36d1414f9b409a1d98d4792b23f81c6e Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 18 Oct 2023 11:39:30 +0200 Subject: [PATCH 008/129] fix parse error reporting --- libheif/file.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libheif/file.cc b/libheif/file.cc index 0a7e4e7722..6de322bfdf 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -253,14 +253,19 @@ Error HeifFile::parse_heif_file(BitstreamRange& range) std::shared_ptr box; Error error = Box::read(range, &box); + if (range.error() || range.eof()) { + break; + } + // When an EOF error is returned, this is not really a fatal exception, // but simply the indication that we reached the end of the file. - if (error != Error::Ok) { - return error; + // TODO: this design should be cleaned up + if (error.error_code == heif_error_Invalid_input && error.sub_error_code == heif_suberror_End_of_data) { + break; } - if (range.error() || range.eof()) { - break; + if (error != Error::Ok) { + return error; } m_top_level_boxes.push_back(box); From 47cde928cc955180b40c9a40d438c947bd5b0e7d Mon Sep 17 00:00:00 2001 From: dukesook Date: Fri, 31 May 2024 06:43:53 -0600 Subject: [PATCH 009/129] heif_context_encode_grid_image() --- libheif/context.cc | 60 ++++++++++++++++++++++++++++++++++++++++ libheif/context.h | 8 ++++++ libheif/heif.cc | 68 ++++++++++++++++++++++++++++++++++++++++++++++ libheif/heif.h | 9 ++++++ 4 files changed, 145 insertions(+) diff --git a/libheif/context.cc b/libheif/context.cc index c6001ef45f..65f5300853 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -2314,6 +2314,66 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima return error; } +Error HeifContext::encode_grid_image(const std::vector>& pixel_images, + uint16_t rows, + uint16_t columns, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class, + std::shared_ptr& out_grid_image) +{ + Error error; + + // TODO: the hdlr box is not the right place for comments + // m_heif_file->set_hdlr_library_info(encoder->plugin->get_plugin_name()); + + int tile_width = pixel_images[0]->get_width(heif_channel_interleaved); + int tile_height = pixel_images[0]->get_height(heif_channel_interleaved); + + ImageGrid grid; + grid.set_num_tiles(columns, rows); + grid.set_output_size(tile_width * columns, tile_height * rows); + std::vector grid_data = grid.write(); + + std::vector image_ids; + + for (int i=0; i out_image; + + error = encode_image(pixel_images[i], + encoder, + options, + heif_image_input_class_normal, + out_image); + + heif_item_id image_id = out_image->get_id(); + + // hide each individual image, only the full grid should be shown. + m_heif_file->get_infe_box(image_id)->set_hidden_item(true); + + image_ids.push_back(out_image->get_id()); + } + + // Add Grid + heif_item_id grid_image_id = m_heif_file->add_new_image("grid"); + out_grid_image = std::make_shared(this, grid_image_id); + m_heif_file->add_iref_reference(grid_image_id, fourcc("dimg"), image_ids); + m_heif_file->append_iloc_data(grid_image_id, grid_data, 1); + + // Add ISPE property + int image_width = tile_width * columns; + int image_height = tile_height * rows; + m_heif_file->add_ispe_property(grid_image_id, image_width, image_height); + + m_heif_file->set_brand(encoder->plugin->compression_format, + out_grid_image->is_miaf_compatible()); + + m_all_images.insert(std::make_pair(grid_image_id, out_grid_image)); + + return error; +} + + /* static uint32_t get_rotated_width(heif_orientation orientation, uint32_t w, uint32_t h) { diff --git a/libheif/context.h b/libheif/context.h index 6e88d85022..502aa39825 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -390,6 +390,14 @@ class HeifContext : public ErrorBuffer enum heif_image_input_class input_class, std::shared_ptr& out_image); + Error encode_grid_image(const std::vector>& images, + uint16_t rows, + uint16_t columns, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class, + std::shared_ptr& out_image); + Error encode_image_as_hevc(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, diff --git a/libheif/heif.cc b/libheif/heif.cc index 4745d7035b..e89afa48a3 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -2707,6 +2707,74 @@ struct heif_error heif_context_encode_image(struct heif_context* ctx, } +struct heif_error heif_context_encode_grid_image(struct heif_context* ctx, + struct heif_image** input_images, // array of tile images + uint16_t columns, + uint16_t rows, + struct heif_encoder* encoder, + const struct heif_encoding_options* input_options, + struct heif_image_handle** out_image_handle) +{ + if (!encoder) { + return Error(heif_error_Usage_error, + heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); + } + + heif_encoding_options options; + heif_color_profile_nclx nclx; + if (input_options == nullptr) { + set_default_options(options); + } + else { + copy_options(options, *input_options); + + if (options.output_nclx_profile == nullptr) { + auto input_nclx = input_images[0]->image->get_color_profile_nclx(); + if (input_nclx) { + options.output_nclx_profile = &nclx; + nclx.version = 1; + nclx.color_primaries = (enum heif_color_primaries)input_nclx->get_colour_primaries(); + nclx.transfer_characteristics = (enum heif_transfer_characteristics)input_nclx->get_transfer_characteristics(); + nclx.matrix_coefficients = (enum heif_matrix_coefficients)input_nclx->get_matrix_coefficients(); + nclx.full_range_flag = input_nclx->get_full_range_flag(); + } + } + } + + std::shared_ptr image; + Error error; + + std::vector> pixel_images; + for (int i=0; iimage); + } + + error = ctx->context->encode_grid_image(pixel_images, + rows, columns, + encoder, + options, + heif_image_input_class_normal, + image); + if (error != Error::Ok) { + return error.error_struct(ctx->context.get()); + } + + // mark the new image as primary image + + if (ctx->context->is_primary_image_set() == false) { + ctx->context->set_primary_image(image); + } + + if (out_image_handle) { + *out_image_handle = new heif_image_handle; + (*out_image_handle)->image = image; + (*out_image_handle)->context = ctx->context; + } + + return heif_error_success; +} + + struct heif_error heif_context_assign_thumbnail(struct heif_context* ctx, const struct heif_image_handle* master_image, const struct heif_image_handle* thumbnail_image) diff --git a/libheif/heif.h b/libheif/heif.h index 2c65a01366..bee671b81f 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -2037,6 +2037,15 @@ struct heif_error heif_context_encode_image(struct heif_context*, const struct heif_encoding_options* options, struct heif_image_handle** out_image_handle); +LIBHEIF_API +struct heif_error heif_context_encode_grid_image(struct heif_context* ctx, + struct heif_image** input_images, // array of tile images + uint16_t rows, + uint16_t columns, + struct heif_encoder* encoder, + const struct heif_encoding_options* input_options, + struct heif_image_handle** out_image_handle); + LIBHEIF_API struct heif_error heif_context_set_primary_image(struct heif_context*, struct heif_image_handle* image_handle); From 6cf978c0b5932165c9080e02ead278e3d504f214 Mon Sep 17 00:00:00 2001 From: dukesook Date: Fri, 31 May 2024 07:37:27 -0600 Subject: [PATCH 010/129] renamed several variables --- libheif/context.cc | 51 +++++++++++++++++++++------------------------- libheif/context.h | 13 ++++++------ libheif/heif.cc | 37 ++++++++++++++++----------------- libheif/heif.h | 14 ++++++------- 4 files changed, 53 insertions(+), 62 deletions(-) diff --git a/libheif/context.cc b/libheif/context.cc index 65f5300853..95fa996a5c 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -2314,19 +2314,15 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima return error; } -Error HeifContext::encode_grid_image(const std::vector>& pixel_images, - uint16_t rows, - uint16_t columns, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class, - std::shared_ptr& out_grid_image) +Error HeifContext::encode_grid(const std::vector>& pixel_images, + uint16_t rows, + uint16_t columns, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + std::shared_ptr& out_grid_image) { Error error; - // TODO: the hdlr box is not the right place for comments - // m_heif_file->set_hdlr_library_info(encoder->plugin->get_plugin_name()); - int tile_width = pixel_images[0]->get_width(heif_channel_interleaved); int tile_height = pixel_images[0]->get_height(heif_channel_interleaved); @@ -2337,39 +2333,38 @@ Error HeifContext::encode_grid_image(const std::vector image_ids; + // Encode Tiles for (int i=0; i out_image; - + std::shared_ptr out_tile; error = encode_image(pixel_images[i], encoder, options, heif_image_input_class_normal, - out_image); - - heif_item_id image_id = out_image->get_id(); - - // hide each individual image, only the full grid should be shown. - m_heif_file->get_infe_box(image_id)->set_hidden_item(true); - - image_ids.push_back(out_image->get_id()); + out_tile); + heif_item_id tile_id = out_tile->get_id(); + m_heif_file->get_infe_box(tile_id)->set_hidden_item(true); // only show the full grid + image_ids.push_back(out_tile->get_id()); } - // Add Grid - heif_item_id grid_image_id = m_heif_file->add_new_image("grid"); - out_grid_image = std::make_shared(this, grid_image_id); - m_heif_file->add_iref_reference(grid_image_id, fourcc("dimg"), image_ids); - m_heif_file->append_iloc_data(grid_image_id, grid_data, 1); + // Add Grid Item + heif_item_id grid_id = m_heif_file->add_new_image("grid"); + const int construction_method = 1; // 0=mdat 1=idat + m_heif_file->append_iloc_data(grid_id, grid_data, construction_method); + out_grid_image = std::make_shared(this, grid_id); + m_all_images.insert(std::make_pair(grid_id, out_grid_image)); + + // Connect tiles to grid + m_heif_file->add_iref_reference(grid_id, fourcc("dimg"), image_ids); // Add ISPE property int image_width = tile_width * columns; int image_height = tile_height * rows; - m_heif_file->add_ispe_property(grid_image_id, image_width, image_height); + m_heif_file->add_ispe_property(grid_id, image_width, image_height); + // Set Brands m_heif_file->set_brand(encoder->plugin->compression_format, out_grid_image->is_miaf_compatible()); - m_all_images.insert(std::make_pair(grid_image_id, out_grid_image)); - return error; } diff --git a/libheif/context.h b/libheif/context.h index 502aa39825..cd83817cb5 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -390,13 +390,12 @@ class HeifContext : public ErrorBuffer enum heif_image_input_class input_class, std::shared_ptr& out_image); - Error encode_grid_image(const std::vector>& images, - uint16_t rows, - uint16_t columns, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class, - std::shared_ptr& out_image); + Error encode_grid(const std::vector>& images, + uint16_t rows, + uint16_t columns, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + std::shared_ptr& out_image); Error encode_image_as_hevc(const std::shared_ptr& image, struct heif_encoder* encoder, diff --git a/libheif/heif.cc b/libheif/heif.cc index e89afa48a3..b99bfd8a33 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -2707,13 +2707,13 @@ struct heif_error heif_context_encode_image(struct heif_context* ctx, } -struct heif_error heif_context_encode_grid_image(struct heif_context* ctx, - struct heif_image** input_images, // array of tile images - uint16_t columns, - uint16_t rows, - struct heif_encoder* encoder, - const struct heif_encoding_options* input_options, - struct heif_image_handle** out_image_handle) +struct heif_error heif_context_encode_grid(struct heif_context* ctx, + struct heif_image** input_images, // array of tile images + uint16_t columns, + uint16_t rows, + struct heif_encoder* encoder, + const struct heif_encoding_options* input_options, + struct heif_image_handle** out_image_handle) { if (!encoder) { return Error(heif_error_Usage_error, @@ -2722,10 +2722,8 @@ struct heif_error heif_context_encode_grid_image(struct heif_context* ctx, heif_encoding_options options; heif_color_profile_nclx nclx; - if (input_options == nullptr) { - set_default_options(options); - } - else { + set_default_options(options); + if (input_options) { copy_options(options, *input_options); if (options.output_nclx_profile == nullptr) { @@ -2733,9 +2731,9 @@ struct heif_error heif_context_encode_grid_image(struct heif_context* ctx, if (input_nclx) { options.output_nclx_profile = &nclx; nclx.version = 1; - nclx.color_primaries = (enum heif_color_primaries)input_nclx->get_colour_primaries(); - nclx.transfer_characteristics = (enum heif_transfer_characteristics)input_nclx->get_transfer_characteristics(); - nclx.matrix_coefficients = (enum heif_matrix_coefficients)input_nclx->get_matrix_coefficients(); + nclx.color_primaries = (enum heif_color_primaries) input_nclx->get_colour_primaries(); + nclx.transfer_characteristics = (enum heif_transfer_characteristics) input_nclx->get_transfer_characteristics(); + nclx.matrix_coefficients = (enum heif_matrix_coefficients) input_nclx->get_matrix_coefficients(); nclx.full_range_flag = input_nclx->get_full_range_flag(); } } @@ -2749,12 +2747,11 @@ struct heif_error heif_context_encode_grid_image(struct heif_context* ctx, pixel_images.push_back(input_images[i]->image); } - error = ctx->context->encode_grid_image(pixel_images, - rows, columns, - encoder, - options, - heif_image_input_class_normal, - image); + error = ctx->context->encode_grid(pixel_images, + rows, columns, + encoder, + options, + image); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } diff --git a/libheif/heif.h b/libheif/heif.h index bee671b81f..669f93b807 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -2038,13 +2038,13 @@ struct heif_error heif_context_encode_image(struct heif_context*, struct heif_image_handle** out_image_handle); LIBHEIF_API -struct heif_error heif_context_encode_grid_image(struct heif_context* ctx, - struct heif_image** input_images, // array of tile images - uint16_t rows, - uint16_t columns, - struct heif_encoder* encoder, - const struct heif_encoding_options* input_options, - struct heif_image_handle** out_image_handle); +struct heif_error heif_context_encode_grid(struct heif_context* ctx, + struct heif_image** input_images, // array of tile images + uint16_t rows, + uint16_t columns, + struct heif_encoder* encoder, + const struct heif_encoding_options* input_options, + struct heif_image_handle** out_image_handle); LIBHEIF_API struct heif_error heif_context_set_primary_image(struct heif_context*, From fce23a65347151341355189cdb9c14a7bd359485 Mon Sep 17 00:00:00 2001 From: dukesook Date: Fri, 31 May 2024 07:49:21 -0600 Subject: [PATCH 011/129] document function --- libheif/heif.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libheif/heif.h b/libheif/heif.h index 669f93b807..0273b9a191 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -2037,9 +2037,21 @@ struct heif_error heif_context_encode_image(struct heif_context*, const struct heif_encoding_options* options, struct heif_image_handle** out_image_handle); +/** + * @brief Encodes an array of images into a grid. + * + * @param ctx The file context + * @param input_images User allocated array of tile images. + * @param rows The number of rows in the grid. + * @param columns The number of columns in the grid. + * @param encoder Defines the encoder to use. See heif_context_get_encoder_for_format() + * @param input_options Optional, may be nullptr. + * @param out_image_handle + * @return struct heif_error + */ LIBHEIF_API struct heif_error heif_context_encode_grid(struct heif_context* ctx, - struct heif_image** input_images, // array of tile images + struct heif_image** input_images, uint16_t rows, uint16_t columns, struct heif_encoder* encoder, From bdd6380b7354e6bbff796473ea931f8f7d79e513 Mon Sep 17 00:00:00 2001 From: dukesook Date: Fri, 31 May 2024 08:09:53 -0600 Subject: [PATCH 012/129] misc cleanup --- libheif/heif.cc | 27 ++++++++++++++------------- libheif/heif.h | 6 +++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/libheif/heif.cc b/libheif/heif.cc index b99bfd8a33..99942e41a1 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -2708,7 +2708,7 @@ struct heif_error heif_context_encode_image(struct heif_context* ctx, struct heif_error heif_context_encode_grid(struct heif_context* ctx, - struct heif_image** input_images, // array of tile images + struct heif_image** tiles, // array of tile images uint16_t columns, uint16_t rows, struct heif_encoder* encoder, @@ -2720,6 +2720,7 @@ struct heif_error heif_context_encode_grid(struct heif_context* ctx, heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); } + // TODO: Don't repeat this code from heif_context_encode_image() heif_encoding_options options; heif_color_profile_nclx nclx; set_default_options(options); @@ -2727,7 +2728,7 @@ struct heif_error heif_context_encode_grid(struct heif_context* ctx, copy_options(options, *input_options); if (options.output_nclx_profile == nullptr) { - auto input_nclx = input_images[0]->image->get_color_profile_nclx(); + auto input_nclx = tiles[0]->image->get_color_profile_nclx(); if (input_nclx) { options.output_nclx_profile = &nclx; nclx.version = 1; @@ -2739,32 +2740,32 @@ struct heif_error heif_context_encode_grid(struct heif_context* ctx, } } - std::shared_ptr image; - Error error; - - std::vector> pixel_images; + // Convert heif_images to a vector of HeifPixelImages + std::vector> pixel_tiles; for (int i=0; iimage); + pixel_tiles.push_back(tiles[i]->image); } - error = ctx->context->encode_grid(pixel_images, + // Encode Grid + Error error; + std::shared_ptr out_grid; + error = ctx->context->encode_grid(pixel_tiles, rows, columns, encoder, options, - image); + out_grid); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } - // mark the new image as primary image - + // Mark as primary image if (ctx->context->is_primary_image_set() == false) { - ctx->context->set_primary_image(image); + ctx->context->set_primary_image(out_grid); } if (out_image_handle) { *out_image_handle = new heif_image_handle; - (*out_image_handle)->image = image; + (*out_image_handle)->image = out_grid; (*out_image_handle)->context = ctx->context; } diff --git a/libheif/heif.h b/libheif/heif.h index 0273b9a191..0a33a3fb66 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -2041,17 +2041,17 @@ struct heif_error heif_context_encode_image(struct heif_context*, * @brief Encodes an array of images into a grid. * * @param ctx The file context - * @param input_images User allocated array of tile images. + * @param tiles User allocated array of images that will form the grid. * @param rows The number of rows in the grid. * @param columns The number of columns in the grid. * @param encoder Defines the encoder to use. See heif_context_get_encoder_for_format() * @param input_options Optional, may be nullptr. - * @param out_image_handle + * @param out_image_handle Returns a handle to the grid. The caller is responsible for freeing it. * @return struct heif_error */ LIBHEIF_API struct heif_error heif_context_encode_grid(struct heif_context* ctx, - struct heif_image** input_images, + struct heif_image** tiles, uint16_t rows, uint16_t columns, struct heif_encoder* encoder, From b2fa3522584d9ed1b433549fe86e13e5e95d0497 Mon Sep 17 00:00:00 2001 From: dukesook Date: Fri, 31 May 2024 08:16:16 -0600 Subject: [PATCH 013/129] Better error checking --- libheif/heif.cc | 8 ++++++-- libheif/heif.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libheif/heif.cc b/libheif/heif.cc index 99942e41a1..ceadac1f45 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -2708,17 +2708,21 @@ struct heif_error heif_context_encode_image(struct heif_context* ctx, struct heif_error heif_context_encode_grid(struct heif_context* ctx, - struct heif_image** tiles, // array of tile images + struct heif_image** tiles, uint16_t columns, uint16_t rows, struct heif_encoder* encoder, const struct heif_encoding_options* input_options, struct heif_image_handle** out_image_handle) { - if (!encoder) { + if (!encoder || !tiles) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); } + else if (rows == 0 || columns == 0) { + return Error(heif_error_Usage_error, + heif_suberror_Invalid_parameter_value).error_struct(ctx->context.get()); + } // TODO: Don't repeat this code from heif_context_encode_image() heif_encoding_options options; diff --git a/libheif/heif.h b/libheif/heif.h index 0a33a3fb66..2f20868187 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -2047,7 +2047,7 @@ struct heif_error heif_context_encode_image(struct heif_context*, * @param encoder Defines the encoder to use. See heif_context_get_encoder_for_format() * @param input_options Optional, may be nullptr. * @param out_image_handle Returns a handle to the grid. The caller is responsible for freeing it. - * @return struct heif_error + * @return Returns an error if ctx, tiles, or encoder is nullptr. If rows or columns is 0. */ LIBHEIF_API struct heif_error heif_context_encode_grid(struct heif_context* ctx, From e35c59907f148ec6e1c7e473da23fdee29e49368 Mon Sep 17 00:00:00 2001 From: dukesook Date: Fri, 31 May 2024 08:21:20 -0600 Subject: [PATCH 014/129] additional minor cleanup --- libheif/context.cc | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/libheif/context.cc b/libheif/context.cc index 95fa996a5c..5da1d09213 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -2314,47 +2314,45 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima return error; } -Error HeifContext::encode_grid(const std::vector>& pixel_images, +Error HeifContext::encode_grid(const std::vector>& tiles, uint16_t rows, uint16_t columns, struct heif_encoder* encoder, const struct heif_encoding_options& options, std::shared_ptr& out_grid_image) { - Error error; - - int tile_width = pixel_images[0]->get_width(heif_channel_interleaved); - int tile_height = pixel_images[0]->get_height(heif_channel_interleaved); - + // Create ImageGrid ImageGrid grid; grid.set_num_tiles(columns, rows); + int tile_width = tiles[0]->get_width(heif_channel_interleaved); + int tile_height = tiles[0]->get_height(heif_channel_interleaved); grid.set_output_size(tile_width * columns, tile_height * rows); std::vector grid_data = grid.write(); - std::vector image_ids; - // Encode Tiles + Error error; + std::vector tile_ids; for (int i=0; i out_tile; - error = encode_image(pixel_images[i], - encoder, - options, - heif_image_input_class_normal, - out_tile); + error = encode_image(tiles[i], + encoder, + options, + heif_image_input_class_normal, + out_tile); heif_item_id tile_id = out_tile->get_id(); m_heif_file->get_infe_box(tile_id)->set_hidden_item(true); // only show the full grid - image_ids.push_back(out_tile->get_id()); + tile_ids.push_back(out_tile->get_id()); } - // Add Grid Item + // Create Grid Item heif_item_id grid_id = m_heif_file->add_new_image("grid"); - const int construction_method = 1; // 0=mdat 1=idat - m_heif_file->append_iloc_data(grid_id, grid_data, construction_method); out_grid_image = std::make_shared(this, grid_id); m_all_images.insert(std::make_pair(grid_id, out_grid_image)); + const int construction_method = 1; // 0=mdat 1=idat + m_heif_file->append_iloc_data(grid_id, grid_data, construction_method); // Connect tiles to grid - m_heif_file->add_iref_reference(grid_id, fourcc("dimg"), image_ids); + m_heif_file->add_iref_reference(grid_id, fourcc("dimg"), tile_ids); // Add ISPE property int image_width = tile_width * columns; From 1392198b30a6553493f87dcd98885c99b207db70 Mon Sep 17 00:00:00 2001 From: dukesook Date: Fri, 31 May 2024 08:28:45 -0600 Subject: [PATCH 015/129] use 'tiles' instead of 'images' The name tile is more specific as it implies that it's part of a grid --- libheif/context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libheif/context.h b/libheif/context.h index cd83817cb5..8529ae90c1 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -390,7 +390,7 @@ class HeifContext : public ErrorBuffer enum heif_image_input_class input_class, std::shared_ptr& out_image); - Error encode_grid(const std::vector>& images, + Error encode_grid(const std::vector>& tiles, uint16_t rows, uint16_t columns, struct heif_encoder* encoder, From 4e803dfdfaab8dd72ca47a106e052fd3e7687951 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 11 Jun 2024 11:56:57 +0200 Subject: [PATCH 016/129] revert whitespace-only diff --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 394a484968..a61145b324 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -260,7 +260,6 @@ configure_file(libheif.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libheif.pc @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libheif.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) - # --- option(WITH_EXAMPLES "Build examples" ON) From 37d5102052c3cc4ea35ea4901ab144f881569f62 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 11 Jun 2024 12:05:45 +0200 Subject: [PATCH 017/129] Revert "fix parse error reporting" as it does not belong into this PR. This reverts commit 30b696bd36d1414f9b409a1d98d4792b23f81c6e. --- libheif/file.cc | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libheif/file.cc b/libheif/file.cc index 6de322bfdf..0a7e4e7722 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -253,21 +253,16 @@ Error HeifFile::parse_heif_file(BitstreamRange& range) std::shared_ptr box; Error error = Box::read(range, &box); - if (range.error() || range.eof()) { - break; - } - // When an EOF error is returned, this is not really a fatal exception, // but simply the indication that we reached the end of the file. - // TODO: this design should be cleaned up - if (error.error_code == heif_error_Invalid_input && error.sub_error_code == heif_suberror_End_of_data) { - break; - } - if (error != Error::Ok) { return error; } + if (range.error() || range.eof()) { + break; + } + m_top_level_boxes.push_back(box); From 1468e7d75a8b5e57d8cf3b7646e875f54379e81f Mon Sep 17 00:00:00 2001 From: Wan-Teh Chang Date: Wed, 12 Jun 2024 09:29:07 -0700 Subject: [PATCH 018/129] Update to the dav1d 1.4.3 release --- third-party/dav1d.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/dav1d.cmd b/third-party/dav1d.cmd index e546222bd9..0c238d8a5b 100644 --- a/third-party/dav1d.cmd +++ b/third-party/dav1d.cmd @@ -10,7 +10,7 @@ : # If you're running this on Windows, be sure you've already run this (from your VC2019 install dir): : # "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Auxiliary\Build\vcvars64.bat" -git clone -b 1.4.1 --depth 1 https://code.videolan.org/videolan/dav1d.git +git clone -b 1.4.3 --depth 1 https://code.videolan.org/videolan/dav1d.git cd dav1d From 5885b4d6c8de63d8ed73a6a78f220cf6f328b646 Mon Sep 17 00:00:00 2001 From: Victor Loh Date: Mon, 24 Jun 2024 17:17:24 -0700 Subject: [PATCH 019/129] [ffmpeg plugin] Add more chroma subsampling support In this patch, support for mono, 422, 444 are added. --- libheif/plugins/decoder_ffmpeg.cc | 70 +++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/libheif/plugins/decoder_ffmpeg.cc b/libheif/plugins/decoder_ffmpeg.cc index 04fa255d80..81ab2b93d7 100644 --- a/libheif/plugins/decoder_ffmpeg.cc +++ b/libheif/plugins/decoder_ffmpeg.cc @@ -187,6 +187,60 @@ static struct heif_error ffmpeg_v1_push_data(void* decoder_raw, const void* data return heif_error_success; } +static heif_chroma ffmpeg_get_chroma_format(enum AVPixelFormat pix_fmt) { + if (pix_fmt == AV_PIX_FMT_GRAY8) + { + return heif_chroma_monochrome; + } + else if ((pix_fmt == AV_PIX_FMT_YUV420P) || (pix_fmt == AV_PIX_FMT_YUVJ420P) || + (pix_fmt == AV_PIX_FMT_YUV420P10LE)) + { + return heif_chroma_420; + } + else if (pix_fmt == AV_PIX_FMT_YUV422P) + { + return heif_chroma_422; + } + else if (pix_fmt == AV_PIX_FMT_YUV444P) + { + return heif_chroma_444; + } + // Unsupported pix_fmt + return heif_chroma_undefined; +} + +static int ffmpeg_get_chroma_width(const AVFrame* frame, heif_channel channel, heif_chroma chroma) +{ + if (channel == heif_channel_Y) + { + return frame->width; + } + else if (chroma == heif_chroma_420 || chroma == heif_chroma_422) + { + return (frame->width) / 2; + } + else + { + return frame->width; + } +} + +static int ffmpeg_get_chroma_height(const AVFrame* frame, heif_channel channel, heif_chroma chroma) +{ + if (channel == heif_channel_Y) + { + return frame->height; + } + else if (chroma == heif_chroma_420) + { + return (frame->height) / 2; + } + else + { + return frame->height; + } +} + static struct heif_error hevc_decode(AVCodecContext* hevc_dec_ctx, AVFrame* hevc_frame, AVPacket* hevc_pkt, struct heif_image** image) { int ret; @@ -209,14 +263,16 @@ static struct heif_error hevc_decode(AVCodecContext* hevc_dec_ctx, AVFrame* hevc } - if ((hevc_dec_ctx->pix_fmt == AV_PIX_FMT_YUV420P) || (hevc_dec_ctx->pix_fmt == AV_PIX_FMT_YUVJ420P) || //planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) - (hevc_dec_ctx->pix_fmt == AV_PIX_FMT_YUV420P10LE)) + heif_chroma chroma = ffmpeg_get_chroma_format(hevc_dec_ctx->pix_fmt); + if (chroma != heif_chroma_undefined) { + bool is_mono = (chroma == heif_chroma_monochrome); + heif_error err; err = heif_image_create(hevc_frame->width, hevc_frame->height, - heif_colorspace_YCbCr, - heif_chroma_420, + is_mono ? heif_colorspace_monochrome : heif_colorspace_YCbCr, + chroma, image); if (err.code) { return err; @@ -228,7 +284,7 @@ static struct heif_error hevc_decode(AVCodecContext* hevc_dec_ctx, AVFrame* hevc heif_channel_Cr }; - int nPlanes = 3; + int nPlanes = is_mono ? 1 : 3; for (int channel = 0; channel < nPlanes; channel++) { @@ -236,8 +292,8 @@ static struct heif_error hevc_decode(AVCodecContext* hevc_dec_ctx, AVFrame* hevc int stride = hevc_frame->linesize[channel]; const uint8_t* data = hevc_frame->data[channel]; - int w = (channel == 0) ? hevc_frame->width : hevc_frame->width >> 1; - int h = (channel == 0) ? hevc_frame->height : hevc_frame->height >> 1; + int w = ffmpeg_get_chroma_width(hevc_frame, channel2plane[channel], chroma); + int h = ffmpeg_get_chroma_height(hevc_frame, channel2plane[channel], chroma); if (w <= 0 || h <= 0) { heif_image_release(*image); err = { heif_error_Decoder_plugin_error, From 8d7ac4c53a9e342bd73d62638627cba7fe511a19 Mon Sep 17 00:00:00 2001 From: Wan-Teh Chang Date: Tue, 25 Jun 2024 12:12:53 -0700 Subject: [PATCH 020/129] Update to SVT-AV1 v2.1.1 Remove the no-dec build option because the decoder portion of SVT-AV1 was removed in v2.1.1. --- third-party/svt.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/third-party/svt.cmd b/third-party/svt.cmd index 3160622952..b45d56a11b 100644 --- a/third-party/svt.cmd +++ b/third-party/svt.cmd @@ -5,12 +5,12 @@ : # If you want to enable the SVT-AV1 encoder, please check that the WITH_SvtEnc and WITH_SvtEnc_BUILTIN CMake variables are set correctly. : # You will also have to set the PKG_CONFIG_PATH to "third-party/SVT-AV1/Build/linux/Release" so that the local SVT-AV1 library is found. -git clone -b v2.1.0 --depth 1 https://gitlab.com/AOMediaCodec/SVT-AV1.git +git clone -b v2.1.1 --depth 1 https://gitlab.com/AOMediaCodec/SVT-AV1.git cd SVT-AV1 cd Build/linux -./build.sh release static no-dec no-apps prefix=$(pwd)/install install +./build.sh release static no-apps prefix=$(pwd)/install install cd ../../.. echo "" From 85381e509a380cd29b008271635962355c10dbe3 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 19:30:51 +0200 Subject: [PATCH 021/129] ffmpeg-dec: round up chroma sizes (#1193) --- libheif/plugins/decoder_ffmpeg.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libheif/plugins/decoder_ffmpeg.cc b/libheif/plugins/decoder_ffmpeg.cc index 81ab2b93d7..abf27aeecf 100644 --- a/libheif/plugins/decoder_ffmpeg.cc +++ b/libheif/plugins/decoder_ffmpeg.cc @@ -26,8 +26,6 @@ #include "config.h" #endif -#include -#include #include extern "C" @@ -217,7 +215,7 @@ static int ffmpeg_get_chroma_width(const AVFrame* frame, heif_channel channel, h } else if (chroma == heif_chroma_420 || chroma == heif_chroma_422) { - return (frame->width) / 2; + return (frame->width + 1) / 2; } else { @@ -233,7 +231,7 @@ static int ffmpeg_get_chroma_height(const AVFrame* frame, heif_channel channel, } else if (chroma == heif_chroma_420) { - return (frame->height) / 2; + return (frame->height + 1) / 2; } else { From 78f97e3e7ee3b622bef1e69b23e6f78cf92532b2 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 20:16:34 +0200 Subject: [PATCH 022/129] uncompressed: define some default values to prevent undefined behavior --- libheif/uncompressed_box.h | 28 ++++++++++++++-------------- libheif/uncompressed_image.cc | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/libheif/uncompressed_box.h b/libheif/uncompressed_box.h index e48000c9a0..69c1672a85 100644 --- a/libheif/uncompressed_box.h +++ b/libheif/uncompressed_box.h @@ -215,22 +215,22 @@ class Box_uncC : public FullBox protected: Error parse(BitstreamRange& range) override; - uint32_t m_profile; + uint32_t m_profile = 0; // not compliant to any profile std::vector m_components; - uint8_t m_sampling_type; - uint8_t m_interleave_type; - uint8_t m_block_size; - bool m_components_little_endian; - bool m_block_pad_lsb; - bool m_block_little_endian; - bool m_block_reversed; - bool m_pad_unknown; - uint32_t m_pixel_size; - uint32_t m_row_align_size; - uint32_t m_tile_align_size; - uint32_t m_num_tile_cols; - uint32_t m_num_tile_rows; + uint8_t m_sampling_type = 0; // no subsampling + uint8_t m_interleave_type = 0; // component interleaving + uint8_t m_block_size = 0; + bool m_components_little_endian = true; + bool m_block_pad_lsb = false; + bool m_block_little_endian = true; + bool m_block_reversed = false; + bool m_pad_unknown = true; + uint32_t m_pixel_size = 0; + uint32_t m_row_align_size = 0; + uint32_t m_tile_align_size = 0; + uint32_t m_num_tile_cols = 0; + uint32_t m_num_tile_rows = 0; }; #endif //LIBHEIF_UNCOMPRESSED_BOX_H diff --git a/libheif/uncompressed_image.cc b/libheif/uncompressed_image.cc index 51053743c8..0548f6d867 100644 --- a/libheif/uncompressed_image.cc +++ b/libheif/uncompressed_image.cc @@ -468,7 +468,7 @@ class AbstractDecoder return dst_row_number * dst_plane_stride; } - heif_channel channel; + heif_channel channel = heif_channel_Y; uint8_t* dst_plane; uint8_t* other_chroma_dst_plane; int dst_plane_stride; From 875963c015572f5ca85598eca98c91efdac4829a Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 20:27:58 +0200 Subject: [PATCH 023/129] VVC: integer range checking and casting to prevent compiler errors --- libheif/vvc.cc | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/libheif/vvc.cc b/libheif/vvc.cc index 9bdb79aef4..c1446e893a 100644 --- a/libheif/vvc.cc +++ b/libheif/vvc.cc @@ -156,7 +156,7 @@ Error Box_vvcC::write(StreamWriter& writer) const } if (c.bit_depth_present_flag) { - v |= 0x10 | ((c.bit_depth - 8) << 1); + v |= (uint8_t)(0x10 | ((c.bit_depth - 8) << 1)); } else { v |= 0x0e; @@ -169,15 +169,28 @@ Error Box_vvcC::write(StreamWriter& writer) const // TODO: error } - writer.write8((int)m_nal_array.size()); + if (m_nal_array.size() > 255) { + return {heif_error_Encoding_error, heif_suberror_Unspecified, "Too many VVC NAL arrays."}; + } + + writer.write8((uint8_t)m_nal_array.size()); for (const NalArray& nal_array : m_nal_array) { uint8_t v2 = (nal_array.m_array_completeness ? 0x80 : 0); v2 |= nal_array.m_NAL_unit_type; writer.write8(v2); - writer.write16(nal_array.m_nal_units.size()); + if (nal_array.m_nal_units.size() > 0xFFFF) { + return {heif_error_Encoding_error, heif_suberror_Unspecified, "Too many VVC NAL units."}; + } + + writer.write16((uint16_t)nal_array.m_nal_units.size()); for (const auto& nal : nal_array.m_nal_units) { - writer.write16(nal.size()); + + if (nal.size() > 0xFFFF) { + return {heif_error_Encoding_error, heif_suberror_Unspecified, "VVC NAL too large."}; + } + + writer.write16((uint16_t)nal.size()); writer.write(nal); } } @@ -282,8 +295,8 @@ Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, // skip VPS ID reader.skip_bits(4); - config->numTemporalLayers = reader.get_bits(3) + 1; - config->chroma_format_idc = reader.get_bits(2); + config->numTemporalLayers = (uint8_t)(reader.get_bits(3) + 1); + config->chroma_format_idc = (uint8_t)(reader.get_bits(2)); config->chroma_format_present_flag = true; reader.skip_bits(2); @@ -363,7 +376,11 @@ Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, success = reader.get_uvlc(&bitDepth_minus8); (void)success; - config->bit_depth = bitDepth_minus8 + 8; + if (bitDepth_minus8 > 0xFF - 8) { + return {heif_error_Encoding_error, heif_suberror_Unspecified, "VCC bit depth out of range."}; + } + + config->bit_depth = (uint8_t)(bitDepth_minus8 + 8); config->bit_depth_present_flag = true; return Error::Ok; From 35104e629a04231bf5e6685eaf40177765a3c4b2 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 20:52:52 +0200 Subject: [PATCH 024/129] define M_PI if not defined --- libheif/box.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libheif/box.cc b/libheif/box.cc index 9f5c0d2b55..f2e3e9ef5c 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -43,6 +43,10 @@ #include "uncompressed_box.h" #endif +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + Fraction::Fraction(int32_t num, int32_t den) { From 9933395644f2f8245a50f89af65b977f93e80763 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 20:56:16 +0200 Subject: [PATCH 025/129] add missing includes --- libheif/avc.cc | 1 - libheif/avc.h | 1 + libheif/heif_properties.cc | 1 + libheif/vvc.cc | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libheif/avc.cc b/libheif/avc.cc index 47739c8e13..730613df10 100644 --- a/libheif/avc.cc +++ b/libheif/avc.cc @@ -19,7 +19,6 @@ */ #include "avc.h" -#include #include #include #include diff --git a/libheif/avc.h b/libheif/avc.h index 595126442d..5f3c826a3a 100644 --- a/libheif/avc.h +++ b/libheif/avc.h @@ -25,6 +25,7 @@ #include "error.h" #include #include +#include class Box_avcC : public Box { public: diff --git a/libheif/heif_properties.cc b/libheif/heif_properties.cc index a2259ffde8..8452f09888 100644 --- a/libheif/heif_properties.cc +++ b/libheif/heif_properties.cc @@ -28,6 +28,7 @@ #include #include #include +#include int heif_item_get_properties_of_type(const struct heif_context* context, diff --git a/libheif/vvc.cc b/libheif/vvc.cc index c1446e893a..e4aa2e4720 100644 --- a/libheif/vvc.cc +++ b/libheif/vvc.cc @@ -23,6 +23,7 @@ #include #include #include +#include Error Box_vvcC::parse(BitstreamRange& range) { From 44479dd0c8da1c61fce971c0a84fe148609fc70b Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 21:00:29 +0200 Subject: [PATCH 026/129] add error enums to emscripten --- libheif/heif_emscripten.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libheif/heif_emscripten.h b/libheif/heif_emscripten.h index b91c5fc504..970de878f2 100644 --- a/libheif/heif_emscripten.h +++ b/libheif/heif_emscripten.h @@ -365,7 +365,12 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_Wrong_tile_image_pixel_depth", heif_suberror_Wrong_tile_image_pixel_depth) .value("heif_suberror_Unknown_NCLX_color_primaries", heif_suberror_Unknown_NCLX_color_primaries) .value("heif_suberror_Unknown_NCLX_transfer_characteristics", heif_suberror_Unknown_NCLX_transfer_characteristics) - .value("heif_suberror_Unknown_NCLX_matrix_coefficients", heif_suberror_Unknown_NCLX_matrix_coefficients); + .value("heif_suberror_Unknown_NCLX_matrix_coefficients", heif_suberror_Unknown_NCLX_matrix_coefficients) + .value("heif_suberror_No_ispe_property", heif_suberror_No_ispe_property) + .value("heif_suberror_Camera_intrinsic_matrix_undefined", heif_suberror_Camera_intrinsic_matrix_undefined) + .value("heif_suberror_Camera_extrinsic_matrix_undefined", heif_suberror_Camera_extrinsic_matrix_undefined) + .value("heif_suberror_No_vvcC_box", heif_suberror_No_vvcC_box); + emscripten::enum_("heif_compression_format") .value("heif_compression_undefined", heif_compression_undefined) .value("heif_compression_HEVC", heif_compression_HEVC) From c556a31ccdf1870c441ef9a1027551f7c610e8ff Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 21:19:53 +0200 Subject: [PATCH 027/129] add error enums to emscripten --- libheif/heif_emscripten.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libheif/heif_emscripten.h b/libheif/heif_emscripten.h index 970de878f2..f31f1179ce 100644 --- a/libheif/heif_emscripten.h +++ b/libheif/heif_emscripten.h @@ -336,6 +336,7 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_Plugin_loading_error", heif_suberror_Plugin_loading_error) .value("heif_suberror_Auxiliary_image_type_unspecified", heif_suberror_Auxiliary_image_type_unspecified) .value("heif_suberror_Cannot_read_plugin_directory", heif_suberror_Cannot_read_plugin_directory) + .value("heif_suberror_No_matching_decoder_installed", heif_suberror_No_matching_decoder_installed) .value("heif_suberror_No_or_invalid_primary_item", heif_suberror_No_or_invalid_primary_item) .value("heif_suberror_No_infe_box", heif_suberror_No_infe_box) .value("heif_suberror_Security_limit_exceeded", heif_suberror_Security_limit_exceeded) @@ -354,7 +355,6 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_Item_reference_cycle", heif_suberror_Item_reference_cycle) .value("heif_suberror_Invalid_pixi_box", heif_suberror_Invalid_pixi_box) .value("heif_suberror_Invalid_region_data", heif_suberror_Invalid_region_data) - .value("heif_suberror_Invalid_J2K_codestream", heif_suberror_Invalid_J2K_codestream) .value("heif_suberror_Unsupported_codec", heif_suberror_Unsupported_codec) .value("heif_suberror_Unsupported_image_type", heif_suberror_Unsupported_image_type) .value("heif_suberror_Unsupported_data_version", heif_suberror_Unsupported_data_version) @@ -369,6 +369,7 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_No_ispe_property", heif_suberror_No_ispe_property) .value("heif_suberror_Camera_intrinsic_matrix_undefined", heif_suberror_Camera_intrinsic_matrix_undefined) .value("heif_suberror_Camera_extrinsic_matrix_undefined", heif_suberror_Camera_extrinsic_matrix_undefined) + .value("heif_suberror_Invalid_J2K_codestream", heif_suberror_Invalid_J2K_codestream) .value("heif_suberror_No_vvcC_box", heif_suberror_No_vvcC_box); emscripten::enum_("heif_compression_format") From b9d39e1b9c83530857cc559e721754db8a91bf77 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 21:22:53 +0200 Subject: [PATCH 028/129] remove duplicate error from emscripten --- libheif/heif_emscripten.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libheif/heif_emscripten.h b/libheif/heif_emscripten.h index f31f1179ce..cb219f24bc 100644 --- a/libheif/heif_emscripten.h +++ b/libheif/heif_emscripten.h @@ -369,8 +369,7 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_No_ispe_property", heif_suberror_No_ispe_property) .value("heif_suberror_Camera_intrinsic_matrix_undefined", heif_suberror_Camera_intrinsic_matrix_undefined) .value("heif_suberror_Camera_extrinsic_matrix_undefined", heif_suberror_Camera_extrinsic_matrix_undefined) - .value("heif_suberror_Invalid_J2K_codestream", heif_suberror_Invalid_J2K_codestream) - .value("heif_suberror_No_vvcC_box", heif_suberror_No_vvcC_box); + .value("heif_suberror_Invalid_J2K_codestream", heif_suberror_Invalid_J2K_codestream); emscripten::enum_("heif_compression_format") .value("heif_compression_undefined", heif_compression_undefined) From 34bfa4f3c47f6322e68958a34e8b204e0ac38779 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Mon, 26 Feb 2024 12:02:19 +1100 Subject: [PATCH 029/129] uncompressed: support version 1 boxes --- libheif/heif.cc | 7 +- libheif/heif.h | 5 + libheif/uncompressed_box.cc | 188 +++++++++++++++++++--------------- libheif/uncompressed_box.h | 4 +- libheif/uncompressed_image.cc | 135 +++++++++++++++++++++--- tests/uncompressed_encode.cc | 14 ++- 6 files changed, 249 insertions(+), 104 deletions(-) diff --git a/libheif/heif.cc b/libheif/heif.cc index 31eb5384d9..41f4eb51a0 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -2734,7 +2734,7 @@ int heif_encoder_has_default(struct heif_encoder* encoder, static void set_default_options(heif_encoding_options& options) { - options.version = 6; + options.version = 7; options.save_alpha_channel = true; options.macOS_compatibility_workaround = false; @@ -2747,11 +2747,16 @@ static void set_default_options(heif_encoding_options& options) options.color_conversion_options.preferred_chroma_downsampling_algorithm = heif_chroma_downsampling_average; options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear; options.color_conversion_options.only_use_preferred_chroma_algorithm = false; + + options.prefer_minimised = false; } static void copy_options(heif_encoding_options& options, const heif_encoding_options& input_options) { switch (input_options.version) { + case 7: + options.prefer_minimised = input_options.prefer_minimised; + // fallthrough case 6: options.color_conversion_options = input_options.color_conversion_options; // fallthrough diff --git a/libheif/heif.h b/libheif/heif.h index daed356bc6..8390dd3955 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -2090,6 +2090,11 @@ struct heif_encoding_options // version 6 options struct heif_color_conversion_options color_conversion_options; + + // version 7 options + + // Set this to true to used minimised versions of boxes where possible + uint8_t prefer_minimised; }; LIBHEIF_API diff --git a/libheif/uncompressed_box.cc b/libheif/uncompressed_box.cc index 0020225b54..b6afa359c0 100644 --- a/libheif/uncompressed_box.cc +++ b/libheif/uncompressed_box.cc @@ -198,58 +198,77 @@ Error Box_uncC::parse(BitstreamRange& range) { parse_full_box_header(range); m_profile = range.read32(); - if (get_version() != 0) { - return Error{heif_error_Invalid_input, heif_suberror_Unsupported_data_version, "Unsupported version (only 0 is currently supported)"}; - } + if (get_version() == 1) { + if (m_profile == fourcc_to_uint32("rgb3")) { + Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; + add_component(component0); + Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; + add_component(component1); + Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0}; + add_component(component2); + } else if ((m_profile == fourcc_to_uint32("rgba")) || (m_profile == fourcc_to_uint32("abgr"))) { + Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; + add_component(component0); + Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; + add_component(component1); + Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0}; + add_component(component2); + Box_uncC::Component component3 = {3, 8, component_format_unsigned, 0}; + add_component(component3); + } else { + return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; + } + } else if (get_version() == 0) { - unsigned int component_count = range.read32(); + unsigned int component_count = range.read32(); - for (unsigned int i = 0; i < component_count && !range.error() && !range.eof(); i++) { - Component component; - component.component_index = range.read16(); - component.component_bit_depth = uint16_t(range.read8() + 1); - component.component_format = range.read8(); - component.component_align_size = range.read8(); - m_components.push_back(component); + for (unsigned int i = 0; i < component_count && !range.error() && !range.eof(); i++) { + Component component; + component.component_index = range.read16(); + component.component_bit_depth = uint16_t(range.read8() + 1); + component.component_format = range.read8(); + component.component_align_size = range.read8(); + m_components.push_back(component); - if (!is_valid_component_format(component.component_format)) { - return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; + if (!is_valid_component_format(component.component_format)) { + return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid component format"}; + } } - } - m_sampling_type = range.read8(); - if (!is_valid_sampling_mode(m_sampling_type)) { - return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid sampling mode"}; - } - - m_interleave_type = range.read8(); - if (!is_valid_interleave_mode(m_interleave_type)) { - return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid interleave mode"}; - } + m_sampling_type = range.read8(); + if (!is_valid_sampling_mode(m_sampling_type)) { + return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid sampling mode"}; + } - m_block_size = range.read8(); + m_interleave_type = range.read8(); + if (!is_valid_interleave_mode(m_interleave_type)) { + return Error{heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "Invalid interleave mode"}; + } - uint8_t flags = range.read8(); - m_components_little_endian = !!(flags & 0x80); - m_block_pad_lsb = !!(flags & 0x40); - m_block_little_endian = !!(flags & 0x20); - m_block_reversed = !!(flags & 0x10); - m_pad_unknown = !!(flags & 0x08); + m_block_size = range.read8(); - m_pixel_size = range.read32(); + uint8_t flags = range.read8(); + m_components_little_endian = !!(flags & 0x80); + m_block_pad_lsb = !!(flags & 0x40); + m_block_little_endian = !!(flags & 0x20); + m_block_reversed = !!(flags & 0x10); + m_pad_unknown = !!(flags & 0x08); - m_row_align_size = range.read32(); + m_pixel_size = range.read32(); - m_tile_align_size = range.read32(); + m_row_align_size = range.read32(); - m_num_tile_cols = range.read32() + 1; + m_tile_align_size = range.read32(); - m_num_tile_rows = range.read32() + 1; + m_num_tile_cols = range.read32() + 1; + m_num_tile_rows = range.read32() + 1; + } return range.get_error(); } + std::string Box_uncC::dump(Indent& indent) const { std::ostringstream sstr; @@ -258,75 +277,76 @@ std::string Box_uncC::dump(Indent& indent) const sstr << indent << "profile: " << m_profile; if (m_profile != 0) { sstr << " (" << to_fourcc(m_profile) << ")"; + sstr << "\n"; } - sstr << "\n"; - - for (const auto& component : m_components) { - sstr << indent << "component_index: " << component.component_index << "\n"; - sstr << indent << "component_bit_depth: " << (int) component.component_bit_depth << "\n"; - sstr << indent << "component_format: " << get_name(heif_uncompressed_component_format(component.component_format), sNames_uncompressed_component_format) << "\n"; - sstr << indent << "component_align_size: " << (int) component.component_align_size << "\n"; - } - - sstr << indent << "sampling_type: " << get_name(heif_uncompressed_sampling_mode(m_sampling_type), sNames_uncompressed_sampling_mode) << "\n"; + if (get_version() == 0) { + for (const auto& component : m_components) { + sstr << indent << "component_index: " << component.component_index << "\n"; + sstr << indent << "component_bit_depth: " << (int) component.component_bit_depth << "\n"; + sstr << indent << "component_format: " << get_name(heif_uncompressed_component_format(component.component_format), sNames_uncompressed_component_format) << "\n"; + sstr << indent << "component_align_size: " << (int) component.component_align_size << "\n"; + } - sstr << indent << "interleave_type: " << get_name(heif_uncompressed_interleave_mode(m_interleave_type), sNames_uncompressed_interleave_mode) << "\n"; + sstr << indent << "sampling_type: " << get_name(heif_uncompressed_sampling_mode(m_sampling_type), sNames_uncompressed_sampling_mode) << "\n"; - sstr << indent << "block_size: " << (int) m_block_size << "\n"; + sstr << indent << "interleave_type: " << get_name(heif_uncompressed_interleave_mode(m_interleave_type), sNames_uncompressed_interleave_mode) << "\n"; - sstr << indent << "components_little_endian: " << m_components_little_endian << "\n"; - sstr << indent << "block_pad_lsb: " << m_block_pad_lsb << "\n"; - sstr << indent << "block_little_endian: " << m_block_little_endian << "\n"; - sstr << indent << "block_reversed: " << m_block_reversed << "\n"; - sstr << indent << "pad_unknown: " << m_pad_unknown << "\n"; + sstr << indent << "block_size: " << (int) m_block_size << "\n"; - sstr << indent << "pixel_size: " << m_pixel_size << "\n"; + sstr << indent << "components_little_endian: " << m_components_little_endian << "\n"; + sstr << indent << "block_pad_lsb: " << m_block_pad_lsb << "\n"; + sstr << indent << "block_little_endian: " << m_block_little_endian << "\n"; + sstr << indent << "block_reversed: " << m_block_reversed << "\n"; + sstr << indent << "pad_unknown: " << m_pad_unknown << "\n"; - sstr << indent << "row_align_size: " << m_row_align_size << "\n"; + sstr << indent << "pixel_size: " << m_pixel_size << "\n"; - sstr << indent << "tile_align_size: " << m_tile_align_size << "\n"; + sstr << indent << "row_align_size: " << m_row_align_size << "\n"; - sstr << indent << "num_tile_cols: " << m_num_tile_cols << "\n"; + sstr << indent << "tile_align_size: " << m_tile_align_size << "\n"; - sstr << indent << "num_tile_rows: " << m_num_tile_rows << "\n"; + sstr << indent << "num_tile_cols: " << m_num_tile_cols << "\n"; + sstr << indent << "num_tile_rows: " << m_num_tile_rows << "\n"; + } return sstr.str(); } - Error Box_uncC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); - writer.write32(m_profile); - writer.write32((uint32_t) m_components.size()); - for (const auto& component : m_components) { - if (component.component_bit_depth < 1 || component.component_bit_depth > 256) { - return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "component bit-depth out of range [1..256]"}; + if (get_version() == 1) { + } + else if (get_version() == 0) { + writer.write32((uint32_t)m_components.size()); + for (const auto &component : m_components) { + if (component.component_bit_depth < 1 || component.component_bit_depth > 256) { + return {heif_error_Invalid_input, heif_suberror_Invalid_parameter_value, "component bit-depth out of range [1..256]"}; + } + + writer.write16(component.component_index); + writer.write8(uint8_t(component.component_bit_depth - 1)); + writer.write8(component.component_format); + writer.write8(component.component_align_size); } - - writer.write16(component.component_index); - writer.write8(uint8_t(component.component_bit_depth - 1)); - writer.write8(component.component_format); - writer.write8(component.component_align_size); + writer.write8(m_sampling_type); + writer.write8(m_interleave_type); + writer.write8(m_block_size); + uint8_t flags = 0; + flags |= (m_components_little_endian ? 0x80 : 0); + flags |= (m_block_pad_lsb ? 0x40 : 0); + flags |= (m_block_little_endian ? 0x20 : 0); + flags |= (m_block_reversed ? 0x10 : 0); + flags |= (m_pad_unknown ? 0x08 : 0); + writer.write8(flags); + writer.write32(m_pixel_size); + writer.write32(m_row_align_size); + writer.write32(m_tile_align_size); + writer.write32(m_num_tile_cols - 1); + writer.write32(m_num_tile_rows - 1); } - writer.write8(m_sampling_type); - writer.write8(m_interleave_type); - writer.write8(m_block_size); - uint8_t flags = 0; - flags |= (m_components_little_endian ? 0x80 : 0); - flags |= (m_block_pad_lsb ? 0x40 : 0); - flags |= (m_block_little_endian ? 0x20 : 0); - flags |= (m_block_reversed ? 0x10 : 0); - flags |= (m_pad_unknown ? 0x08 : 0); - writer.write8(flags); - writer.write32(m_pixel_size); - writer.write32(m_row_align_size); - writer.write32(m_tile_align_size); - writer.write32(m_num_tile_cols - 1); - writer.write32(m_num_tile_rows - 1); prepend_header(writer, box_start); return Error::Ok; } - diff --git a/libheif/uncompressed_box.h b/libheif/uncompressed_box.h index 69c1672a85..a1d75026d3 100644 --- a/libheif/uncompressed_box.h +++ b/libheif/uncompressed_box.h @@ -79,7 +79,7 @@ class Box_uncC : public FullBox Box_uncC() : m_profile(0), m_sampling_type(sampling_mode_no_subsampling), - m_interleave_type(interleave_mode_component), + m_interleave_type(interleave_mode_pixel), m_block_size(0), m_components_little_endian(false), m_block_pad_lsb(false), @@ -95,6 +95,8 @@ class Box_uncC : public FullBox set_short_type(fourcc("uncC")); } + void derive_box_version() override {}; + std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; diff --git a/libheif/uncompressed_image.cc b/libheif/uncompressed_image.cc index 0548f6d867..882d54718a 100644 --- a/libheif/uncompressed_image.cc +++ b/libheif/uncompressed_image.cc @@ -34,8 +34,23 @@ #include "uncompressed_box.h" #include "uncompressed_image.h" +static bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr &uncC) +{ + return ((uncC != nullptr) && (uncC->get_version() == 1) && ((uncC->get_profile() == fourcc("rgb3")) || (uncC->get_profile() == fourcc("rgba")) || (uncC->get_profile() == fourcc("abgr")))); +} + static Error uncompressed_image_type_is_supported(std::shared_ptr& uncC, std::shared_ptr& cmpd) { + if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) + { + return Error::Ok; + } + if (!cmpd) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Missing required cmpd box (no match in uncC box) for uncompressed codec"); + } + for (Box_uncC::Component component : uncC->get_components()) { uint16_t component_index = component.component_index; uint16_t component_type = cmpd->get_components()[component_index].component_type; @@ -228,6 +243,12 @@ static Error get_heif_chroma_uncompressed(std::shared_ptr& uncC, std:: *out_chroma = heif_chroma_undefined; *out_colourspace = heif_colorspace_undefined; + if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) { + *out_chroma = heif_chroma_444; + *out_colourspace = heif_colorspace_RGB; + return Error::Ok; + } + // each 1-bit represents an existing component in the image uint16_t componentSet = 0; @@ -339,9 +360,56 @@ int UncompressedImageCodec::get_luma_bits_per_pixel_from_configuration_unci(cons } } -static bool map_uncompressed_component_to_channel(const std::shared_ptr &cmpd, const Box_uncC::Component component, heif_channel *channel) { +static bool map_uncompressed_component_to_channel(const std::shared_ptr &cmpd, const std::shared_ptr &uncC, Box_uncC::Component component, heif_channel *channel) +{ uint16_t component_index = component.component_index; + if (isKnownUncompressedFrameConfigurationBoxProfile(uncC)) { + if (uncC->get_profile() == fourcc("rgb3")) { + switch (component_index) { + case 0: + *channel = heif_channel_R; + return true; + case 1: + *channel = heif_channel_G; + return true; + case 2: + *channel = heif_channel_B; + return true; + } + } else if (uncC->get_profile() == fourcc("rgba")) { + switch (component_index) { + case 0: + *channel = heif_channel_Alpha; + return true; + case 1: + *channel = heif_channel_R; + return true; + case 2: + *channel = heif_channel_G; + return true; + case 3: + *channel = heif_channel_B; + return true; + } + } else if (uncC->get_profile() == fourcc("abgr")) { + switch (component_index) { + case 0: + *channel = heif_channel_Alpha; + return true; + case 1: + *channel = heif_channel_B; + return true; + case 2: + *channel = heif_channel_G; + return true; + case 3: + *channel = heif_channel_R; + return true; + } + } + } uint16_t component_type = cmpd->get_components()[component_index].component_type; + switch (component_type) { case component_type_monochrome: *channel = heif_channel_Y; @@ -375,8 +443,8 @@ static bool map_uncompressed_component_to_channel(const std::shared_ptr& data) : BitReader(data.data(), (int)data.size()) {} @@ -519,7 +587,7 @@ class AbstractDecoder private: ChannelListEntry buildChannelListEntry(Box_uncC::Component component, std::shared_ptr &img) { ChannelListEntry entry; - entry.use_channel = map_uncompressed_component_to_channel(m_cmpd, component, &(entry.channel)); + entry.use_channel = map_uncompressed_component_to_channel(m_cmpd, m_uncC, component, &(entry.channel)); entry.dst_plane = img->get_plane(entry.channel, &(entry.dst_plane_stride)); entry.tile_width = m_tile_width; entry.tile_height = m_tile_height; @@ -840,14 +908,21 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptrget_version() !=1)) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Missing required cmpd or uncC version 1 box for uncompressed codec"); +} // check if we support the type of image @@ -871,7 +946,7 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptrget_components()) { heif_channel channel; - if (map_uncompressed_component_to_channel(cmpd, component, &channel)) { + if (map_uncompressed_component_to_channel(cmpd, uncC, component, &channel)) { if ((channel == heif_channel_Cb) || (channel == heif_channel_Cr)) { img->add_plane(channel, (width / chroma_h_subsampling(chroma)), (height / chroma_v_subsampling(chroma)), component.component_bit_depth); } else { @@ -1089,22 +1164,48 @@ Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptr& uncC, const std::shared_ptr& image) +{ + uncC->set_version(0); + if (image->get_colorspace() != heif_colorspace_RGB) { + return; + } + if (!((image->get_chroma_format() == heif_chroma_interleaved_RGB) || (image->get_chroma_format() == heif_chroma_interleaved_RGBA))) { + return; + } + if (image->get_bits_per_pixel(heif_channel_interleaved) != 8) { + return; + } + if (image->get_chroma_format() == heif_chroma_interleaved_RGBA) { + uncC->set_profile(fourcc_to_uint32("rgba")); + } else { + uncC->set_profile(fourcc_to_uint32("rgb3")); + } + uncC->set_version(1); +} + Error UncompressedImageCodec::encode_uncompressed_image(const std::shared_ptr& heif_file, const std::shared_ptr& src_image, void* encoder_struct, const struct heif_encoding_options& options, std::shared_ptr& out_image) { - std::shared_ptr cmpd = std::make_shared(); std::shared_ptr uncC = std::make_shared(); - Error error = fill_cmpd_and_uncC(cmpd, uncC, src_image); - if (error) - { - return error; + if (options.prefer_minimised) { + maybe_make_minimised_uncC(uncC, src_image); } - heif_file->add_property(out_image->get_id(), cmpd, true); - heif_file->add_property(out_image->get_id(), uncC, true); + if (uncC->get_version() == 1) { + heif_file->add_property(out_image->get_id(), uncC, true); + } else { + std::shared_ptr cmpd = std::make_shared(); + Error error = fill_cmpd_and_uncC(cmpd, uncC, src_image); + if (error) { + return error; + } + heif_file->add_property(out_image->get_id(), cmpd, true); + heif_file->add_property(out_image->get_id(), uncC, true); + } std::vector data; if (src_image->get_colorspace() == heif_colorspace_YCbCr) { diff --git a/tests/uncompressed_encode.cc b/tests/uncompressed_encode.cc index fbec7d2376..714fd1773f 100644 --- a/tests/uncompressed_encode.cc +++ b/tests/uncompressed_encode.cc @@ -565,7 +565,7 @@ struct heif_image *createImage_RGBA_planar() return image; } -static void do_encode(heif_image* input_image, const char* filename, bool check_decode) +static void do_encode(heif_image* input_image, const char* filename, bool check_decode, uint8_t preferMinimised = 0) { REQUIRE(input_image != nullptr); @@ -580,6 +580,7 @@ static void do_encode(heif_image* input_image, const char* filename, bool check_ options->macOS_compatibility_workaround = false; options->macOS_compatibility_workaround_no_nclx_profile = true; options->image_orientation = heif_orientation_normal; + options->prefer_minimised = preferMinimised; heif_image_handle *output_image_handle; err = heif_context_encode_image(ctx, input_image, encoder, options, &output_image_handle); @@ -644,6 +645,11 @@ TEST_CASE("Encode Mono") do_encode(input_image, "encode_mono.heif", true); } +TEST_CASE("Encode RGB Version1") +{ + heif_image *input_image = createImage_RGB_interleaved(); + do_encode(input_image, "encode_rgb_version1.heif", true, true); +} TEST_CASE("Encode Mono with alpha") { @@ -708,6 +714,12 @@ TEST_CASE("Encode RGBA") do_encode(input_image, "encode_rgba.heif", true); } +TEST_CASE("Encode RGBA Version 1") +{ + heif_image *input_image = createImage_RGBA_interleaved(); + do_encode(input_image, "encode_rgba_version1.heif", true, true); +} + TEST_CASE("Encode RRRGGBBAA_LE 10 bit") { From c590d8f3640b691e1595b8104fbfbb5bff4d3e57 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Thu, 27 Jun 2024 04:27:09 +1000 Subject: [PATCH 030/129] uncompressed: use more specific options --- libheif/heif.cc | 4 ++-- libheif/heif.h | 4 ++-- libheif/uncompressed_image.cc | 2 +- tests/uncompressed_encode.cc | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libheif/heif.cc b/libheif/heif.cc index 41f4eb51a0..b426585e41 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -2748,14 +2748,14 @@ static void set_default_options(heif_encoding_options& options) options.color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_bilinear; options.color_conversion_options.only_use_preferred_chroma_algorithm = false; - options.prefer_minimised = false; + options.prefer_uncC_short_form = true; } static void copy_options(heif_encoding_options& options, const heif_encoding_options& input_options) { switch (input_options.version) { case 7: - options.prefer_minimised = input_options.prefer_minimised; + options.prefer_uncC_short_form = input_options.prefer_uncC_short_form; // fallthrough case 6: options.color_conversion_options = input_options.color_conversion_options; diff --git a/libheif/heif.h b/libheif/heif.h index 8390dd3955..a560c3a77b 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -2093,8 +2093,8 @@ struct heif_encoding_options // version 7 options - // Set this to true to used minimised versions of boxes where possible - uint8_t prefer_minimised; + // Set this to true to use compressed form of uncC where possible + uint8_t prefer_uncC_short_form; }; LIBHEIF_API diff --git a/libheif/uncompressed_image.cc b/libheif/uncompressed_image.cc index 882d54718a..e14cfbd774 100644 --- a/libheif/uncompressed_image.cc +++ b/libheif/uncompressed_image.cc @@ -1191,7 +1191,7 @@ Error UncompressedImageCodec::encode_uncompressed_image(const std::shared_ptr& out_image) { std::shared_ptr uncC = std::make_shared(); - if (options.prefer_minimised) { + if (options.prefer_uncC_short_form) { maybe_make_minimised_uncC(uncC, src_image); } if (uncC->get_version() == 1) { diff --git a/tests/uncompressed_encode.cc b/tests/uncompressed_encode.cc index 714fd1773f..b3326dc4d4 100644 --- a/tests/uncompressed_encode.cc +++ b/tests/uncompressed_encode.cc @@ -565,7 +565,7 @@ struct heif_image *createImage_RGBA_planar() return image; } -static void do_encode(heif_image* input_image, const char* filename, bool check_decode, uint8_t preferMinimised = 0) +static void do_encode(heif_image* input_image, const char* filename, bool check_decode, uint8_t prefer_uncC_short_form = 0) { REQUIRE(input_image != nullptr); @@ -580,7 +580,7 @@ static void do_encode(heif_image* input_image, const char* filename, bool check_ options->macOS_compatibility_workaround = false; options->macOS_compatibility_workaround_no_nclx_profile = true; options->image_orientation = heif_orientation_normal; - options->prefer_minimised = preferMinimised; + options->prefer_uncC_short_form = prefer_uncC_short_form; heif_image_handle *output_image_handle; err = heif_context_encode_image(ctx, input_image, encoder, options, &output_image_handle); From 8a686f19be802c67b7c0420889db5882e8489b4e Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 22:30:38 +0200 Subject: [PATCH 031/129] Moved public API into 'api', image codecs into 'codecs' directory. --- examples/CMakeLists.txt | 2 +- examples/decoder_jpeg.cc | 2 +- examples/decoder_png.cc | 2 +- examples/encoder_jpeg.cc | 2 +- examples/encoder_png.cc | 2 +- examples/heif_enc.cc | 2 +- gdk-pixbuf/CMakeLists.txt | 2 +- libheif/CMakeLists.txt | 88 +++++++++---------- libheif/{ => api/libheif}/api_structs.h | 0 libheif/{ => api/libheif}/heif.cc | 6 +- libheif/{ => api/libheif}/heif.h | 0 libheif/{ => api/libheif}/heif_cxx.h | 0 libheif/{ => api/libheif}/heif_emscripten.h | 0 .../{ => api/libheif}/heif_experimental.cc | 2 +- libheif/{ => api/libheif}/heif_experimental.h | 0 libheif/{ => api/libheif}/heif_items.cc | 2 +- libheif/{ => api/libheif}/heif_items.h | 0 libheif/{ => api/libheif}/heif_plugin.cc | 0 libheif/{ => api/libheif}/heif_plugin.h | 0 libheif/{ => api/libheif}/heif_properties.cc | 0 libheif/{ => api/libheif}/heif_properties.h | 2 +- libheif/{ => api/libheif}/heif_regions.cc | 6 +- libheif/{ => api/libheif}/heif_regions.h | 2 +- libheif/{ => api/libheif}/heif_version.h.in | 0 libheif/box.cc | 14 +-- libheif/box.h | 4 +- libheif/{ => codecs}/avc.cc | 0 libheif/{ => codecs}/avc.h | 0 libheif/{ => codecs}/avif.cc | 0 libheif/{ => codecs}/avif.h | 2 +- libheif/{ => codecs}/hevc.cc | 0 libheif/{ => codecs}/hevc.h | 2 +- libheif/{ => codecs}/jpeg.cc | 0 libheif/{ => codecs}/jpeg.h | 0 libheif/{ => codecs}/jpeg2000.cc | 0 libheif/{ => codecs}/jpeg2000.h | 0 libheif/{ => codecs}/mask_image.cc | 4 +- libheif/{ => codecs}/mask_image.h | 0 libheif/{ => codecs}/uncompressed.h | 0 libheif/{ => codecs}/uncompressed_box.cc | 0 libheif/{ => codecs}/uncompressed_box.h | 2 +- libheif/{ => codecs}/uncompressed_image.cc | 4 +- libheif/{ => codecs}/uncompressed_image.h | 0 libheif/{ => codecs}/vvc.cc | 0 libheif/{ => codecs}/vvc.h | 0 libheif/color-conversion/chroma_sampling.h | 2 +- libheif/color-conversion/colorconversion.cc | 4 +- libheif/color-conversion/colorconversion.h | 2 +- libheif/color-conversion/rgb2yuv.cc | 4 +- libheif/color-conversion/rgb2yuv.h | 2 +- libheif/color-conversion/rgb2yuv_sharp.cc | 4 +- libheif/color-conversion/yuv2rgb.cc | 4 +- libheif/common_utils.h | 2 +- libheif/context.cc | 26 +++--- libheif/context.h | 4 +- libheif/error.h | 3 +- libheif/file.cc | 13 ++- libheif/file.h | 6 +- libheif/init.cc | 4 +- libheif/pixelimage.h | 2 +- libheif/plugin_registry.cc | 34 +++---- libheif/plugin_registry.h | 4 +- libheif/plugins/CMakeLists.txt | 4 +- libheif/plugins/decoder_aom.h | 2 +- libheif/plugins/decoder_dav1d.cc | 4 +- libheif/plugins/decoder_dav1d.h | 2 +- libheif/plugins/decoder_ffmpeg.h | 2 +- libheif/plugins/decoder_jpeg.h | 2 +- libheif/plugins/decoder_libde265.h | 2 +- libheif/plugins/decoder_openjpeg.h | 2 +- libheif/plugins/encoder_aom.cc | 2 +- libheif/plugins/encoder_aom.h | 2 +- libheif/plugins/encoder_jpeg.h | 2 +- libheif/plugins/encoder_kvazaar.h | 2 +- libheif/plugins/encoder_mask.h | 2 +- libheif/plugins/encoder_openjpeg.h | 2 +- libheif/plugins/encoder_openjph.h | 2 +- libheif/plugins/encoder_rav1e.h | 2 +- libheif/plugins/encoder_svt.h | 2 +- libheif/plugins/encoder_uncompressed.h | 2 +- libheif/plugins/encoder_uvg266.cc | 3 - libheif/plugins/encoder_uvg266.h | 2 +- libheif/plugins/encoder_x265.h | 2 +- libheif/plugins_unix.cc | 2 +- libheif/plugins_windows.cc | 2 +- tests/CMakeLists.txt | 2 +- tests/avc_box.cc | 4 +- tests/box_equals.cc | 2 +- tests/conversion.cc | 4 +- tests/encode.cc | 2 +- tests/encode_htj2k.cc | 2 +- tests/encode_jpeg2000.cc | 2 +- tests/idat.cc | 2 +- tests/jpeg2000.cc | 4 +- tests/uncompressed_box.cc | 4 +- 95 files changed, 173 insertions(+), 180 deletions(-) rename libheif/{ => api/libheif}/api_structs.h (100%) rename libheif/{ => api/libheif}/heif.cc (99%) rename libheif/{ => api/libheif}/heif.h (100%) rename libheif/{ => api/libheif}/heif_cxx.h (100%) rename libheif/{ => api/libheif}/heif_emscripten.h (100%) rename libheif/{ => api/libheif}/heif_experimental.cc (99%) rename libheif/{ => api/libheif}/heif_experimental.h (100%) rename libheif/{ => api/libheif}/heif_items.cc (99%) rename libheif/{ => api/libheif}/heif_items.h (100%) rename libheif/{ => api/libheif}/heif_plugin.cc (100%) rename libheif/{ => api/libheif}/heif_plugin.h (100%) rename libheif/{ => api/libheif}/heif_properties.cc (100%) rename libheif/{ => api/libheif}/heif_properties.h (99%) rename libheif/{ => api/libheif}/heif_regions.cc (99%) rename libheif/{ => api/libheif}/heif_regions.h (99%) rename libheif/{ => api/libheif}/heif_version.h.in (100%) rename libheif/{ => codecs}/avc.cc (100%) rename libheif/{ => codecs}/avc.h (100%) rename libheif/{ => codecs}/avif.cc (100%) rename libheif/{ => codecs}/avif.h (99%) rename libheif/{ => codecs}/hevc.cc (100%) rename libheif/{ => codecs}/hevc.h (99%) rename libheif/{ => codecs}/jpeg.cc (100%) rename libheif/{ => codecs}/jpeg.h (100%) rename libheif/{ => codecs}/jpeg2000.cc (100%) rename libheif/{ => codecs}/jpeg2000.h (100%) rename libheif/{ => codecs}/mask_image.cc (99%) rename libheif/{ => codecs}/mask_image.h (100%) rename libheif/{ => codecs}/uncompressed.h (100%) rename libheif/{ => codecs}/uncompressed_box.cc (100%) rename libheif/{ => codecs}/uncompressed_box.h (99%) rename libheif/{ => codecs}/uncompressed_image.cc (99%) rename libheif/{ => codecs}/uncompressed_image.h (100%) rename libheif/{ => codecs}/vvc.cc (100%) rename libheif/{ => codecs}/vvc.h (100%) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 059606c7c6..bdda0eb9bc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,5 +1,5 @@ # Needed to find libheif/heif_version.h while compiling the library -include_directories(${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}) +include_directories(${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif/api ${libheif_SOURCE_DIR}/libheif) if (MSVC) set(getopt_sources diff --git a/examples/decoder_jpeg.cc b/examples/decoder_jpeg.cc index 65b5769412..2297d4dc4d 100644 --- a/examples/decoder_jpeg.cc +++ b/examples/decoder_jpeg.cc @@ -33,7 +33,7 @@ #include #include #include "decoder_jpeg.h" -#include "libheif/exif.h" +#include "exif.h" extern "C" { // Prevent duplicate definition for libjpeg-turbo v2.0 diff --git a/examples/decoder_png.cc b/examples/decoder_png.cc index c3eeed88a8..9d30329b02 100644 --- a/examples/decoder_png.cc +++ b/examples/decoder_png.cc @@ -30,7 +30,7 @@ #include #include #include "decoder_png.h" -#include "libheif/exif.h" +#include "exif.h" extern "C" { #include diff --git a/examples/encoder_jpeg.cc b/examples/encoder_jpeg.cc index b82f8aa39b..529ab9409d 100644 --- a/examples/encoder_jpeg.cc +++ b/examples/encoder_jpeg.cc @@ -32,7 +32,7 @@ #include #include "encoder_jpeg.h" -#include "libheif/exif.h" +#include "exif.h" #define JPEG_XMP_MARKER (JPEG_APP0+1) /* JPEG marker code for XMP */ #define JPEG_XMP_MARKER_ID "http://ns.adobe.com/xap/1.0/" diff --git a/examples/encoder_png.cc b/examples/encoder_png.cc index 8b918f96e9..f89438a5c7 100644 --- a/examples/encoder_png.cc +++ b/examples/encoder_png.cc @@ -30,7 +30,7 @@ #include #include "encoder_png.h" -#include "libheif/exif.h" +#include "exif.h" PngEncoder::PngEncoder() = default; diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc index 69c8441c58..5115840c5b 100644 --- a/examples/heif_enc.cc +++ b/examples/heif_enc.cc @@ -52,7 +52,7 @@ #include #include "benchmark.h" -#include "libheif/exif.h" +#include "exif.h" #include "common.h" int master_alpha = 1; diff --git a/gdk-pixbuf/CMakeLists.txt b/gdk-pixbuf/CMakeLists.txt index eeb2727d05..de67e79624 100644 --- a/gdk-pixbuf/CMakeLists.txt +++ b/gdk-pixbuf/CMakeLists.txt @@ -10,7 +10,7 @@ if(UNIX OR MINGW) add_library(pixbufloader-heif MODULE pixbufloader-heif.c) - target_include_directories(pixbufloader-heif PRIVATE ${GDKPIXBUF2_INCLUDE_DIRS} ${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}) + target_include_directories(pixbufloader-heif PRIVATE ${GDKPIXBUF2_INCLUDE_DIRS} ${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif/api) target_link_directories(pixbufloader-heif PRIVATE ${GDKPIXBUF2_LIBRARY_DIRS}) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 7d15a465f1..e606666153 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -1,60 +1,66 @@ include(CMakePackageConfigHelpers) -configure_file(heif_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/heif_version.h) +configure_file(api/libheif/heif_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/heif_version.h) set(libheif_headers - heif.h - heif_cxx.h - heif_plugin.h - heif_properties.h - heif_regions.h - heif_items.h + api/libheif/heif.h + api/libheif/heif_cxx.h + api/libheif/heif_plugin.h + api/libheif/heif_properties.h + api/libheif/heif_regions.h + api/libheif/heif_items.h ${CMAKE_CURRENT_BINARY_DIR}/heif_version.h) set(libheif_sources bitstream.cc + bitstream.h box.cc + box.h error.cc - heif.cc + error.h context.cc + context.h file.cc + file.h pixelimage.cc - hevc.cc - avif.cc + pixelimage.h plugin_registry.cc - heif_plugin.cc nclx.cc - bitstream.h - box.h - error.h - api_structs.h - context.h - file.h - pixelimage.h - hevc.h - avif.h + nclx.h plugin_registry.h security_limits.h - init.cc init.h - nclx.h + init.cc + init.h logging.h logging.cc - mask_image.cc - mask_image.h metadata_compression.cc metadata_compression.h common_utils.cc common_utils.h region.cc region.h - heif_regions.h - heif_regions.cc - heif_properties.h - heif_properties.cc - heif_items.h - heif_items.cc - heif_experimental.h - heif_experimental.cc + api/libheif/api_structs.h + api/libheif/heif.cc + api/libheif/heif_regions.cc + api/libheif/heif_plugin.cc + api/libheif/heif_properties.cc + api/libheif/heif_items.cc + api/libheif/heif_experimental.h + api/libheif/heif_experimental.cc + codecs/hevc.cc + codecs/hevc.h + codecs/avif.cc + codecs/avif.h + codecs/jpeg.h + codecs/jpeg.cc + codecs/jpeg2000.h + codecs/jpeg2000.cc + codecs/vvc.h + codecs/vvc.cc + codecs/avc.h + codecs/avc.cc + codecs/mask_image.cc + codecs/mask_image.h color-conversion/colorconversion.cc color-conversion/colorconversion.h color-conversion/rgb2yuv.cc @@ -73,14 +79,6 @@ set(libheif_sources color-conversion/alpha.h color-conversion/chroma_sampling.cc color-conversion/chroma_sampling.h - jpeg.h - jpeg.cc - jpeg2000.h - jpeg2000.cc - vvc.h - vvc.cc - avc.h - avc.cc ${libheif_headers}) add_library(heif ${libheif_sources}) @@ -94,7 +92,7 @@ if (ENABLE_PLUGIN_LOADING) endif () # Needed to find libheif/heif_version.h while compiling the library -target_include_directories(heif PRIVATE ${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}) +target_include_directories(heif PRIVATE ${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif ${libheif_SOURCE_DIR}/libheif/api) # Propagate include/libheif to consume the headers from other projects target_include_directories(heif @@ -154,10 +152,10 @@ endif () if (WITH_UNCOMPRESSED_CODEC) target_compile_definitions(heif PUBLIC WITH_UNCOMPRESSED_CODEC=1) target_sources(heif PRIVATE - uncompressed_box.h - uncompressed_box.cc - uncompressed_image.h - uncompressed_image.cc) + codecs/uncompressed_box.h + codecs/uncompressed_box.cc + codecs/uncompressed_image.h + codecs/uncompressed_image.cc) endif () write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion) diff --git a/libheif/api_structs.h b/libheif/api/libheif/api_structs.h similarity index 100% rename from libheif/api_structs.h rename to libheif/api/libheif/api_structs.h diff --git a/libheif/heif.cc b/libheif/api/libheif/heif.cc similarity index 99% rename from libheif/heif.cc rename to libheif/api/libheif/heif.cc index b426585e41..4bab78dd06 100644 --- a/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -18,9 +18,9 @@ * along with libheif. If not, see . */ -#include "libheif/heif_plugin.h" -#include "libheif/region.h" -#include "libheif/common_utils.h" +#include "heif_plugin.h" +#include "region.h" +#include "common_utils.h" #include #include "heif.h" #include "file.h" diff --git a/libheif/heif.h b/libheif/api/libheif/heif.h similarity index 100% rename from libheif/heif.h rename to libheif/api/libheif/heif.h diff --git a/libheif/heif_cxx.h b/libheif/api/libheif/heif_cxx.h similarity index 100% rename from libheif/heif_cxx.h rename to libheif/api/libheif/heif_cxx.h diff --git a/libheif/heif_emscripten.h b/libheif/api/libheif/heif_emscripten.h similarity index 100% rename from libheif/heif_emscripten.h rename to libheif/api/libheif/heif_emscripten.h diff --git a/libheif/heif_experimental.cc b/libheif/api/libheif/heif_experimental.cc similarity index 99% rename from libheif/heif_experimental.cc rename to libheif/api/libheif/heif_experimental.cc index 3b89a6f82c..ed850347c9 100644 --- a/libheif/heif_experimental.cc +++ b/libheif/api/libheif/heif_experimental.cc @@ -18,7 +18,7 @@ * along with libheif. If not, see . */ -#include "libheif/heif_experimental.h" +#include "heif_experimental.h" #include "context.h" #include "api_structs.h" #include "file.h" diff --git a/libheif/heif_experimental.h b/libheif/api/libheif/heif_experimental.h similarity index 100% rename from libheif/heif_experimental.h rename to libheif/api/libheif/heif_experimental.h diff --git a/libheif/heif_items.cc b/libheif/api/libheif/heif_items.cc similarity index 99% rename from libheif/heif_items.cc rename to libheif/api/libheif/heif_items.cc index 06a6d7ac55..dd96dd4ae4 100644 --- a/libheif/heif_items.cc +++ b/libheif/api/libheif/heif_items.cc @@ -18,7 +18,7 @@ * along with libheif. If not, see . */ -#include "libheif/heif_items.h" +#include "heif_items.h" #include "context.h" #include "api_structs.h" #include "file.h" diff --git a/libheif/heif_items.h b/libheif/api/libheif/heif_items.h similarity index 100% rename from libheif/heif_items.h rename to libheif/api/libheif/heif_items.h diff --git a/libheif/heif_plugin.cc b/libheif/api/libheif/heif_plugin.cc similarity index 100% rename from libheif/heif_plugin.cc rename to libheif/api/libheif/heif_plugin.cc diff --git a/libheif/heif_plugin.h b/libheif/api/libheif/heif_plugin.h similarity index 100% rename from libheif/heif_plugin.h rename to libheif/api/libheif/heif_plugin.h diff --git a/libheif/heif_properties.cc b/libheif/api/libheif/heif_properties.cc similarity index 100% rename from libheif/heif_properties.cc rename to libheif/api/libheif/heif_properties.cc diff --git a/libheif/heif_properties.h b/libheif/api/libheif/heif_properties.h similarity index 99% rename from libheif/heif_properties.h rename to libheif/api/libheif/heif_properties.h index e5917785df..4f54561871 100644 --- a/libheif/heif_properties.h +++ b/libheif/api/libheif/heif_properties.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_HEIF_PROPERTIES_H #define LIBHEIF_HEIF_PROPERTIES_H -#include "libheif/heif.h" +#include "heif.h" #ifdef __cplusplus extern "C" { diff --git a/libheif/heif_regions.cc b/libheif/api/libheif/heif_regions.cc similarity index 99% rename from libheif/heif_regions.cc rename to libheif/api/libheif/heif_regions.cc index 54eb43f74f..2d26f2fb0d 100644 --- a/libheif/heif_regions.cc +++ b/libheif/api/libheif/heif_regions.cc @@ -19,9 +19,9 @@ * along with libheif. If not, see . */ -#include "libheif/heif_plugin.h" -#include "libheif/region.h" -#include "libheif/heif_regions.h" +#include "heif_plugin.h" +#include "region.h" +#include "heif_regions.h" #include "file.h" #include "api_structs.h" #include "context.h" diff --git a/libheif/heif_regions.h b/libheif/api/libheif/heif_regions.h similarity index 99% rename from libheif/heif_regions.h rename to libheif/api/libheif/heif_regions.h index 63083fba2a..4b6f662089 100644 --- a/libheif/heif_regions.h +++ b/libheif/api/libheif/heif_regions.h @@ -22,7 +22,7 @@ #ifndef LIBHEIF_HEIF_REGIONS_H #define LIBHEIF_HEIF_REGIONS_H -#include "libheif/heif.h" +#include "heif.h" #ifdef __cplusplus extern "C" { diff --git a/libheif/heif_version.h.in b/libheif/api/libheif/heif_version.h.in similarity index 100% rename from libheif/heif_version.h.in rename to libheif/api/libheif/heif_version.h.in diff --git a/libheif/box.cc b/libheif/box.cc index f2e3e9ef5c..c762f51f32 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -23,12 +23,12 @@ #include "box.h" #include "security_limits.h" #include "nclx.h" -#include "jpeg.h" -#include "jpeg2000.h" -#include "hevc.h" -#include "mask_image.h" -#include "vvc.h" -#include "avc.h" +#include "codecs/jpeg.h" +#include "codecs/jpeg2000.h" +#include "codecs/hevc.h" +#include "codecs/mask_image.h" +#include "codecs/vvc.h" +#include "codecs/avc.h" #include #include @@ -40,7 +40,7 @@ #include #if WITH_UNCOMPRESSED_CODEC -#include "uncompressed_box.h" +#include "codecs/uncompressed_box.h" #endif #ifndef M_PI diff --git a/libheif/box.h b/libheif/box.h index 3fcc8e3da2..61f736c797 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -22,7 +22,8 @@ #define LIBHEIF_BOX_H #include -#include "libheif/common_utils.h" +#include "common_utils.h" +#include "libheif/heif.h" #include "libheif/heif_properties.h" #include #include @@ -36,7 +37,6 @@ #include #include "error.h" -#include "heif.h" #include "logging.h" #include "bitstream.h" diff --git a/libheif/avc.cc b/libheif/codecs/avc.cc similarity index 100% rename from libheif/avc.cc rename to libheif/codecs/avc.cc diff --git a/libheif/avc.h b/libheif/codecs/avc.h similarity index 100% rename from libheif/avc.h rename to libheif/codecs/avc.h diff --git a/libheif/avif.cc b/libheif/codecs/avif.cc similarity index 100% rename from libheif/avif.cc rename to libheif/codecs/avif.cc diff --git a/libheif/avif.h b/libheif/codecs/avif.h similarity index 99% rename from libheif/avif.h rename to libheif/codecs/avif.h index 6924ac2315..7805ff5c54 100644 --- a/libheif/avif.h +++ b/libheif/codecs/avif.h @@ -27,7 +27,7 @@ #include #include -#include "heif.h" +#include "libheif/heif.h" #include "box.h" #include "error.h" diff --git a/libheif/hevc.cc b/libheif/codecs/hevc.cc similarity index 100% rename from libheif/hevc.cc rename to libheif/codecs/hevc.cc diff --git a/libheif/hevc.h b/libheif/codecs/hevc.h similarity index 99% rename from libheif/hevc.h rename to libheif/codecs/hevc.h index af5b285c06..3d2a0c3e88 100644 --- a/libheif/hevc.h +++ b/libheif/codecs/hevc.h @@ -21,7 +21,7 @@ #ifndef HEIF_HEVC_H #define HEIF_HEVC_H -#include "heif.h" +#include "libheif/heif.h" #include "box.h" #include "error.h" diff --git a/libheif/jpeg.cc b/libheif/codecs/jpeg.cc similarity index 100% rename from libheif/jpeg.cc rename to libheif/codecs/jpeg.cc diff --git a/libheif/jpeg.h b/libheif/codecs/jpeg.h similarity index 100% rename from libheif/jpeg.h rename to libheif/codecs/jpeg.h diff --git a/libheif/jpeg2000.cc b/libheif/codecs/jpeg2000.cc similarity index 100% rename from libheif/jpeg2000.cc rename to libheif/codecs/jpeg2000.cc diff --git a/libheif/jpeg2000.h b/libheif/codecs/jpeg2000.h similarity index 100% rename from libheif/jpeg2000.h rename to libheif/codecs/jpeg2000.h diff --git a/libheif/mask_image.cc b/libheif/codecs/mask_image.cc similarity index 99% rename from libheif/mask_image.cc rename to libheif/codecs/mask_image.cc index 7abdb10b1b..fc977818b4 100644 --- a/libheif/mask_image.cc +++ b/libheif/codecs/mask_image.cc @@ -25,10 +25,10 @@ #include #include #include -#include +#include #include "libheif/heif.h" -#include "libheif/logging.h" +#include "logging.h" #include "mask_image.h" Error Box_mskC::parse(BitstreamRange& range) diff --git a/libheif/mask_image.h b/libheif/codecs/mask_image.h similarity index 100% rename from libheif/mask_image.h rename to libheif/codecs/mask_image.h diff --git a/libheif/uncompressed.h b/libheif/codecs/uncompressed.h similarity index 100% rename from libheif/uncompressed.h rename to libheif/codecs/uncompressed.h diff --git a/libheif/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc similarity index 100% rename from libheif/uncompressed_box.cc rename to libheif/codecs/uncompressed_box.cc diff --git a/libheif/uncompressed_box.h b/libheif/codecs/uncompressed_box.h similarity index 99% rename from libheif/uncompressed_box.h rename to libheif/codecs/uncompressed_box.h index a1d75026d3..891d011dda 100644 --- a/libheif/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -24,7 +24,7 @@ #include "box.h" #include "bitstream.h" -#include "libheif/uncompressed.h" +#include "uncompressed.h" #include #include diff --git a/libheif/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc similarity index 99% rename from libheif/uncompressed_image.cc rename to libheif/codecs/uncompressed_image.cc index e14cfbd774..c40b659ec9 100644 --- a/libheif/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -27,8 +27,8 @@ #include #include -#include "libheif/common_utils.h" -#include "libheif/error.h" +#include "common_utils.h" +#include "error.h" #include "libheif/heif.h" #include "uncompressed.h" #include "uncompressed_box.h" diff --git a/libheif/uncompressed_image.h b/libheif/codecs/uncompressed_image.h similarity index 100% rename from libheif/uncompressed_image.h rename to libheif/codecs/uncompressed_image.h diff --git a/libheif/vvc.cc b/libheif/codecs/vvc.cc similarity index 100% rename from libheif/vvc.cc rename to libheif/codecs/vvc.cc diff --git a/libheif/vvc.h b/libheif/codecs/vvc.h similarity index 100% rename from libheif/vvc.h rename to libheif/codecs/vvc.h diff --git a/libheif/color-conversion/chroma_sampling.h b/libheif/color-conversion/chroma_sampling.h index 441f88e3ce..446bd952f8 100644 --- a/libheif/color-conversion/chroma_sampling.h +++ b/libheif/color-conversion/chroma_sampling.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_CHROMA_SAMPLING_H #define LIBHEIF_CHROMA_SAMPLING_H -#include "libheif/color-conversion/colorconversion.h" +#include "color-conversion/colorconversion.h" #include #include diff --git a/libheif/color-conversion/colorconversion.cc b/libheif/color-conversion/colorconversion.cc index 5a0d6b57a4..412e0b8df7 100644 --- a/libheif/color-conversion/colorconversion.cc +++ b/libheif/color-conversion/colorconversion.cc @@ -20,8 +20,8 @@ #include "colorconversion.h" -#include "libheif/common_utils.h" -#include "libheif/nclx.h" +#include "common_utils.h" +#include "nclx.h" #include #include #include diff --git a/libheif/color-conversion/colorconversion.h b/libheif/color-conversion/colorconversion.h index bcd4940aa4..969093e16d 100644 --- a/libheif/color-conversion/colorconversion.h +++ b/libheif/color-conversion/colorconversion.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_COLORCONVERSION_H #define LIBHEIF_COLORCONVERSION_H -#include "libheif/pixelimage.h" +#include "pixelimage.h" #include #include #include diff --git a/libheif/color-conversion/rgb2yuv.cc b/libheif/color-conversion/rgb2yuv.cc index 1c4104a438..0be8042e51 100644 --- a/libheif/color-conversion/rgb2yuv.cc +++ b/libheif/color-conversion/rgb2yuv.cc @@ -23,8 +23,8 @@ #include #include #include "rgb2yuv.h" -#include "libheif/nclx.h" -#include "libheif/common_utils.h" +#include "nclx.h" +#include "common_utils.h" template diff --git a/libheif/color-conversion/rgb2yuv.h b/libheif/color-conversion/rgb2yuv.h index 1f2fdadd52..74b929b0a0 100644 --- a/libheif/color-conversion/rgb2yuv.h +++ b/libheif/color-conversion/rgb2yuv.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_COLORCONVERSION_RGB2YUV_H #define LIBHEIF_COLORCONVERSION_RGB2YUV_H -#include "libheif/color-conversion/colorconversion.h" +#include "color-conversion/colorconversion.h" #include #include diff --git a/libheif/color-conversion/rgb2yuv_sharp.cc b/libheif/color-conversion/rgb2yuv_sharp.cc index b7994f62e4..8423391c9f 100644 --- a/libheif/color-conversion/rgb2yuv_sharp.cc +++ b/libheif/color-conversion/rgb2yuv_sharp.cc @@ -27,8 +27,8 @@ #include #include -#include "libheif/nclx.h" -#include "libheif/common_utils.h" +#include "nclx.h" +#include "common_utils.h" static inline bool PlatformIsBigEndian() { diff --git a/libheif/color-conversion/yuv2rgb.cc b/libheif/color-conversion/yuv2rgb.cc index 473763ad37..06f7ae756c 100644 --- a/libheif/color-conversion/yuv2rgb.cc +++ b/libheif/color-conversion/yuv2rgb.cc @@ -21,8 +21,8 @@ #include #include #include "yuv2rgb.h" -#include "libheif/nclx.h" -#include "libheif/common_utils.h" +#include "nclx.h" +#include "common_utils.h" template diff --git a/libheif/common_utils.h b/libheif/common_utils.h index d0913f5d41..1ab887ec10 100644 --- a/libheif/common_utils.h +++ b/libheif/common_utils.h @@ -22,7 +22,7 @@ #define LIBHEIF_COMMON_UTILS_H #include -#include +#include "libheif/heif.h" #ifdef _MSC_VER #define MAYBE_UNUSED diff --git a/libheif/context.cc b/libheif/context.cc index bc0fd323fb..ef6edad258 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -18,10 +18,10 @@ * along with libheif. If not, see . */ -#include "libheif/box.h" -#include "libheif/error.h" +#include "box.h" +#include "error.h" #include "libheif/heif.h" -#include "libheif/region.h" +#include "region.h" #include #include #include @@ -38,20 +38,20 @@ #include "context.h" #include "file.h" #include "pixelimage.h" -#include "api_structs.h" +#include "libheif/api_structs.h" #include "security_limits.h" -#include "hevc.h" -#include "vvc.h" -#include "avif.h" -#include "jpeg.h" -#include "plugin_registry.h" -#include "libheif/color-conversion/colorconversion.h" -#include "mask_image.h" #include "metadata_compression.h" -#include "jpeg2000.h" +#include "color-conversion/colorconversion.h" +#include "plugin_registry.h" +#include "codecs/hevc.h" +#include "codecs/vvc.h" +#include "codecs/avif.h" +#include "codecs/jpeg.h" +#include "codecs/mask_image.h" +#include "codecs/jpeg2000.h" #if WITH_UNCOMPRESSED_CODEC -#include "uncompressed_image.h" +#include "codecs/uncompressed_image.h" #endif diff --git a/libheif/context.h b/libheif/context.h index 1b608c1b35..3928d53937 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -30,8 +30,8 @@ #include "error.h" -#include "heif.h" -#include "heif_plugin.h" +#include "libheif/heif.h" +#include "libheif/heif_plugin.h" #include "bitstream.h" #include "box.h" // only for color_profile, TODO: maybe move the color_profiles to its own header diff --git a/libheif/error.h b/libheif/error.h index af2af88f07..9a68dd4f23 100644 --- a/libheif/error.h +++ b/libheif/error.h @@ -32,8 +32,7 @@ #include #include - -#include "heif.h" +#include "libheif/heif.h" static constexpr char kSuccess[] = "Success"; diff --git a/libheif/file.cc b/libheif/file.cc index 9ae765ce30..ea66d5279a 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -19,12 +19,13 @@ */ #include "file.h" -#include "libheif/box.h" +#include "box.h" #include "libheif/heif.h" #include "libheif/heif_properties.h" -#include "libheif/jpeg2000.h" -#include "libheif/jpeg.h" -#include "libheif/vvc.h" +#include "metadata_compression.h" +#include "codecs/jpeg2000.h" +#include "codecs/jpeg.h" +#include "codecs/vvc.h" #include #include @@ -44,11 +45,9 @@ #include #endif -#include "metadata_compression.h" -#include "jpeg2000.h" #if WITH_UNCOMPRESSED_CODEC -#include "uncompressed_image.h" +#include "codecs/uncompressed_image.h" #endif // TODO: make this a decoder option diff --git a/libheif/file.h b/libheif/file.h index 2f85a5a8e9..a489af9a87 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -21,11 +21,11 @@ #ifndef LIBHEIF_FILE_H #define LIBHEIF_FILE_H -#include "avif.h" #include "box.h" -#include "hevc.h" -#include "vvc.h" #include "nclx.h" +#include "codecs/avif.h" +#include "codecs/hevc.h" +#include "codecs/vvc.h" #include #include diff --git a/libheif/init.cc b/libheif/init.cc index 3ff4f5bccf..bbd5f6c4af 100644 --- a/libheif/init.cc +++ b/libheif/init.cc @@ -19,11 +19,11 @@ */ #include "init.h" -#include "heif.h" +#include "libheif/heif.h" #include "error.h" #include "plugin_registry.h" #include "common_utils.h" -#include "libheif/color-conversion/colorconversion.h" +#include "color-conversion/colorconversion.h" #if ENABLE_MULTITHREADING_SUPPORT diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h index 429e50e977..d050776cb8 100644 --- a/libheif/pixelimage.h +++ b/libheif/pixelimage.h @@ -22,7 +22,7 @@ #ifndef LIBHEIF_IMAGE_H #define LIBHEIF_IMAGE_H -#include "heif.h" +//#include "heif.h" #include "error.h" #include "nclx.h" diff --git a/libheif/plugin_registry.cc b/libheif/plugin_registry.cc index 897fa4780b..6d1b1b8c13 100644 --- a/libheif/plugin_registry.cc +++ b/libheif/plugin_registry.cc @@ -27,69 +27,69 @@ #if HAVE_LIBDE265 -#include "libheif/plugins/decoder_libde265.h" +#include "plugins/decoder_libde265.h" #endif #if HAVE_X265 -#include "libheif/plugins/encoder_x265.h" +#include "plugins/encoder_x265.h" #endif #if HAVE_KVAZAAR -#include "libheif/plugins/encoder_kvazaar.h" +#include "plugins/encoder_kvazaar.h" #endif #if HAVE_UVG266 -#include "libheif/plugins/encoder_uvg266.h" +#include "plugins/encoder_uvg266.h" #endif #if HAVE_AOM_ENCODER -#include "libheif/plugins/encoder_aom.h" +#include "plugins/encoder_aom.h" #endif #if HAVE_AOM_DECODER -#include "libheif/plugins/decoder_aom.h" +#include "plugins/decoder_aom.h" #endif #if HAVE_RAV1E -#include "libheif/plugins/encoder_rav1e.h" +#include "plugins/encoder_rav1e.h" #endif #if HAVE_DAV1D -#include "libheif/plugins/decoder_dav1d.h" +#include "plugins/decoder_dav1d.h" #endif #if HAVE_SvtEnc -#include "libheif/plugins/encoder_svt.h" +#include "plugins/encoder_svt.h" #endif #if HAVE_FFMPEG_DECODER -#include "libheif/plugins/decoder_ffmpeg.h" +#include "plugins/decoder_ffmpeg.h" #endif #if WITH_UNCOMPRESSED_CODEC -#include "libheif/plugins/encoder_uncompressed.h" +#include "plugins/encoder_uncompressed.h" #endif #if HAVE_JPEG_DECODER -#include "libheif/plugins/decoder_jpeg.h" +#include "plugins/decoder_jpeg.h" #endif #if HAVE_JPEG_ENCODER -#include "libheif/plugins/encoder_jpeg.h" +#include "plugins/encoder_jpeg.h" #endif #if HAVE_OPENJPEG_ENCODER -#include "libheif/plugins/encoder_openjpeg.h" +#include "plugins/encoder_openjpeg.h" #endif #if HAVE_OPENJPEG_DECODER -#include "libheif/plugins/decoder_openjpeg.h" +#include "plugins/decoder_openjpeg.h" #endif -#include "libheif/plugins/encoder_mask.h" +#include "plugins/encoder_mask.h" #if HAVE_OPENJPH_ENCODER -#include "libheif/plugins/encoder_openjph.h" +#include "plugins/encoder_openjph.h" #endif std::set s_decoder_plugins; diff --git a/libheif/plugin_registry.h b/libheif/plugin_registry.h index deb92c7fc4..962a47cff5 100644 --- a/libheif/plugin_registry.h +++ b/libheif/plugin_registry.h @@ -29,8 +29,8 @@ #include "error.h" -#include "heif.h" -#include "heif_plugin.h" +#include "libheif/heif.h" +#include "libheif/heif_plugin.h" struct heif_encoder_descriptor diff --git a/libheif/plugins/CMakeLists.txt b/libheif/plugins/CMakeLists.txt index 6b9cd700ba..61516adc8b 100644 --- a/libheif/plugins/CMakeLists.txt +++ b/libheif/plugins/CMakeLists.txt @@ -5,7 +5,7 @@ macro(plugin_compilation name varName foundName optionName defineName) message("Compiling '" ${name} "' as dynamic plugin") add_library(heif-${name} MODULE ${${optionName}_sources} ${${optionName}_extra_plugin_sources} - ../heif_plugin.cc + ../api/libheif/heif_plugin.cc ) if (NOT APPLE) set_target_properties(heif-${name} @@ -18,7 +18,7 @@ macro(plugin_compilation name varName foundName optionName defineName) LIBHEIF_EXPORTS HAVE_VISIBILITY) target_compile_definitions(heif-${name} PRIVATE PLUGIN_${defineName}=1) - target_include_directories(heif-${name} PRIVATE ${PROJECT_SOURCE_DIR} ${libheif_BINARY_DIR} ${libheif_SOURCE_DIR} ${${varName}_INCLUDE_DIRS}) + target_include_directories(heif-${name} PRIVATE ${PROJECT_SOURCE_DIR} ${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif ${libheif_SOURCE_DIR}/libheif/api ${${varName}_INCLUDE_DIRS}) target_link_libraries(heif-${name} PRIVATE ${${varName}_LIBRARIES} heif) install(TARGETS heif-${name} diff --git a/libheif/plugins/decoder_aom.h b/libheif/plugins/decoder_aom.h index a5f70b8de0..23ed80b9eb 100644 --- a/libheif/plugins/decoder_aom.h +++ b/libheif/plugins/decoder_aom.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_DECODER_AOM_H #define LIBHEIF_DECODER_AOM_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_decoder_plugin* get_decoder_plugin_aom(); diff --git a/libheif/plugins/decoder_dav1d.cc b/libheif/plugins/decoder_dav1d.cc index 9e46f1fe53..de68644125 100644 --- a/libheif/plugins/decoder_dav1d.cc +++ b/libheif/plugins/decoder_dav1d.cc @@ -20,8 +20,8 @@ #include "libheif/heif.h" #include "libheif/heif_plugin.h" -#include "libheif/security_limits.h" -#include "libheif/common_utils.h" +#include "security_limits.h" +#include "common_utils.h" #include "decoder_dav1d.h" #include #include diff --git a/libheif/plugins/decoder_dav1d.h b/libheif/plugins/decoder_dav1d.h index f18c0ccae9..e32d8b4ff2 100644 --- a/libheif/plugins/decoder_dav1d.h +++ b/libheif/plugins/decoder_dav1d.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_DECODER_DAV1D_H #define LIBHEIF_DECODER_DAV1D_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_decoder_plugin* get_decoder_plugin_dav1d(); diff --git a/libheif/plugins/decoder_ffmpeg.h b/libheif/plugins/decoder_ffmpeg.h index 4b0161a6cc..d4ee87bf79 100644 --- a/libheif/plugins/decoder_ffmpeg.h +++ b/libheif/plugins/decoder_ffmpeg.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_HEIF_DECODER_FFMPEG_H #define LIBHEIF_HEIF_DECODER_FFMPEG_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_decoder_plugin* get_decoder_plugin_ffmpeg(); diff --git a/libheif/plugins/decoder_jpeg.h b/libheif/plugins/decoder_jpeg.h index 9d63b74cfa..d40e441b15 100644 --- a/libheif/plugins/decoder_jpeg.h +++ b/libheif/plugins/decoder_jpeg.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_DECODER_JPEG_H #define LIBHEIF_DECODER_JPEG_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_decoder_plugin* get_decoder_plugin_jpeg(); diff --git a/libheif/plugins/decoder_libde265.h b/libheif/plugins/decoder_libde265.h index 0b2e8c3e69..ec93a45cc8 100644 --- a/libheif/plugins/decoder_libde265.h +++ b/libheif/plugins/decoder_libde265.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_HEIF_DECODER_DE265_H #define LIBHEIF_HEIF_DECODER_DE265_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_decoder_plugin* get_decoder_plugin_libde265(); diff --git a/libheif/plugins/decoder_openjpeg.h b/libheif/plugins/decoder_openjpeg.h index a93cfb0492..8380acb9e5 100644 --- a/libheif/plugins/decoder_openjpeg.h +++ b/libheif/plugins/decoder_openjpeg.h @@ -22,7 +22,7 @@ #ifndef LIBHEIF_DECODER_OPENJPEG_H #define LIBHEIF_DECODER_OPENJPEG_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_decoder_plugin* get_decoder_plugin_openjpeg(); diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc index a36e0c540b..445cf9e53c 100644 --- a/libheif/plugins/encoder_aom.cc +++ b/libheif/plugins/encoder_aom.cc @@ -20,7 +20,7 @@ #include "libheif/heif.h" #include "libheif/heif_plugin.h" -#include "libheif/common_utils.h" +#include "common_utils.h" #include #include #include diff --git a/libheif/plugins/encoder_aom.h b/libheif/plugins/encoder_aom.h index 5bd1d3183f..f1717e3588 100644 --- a/libheif/plugins/encoder_aom.h +++ b/libheif/plugins/encoder_aom.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_AOM_H #define LIBHEIF_ENCODER_AOM_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_encoder_plugin* get_encoder_plugin_aom(); diff --git a/libheif/plugins/encoder_jpeg.h b/libheif/plugins/encoder_jpeg.h index cdd7248934..bd59aa660d 100644 --- a/libheif/plugins/encoder_jpeg.h +++ b/libheif/plugins/encoder_jpeg.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_JPEG_H #define LIBHEIF_ENCODER_JPEG_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_encoder_plugin* get_encoder_plugin_jpeg(); diff --git a/libheif/plugins/encoder_kvazaar.h b/libheif/plugins/encoder_kvazaar.h index e0a98c5e48..772d4419b6 100644 --- a/libheif/plugins/encoder_kvazaar.h +++ b/libheif/plugins/encoder_kvazaar.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_KVAZAAR_H #define LIBHEIF_ENCODER_KVAZAAR_H -#include "libheif/common_utils.h" +#include "common_utils.h" /* TODO: check whether this is also the case with kvazaar. * diff --git a/libheif/plugins/encoder_mask.h b/libheif/plugins/encoder_mask.h index eb769f6580..0a55857acf 100644 --- a/libheif/plugins/encoder_mask.h +++ b/libheif/plugins/encoder_mask.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_MASK_H #define LIBHEIF_ENCODER_MASK_H -#include "libheif/common_utils.h" +#include "common_utils.h" // This is a dummy module. It does not actually do anything except parameter parsing. // The actual codec is included in the library. diff --git a/libheif/plugins/encoder_openjpeg.h b/libheif/plugins/encoder_openjpeg.h index 55dcfb8aad..1f2828ca8a 100644 --- a/libheif/plugins/encoder_openjpeg.h +++ b/libheif/plugins/encoder_openjpeg.h @@ -22,7 +22,7 @@ #ifndef LIBHEIF_ENCODER_OPENJPEG_H #define LIBHEIF_ENCODER_OPENJPEG_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_encoder_plugin* get_encoder_plugin_openjpeg(); diff --git a/libheif/plugins/encoder_openjph.h b/libheif/plugins/encoder_openjph.h index 1b8002834b..25e77e5719 100644 --- a/libheif/plugins/encoder_openjph.h +++ b/libheif/plugins/encoder_openjph.h @@ -23,7 +23,7 @@ #ifndef LIBHEIF_ENCODER_OPENJPH_H #define LIBHEIF_ENCODER_OPENJPH_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_encoder_plugin* get_encoder_plugin_openjph(); diff --git a/libheif/plugins/encoder_rav1e.h b/libheif/plugins/encoder_rav1e.h index d570f02926..bb0eb9c915 100644 --- a/libheif/plugins/encoder_rav1e.h +++ b/libheif/plugins/encoder_rav1e.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_RAV1E_H #define LIBHEIF_ENCODER_RAV1E_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_encoder_plugin* get_encoder_plugin_rav1e(); diff --git a/libheif/plugins/encoder_svt.h b/libheif/plugins/encoder_svt.h index 42c820a748..461b575bf0 100644 --- a/libheif/plugins/encoder_svt.h +++ b/libheif/plugins/encoder_svt.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_SVT_H #define LIBHEIF_ENCODER_SVT_H -#include "libheif/common_utils.h" +#include "common_utils.h" const struct heif_encoder_plugin* get_encoder_plugin_svt(); diff --git a/libheif/plugins/encoder_uncompressed.h b/libheif/plugins/encoder_uncompressed.h index e2bba2b5db..4cfc59986f 100644 --- a/libheif/plugins/encoder_uncompressed.h +++ b/libheif/plugins/encoder_uncompressed.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_UNCOMPRESSED_H #define LIBHEIF_ENCODER_UNCOMPRESSED_H -#include "libheif/common_utils.h" +#include "common_utils.h" // This is a dummy module. It does not actually do anything except parameter parsing. // The actual codec is included in the library. diff --git a/libheif/plugins/encoder_uvg266.cc b/libheif/plugins/encoder_uvg266.cc index 101743728d..812c3b4830 100644 --- a/libheif/plugins/encoder_uvg266.cc +++ b/libheif/plugins/encoder_uvg266.cc @@ -32,9 +32,6 @@ extern "C" { #include } -#include -#include - static const char* kError_unspecified_error = "Unspecified encoder error"; static const char* kError_unsupported_bit_depth = "Bit depth not supported by uvg266"; diff --git a/libheif/plugins/encoder_uvg266.h b/libheif/plugins/encoder_uvg266.h index b9eec90c25..61204c8e5c 100644 --- a/libheif/plugins/encoder_uvg266.h +++ b/libheif/plugins/encoder_uvg266.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_UVG266_H #define LIBHEIF_ENCODER_UVG266_H -#include "libheif/common_utils.h" +#include "common_utils.h" /* TODO: check whether this is also the case with uvg266. * diff --git a/libheif/plugins/encoder_x265.h b/libheif/plugins/encoder_x265.h index f9dbdf20f6..4869280e23 100644 --- a/libheif/plugins/encoder_x265.h +++ b/libheif/plugins/encoder_x265.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_ENCODER_X265_H #define LIBHEIF_ENCODER_X265_H -#include "libheif/common_utils.h" +#include "common_utils.h" /* Image sizes in HEVC: since HEVC does not allow for odd image dimensions when using chroma 4:2:0, our strategy is as follows. diff --git a/libheif/plugins_unix.cc b/libheif/plugins_unix.cc index 98687a13e6..e7ca9e6666 100644 --- a/libheif/plugins_unix.cc +++ b/libheif/plugins_unix.cc @@ -20,7 +20,7 @@ #include "plugins_unix.h" -#include "heif_plugin.h" +#include "libheif/heif_plugin.h" #include #include diff --git a/libheif/plugins_windows.cc b/libheif/plugins_windows.cc index ad0e8ae3b6..b11cec9440 100644 --- a/libheif/plugins_windows.cc +++ b/libheif/plugins_windows.cc @@ -20,7 +20,7 @@ #include "plugins_windows.h" -#include "heif_plugin.h" +#include "libheif/heif_plugin.h" #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ae3869b723..399b48f11a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ # Needed to find libheif/heif_version.h while compiling the library -include_directories(${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}) +include_directories(${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif ${libheif_SOURCE_DIR}/libheif/api) # prepare C++ configuration file (test-config.cc) diff --git a/tests/avc_box.cc b/tests/avc_box.cc index c9165de8d0..4976fbbb65 100644 --- a/tests/avc_box.cc +++ b/tests/avc_box.cc @@ -25,8 +25,8 @@ */ #include "catch.hpp" -#include "libheif/avc.h" -#include "libheif/error.h" +#include "avc.h" +#include "error.h" #include #include #include diff --git a/tests/box_equals.cc b/tests/box_equals.cc index 1cfb596320..a9ee6c9c82 100644 --- a/tests/box_equals.cc +++ b/tests/box_equals.cc @@ -25,7 +25,7 @@ */ #include "catch.hpp" -#include "libheif/box.h" +#include "box.h" TEST_CASE("box equals") { std::shared_ptr ispe1 = std::make_shared(); diff --git a/tests/conversion.cc b/tests/conversion.cc index 86d96da6fc..3ca71419bd 100644 --- a/tests/conversion.cc +++ b/tests/conversion.cc @@ -26,8 +26,8 @@ #include #include "catch.hpp" -#include "libheif/color-conversion/colorconversion.h" -#include "libheif/pixelimage.h" +#include "color-conversion/colorconversion.h" +#include "pixelimage.h" // Enable for more verbose test output. constexpr bool kEnableDebugOutput = false; diff --git a/tests/encode.cc b/tests/encode.cc index 48b83ee889..d6adff5bb8 100644 --- a/tests/encode.cc +++ b/tests/encode.cc @@ -26,7 +26,7 @@ #include "catch.hpp" #include "libheif/heif.h" -#include "libheif/pixelimage.h" +#include "pixelimage.h" #include "libheif/api_structs.h" #include diff --git a/tests/encode_htj2k.cc b/tests/encode_htj2k.cc index 9214c7031e..98c84efd4d 100644 --- a/tests/encode_htj2k.cc +++ b/tests/encode_htj2k.cc @@ -26,8 +26,8 @@ #include "catch.hpp" #include "libheif/heif.h" -#include "libheif/pixelimage.h" #include "libheif/api_structs.h" +#include "pixelimage.h" #include "test_utils.h" diff --git a/tests/encode_jpeg2000.cc b/tests/encode_jpeg2000.cc index bb2c7cb2e0..1acf532be5 100644 --- a/tests/encode_jpeg2000.cc +++ b/tests/encode_jpeg2000.cc @@ -26,8 +26,8 @@ #include "catch.hpp" #include "libheif/heif.h" -#include "libheif/pixelimage.h" #include "libheif/api_structs.h" +#include "pixelimage.h" #include "test_utils.h" diff --git a/tests/idat.cc b/tests/idat.cc index f03721b1b7..7782516891 100644 --- a/tests/idat.cc +++ b/tests/idat.cc @@ -25,7 +25,7 @@ */ #include "catch.hpp" -#include "libheif/box.h" +#include "box.h" #include #include diff --git a/tests/jpeg2000.cc b/tests/jpeg2000.cc index d6a1155711..81a46702ca 100644 --- a/tests/jpeg2000.cc +++ b/tests/jpeg2000.cc @@ -25,8 +25,8 @@ */ #include "catch.hpp" -#include "libheif/heif.h" -#include "libheif/jpeg2000.h" +#include "heif.h" +#include "jpeg2000.h" #include #include diff --git a/tests/uncompressed_box.cc b/tests/uncompressed_box.cc index 0b6a2b464d..3726ca229c 100644 --- a/tests/uncompressed_box.cc +++ b/tests/uncompressed_box.cc @@ -27,8 +27,8 @@ #include "catch.hpp" #include "libheif/box.h" #include "libheif/heif.h" -#include "libheif/uncompressed.h" -#include "libheif/uncompressed_box.h" +#include "libheif/codecs/uncompressed.h" +#include "libheif/codecs/uncompressed_box.h" #include #include From 9dd33d718ad1235304abb273549aa41d40434abd Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 22:34:56 +0200 Subject: [PATCH 032/129] adapt new paths in CI --- scripts/check-emscripten-enums.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/check-emscripten-enums.sh b/scripts/check-emscripten-enums.sh index 5c2d5486c6..aec939d22e 100755 --- a/scripts/check-emscripten-enums.sh +++ b/scripts/check-emscripten-enums.sh @@ -31,14 +31,14 @@ DEFINE_TYPES=" API_DEFINES="" for type in $DEFINE_TYPES; do - DEFINES=$(grep "^[ \t]*$type" libheif/heif.h | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g') + DEFINES=$(grep "^[ \t]*$type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g') if [ -z "$API_DEFINES" ]; then API_DEFINES="$DEFINES" else API_DEFINES="$API_DEFINES $DEFINES" fi - ALIASES=$(grep "^[ \t]*#define $type" libheif/heif.h | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g') + ALIASES=$(grep "^[ \t]*#define $type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g') if [ ! -z "$ALIASES" ]; then API_DEFINES="$API_DEFINES $ALIASES" @@ -48,7 +48,7 @@ API_DEFINES=$(echo "$API_DEFINES" | sort) EMSCRIPTEN_DEFINES="" for type in $DEFINE_TYPES; do - DEFINES=$(grep "\.value(\"$type" libheif/heif_emscripten.h | sed 's|[^\"]*\"\(.*\)\".*|\1|g') + DEFINES=$(grep "\.value(\"$type" libheif/api/libheif/heif_emscripten.h | sed 's|[^\"]*\"\(.*\)\".*|\1|g') if [ -z "$EMSCRIPTEN_DEFINES" ]; then EMSCRIPTEN_DEFINES="$DEFINES" else From 3ad5c0aed28c11aeade500d04a565b505347177c Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 22:36:27 +0200 Subject: [PATCH 033/129] adapt new paths in CI --- scripts/check-go-enums.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/check-go-enums.sh b/scripts/check-go-enums.sh index d77f3e7576..06270daf0c 100755 --- a/scripts/check-go-enums.sh +++ b/scripts/check-go-enums.sh @@ -31,14 +31,14 @@ DEFINE_TYPES=" API_DEFINES="" for type in $DEFINE_TYPES; do - DEFINES=$(grep "^[ \t]*$type" libheif/heif.h | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g') + DEFINES=$(grep "^[ \t]*$type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*\([^ \t=]*\)[[:space:]]*=.*|\1|g') if [ -z "$API_DEFINES" ]; then API_DEFINES="$DEFINES" else API_DEFINES="$API_DEFINES $DEFINES" fi - ALIASES=$(grep "^[ \t]*#define $type" libheif/heif.h | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g') + ALIASES=$(grep "^[ \t]*#define $type" libheif/api/libheif/heif.h | sed 's|[[:space:]]*#define \([^ \t]*\)[[:space:]]*.*|\1|g') if [ ! -z "$ALIASES" ]; then API_DEFINES="$API_DEFINES $ALIASES" From 7a34b62a8bd3b8c4051c3e93569c7292cb41a5d6 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 22:39:15 +0200 Subject: [PATCH 034/129] adapt more paths --- fuzzing/CMakeLists.txt | 2 +- tests/jpeg2000.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt index 0aeb63064b..02fb513803 100644 --- a/fuzzing/CMakeLists.txt +++ b/fuzzing/CMakeLists.txt @@ -1,4 +1,4 @@ -include_directories(${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}) +include_directories(${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif ${libheif_SOURCE_DIR}/libheif/api) add_executable(box_fuzzer box_fuzzer.cc) diff --git a/tests/jpeg2000.cc b/tests/jpeg2000.cc index 81a46702ca..7f96398e6b 100644 --- a/tests/jpeg2000.cc +++ b/tests/jpeg2000.cc @@ -25,7 +25,7 @@ */ #include "catch.hpp" -#include "heif.h" +#include "libheif/heif.h" #include "jpeg2000.h" #include #include From a137da9740c3f68443c1ad98eaa993db2dced80d Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 22:45:36 +0200 Subject: [PATCH 035/129] add new errors to Go binding --- go/heif/heif.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/go/heif/heif.go b/go/heif/heif.go index ad2fa5b641..71e531e967 100644 --- a/go/heif/heif.go +++ b/go/heif/heif.go @@ -251,6 +251,8 @@ const ( SuberrorCannotReadPluginDirectory = C.heif_suberror_Cannot_read_plugin_directory + SuberrorNoMatchingDecoderInstalled = C.heif_suberror_No_matching_decoder_installed + SuberrorNoOrInvalidPrimaryItem = C.heif_suberror_No_or_invalid_primary_item SuberrorNoInfeBox = C.heif_suberror_No_infe_box @@ -263,6 +265,10 @@ const ( SuberrorInvalidImageSize = C.heif_suberror_Invalid_image_size + SuberrorCameraIntrinsicMatrixUndefined = C.heif_suberror_Camera_intrinsic_matrix_undefined + + SuberrorCameraExtrinsicMatrixUndefined = C.heif_suberror_Camera_extrinsic_matrix_undefined + // --- Memory_allocation_error --- // A security limit preventing unreasonable memory allocations was exceeded by the input file. @@ -301,6 +307,8 @@ const ( SuberrorInvalidRegionData = C.heif_suberror_Invalid_region_data + SuberrorNoIspeProperty = C.heif_suberror_No_ispe_property + SuberrorWrongTileImagePixelDepth = C.heif_suberror_Wrong_tile_image_pixel_depth SuberrorUnknownNCLXColorPrimaries = C.heif_suberror_Unknown_NCLX_color_primaries @@ -309,7 +317,9 @@ const ( SuberrorUnknownNCLXMatrixCoefficients = C.heif_suberror_Unknown_NCLX_matrix_coefficients - SuberrorInvalidJPEG2000Codestream = C.heif_suberror_Invalid_J2K_codestream + SuberrorInvalidJ2KCodestream = C.heif_suberror_Invalid_J2K_codestream + + SuberrorNoVcCBox = C.heif_suberror_No_vvcC_box // --- Unsupported_feature --- From 0730781414f72292b541c0fccf493d18ba11f4a3 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 22:48:33 +0200 Subject: [PATCH 036/129] uncompressed: move member variable initialization --- libheif/codecs/uncompressed_box.h | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed_box.h index 891d011dda..98ae3da60b 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -76,22 +76,7 @@ class Box_cmpd : public Box class Box_uncC : public FullBox { public: - Box_uncC() : - m_profile(0), - m_sampling_type(sampling_mode_no_subsampling), - m_interleave_type(interleave_mode_pixel), - m_block_size(0), - m_components_little_endian(false), - m_block_pad_lsb(false), - m_block_little_endian(false), - m_block_reversed(false), - m_pad_unknown(false), - m_pixel_size(0), - m_row_align_size(0), - m_tile_align_size(0), - m_num_tile_cols(1), - m_num_tile_rows(1) - { + Box_uncC() { set_short_type(fourcc("uncC")); } @@ -220,19 +205,19 @@ class Box_uncC : public FullBox uint32_t m_profile = 0; // not compliant to any profile std::vector m_components; - uint8_t m_sampling_type = 0; // no subsampling - uint8_t m_interleave_type = 0; // component interleaving + uint8_t m_sampling_type = sampling_mode_no_subsampling; // no subsampling + uint8_t m_interleave_type = interleave_mode_pixel; // component interleaving uint8_t m_block_size = 0; - bool m_components_little_endian = true; + bool m_components_little_endian = false; bool m_block_pad_lsb = false; - bool m_block_little_endian = true; + bool m_block_little_endian = false; bool m_block_reversed = false; - bool m_pad_unknown = true; + bool m_pad_unknown = false; uint32_t m_pixel_size = 0; uint32_t m_row_align_size = 0; uint32_t m_tile_align_size = 0; - uint32_t m_num_tile_cols = 0; - uint32_t m_num_tile_rows = 0; + uint32_t m_num_tile_cols = 1; + uint32_t m_num_tile_rows = 1; }; #endif //LIBHEIF_UNCOMPRESSED_BOX_H From cc392b7d5a1a765cbada5e0c0e50f89cbf16a69b Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 22:49:43 +0200 Subject: [PATCH 037/129] adapt path for jpeg2000 test --- tests/jpeg2000.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/jpeg2000.cc b/tests/jpeg2000.cc index 7f96398e6b..f528a5ddcc 100644 --- a/tests/jpeg2000.cc +++ b/tests/jpeg2000.cc @@ -26,7 +26,7 @@ #include "catch.hpp" #include "libheif/heif.h" -#include "jpeg2000.h" +#include "codecs/jpeg2000.h" #include #include From cb3f8f13b4bef04e6ff62587d4ff120f6e721409 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 23:09:43 +0200 Subject: [PATCH 038/129] fix uninitialized value in JPEG2000_Extension_Capability --- libheif/codecs/jpeg2000.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libheif/codecs/jpeg2000.h b/libheif/codecs/jpeg2000.h index e21992041a..ef2bba04e2 100644 --- a/libheif/codecs/jpeg2000.h +++ b/libheif/codecs/jpeg2000.h @@ -405,7 +405,7 @@ class JPEG2000_Extension_Capability { private: const uint8_t ident; - uint16_t value; + uint16_t value = 0; }; class JPEG2000_Extension_Capability_HT : public JPEG2000_Extension_Capability From 75ecfb549c4978843c2257cf1ca2488b33ee3daf Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 23:12:18 +0200 Subject: [PATCH 039/129] fix clang-tidy warnings/errors --- libheif/codecs/jpeg2000.h | 4 ++-- libheif/codecs/vvc.cc | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libheif/codecs/jpeg2000.h b/libheif/codecs/jpeg2000.h index ef2bba04e2..e1b00486ab 100644 --- a/libheif/codecs/jpeg2000.h +++ b/libheif/codecs/jpeg2000.h @@ -438,7 +438,7 @@ class JPEG2000_CAP_segment struct JPEG2000MainHeader { public: - JPEG2000MainHeader() {} + JPEG2000MainHeader() = default; Error parseHeader(const HeifFile& file, const heif_item_id imageID); @@ -499,7 +499,7 @@ struct JPEG2000MainHeader uint16_t read16() { - uint16_t res = (headerData[cursor] << 8) | headerData[cursor + 1]; + uint16_t res = (uint16_t)((headerData[cursor] << 8) | headerData[cursor + 1]); cursor += 2; return res; } diff --git a/libheif/codecs/vvc.cc b/libheif/codecs/vvc.cc index e4aa2e4720..2fb04f835a 100644 --- a/libheif/codecs/vvc.cc +++ b/libheif/codecs/vvc.cc @@ -136,10 +136,10 @@ Error Box_vvcC::write(StreamWriter& writer) const assert(c.lengthSize == 1 || c.lengthSize == 2 || c.lengthSize == 4); - uint8_t v = ((c.constantFrameRate << 6) | - (c.numTemporalLayers << 3) | - ((c.lengthSize - 1) << 1) | - (c.ptl_present_flag ? 1 : 0)); + uint8_t v = (uint8_t) ((c.constantFrameRate << 6) | + (c.numTemporalLayers << 3) | + ((c.lengthSize - 1) << 1) | + (c.ptl_present_flag ? 1 : 0)); writer.write8(v); if (c.ptl_present_flag) { @@ -357,6 +357,7 @@ Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, bool success; success = reader.get_uvlc(&sps_pic_width_max_in_luma_samples); + (void)success; success = reader.get_uvlc(&sps_pic_height_max_in_luma_samples); (void)success; From 56211ac6567305c6290dc908515ea82a55ef2b7c Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 23:31:05 +0200 Subject: [PATCH 040/129] trying to fix clang-tidy --- libheif/api/libheif/heif.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index 4bab78dd06..d702fb7798 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -363,10 +363,11 @@ struct heif_error heif_list_compatible_brands(const uint8_t* data, int len, heif } auto brands = ftyp->list_brands(); - *out_brands = (heif_brand2*) malloc(sizeof(heif_brand2) * brands.size()); - *out_size = (int) brands.size(); + size_t nBrands = brands.size(); + *out_brands = (heif_brand2*) malloc(sizeof(heif_brand2) * nBrands); + *out_size = (int)nBrands; - for (int i = 0; i < (int) brands.size(); i++) { + for (size_t i = 0; i < nBrands; i++) { (*out_brands)[i] = brands[i]; } From f9113af713c6fd2f9700880d3dc3adc26653dd0f Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 23:40:11 +0200 Subject: [PATCH 041/129] AVC: fix path --- tests/avc_box.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/avc_box.cc b/tests/avc_box.cc index 4976fbbb65..4a2fd7bc3b 100644 --- a/tests/avc_box.cc +++ b/tests/avc_box.cc @@ -25,7 +25,7 @@ */ #include "catch.hpp" -#include "avc.h" +#include "codecs/avc.h" #include "error.h" #include #include From d15b6fdd29c4411fd54124057b3b354a47a4e6ad Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 26 Jun 2024 23:53:02 +0200 Subject: [PATCH 042/129] fuzzing: fix undefined shift --- fuzzing/box_fuzzer.cc | 6 +++--- fuzzing/color_conversion_fuzzer.cc | 6 +++--- libheif/common_utils.h | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/fuzzing/box_fuzzer.cc b/fuzzing/box_fuzzer.cc index ac95175545..f47e9ae111 100644 --- a/fuzzing/box_fuzzer.cc +++ b/fuzzing/box_fuzzer.cc @@ -20,9 +20,9 @@ #include -#include "libheif/box.h" -#include "libheif/bitstream.h" -#include "libheif/logging.h" +#include "box.h" +#include "bitstream.h" +#include "logging.h" extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { diff --git a/fuzzing/color_conversion_fuzzer.cc b/fuzzing/color_conversion_fuzzer.cc index 2901d6ffea..3de50ffeaa 100644 --- a/fuzzing/color_conversion_fuzzer.cc +++ b/fuzzing/color_conversion_fuzzer.cc @@ -22,9 +22,9 @@ #include -#include "libheif/bitstream.h" -#include "libheif/color-conversion/colorconversion.h" -#include "libheif/pixelimage.h" +#include "bitstream.h" +#include "color-conversion/colorconversion.h" +#include "pixelimage.h" static bool is_valid_chroma(uint8_t chroma) { diff --git a/libheif/common_utils.h b/libheif/common_utils.h index 1ab887ec10..f7f4a5f5ab 100644 --- a/libheif/common_utils.h +++ b/libheif/common_utils.h @@ -33,10 +33,10 @@ constexpr inline uint32_t fourcc_to_uint32(const char* id) { - return ((((uint32_t) id[0]) << 24) | - (((uint32_t) id[1]) << 16) | - (((uint32_t) id[2]) << 8) | - (((uint32_t) id[3]) << 0)); + return (((((uint32_t) id[0])&0xFF) << 24) | + ((((uint32_t) id[1])&0xFF) << 16) | + ((((uint32_t) id[2])&0xFF) << 8) | + ((((uint32_t) id[3])&0xFF) << 0)); } From 1b51e0d62780bebb5792ccd7dd53b9793abb3e6b Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 00:03:46 +0200 Subject: [PATCH 043/129] only build uncompressed_box test when visibility is off and uncompressed is enabled --- CMakePresets.json | 3 ++- tests/CMakeLists.txt | 3 +++ tests/uncompressed_box.cc | 6 +++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index e449dec42e..ef53ba5134 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -137,7 +137,8 @@ "CMAKE_BUILD_TYPE": "Debug", "ENABLE_PLUGIN_LOADING" : "OFF", "BUILD_TESTING" : "ON", - "WITH_REDUCED_VISIBILITY" : "OFF" + "WITH_REDUCED_VISIBILITY" : "OFF", + "WITH_UNCOMPRESSED_CODEC" : "ON } }, { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 399b48f11a..82152065b9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,6 +23,9 @@ else() add_libheif_test(idat) add_libheif_test(jpeg2000) add_libheif_test(avc_box) +endif() + +if (NOT WITH_REDUCED_VISIBILITY AND WITH_UNCOMPRESSED_CODEC) add_libheif_test(uncompressed_box) endif() diff --git a/tests/uncompressed_box.cc b/tests/uncompressed_box.cc index 3726ca229c..630e79b1f3 100644 --- a/tests/uncompressed_box.cc +++ b/tests/uncompressed_box.cc @@ -25,10 +25,10 @@ */ #include "catch.hpp" -#include "libheif/box.h" +#include "box.h" #include "libheif/heif.h" -#include "libheif/codecs/uncompressed.h" -#include "libheif/codecs/uncompressed_box.h" +#include "codecs/uncompressed.h" +#include "codecs/uncompressed_box.h" #include #include From dca605f900b2c0036acdee658888c72b92e1c242 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 00:07:00 +0200 Subject: [PATCH 044/129] cmin/cmex: use unsigned shift for denominators --- libheif/box.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index c762f51f32..645dc4b27a 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -3348,7 +3348,7 @@ Error Box_cmin::parse(BitstreamRange& range) parse_full_box_header(range); m_denominatorShift = (get_flags() & 0x1F00) >> 8; - uint32_t denominator = (1 << m_denominatorShift); + uint32_t denominator = (1U << m_denominatorShift); m_matrix.focal_length_x = range.read32s() / (double)denominator; m_matrix.principal_point_x = range.read32s() / (double)denominator; @@ -3356,7 +3356,7 @@ Error Box_cmin::parse(BitstreamRange& range) if (get_flags() & 1) { m_skewDenominatorShift = ((get_flags()) & 0x1F0000) >> 16; - uint32_t skewDenominator = (1<(m_matrix.focal_length_x * denominator)); writer.write32s(static_cast(m_matrix.principal_point_x * denominator)); @@ -3436,7 +3436,7 @@ Error Box_cmin::write(StreamWriter& writer) const if (get_flags() & 1) { writer.write32s(static_cast(m_matrix.focal_length_y * denominator)); - uint32_t skewDenominator = (1 << m_skewDenominatorShift); + uint32_t skewDenominator = (1U << m_skewDenominatorShift); writer.write32s(static_cast(m_matrix.skew * skewDenominator)); } @@ -3556,7 +3556,7 @@ Error Box_cmex::parse(BitstreamRange& range) int32_t quat_y = use32bit ? range.read32s() : range.read16s(); int32_t quat_z = use32bit ? range.read32s() : range.read16s(); - uint32_t div = 1<<(14 + (use32bit ? 16 : 0)); + uint32_t div = 1U << (14 + (use32bit ? 16 : 0)); m_matrix.rotation_as_quaternions = true; m_matrix.quaternion_x = quat_x / (double)div; From 527ad059424cd11fc690ddfcb5e4a5f1758f17ba Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 00:07:56 +0200 Subject: [PATCH 045/129] fix typo in CMakePresets --- CMakePresets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakePresets.json b/CMakePresets.json index ef53ba5134..845adf4eec 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -138,7 +138,7 @@ "ENABLE_PLUGIN_LOADING" : "OFF", "BUILD_TESTING" : "ON", "WITH_REDUCED_VISIBILITY" : "OFF", - "WITH_UNCOMPRESSED_CODEC" : "ON + "WITH_UNCOMPRESSED_CODEC" : "ON" } }, { From 0931b2ab0bb46fa495b73ea0caaa16316ff69557 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 00:44:37 +0200 Subject: [PATCH 046/129] increase version to v1.18.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 465e25210d..b1de7d68f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.16.3) # Oldest Ubuntu LTS (20.04 currently) -project(libheif LANGUAGES C CXX VERSION 1.17.6) +project(libheif LANGUAGES C CXX VERSION 1.18.0) # compatibility_version is never allowed to be decreased for any specific SONAME. # Libtool in the libheif-1.15.1 release had set it to 17.0.0, so we have to use this for the v1.x.y versions. From 7b6c39c2427f711f15c3d332e6ed6245d180fc43 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 00:48:18 +0200 Subject: [PATCH 047/129] update README with uvg266 flag --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2da93f0104..d40d97063b 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ For each codec, there are two configuration variables: * `WITH_{codec}_PLUGIN`: when enabled, the codec is compiled as a separate plugin. In order to use dynamic plugins, also make sure that `ENABLE_PLUGIN_LOADING` is enabled. -The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`, `OPENJPH_ENCODER` +The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`, `OPENJPH_ENCODER`, `UVG266`. Further options are: From 6fe94e956772e5e9902cc378637d13792ceced01 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Thu, 27 Jun 2024 22:49:26 +1000 Subject: [PATCH 048/129] doxygen: minor updates --- libheif/Doxyfile.in | 108 +------------------------------ libheif/api/libheif/heif_items.h | 2 +- 2 files changed, 4 insertions(+), 106 deletions(-) diff --git a/libheif/Doxyfile.in b/libheif/Doxyfile.in index 57aaa6523e..52d7ff6e1a 100644 --- a/libheif/Doxyfile.in +++ b/libheif/Doxyfile.in @@ -93,14 +93,6 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -864,9 +856,9 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_CURRENT_SOURCE_DIR@/libheif/heif.h \ -@CMAKE_CURRENT_SOURCE_DIR@/libheif/heif_items.h \ -@CMAKE_CURRENT_SOURCE_DIR@/libheif/heif_regions.h +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif.h \ +@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_items.h \ +@CMAKE_CURRENT_SOURCE_DIR@/libheif/api/libheif/heif_regions.h # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1327,15 +1319,6 @@ HTML_COLORSTYLE_SAT = 100 HTML_COLORSTYLE_GAMMA = 80 -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = NO - # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will @@ -1630,17 +1613,6 @@ HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. @@ -1944,16 +1916,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1962,14 +1924,6 @@ LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - # The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) # path from which the emoji images will be read. If a relative path is entered, # it will be relative to the LATEX_OUTPUT directory. If left blank the @@ -2034,16 +1988,6 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -2140,15 +2084,6 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -2327,15 +2262,6 @@ EXTERNAL_PAGES = YES # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2368,22 +2294,6 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the default font as specified with # DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set @@ -2621,18 +2531,6 @@ DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = NO - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support diff --git a/libheif/api/libheif/heif_items.h b/libheif/api/libheif/heif_items.h index eecaf0733c..baca69f0aa 100644 --- a/libheif/api/libheif/heif_items.h +++ b/libheif/api/libheif/heif_items.h @@ -158,7 +158,7 @@ struct heif_error heif_item_get_item_data(const struct heif_context* ctx, * Free the item data. * * This is used to free memory associated with the data returned by - * {@link heif_context_get_item_data} in 'out_data' and set the pointer to NULL. + * {@link heif_item_get_item_data} in 'out_data' and set the pointer to NULL. * * @param ctx the file context * @param item_data the data to free From f14f7a3dbed2ab7e13460000398f6100112bc53b Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 18:29:16 +0200 Subject: [PATCH 049/129] add vvdec based VVC decoder (#519) --- CMakeLists.txt | 13 ++ libheif/plugin_registry.cc | 8 + libheif/plugins/CMakeLists.txt | 4 + libheif/plugins/decoder_vvdec.cc | 334 +++++++++++++++++++++++++++++++ libheif/plugins/decoder_vvdec.h | 34 ++++ 5 files changed, 393 insertions(+) create mode 100644 libheif/plugins/decoder_vvdec.cc create mode 100644 libheif/plugins/decoder_vvdec.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b1de7d68f9..3676450446 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,18 @@ if (WITH_UVG266) endif () endif () +# vvdec + +plugin_option(VVDEC "vvdec VVC decoder (experimental)" OFF OFF) +if (WITH_VVDEC) + # TODO: how to do configure vvdec cleanly? + find_package(Threads REQUIRED) + find_package(vvdec 2.3.0) + if (vvdec_FOUND) + set(vvdec_LIBRARIES vvdec::vvdec) + endif() +endif () + # dav1d plugin_option(DAV1D "Dav1d AV1 decoder" OFF ON) @@ -229,6 +241,7 @@ plugin_compilation_info(OpenJPEG_ENCODER OpenJPEG "OpenJPEG J2K encoder") # plugin_compilation_info(OPENJPH_DECODER OPENJPH "OpenJPH HT-J2K decoder") plugin_compilation_info(OPENJPH_ENCODER OPENJPH "OpenJPH HT-J2K encoder") plugin_compilation_info(UVG266_ENCODER UVG266 "uvg266 VVC enc. (experimental)") +plugin_compilation_info(VVDEC vvdec "vvdec VVC dec. (experimental)") # --- show summary which formats are supported diff --git a/libheif/plugin_registry.cc b/libheif/plugin_registry.cc index 6d1b1b8c13..051d058bfb 100644 --- a/libheif/plugin_registry.cc +++ b/libheif/plugin_registry.cc @@ -42,6 +42,10 @@ #include "plugins/encoder_uvg266.h" #endif +#if HAVE_VVDEC +#include "plugins/decoder_vvdec.h" +#endif + #if HAVE_AOM_ENCODER #include "plugins/encoder_aom.h" #endif @@ -143,6 +147,10 @@ void register_default_plugins() register_encoder(get_encoder_plugin_uvg266()); #endif +#if HAVE_VVDEC + register_decoder(get_decoder_plugin_vvdec()); +#endif + #if HAVE_AOM_ENCODER register_encoder(get_encoder_plugin_aom()); #endif diff --git a/libheif/plugins/CMakeLists.txt b/libheif/plugins/CMakeLists.txt index 61516adc8b..e1a1235e57 100644 --- a/libheif/plugins/CMakeLists.txt +++ b/libheif/plugins/CMakeLists.txt @@ -100,6 +100,10 @@ set(UVG266_sources encoder_uvg266.cc encoder_uvg266.h) set(UVG266_extra_plugin_sources) plugin_compilation(uvg266 UVG266 UVG266_FOUND UVG266 UVG266) +set(VVDEC_sources decoder_vvdec.cc decoder_vvdec.h) +set(VVDEC_extra_plugin_sources) +plugin_compilation(vvdec vvdec vvdec_FOUND VVDEC VVDEC) + target_sources(heif PRIVATE encoder_mask.h encoder_mask.cc) diff --git a/libheif/plugins/decoder_vvdec.cc b/libheif/plugins/decoder_vvdec.cc new file mode 100644 index 0000000000..5cd7141ad4 --- /dev/null +++ b/libheif/plugins/decoder_vvdec.cc @@ -0,0 +1,334 @@ +/* + * AVIF codec. + * Copyright (c) 2019 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "libheif/heif.h" +#include "libheif/heif_plugin.h" +#include "decoder_vvdec.h" +#include "logging.h" +#include +#include +#include + +#include + + +struct vvdec_decoder +{ + vvdecDecoder* decoder = nullptr; + vvdecAccessUnit* au = nullptr; + size_t au_maxsize = 0; + + bool strict_decoding = false; + + std::vector> nalus; +}; + +static const char kSuccess[] = "Success"; +static const char kEmptyString[] = ""; + +static const int VVDEC_PLUGIN_PRIORITY = 100; + +#define MAX_PLUGIN_NAME_LENGTH 80 + +static char plugin_name[MAX_PLUGIN_NAME_LENGTH]; + + +static const char* vvdec_plugin_name() +{ + const char* version = vvdec_get_version(); + + if (strlen(version) < 60) { + sprintf(plugin_name, "VVCDEC decoder (%s)", version); + } + else + { + strcpy(plugin_name, "VVDEC decoder"); + } + + return plugin_name; +} + + +static void vvdec_init_plugin() +{ +} + + +static void vvdec_deinit_plugin() +{ +} + + +static int vvdec_does_support_format(enum heif_compression_format format) +{ + if (format == heif_compression_VVC) { + return VVDEC_PLUGIN_PRIORITY; + } + else { + return 0; + } +} + + +struct heif_error vvdec_new_decoder(void** dec) +{ + struct vvdec_decoder* decoder = new vvdec_decoder(); + + vvdecParams params; + vvdec_params_default(¶ms); + params.logLevel = VVDEC_INFO; + decoder->decoder = vvdec_decoder_open(¶ms); + + const int MaxNaluSize = 256 * 1024; + decoder->au = vvdec_accessUnit_alloc(); + vvdec_accessUnit_default(decoder->au); + vvdec_accessUnit_alloc_payload(decoder->au, MaxNaluSize); + decoder->au_maxsize = MaxNaluSize; + + *dec = decoder; + + struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; + return err; +} + + +void vvdec_free_decoder(void* decoder_raw) +{ + struct vvdec_decoder* decoder = (vvdec_decoder*) decoder_raw; + + if (!decoder) { + return; + } + + if (decoder->au) { + vvdec_accessUnit_free(decoder->au); + decoder->au = nullptr; + decoder->au_maxsize = 0; + } + + if (decoder->decoder) { + vvdec_decoder_close(decoder->decoder); + decoder->decoder = nullptr; + } + + delete decoder; +} + + +void vvdec_set_strict_decoding(void* decoder_raw, int flag) +{ + struct vvdec_decoder* decoder = (vvdec_decoder*) decoder_raw; + + decoder->strict_decoding = flag; +} + + +struct heif_error vvdec_push_data(void* decoder_raw, const void* frame_data, size_t frame_size) +{ + struct vvdec_decoder* decoder = (struct vvdec_decoder*) decoder_raw; + + uint8_t* data = (uint8_t*) frame_data; + + for (;;) { + uint32_t size = ((((uint32_t) data[0]) << 24) | + (((uint32_t) data[1]) << 16) | + (((uint32_t) data[2]) << 8) | + (data[3])); + + data += 4; + + std::vector nalu; + nalu.push_back(0); + nalu.push_back(0); + nalu.push_back(1); + nalu.insert(nalu.end(), data, data + size); + + decoder->nalus.push_back(nalu); + data += size; + frame_size -= 4 + size; + if (frame_size == 0) { + break; + } + } + + struct heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess}; + return err; +} + + +struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_img) +{ + struct vvdec_decoder* decoder = (struct vvdec_decoder*) decoder_raw; + + vvdecFrame* frame = nullptr; + + for (int i = 0;; i++) { + int ret; + + if (i < (int)decoder->nalus.size()) { + const auto& nalu = decoder->nalus[i]; + + if (nalu.size() > decoder->au_maxsize) { + vvdec_accessUnit_free(decoder->au); + + decoder->au = vvdec_accessUnit_alloc(); + vvdec_accessUnit_default(decoder->au); + vvdec_accessUnit_alloc_payload(decoder->au, nalu.size()); + decoder->au_maxsize = nalu.size(); + } + + memcpy(decoder->au->payload, nalu.data(), nalu.size()); + decoder->au->payloadUsedSize = nalu.size(); + + ret = vvdec_decode(decoder->decoder, decoder->au, &frame); + } + else { + ret = vvdec_flush(decoder->decoder, &frame); + } + + if (ret != VVDEC_OK && ret != VVDEC_EOF && ret != VVDEC_TRY_AGAIN) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "vvdec decoding error"}; + } + + if (frame) { + break; + } + } + + + decoder->nalus.clear(); + + heif_chroma chroma; + heif_colorspace colorspace; + + if (frame->colorFormat == VVDEC_CF_YUV400_PLANAR) { + chroma = heif_chroma_monochrome; + colorspace = heif_colorspace_monochrome; + } + else { + if (frame->colorFormat == VVDEC_CF_YUV444_PLANAR) { + chroma = heif_chroma_444; + } + else if (frame->colorFormat == VVDEC_CF_YUV422_PLANAR) { + chroma = heif_chroma_422; + } + else { + chroma = heif_chroma_420; + } + colorspace = heif_colorspace_YCbCr; + } + + struct heif_image* heif_img = nullptr; + struct heif_error err = heif_image_create(frame->width, frame->height, + colorspace, + chroma, + &heif_img); + if (err.code != heif_error_Ok) { + assert(heif_img == nullptr); + return err; + } + + + // --- read nclx parameters from decoded AV1 bitstream + +#if 0 + heif_color_profile_nclx nclx; + nclx.version = 1; + HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_color_primaries(&nclx, static_cast(img->cp)), { heif_image_release(heif_img); }); + HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_transfer_characteristics(&nclx, static_cast(img->tc)), { heif_image_release(heif_img); }); + HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_matrix_coefficients(&nclx, static_cast(img->mc)), { heif_image_release(heif_img); }); + nclx.full_range_flag = (img->range == AOM_CR_FULL_RANGE); + heif_image_set_nclx_color_profile(heif_img, &nclx); +#endif + + // --- transfer data from vvdecFrame to HeifPixelImage + + heif_channel channel2plane[3] = { + heif_channel_Y, + heif_channel_Cb, + heif_channel_Cr + }; + + + int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3); + + for (int c = 0; c < num_planes; c++) { + int bpp = frame->bitDepth; + + const auto& plane = frame->planes[c]; + const uint8_t* data = plane.ptr; + int stride = plane.stride; + + int w = plane.width; + int h = plane.height; + + err = heif_image_add_plane(heif_img, channel2plane[c], w, h, bpp); + if (err.code != heif_error_Ok) { + heif_image_release(heif_img); + return err; + } + + int dst_stride; + uint8_t* dst_mem = heif_image_get_plane(heif_img, channel2plane[c], &dst_stride); + + int bytes_per_pixel = (bpp + 7) / 8; + + for (int y = 0; y < h; y++) { + memcpy(dst_mem + y * dst_stride, data + y * stride, w * bytes_per_pixel); + } + } + + *out_img = heif_img; + + vvdec_frame_unref(decoder->decoder, frame); + + return err; +} + + +static const struct heif_decoder_plugin decoder_vvdec + { + 3, + vvdec_plugin_name, + vvdec_init_plugin, + vvdec_deinit_plugin, + vvdec_does_support_format, + vvdec_new_decoder, + vvdec_free_decoder, + vvdec_push_data, + vvdec_decode_image, + vvdec_set_strict_decoding, + "vvdec" + }; + + +const struct heif_decoder_plugin* get_decoder_plugin_vvdec() +{ + return &decoder_vvdec; +} + + +#if PLUGIN_VVDEC +heif_plugin_info plugin_info { + 1, + heif_plugin_type_decoder, + &decoder_vvdec +}; +#endif diff --git a/libheif/plugins/decoder_vvdec.h b/libheif/plugins/decoder_vvdec.h new file mode 100644 index 0000000000..15f01df2c6 --- /dev/null +++ b/libheif/plugins/decoder_vvdec.h @@ -0,0 +1,34 @@ +/* + * HEIF codec. + * Copyright (c) 2017 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_DECODER_VVDEC_H +#define LIBHEIF_DECODER_VVDEC_H + +#include "common_utils.h" + +const struct heif_decoder_plugin* get_decoder_plugin_vvdec(); + +#if PLUGIN_VVDEC +extern "C" { +MAYBE_UNUSED LIBHEIF_API extern heif_plugin_info plugin_info; +} +#endif + +#endif From ebb4dd3746ba6a57fbd38729a72833c5a4e8af8d Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 18:31:24 +0200 Subject: [PATCH 050/129] mention vvdec in README --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d40d97063b..e9064f7931 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,14 @@ libheif has support for: * decoding of files while downloading (e.g. extract image size before file has been completely downloaded) Supported codecs: -| Format | Decoders | Encoders | -|:-------------|:----------------:|:---------------------:| -| HEIC | libde265, ffmpeg | x265, kvazaar | -| AVIF | AOM, dav1d | AOM, rav1e, svt-av1 | -| VVC | - | uvg266 (experimental) | -| JPEG | libjpeg(-turbo) | libjpeg(-turbo) | -| JPEG2000 | OpenJPEG | OpenJPEG | -| uncompressed | built-in | built-in | +| Format | Decoders | Encoders | +|:-------------|:-------------------:|:---------------------:| +| HEIC | libde265, ffmpeg | x265, kvazaar | +| AVIF | AOM, dav1d | AOM, rav1e, svt-av1 | +| VVC | vvdec (experimental)| uvg266 (experimental) | +| JPEG | libjpeg(-turbo) | libjpeg(-turbo) | +| JPEG2000 | OpenJPEG | OpenJPEG | +| uncompressed | built-in | built-in | ## API @@ -155,7 +155,7 @@ For each codec, there are two configuration variables: * `WITH_{codec}_PLUGIN`: when enabled, the codec is compiled as a separate plugin. In order to use dynamic plugins, also make sure that `ENABLE_PLUGIN_LOADING` is enabled. -The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`, `OPENJPH_ENCODER`, `UVG266`. +The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`, `OPENJPH_ENCODER`, `UVG266`, `VVDEC`. Further options are: From 51c496b773cc242b17b767b06f7c55735c524e99 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 18:41:25 +0200 Subject: [PATCH 051/129] vvc decoder cleanup --- libheif/plugins/decoder_vvdec.cc | 70 +++++++++++++++++++------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/libheif/plugins/decoder_vvdec.cc b/libheif/plugins/decoder_vvdec.cc index 5cd7141ad4..10ef07efaf 100644 --- a/libheif/plugins/decoder_vvdec.cc +++ b/libheif/plugins/decoder_vvdec.cc @@ -21,19 +21,17 @@ #include "libheif/heif.h" #include "libheif/heif_plugin.h" #include "decoder_vvdec.h" -#include "logging.h" -#include #include #include #include +#include struct vvdec_decoder { vvdecDecoder* decoder = nullptr; vvdecAccessUnit* au = nullptr; - size_t au_maxsize = 0; bool strict_decoding = false; @@ -57,8 +55,7 @@ static const char* vvdec_plugin_name() if (strlen(version) < 60) { sprintf(plugin_name, "VVCDEC decoder (%s)", version); } - else - { + else { strcpy(plugin_name, "VVDEC decoder"); } @@ -89,7 +86,7 @@ static int vvdec_does_support_format(enum heif_compression_format format) struct heif_error vvdec_new_decoder(void** dec) { - struct vvdec_decoder* decoder = new vvdec_decoder(); + auto* decoder = new vvdec_decoder(); vvdecParams params; vvdec_params_default(¶ms); @@ -100,7 +97,6 @@ struct heif_error vvdec_new_decoder(void** dec) decoder->au = vvdec_accessUnit_alloc(); vvdec_accessUnit_default(decoder->au); vvdec_accessUnit_alloc_payload(decoder->au, MaxNaluSize); - decoder->au_maxsize = MaxNaluSize; *dec = decoder; @@ -111,7 +107,7 @@ struct heif_error vvdec_new_decoder(void** dec) void vvdec_free_decoder(void* decoder_raw) { - struct vvdec_decoder* decoder = (vvdec_decoder*) decoder_raw; + auto* decoder = (vvdec_decoder*) decoder_raw; if (!decoder) { return; @@ -120,7 +116,6 @@ void vvdec_free_decoder(void* decoder_raw) if (decoder->au) { vvdec_accessUnit_free(decoder->au); decoder->au = nullptr; - decoder->au_maxsize = 0; } if (decoder->decoder) { @@ -134,7 +129,7 @@ void vvdec_free_decoder(void* decoder_raw) void vvdec_set_strict_decoding(void* decoder_raw, int flag) { - struct vvdec_decoder* decoder = (vvdec_decoder*) decoder_raw; + auto* decoder = (vvdec_decoder*) decoder_raw; decoder->strict_decoding = flag; } @@ -142,9 +137,9 @@ void vvdec_set_strict_decoding(void* decoder_raw, int flag) struct heif_error vvdec_push_data(void* decoder_raw, const void* frame_data, size_t frame_size) { - struct vvdec_decoder* decoder = (struct vvdec_decoder*) decoder_raw; + auto* decoder = (struct vvdec_decoder*) decoder_raw; - uint8_t* data = (uint8_t*) frame_data; + const auto* data = (const uint8_t*) frame_data; for (;;) { uint32_t size = ((((uint32_t) data[0]) << 24) | @@ -175,27 +170,37 @@ struct heif_error vvdec_push_data(void* decoder_raw, const void* frame_data, siz struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_img) { - struct vvdec_decoder* decoder = (struct vvdec_decoder*) decoder_raw; + auto* decoder = (struct vvdec_decoder*) decoder_raw; vvdecFrame* frame = nullptr; + // --- prepare AU payload with maximum NALU size + + size_t max_payload_size = 0; + for (const auto& nalu : decoder->nalus) { + max_payload_size = std::max(max_payload_size, nalu.size()); + } + + if (decoder->au == nullptr || max_payload_size > (size_t) decoder->au->payloadSize) { + if (decoder->au) { + vvdec_accessUnit_free(decoder->au); + } + + decoder->au = vvdec_accessUnit_alloc(); + vvdec_accessUnit_default(decoder->au); + vvdec_accessUnit_alloc_payload(decoder->au, (int)max_payload_size); + } + + // --- feed NALUs into decoder, flush when done + for (int i = 0;; i++) { int ret; - if (i < (int)decoder->nalus.size()) { + if (i < (int) decoder->nalus.size()) { const auto& nalu = decoder->nalus[i]; - if (nalu.size() > decoder->au_maxsize) { - vvdec_accessUnit_free(decoder->au); - - decoder->au = vvdec_accessUnit_alloc(); - vvdec_accessUnit_default(decoder->au); - vvdec_accessUnit_alloc_payload(decoder->au, nalu.size()); - decoder->au_maxsize = nalu.size(); - } - memcpy(decoder->au->payload, nalu.data(), nalu.size()); - decoder->au->payloadUsedSize = nalu.size(); + decoder->au->payloadUsedSize = (int) nalu.size(); ret = vvdec_decode(decoder->decoder, decoder->au, &frame); } @@ -210,11 +215,17 @@ struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_ if (frame) { break; } + + if (ret == VVDEC_EOF) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "no frame decoded"}; + } } decoder->nalus.clear(); + // --- convert decoded frame to heif_image + heif_chroma chroma; heif_colorspace colorspace; @@ -236,7 +247,8 @@ struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_ } struct heif_image* heif_img = nullptr; - struct heif_error err = heif_image_create(frame->width, frame->height, + struct heif_error err = heif_image_create((int)frame->width, + (int)frame->height, colorspace, chroma, &heif_img); @@ -270,14 +282,14 @@ struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_ int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3); for (int c = 0; c < num_planes; c++) { - int bpp = frame->bitDepth; + int bpp = (int)frame->bitDepth; const auto& plane = frame->planes[c]; const uint8_t* data = plane.ptr; - int stride = plane.stride; + int stride = (int)plane.stride; - int w = plane.width; - int h = plane.height; + int w = (int)plane.width; + int h = (int)plane.height; err = heif_image_add_plane(heif_img, channel2plane[c], w, h, bpp); if (err.code != heif_error_Ok) { From 0a632931e92dbca0e5053b6d82dae09034cef987 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 18:42:31 +0200 Subject: [PATCH 052/129] add FindUVG266.cmake --- cmake/modules/FindUVG266.cmake | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 cmake/modules/FindUVG266.cmake diff --git a/cmake/modules/FindUVG266.cmake b/cmake/modules/FindUVG266.cmake new file mode 100644 index 0000000000..ba2a7ec056 --- /dev/null +++ b/cmake/modules/FindUVG266.cmake @@ -0,0 +1,24 @@ +include(LibFindMacros) +libfind_pkg_check_modules(UVG266_PKGCONF uvg266) + +find_path(UVG266_INCLUDE_DIR + NAMES uvg266.h + HINTS ${UVG266_PKGCONF_INCLUDE_DIRS} ${UVG266_PKGCONF_INCLUDEDIR} + PATH_SUFFIXES UVG266 uvg266 +) + +find_library(UVG266_LIBRARY + NAMES libuvg266 uvg266 uvg266.dll + HINTS ${UVG266_PKGCONF_LIBRARY_DIRS} ${UVG266_PKGCONF_LIBDIR} +) + +set(UVG266_PROCESS_LIBS UVG266_LIBRARY) +set(UVG266_PROCESS_INCLUDES UVG266_INCLUDE_DIR) +libfind_process(UVG266) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UVG266 + REQUIRED_VARS + UVG266_INCLUDE_DIR + UVG266_LIBRARIES +) From 717999d26a56d26e1c985b2031085b2f24b2465d Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 27 Jun 2024 18:52:27 +0200 Subject: [PATCH 053/129] add missing include --- libheif/plugins/decoder_vvdec.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libheif/plugins/decoder_vvdec.cc b/libheif/plugins/decoder_vvdec.cc index 10ef07efaf..219242fc4f 100644 --- a/libheif/plugins/decoder_vvdec.cc +++ b/libheif/plugins/decoder_vvdec.cc @@ -23,9 +23,10 @@ #include "decoder_vvdec.h" #include #include +#include +#include #include -#include struct vvdec_decoder From 42245672d2d86d2cb37d88c828d220db159d7b62 Mon Sep 17 00:00:00 2001 From: Jamaika1 Date: Fri, 28 Jun 2024 10:38:44 +0200 Subject: [PATCH 054/129] Fix unsigned integer --- libheif/codecs/uncompressed_image.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index c40b659ec9..19005f03ae 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -986,13 +986,13 @@ Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptradd_component(cbComponent); Box_cmpd::Component crComponent = {component_type_Cr}; cmpd->add_component(crComponent); - u_int8_t bpp_y = image->get_bits_per_pixel(heif_channel_Y); + uint8_t bpp_y = image->get_bits_per_pixel(heif_channel_Y); Box_uncC::Component component0 = {0, bpp_y, component_format_unsigned, 0}; uncC->add_component(component0); - u_int8_t bpp_cb = image->get_bits_per_pixel(heif_channel_Cb); + uint8_t bpp_cb = image->get_bits_per_pixel(heif_channel_Cb); Box_uncC::Component component1 = {1, bpp_cb, component_format_unsigned, 0}; uncC->add_component(component1); - u_int8_t bpp_cr = image->get_bits_per_pixel(heif_channel_Cr); + uint8_t bpp_cr = image->get_bits_per_pixel(heif_channel_Cr); Box_uncC::Component component2 = {2, bpp_cr, component_format_unsigned, 0}; uncC->add_component(component2); if (image->get_chroma_format() == heif_chroma_444) From c760f0b88446708fa7fbe54203544b0904c0e171 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Fri, 28 Jun 2024 12:58:57 +0200 Subject: [PATCH 055/129] add explicit cast --- libheif/plugins/decoder_libde265.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libheif/plugins/decoder_libde265.cc b/libheif/plugins/decoder_libde265.cc index 1178b2ba9f..6bf5e3f7b9 100644 --- a/libheif/plugins/decoder_libde265.cc +++ b/libheif/plugins/decoder_libde265.cc @@ -281,7 +281,7 @@ static struct heif_error libde265_v1_push_data(void* decoder_raw, const void* da return err; } - uint32_t nal_size = (cdata[ptr] << 24) | (cdata[ptr + 1] << 16) | (cdata[ptr + 2] << 8) | (cdata[ptr + 3]); + uint32_t nal_size = static_cast((cdata[ptr] << 24) | (cdata[ptr + 1] << 16) | (cdata[ptr + 2] << 8) | (cdata[ptr + 3])); ptr += 4; if (nal_size > size - ptr) { From 7a8c58e4be8b3eb77d82330a407a7f5e6da15a44 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Fri, 28 Jun 2024 12:59:08 +0200 Subject: [PATCH 056/129] remove unused string --- libheif/plugins/decoder_vvdec.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/libheif/plugins/decoder_vvdec.cc b/libheif/plugins/decoder_vvdec.cc index 219242fc4f..96fd9df9c3 100644 --- a/libheif/plugins/decoder_vvdec.cc +++ b/libheif/plugins/decoder_vvdec.cc @@ -40,7 +40,6 @@ struct vvdec_decoder }; static const char kSuccess[] = "Success"; -static const char kEmptyString[] = ""; static const int VVDEC_PLUGIN_PRIORITY = 100; From ddb541afdbe8dadfada4536077b8e4af1bb34b5e Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Sun, 30 Jun 2024 12:34:52 +0200 Subject: [PATCH 057/129] on Windows, copy DLL into executable directory to allow easier development --- examples/CMakeLists.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index bdda0eb9bc..2ce270ce9b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -122,3 +122,19 @@ if (TARGET PNG::PNG) install(TARGETS heif-thumbnailer RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES heif-thumbnailer.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () + +# --- on Windows, copy the DLL into the executable directory for easier development + +function(add_dll_copy_command executable) + if (WIN32) + add_custom_command(TARGET ${executable} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + $ + $ + ) + endif () +endfunction() + +add_dll_copy_command(heif-enc) +add_dll_copy_command(heif-dec) +add_dll_copy_command(heif-info) From 0dcadb9a921507237eeda1675fd092b2bf620f70 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 1 Jul 2024 17:01:00 +0200 Subject: [PATCH 058/129] copy DLL whenever the was rebuilt (not whenever the EXE was rebuilt), and only copy when using shared libraries --- examples/CMakeLists.txt | 16 ---------------- libheif/CMakeLists.txt | 11 +++++++++++ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2ce270ce9b..bdda0eb9bc 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -122,19 +122,3 @@ if (TARGET PNG::PNG) install(TARGETS heif-thumbnailer RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES heif-thumbnailer.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () - -# --- on Windows, copy the DLL into the executable directory for easier development - -function(add_dll_copy_command executable) - if (WIN32) - add_custom_command(TARGET ${executable} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - $ - $ - ) - endif () -endfunction() - -add_dll_copy_command(heif-enc) -add_dll_copy_command(heif-dec) -add_dll_copy_command(heif-info) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index e606666153..4555cabc21 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -172,3 +172,14 @@ install(EXPORT ${PROJECT_NAME}-config DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + +# --- on Windows, copy the DLL into the executable directory for easier development + +if (WIN32 AND BUILD_SHARED_LIBS) + add_custom_command(TARGET heif POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + $ + $/../examples + ) +endif () From 3a6f8cdec345e467a538f7c728376fa2e5f3031c Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 1 Jul 2024 18:23:18 +0200 Subject: [PATCH 059/129] skip depth and alpha map references to non-images (#1190) --- libheif/context.cc | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/libheif/context.cc b/libheif/context.cc index ef6edad258..b123e86d8c 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -750,9 +750,14 @@ Error HeifContext::interpret_heif_file() for (heif_item_id ref: refs) { auto master_iter = m_all_images.find(ref); if (master_iter == m_all_images.end()) { - return Error(heif_error_Invalid_input, - heif_suberror_Nonexisting_item_referenced, - "Non-existing alpha image referenced"); + + if (!m_heif_file->has_item_with_id(ref)) { + return Error(heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced, + "Non-existing alpha image referenced"); + } + + continue; } auto master_img = master_iter->second; @@ -778,9 +783,14 @@ Error HeifContext::interpret_heif_file() for (heif_item_id ref: refs) { auto master_iter = m_all_images.find(ref); if (master_iter == m_all_images.end()) { - return Error(heif_error_Invalid_input, - heif_suberror_Nonexisting_item_referenced, - "Non-existing depth image referenced"); + + if (!m_heif_file->has_item_with_id(ref)) { + return Error(heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced, + "Non-existing depth image referenced"); + } + + continue; } if (image.get() == master_iter->second.get()) { return Error(heif_error_Invalid_input, From 3db91ea969c90ac8a82506880daa9464599914e3 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 2 Jul 2024 22:31:19 +0200 Subject: [PATCH 060/129] add vvenc plugin (#519) --- CMakeLists.txt | 20 +- libheif/codecs/vvc.cc | 6 +- libheif/plugin_registry.cc | 8 + libheif/plugins/CMakeLists.txt | 4 + libheif/plugins/decoder_vvdec.cc | 11 + libheif/plugins/encoder_uvg266.cc | 2 +- libheif/plugins/encoder_vvenc.cc | 716 ++++++++++++++++++++++++++++++ libheif/plugins/encoder_vvenc.h | 34 ++ 8 files changed, 797 insertions(+), 4 deletions(-) create mode 100644 libheif/plugins/encoder_vvenc.cc create mode 100644 libheif/plugins/encoder_vvenc.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3676450446..562a115d95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,18 @@ if (WITH_VVDEC) endif() endif () +# vvenc + +plugin_option(VVENC "vvenc VVC encoder (experimental)" OFF OFF) +if (WITH_VVENC) + # TODO: how to do configure vvenc cleanly? + find_package(Threads REQUIRED) + find_package(vvenc 1.12.0) + if (vvenc_FOUND) + set(vvenc_LIBRARIES vvenc::vvenc) + endif() +endif () + # dav1d plugin_option(DAV1D "Dav1d AV1 decoder" OFF ON) @@ -241,6 +253,7 @@ plugin_compilation_info(OpenJPEG_ENCODER OpenJPEG "OpenJPEG J2K encoder") # plugin_compilation_info(OPENJPH_DECODER OPENJPH "OpenJPH HT-J2K decoder") plugin_compilation_info(OPENJPH_ENCODER OPENJPH "OpenJPH HT-J2K encoder") plugin_compilation_info(UVG266_ENCODER UVG266 "uvg266 VVC enc. (experimental)") +plugin_compilation_info(VVENC vvenc "vvenc VVC enc. (experimental)") plugin_compilation_info(VVDEC vvdec "vvdec VVC dec. (experimental)") # --- show summary which formats are supported @@ -297,9 +310,12 @@ endif() if (OPENJPH_FOUND AND WITH_OPENJPH_DECODER) set(SUPPORTS_J2K_HT_ENCODING TRUE) endif() -if (UVG266_FOUND) +if (UVG266_FOUND OR vvenc_FOUND) set(SUPPORTS_VVC_ENCODING TRUE) endif() +if (vvdec_FOUND AND WITH_VVDEC) + set(SUPPORTS_VVC_DECODING TRUE) +endif() if (WITH_UNCOMPRESSED_CODEC) set(SUPPORTS_UNCOMPRESSED_DECODING TRUE) @@ -310,7 +326,7 @@ message("\n=== Supported formats ===") message("format decoding encoding") format_compilation_info("HEIC" SUPPORTS_HEIC_DECODING SUPPORTS_HEIC_ENCODING) format_compilation_info("AVIF" SUPPORTS_AVIF_DECODING SUPPORTS_AVIF_ENCODING) -format_compilation_info("VVC" FALSE SUPPORTS_VVC_ENCODING) +format_compilation_info("VVC" SUPPORTS_VVC_DECODING SUPPORTS_VVC_ENCODING) format_compilation_info("JPEG" SUPPORTS_JPEG_DECODING SUPPORTS_JPEG_ENCODING) format_compilation_info("JPEG2000" SUPPORTS_J2K_DECODING SUPPORTS_J2K_ENCODING) format_compilation_info("JPEG2000-HT" SUPPORTS_J2K_HT_DECODING SUPPORTS_J2K_HT_ENCODING) diff --git a/libheif/codecs/vvc.cc b/libheif/codecs/vvc.cc index 2fb04f835a..a0853667f4 100644 --- a/libheif/codecs/vvc.cc +++ b/libheif/codecs/vvc.cc @@ -366,7 +366,11 @@ Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, int sps_conformance_window_flag = reader.get_bits(1); if (sps_conformance_window_flag) { - assert(false); // TODO + int left,right,top,bottom; + reader.get_uvlc(&left); + reader.get_uvlc(&right); + reader.get_uvlc(&top); + reader.get_uvlc(&bottom); } bool sps_subpic_info_present_flag = reader.get_bits(1); diff --git a/libheif/plugin_registry.cc b/libheif/plugin_registry.cc index 051d058bfb..38e4594f30 100644 --- a/libheif/plugin_registry.cc +++ b/libheif/plugin_registry.cc @@ -46,6 +46,10 @@ #include "plugins/decoder_vvdec.h" #endif +#if HAVE_VVENC +#include "plugins/encoder_vvenc.h" +#endif + #if HAVE_AOM_ENCODER #include "plugins/encoder_aom.h" #endif @@ -147,6 +151,10 @@ void register_default_plugins() register_encoder(get_encoder_plugin_uvg266()); #endif +#if HAVE_VVENC + register_encoder(get_encoder_plugin_vvenc()); +#endif + #if HAVE_VVDEC register_decoder(get_decoder_plugin_vvdec()); #endif diff --git a/libheif/plugins/CMakeLists.txt b/libheif/plugins/CMakeLists.txt index e1a1235e57..67f3e74ead 100644 --- a/libheif/plugins/CMakeLists.txt +++ b/libheif/plugins/CMakeLists.txt @@ -104,6 +104,10 @@ set(VVDEC_sources decoder_vvdec.cc decoder_vvdec.h) set(VVDEC_extra_plugin_sources) plugin_compilation(vvdec vvdec vvdec_FOUND VVDEC VVDEC) +set(VVENC_sources encoder_vvenc.cc encoder_vvenc.h) +set(VVENC_extra_plugin_sources) +plugin_compilation(vvenc vvenc vvenc_FOUND VVENC VVENC) + target_sources(heif PRIVATE encoder_mask.h encoder_mask.cc) diff --git a/libheif/plugins/decoder_vvdec.cc b/libheif/plugins/decoder_vvdec.cc index 96fd9df9c3..cc573776f8 100644 --- a/libheif/plugins/decoder_vvdec.cc +++ b/libheif/plugins/decoder_vvdec.cc @@ -28,6 +28,11 @@ #include +#if 0 +#include +#include +#endif + struct vvdec_decoder { @@ -305,6 +310,12 @@ struct heif_error vvdec_decode_image(void* decoder_raw, struct heif_image** out_ for (int y = 0; y < h; y++) { memcpy(dst_mem + y * dst_stride, data + y * stride, w * bytes_per_pixel); } + +#if 0 + std::cout << "DATA " << c << " " << w << " " << h << " bpp:" << bpp << "\n"; + std::cout << write_raw_data_as_hex(dst_mem, w*h, {}, {}); + std::cout << "---\n"; +#endif } *out_img = heif_img; diff --git a/libheif/plugins/encoder_uvg266.cc b/libheif/plugins/encoder_uvg266.cc index 812c3b4830..80864188c5 100644 --- a/libheif/plugins/encoder_uvg266.cc +++ b/libheif/plugins/encoder_uvg266.cc @@ -48,7 +48,7 @@ struct encoder_struct_uvg266 size_t output_idx = 0; }; -static const int uvg266_PLUGIN_PRIORITY = 100; +static const int uvg266_PLUGIN_PRIORITY = 50; #define MAX_PLUGIN_NAME_LENGTH 80 diff --git a/libheif/plugins/encoder_vvenc.cc b/libheif/plugins/encoder_vvenc.cc new file mode 100644 index 0000000000..792c1780a7 --- /dev/null +++ b/libheif/plugins/encoder_vvenc.cc @@ -0,0 +1,716 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "libheif/heif.h" +#include "libheif/heif_plugin.h" +#include "encoder_vvenc.h" +#include +#include // apparently, this is a false positive of cpplint +#include +#include +#include +#include + +extern "C" { +#include +} + + +static const char* kError_unspecified_error = "Unspecified encoder error"; +static const char* kError_unsupported_bit_depth = "Bit depth not supported by vvenc"; +static const char* kError_unsupported_chroma = "Unsupported chroma type"; +//static const char* kError_unsupported_image_size = "Images smaller than 16 pixels are not supported"; + + +struct encoder_struct_vvenc +{ + int quality = 32; + bool lossless = false; + + std::vector output_data; + size_t output_idx = 0; +}; + +static const int vvenc_PLUGIN_PRIORITY = 100; + +#define MAX_PLUGIN_NAME_LENGTH 80 + +static char plugin_name[MAX_PLUGIN_NAME_LENGTH]; + + +static void vvenc_set_default_parameters(void* encoder); + + +static const char* vvenc_plugin_name() +{ + strcpy(plugin_name, "vvenc VVC encoder"); + return plugin_name; +} + + +#define MAX_NPARAMETERS 10 + +static struct heif_encoder_parameter vvenc_encoder_params[MAX_NPARAMETERS]; +static const struct heif_encoder_parameter* vvenc_encoder_parameter_ptrs[MAX_NPARAMETERS + 1]; + +static void vvenc_init_parameters() +{ + struct heif_encoder_parameter* p = vvenc_encoder_params; + const struct heif_encoder_parameter** d = vvenc_encoder_parameter_ptrs; + int i = 0; + + assert(i < MAX_NPARAMETERS); + p->version = 2; + p->name = heif_encoder_parameter_name_quality; + p->type = heif_encoder_parameter_type_integer; + p->integer.default_value = 32; + p->has_default = true; + p->integer.have_minimum_maximum = true; + p->integer.minimum = 0; + p->integer.maximum = 63; + p->integer.valid_values = NULL; + p->integer.num_valid_values = 0; + d[i++] = p++; + + assert(i < MAX_NPARAMETERS); + p->version = 2; + p->name = heif_encoder_parameter_name_lossless; + p->type = heif_encoder_parameter_type_boolean; + p->boolean.default_value = false; + p->has_default = true; + d[i++] = p++; + + d[i++] = nullptr; +} + + +const struct heif_encoder_parameter** vvenc_list_parameters(void* encoder) +{ + return vvenc_encoder_parameter_ptrs; +} + + +static void vvenc_init_plugin() +{ + vvenc_init_parameters(); +} + + +static void vvenc_cleanup_plugin() +{ +} + + +static struct heif_error vvenc_new_encoder(void** enc) +{ + struct encoder_struct_vvenc* encoder = new encoder_struct_vvenc(); + struct heif_error err = heif_error_ok; + + *enc = encoder; + + // set default parameters + + vvenc_set_default_parameters(encoder); + + return err; +} + +static void vvenc_free_encoder(void* encoder_raw) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + delete encoder; +} + +static struct heif_error vvenc_set_parameter_quality(void* encoder_raw, int quality) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + if (quality < 0 || quality > 100) { + return heif_error_invalid_parameter_value; + } + + encoder->quality = quality; + + return heif_error_ok; +} + +static struct heif_error vvenc_get_parameter_quality(void* encoder_raw, int* quality) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + *quality = encoder->quality; + + return heif_error_ok; +} + +static struct heif_error vvenc_set_parameter_lossless(void* encoder_raw, int enable) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + encoder->lossless = enable ? 1 : 0; + + return heif_error_ok; +} + +static struct heif_error vvenc_get_parameter_lossless(void* encoder_raw, int* enable) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + *enable = encoder->lossless; + + return heif_error_ok; +} + +static struct heif_error vvenc_set_parameter_logging_level(void* encoder_raw, int logging) +{ +// struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + +// return heif_error_invalid_parameter_value; + + return heif_error_ok; +} + +static struct heif_error vvenc_get_parameter_logging_level(void* encoder_raw, int* loglevel) +{ +// struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + *loglevel = 0; + + return heif_error_ok; +} + + +static struct heif_error vvenc_set_parameter_integer(void* encoder_raw, const char* name, int value) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + if (strcmp(name, heif_encoder_parameter_name_quality) == 0) { + return vvenc_set_parameter_quality(encoder, value); + } + else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) { + return vvenc_set_parameter_lossless(encoder, value); + } + + return heif_error_unsupported_parameter; +} + +static struct heif_error vvenc_get_parameter_integer(void* encoder_raw, const char* name, int* value) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + if (strcmp(name, heif_encoder_parameter_name_quality) == 0) { + return vvenc_get_parameter_quality(encoder, value); + } + else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) { + return vvenc_get_parameter_lossless(encoder, value); + } + + return heif_error_unsupported_parameter; +} + + +static struct heif_error vvenc_set_parameter_boolean(void* encoder, const char* name, int value) +{ + if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) { + return vvenc_set_parameter_lossless(encoder, value); + } + + return heif_error_unsupported_parameter; +} + +// Unused, will use "vvenc_get_parameter_integer" instead. +/* +static struct heif_error vvenc_get_parameter_boolean(void* encoder, const char* name, int* value) +{ + if (strcmp(name, heif_encoder_parameter_name_lossless)==0) { + return vvenc_get_parameter_lossless(encoder,value); + } + + return heif_error_unsupported_parameter; +} +*/ + + +static struct heif_error vvenc_set_parameter_string(void* encoder_raw, const char* name, const char* value) +{ + return heif_error_unsupported_parameter; +} + +static struct heif_error vvenc_get_parameter_string(void* encoder_raw, const char* name, + char* value, int value_size) +{ + return heif_error_unsupported_parameter; +} + + +static void vvenc_set_default_parameters(void* encoder) +{ + for (const struct heif_encoder_parameter** p = vvenc_encoder_parameter_ptrs; *p; p++) { + const struct heif_encoder_parameter* param = *p; + + if (param->has_default) { + switch (param->type) { + case heif_encoder_parameter_type_integer: + vvenc_set_parameter_integer(encoder, param->name, param->integer.default_value); + break; + case heif_encoder_parameter_type_boolean: + vvenc_set_parameter_boolean(encoder, param->name, param->boolean.default_value); + break; + case heif_encoder_parameter_type_string: + vvenc_set_parameter_string(encoder, param->name, param->string.default_value); + break; + } + } + } +} + + +static void vvenc_query_input_colorspace(heif_colorspace* colorspace, heif_chroma* chroma) +{ + if (*colorspace == heif_colorspace_monochrome) { + *colorspace = heif_colorspace_monochrome; + *chroma = heif_chroma_monochrome; + } + else { + *colorspace = heif_colorspace_YCbCr; + *chroma = heif_chroma_420; + } +} + + +static void vvenc_query_input_colorspace2(void* encoder_raw, heif_colorspace* colorspace, heif_chroma* chroma) +{ + if (*colorspace == heif_colorspace_monochrome) { + *colorspace = heif_colorspace_monochrome; + *chroma = heif_chroma_monochrome; + } + else { + *colorspace = heif_colorspace_YCbCr; + if (*chroma != heif_chroma_420 && + *chroma != heif_chroma_422 && + *chroma != heif_chroma_444) { + *chroma = heif_chroma_420; + } + } +} + +void vvenc_query_encoded_size(void* encoder_raw, uint32_t input_width, uint32_t input_height, + uint32_t* encoded_width, uint32_t* encoded_height) +{ + *encoded_width = (input_width + 7) & ~0x7; + *encoded_height = (input_height + 7) & ~0x7; +} + + +#include +#include + +static void append_chunk_data(struct encoder_struct_vvenc* encoder, vvencAccessUnit* au) +{ +#if 0 + std::cout << "DATA\n"; + std::cout << write_raw_data_as_hex(au->payload, au->payloadUsedSize, {}, {}); + std::cout << "---\n"; +#endif + + size_t old_size = encoder->output_data.size(); + encoder->output_data.resize(old_size + au->payloadUsedSize); + memcpy(encoder->output_data.data() + old_size, au->payload, au->payloadUsedSize); +} + + +static void copy_plane(int16_t*& out_p, int& out_stride, const uint8_t* in_p, uint32_t in_stride, int w, int h, int padded_width, int padded_height) +{ + out_stride = padded_width; + out_p = new int16_t[out_stride * w * h]; + + for (int y = 0; y < padded_height; y++) { + int sy = std::min(y, h - 1); // source y + + for (int x = 0; x < w; x++) { + out_p[y * out_stride + x] = in_p[sy * in_stride + x]; + } + + for (int x = w; x < padded_width; x++) { + out_p[y * out_stride + x] = in_p[sy * in_stride + w - 1]; + } + } +} + + +static struct heif_error vvenc_encode_image(void* encoder_raw, const struct heif_image* image, + heif_image_input_class input_class) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + int bit_depth = heif_image_get_bits_per_pixel_range(image, heif_channel_Y); + bool isGreyscale = (heif_image_get_colorspace(image) == heif_colorspace_monochrome); + heif_chroma chroma = heif_image_get_chroma_format(image); + + if (bit_depth != 8) { + return heif_error{ + heif_error_Encoder_plugin_error, + heif_suberror_Unsupported_image_type, + kError_unsupported_bit_depth + }; + } + + + int input_width = heif_image_get_width(image, heif_channel_Y); + int input_height = heif_image_get_height(image, heif_channel_Y); + + int input_chroma_width = 0; + int input_chroma_height = 0; + + uint32_t encoded_width, encoded_height; + vvenc_query_encoded_size(encoder_raw, input_width, input_height, &encoded_width, &encoded_height); + + vvencChromaFormat vvencChroma; + int chroma_stride_shift = 0; + int chroma_height_shift = 0; + + if (isGreyscale) { + vvencChroma = VVENC_CHROMA_400; + } + else if (chroma == heif_chroma_420) { + vvencChroma = VVENC_CHROMA_420; + chroma_stride_shift = 1; + chroma_height_shift = 1; + input_chroma_width = (input_width + 1) / 2; + input_chroma_height = (input_height + 1) / 2; + } + else if (chroma == heif_chroma_422) { + vvencChroma = VVENC_CHROMA_422; + chroma_stride_shift = 1; + chroma_height_shift = 0; + input_chroma_width = (input_width + 1) / 2; + input_chroma_height = input_height; + } + else if (chroma == heif_chroma_444) { + vvencChroma = VVENC_CHROMA_444; + chroma_stride_shift = 0; + chroma_height_shift = 0; + input_chroma_width = input_width; + input_chroma_height = input_height; + } + else { + return heif_error{ + heif_error_Encoder_plugin_error, + heif_suberror_Unsupported_image_type, + kError_unsupported_chroma + }; + } + + if (chroma != heif_chroma_monochrome) { + int w = heif_image_get_width(image, heif_channel_Y); + int h = heif_image_get_height(image, heif_channel_Y); + if (chroma != heif_chroma_444) { w = (w + 1) / 2; } + if (chroma == heif_chroma_420) { h = (h + 1) / 2; } + + assert(heif_image_get_width(image, heif_channel_Cb) == w); + assert(heif_image_get_width(image, heif_channel_Cr) == w); + assert(heif_image_get_height(image, heif_channel_Cb) == h); + assert(heif_image_get_height(image, heif_channel_Cr) == h); + (void) w; + (void) h; + } + + + vvenc_config params; + + int ret = vvenc_init_default(¶ms, encoded_width, encoded_height, 25, 0, encoder->quality, VVENC_MEDIUM); + if (ret != VVENC_OK) { + // TODO: cleanup memory + + return heif_error{ + heif_error_Encoder_plugin_error, + heif_suberror_Encoder_encoding, + kError_unspecified_error + }; + } + + params.m_inputBitDepth[0] = bit_depth; + params.m_inputBitDepth[1] = bit_depth; + params.m_outputBitDepth[0] = bit_depth; + params.m_outputBitDepth[1] = bit_depth; + params.m_internalBitDepth[0] = bit_depth; + params.m_internalBitDepth[1] = bit_depth; + + vvencEncoder* vvencoder = vvenc_encoder_create(); + ret = vvenc_encoder_open(vvencoder, ¶ms); + if (ret != VVENC_OK) { + // TODO: cleanup memory + + return heif_error{ + heif_error_Encoder_plugin_error, + heif_suberror_Encoder_encoding, + kError_unspecified_error + }; + } + + + struct heif_color_profile_nclx* nclx = nullptr; + heif_error err = heif_image_get_nclx_color_profile(image, &nclx); + if (err.code != heif_error_Ok) { + nclx = nullptr; + } + + // make sure NCLX profile is deleted at end of function + auto nclx_deleter = std::unique_ptr(nclx, heif_nclx_color_profile_free); + +#if 0 + if (nclx) { + config->vui.fullrange = nclx->full_range_flag; + } + else { + config->vui.fullrange = 1; + } + + if (nclx && + (input_class == heif_image_input_class_normal || + input_class == heif_image_input_class_thumbnail)) { + config->vui.colorprim = nclx->color_primaries; + config->vui.transfer = nclx->transfer_characteristics; + config->vui.colormatrix = nclx->matrix_coefficients; + } + + config->qp = ((100 - encoder->quality) * 51 + 50) / 100; + config->lossless = encoder->lossless ? 1 : 0; + + config->width = encoded_width; + config->height = encoded_height; +#endif + + // Note: it is ok to cast away the const, as the image content is not changed. + // However, we have to guarantee that there are no plane pointers or stride values kept over calling the svt_encode_image() function. + /* + err = heif_image_extend_padding_to_size(const_cast(image), + param->sourceWidth, + param->sourceHeight); + if (err.code) { + return err; + } +*/ + + vvencYUVBuffer* yuvbuf = vvenc_YUVBuffer_alloc(); + vvenc_YUVBuffer_alloc_buffer(yuvbuf, vvencChroma, encoded_width, encoded_height); + + vvencAccessUnit* au = vvenc_accessUnit_alloc(); + + const int auSizeScale = (vvencChroma <= VVENC_CHROMA_420 ? 2 : 3); + vvenc_accessUnit_alloc_payload(au, auSizeScale * encoded_width * encoded_height + 1024); + + // vvenc_init_pass( encoder, pass, statsfilename ); + + int16_t* yptr = nullptr; + int16_t* cbptr = nullptr; + int16_t* crptr = nullptr; + int ystride = 0; + int cbstride = 0; + int crstride = 0; + + if (isGreyscale) { + int stride; + const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride); + + copy_plane(yptr, ystride, data, stride, input_width, input_height, encoded_width, encoded_height); + + yuvbuf->planes[0].ptr = yptr; + yuvbuf->planes[0].width = encoded_width; + yuvbuf->planes[0].height = encoded_height; + yuvbuf->planes[0].stride = ystride; + } + else { + int stride; + const uint8_t* data; + + data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride); + copy_plane(yptr, ystride, data, stride, input_width, input_height, encoded_width, encoded_height); + + data = heif_image_get_plane_readonly(image, heif_channel_Cb, &stride); + copy_plane(cbptr, cbstride, data, stride, input_chroma_width, input_chroma_height, + encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift); + + data = heif_image_get_plane_readonly(image, heif_channel_Cr, &stride); + copy_plane(crptr, crstride, data, stride, input_chroma_width, input_chroma_height, + encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift); + + yuvbuf->planes[0].ptr = yptr; + yuvbuf->planes[0].width = encoded_width; + yuvbuf->planes[0].height = encoded_height; + yuvbuf->planes[0].stride = ystride; + + yuvbuf->planes[1].ptr = cbptr; + yuvbuf->planes[1].width = encoded_width >> chroma_stride_shift; + yuvbuf->planes[1].height = encoded_height >> chroma_height_shift; + yuvbuf->planes[1].stride = cbstride; + + yuvbuf->planes[2].ptr = crptr; + yuvbuf->planes[2].width = encoded_width >> chroma_stride_shift; + yuvbuf->planes[2].height = encoded_height >> chroma_height_shift; + yuvbuf->planes[2].stride = crstride; + } + + //yuvbuf->cts = frame->pts; + //yuvbuf->ctsValid = true; + + + bool encDone; + + ret = vvenc_encode(vvencoder, yuvbuf, au, &encDone); + if (ret != VVENC_OK) { + vvenc_encoder_close(vvencoder); + vvenc_YUVBuffer_free(yuvbuf, true); // release storage and payload memory + vvenc_accessUnit_free(au, true); // release storage and payload memory + + return heif_error{ + heif_error_Encoder_plugin_error, + heif_suberror_Encoder_encoding, + kError_unspecified_error + }; + } + + if (au->payloadUsedSize > 0) { + append_chunk_data(encoder, au); + } + + while (!encDone) { + ret = vvenc_encode(vvencoder, nullptr, au, &encDone); + if (ret != VVENC_OK) { + vvenc_encoder_close(vvencoder); + vvenc_YUVBuffer_free(yuvbuf, true); // release storage and payload memory + vvenc_accessUnit_free(au, true); // release storage and payload memory + + return heif_error{ + heif_error_Encoder_plugin_error, + heif_suberror_Encoder_encoding, + kError_unspecified_error + }; + } + + if (au->payloadUsedSize > 0) { + append_chunk_data(encoder, au); + } + } + + vvenc_encoder_close(vvencoder); + vvenc_YUVBuffer_free(yuvbuf, true); // release storage and payload memory + vvenc_accessUnit_free(au, true); // release storage and payload memory + + /* + delete[] yptr; + delete[] cbptr; + delete[] crptr; +*/ + + return heif_error_ok; +} + + +static struct heif_error vvenc_get_compressed_data(void* encoder_raw, uint8_t** data, int* size, + enum heif_encoded_data_type* type) +{ + struct encoder_struct_vvenc* encoder = (struct encoder_struct_vvenc*) encoder_raw; + + if (encoder->output_idx == encoder->output_data.size()) { + *data = nullptr; + *size = 0; + + return heif_error_ok; + } + + size_t start_idx = encoder->output_idx; + + while (start_idx < encoder->output_data.size() - 3 && + (encoder->output_data[start_idx] != 0 || + encoder->output_data[start_idx + 1] != 0 || + encoder->output_data[start_idx + 2] != 1)) { + start_idx++; + } + + size_t end_idx = start_idx + 1; + + while (end_idx < encoder->output_data.size() - 3 && + (encoder->output_data[end_idx] != 0 || + encoder->output_data[end_idx + 1] != 0 || + encoder->output_data[end_idx + 2] != 1)) { + end_idx++; + } + + if (end_idx == encoder->output_data.size() - 3) { + end_idx = encoder->output_data.size(); + } + + *data = encoder->output_data.data() + start_idx + 3; + *size = (int) (end_idx - start_idx - 3); + + encoder->output_idx = end_idx; + + return heif_error_ok; +} + + +static const struct heif_encoder_plugin encoder_plugin_vvenc + { + /* plugin_api_version */ 3, + /* compression_format */ heif_compression_VVC, + /* id_name */ "vvenc", + /* priority */ vvenc_PLUGIN_PRIORITY, + /* supports_lossy_compression */ true, + /* supports_lossless_compression */ true, + /* get_plugin_name */ vvenc_plugin_name, + /* init_plugin */ vvenc_init_plugin, + /* cleanup_plugin */ vvenc_cleanup_plugin, + /* new_encoder */ vvenc_new_encoder, + /* free_encoder */ vvenc_free_encoder, + /* set_parameter_quality */ vvenc_set_parameter_quality, + /* get_parameter_quality */ vvenc_get_parameter_quality, + /* set_parameter_lossless */ vvenc_set_parameter_lossless, + /* get_parameter_lossless */ vvenc_get_parameter_lossless, + /* set_parameter_logging_level */ vvenc_set_parameter_logging_level, + /* get_parameter_logging_level */ vvenc_get_parameter_logging_level, + /* list_parameters */ vvenc_list_parameters, + /* set_parameter_integer */ vvenc_set_parameter_integer, + /* get_parameter_integer */ vvenc_get_parameter_integer, + /* set_parameter_boolean */ vvenc_set_parameter_integer, // boolean also maps to integer function + /* get_parameter_boolean */ vvenc_get_parameter_integer, // boolean also maps to integer function + /* set_parameter_string */ vvenc_set_parameter_string, + /* get_parameter_string */ vvenc_get_parameter_string, + /* query_input_colorspace */ vvenc_query_input_colorspace, + /* encode_image */ vvenc_encode_image, + /* get_compressed_data */ vvenc_get_compressed_data, + /* query_input_colorspace (v2) */ vvenc_query_input_colorspace2, + /* query_encoded_size (v3) */ vvenc_query_encoded_size + }; + +const struct heif_encoder_plugin* get_encoder_plugin_vvenc() +{ + return &encoder_plugin_vvenc; +} + + +#if PLUGIN_VVENC +heif_plugin_info plugin_info { + 1, + heif_plugin_type_encoder, + &encoder_plugin_vvenc +}; +#endif diff --git a/libheif/plugins/encoder_vvenc.h b/libheif/plugins/encoder_vvenc.h new file mode 100644 index 0000000000..bf739dcdc1 --- /dev/null +++ b/libheif/plugins/encoder_vvenc.h @@ -0,0 +1,34 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_ENCODER_VVENC_H +#define LIBHEIF_ENCODER_VVENC_H + +#include "common_utils.h" + +const struct heif_encoder_plugin* get_encoder_plugin_vvenc(); + +#if PLUGIN_VVENC +extern "C" { +MAYBE_UNUSED LIBHEIF_API extern heif_plugin_info plugin_info; +} +#endif + +#endif From d15b47f3ecbeac91972334e89fb87b39bc6e57b1 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 2 Jul 2024 22:56:19 +0200 Subject: [PATCH 061/129] add vvenc to README --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e9064f7931..677d892168 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,14 @@ libheif has support for: * decoding of files while downloading (e.g. extract image size before file has been completely downloaded) Supported codecs: -| Format | Decoders | Encoders | -|:-------------|:-------------------:|:---------------------:| -| HEIC | libde265, ffmpeg | x265, kvazaar | -| AVIF | AOM, dav1d | AOM, rav1e, svt-av1 | -| VVC | vvdec (experimental)| uvg266 (experimental) | -| JPEG | libjpeg(-turbo) | libjpeg(-turbo) | -| JPEG2000 | OpenJPEG | OpenJPEG | -| uncompressed | built-in | built-in | +| Format | Decoders | Encoders | +|:-------------|:-------------------:|:----------------------------:| +| HEIC | libde265, ffmpeg | x265, kvazaar | +| AVIF | AOM, dav1d | AOM, rav1e, svt-av1 | +| VVC | vvdec (experimental)| vvenc, uvg266 (experimental) | +| JPEG | libjpeg(-turbo) | libjpeg(-turbo) | +| JPEG2000 | OpenJPEG | OpenJPEG | +| uncompressed | built-in | built-in | ## API @@ -155,7 +155,7 @@ For each codec, there are two configuration variables: * `WITH_{codec}_PLUGIN`: when enabled, the codec is compiled as a separate plugin. In order to use dynamic plugins, also make sure that `ENABLE_PLUGIN_LOADING` is enabled. -The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`, `OPENJPH_ENCODER`, `UVG266`, `VVDEC`. +The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`, `OPENJPH_ENCODER`, `VVDEC`, `VVENC`, `UVG266`. Further options are: @@ -377,5 +377,5 @@ The sample applications are distributed under the terms of the MIT License. See COPYING for more details. Copyright (c) 2017-2020 Struktur AG
-Copyright (c) 2017-2023 Dirk Farin
+Copyright (c) 2017-2024 Dirk Farin
Contact: Dirk Farin From 7b11087f31b573ed198db7ba06f4de4fc133fc92 Mon Sep 17 00:00:00 2001 From: Ezra Huang Date: Wed, 3 Jul 2024 10:39:41 -0600 Subject: [PATCH 062/129] add js binding to check filetype --- libheif/api/libheif/heif_emscripten.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libheif/api/libheif/heif_emscripten.h b/libheif/api/libheif/heif_emscripten.h index cb219f24bc..a600a5074d 100644 --- a/libheif/api/libheif/heif_emscripten.h +++ b/libheif/api/libheif/heif_emscripten.h @@ -24,6 +24,11 @@ static struct heif_error _heif_context_read_from_memory( return heif_context_read_from_memory(context, data.data(), data.size(), nullptr); } +static heif_filetype_result heif_js_check_filetype(const std::string& data) +{ + return heif_check_filetype((const uint8_t*) data.data(), data.size()); +} + static emscripten::val heif_js_context_get_image_handle( struct heif_context* context, heif_item_id id) { @@ -272,6 +277,8 @@ EMSCRIPTEN_BINDINGS(libheif) { EXPORT_HEIF_FUNCTION(heif_context_free); emscripten::function("heif_context_read_from_memory", &_heif_context_read_from_memory, emscripten::allow_raw_pointers()); + emscripten::function("heif_js_check_filetype", + &heif_js_check_filetype, emscripten::allow_raw_pointers()); EXPORT_HEIF_FUNCTION(heif_context_get_number_of_top_level_images); emscripten::function("heif_js_context_get_list_of_top_level_image_IDs", &heif_js_context_get_list_of_top_level_image_IDs, emscripten::allow_raw_pointers()); @@ -419,6 +426,11 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_channel_B", heif_channel_B) .value("heif_channel_Alpha", heif_channel_Alpha) .value("heif_channel_interleaved", heif_channel_interleaved); + emscripten::enum_("heif_filetype_result") + .value("heif_filetype_no", heif_filetype_no) + .value("heif_filetype_yes_supported", heif_filetype_yes_supported) + .value("heif_filetype_yes_unsupported", heif_filetype_yes_unsupported) + .value("heif_filetype_maybe", heif_filetype_maybe); emscripten::class_("heif_context"); emscripten::class_("heif_image_handle"); From 87fcd2d3ad3fe3c029a8dcbd4dce1886150ac6f8 Mon Sep 17 00:00:00 2001 From: Ezra Huang Date: Wed, 3 Jul 2024 10:40:07 -0600 Subject: [PATCH 063/129] add js binding to get primary image handle --- libheif/api/libheif/heif_emscripten.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libheif/api/libheif/heif_emscripten.h b/libheif/api/libheif/heif_emscripten.h index a600a5074d..a04124babb 100644 --- a/libheif/api/libheif/heif_emscripten.h +++ b/libheif/api/libheif/heif_emscripten.h @@ -46,6 +46,25 @@ static emscripten::val heif_js_context_get_image_handle( return emscripten::val(handle); } +static emscripten::val heif_js_context_get_primary_image_handle( + struct heif_context* context) +{ + emscripten::val result = emscripten::val::object(); + if (!context) { + return result; + } + + heif_image_handle* handle; + struct heif_error err = heif_context_get_primary_image_handle(context, &handle); + + if (err.code != heif_error_Ok) { + return emscripten::val(err); + } + + return emscripten::val(handle); +} + + static emscripten::val heif_js_context_get_list_of_top_level_image_IDs( struct heif_context* context) { @@ -284,6 +303,8 @@ EMSCRIPTEN_BINDINGS(libheif) { &heif_js_context_get_list_of_top_level_image_IDs, emscripten::allow_raw_pointers()); emscripten::function("heif_js_context_get_image_handle", &heif_js_context_get_image_handle, emscripten::allow_raw_pointers()); + emscripten::function("heif_js_context_get_primary_image_handle", + &heif_js_context_get_primary_image_handle, emscripten::allow_raw_pointers()); //emscripten::function("heif_js_decode_image", //&heif_js_decode_image, emscripten::allow_raw_pointers()); emscripten::function("heif_js_decode_image2", From fcbc79c7a11d8ecda0dc919bcf349859f8bfbd59 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sun, 30 Jun 2024 02:27:31 +1000 Subject: [PATCH 064/129] uncompressed: fix up reading of uncompressed images that use the uncC short form --- libheif/codecs/uncompressed_image.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 19005f03ae..bd2318e637 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -324,9 +324,16 @@ int UncompressedImageCodec::get_luma_bits_per_pixel_from_configuration_unci(cons std::shared_ptr uncC_box = std::dynamic_pointer_cast(box1); auto box2 = ipco->get_property_for_item_ID(imageID, ipma, fourcc("cmpd")); std::shared_ptr cmpd_box = std::dynamic_pointer_cast(box2); - if (!uncC_box || !cmpd_box) { + if (!uncC_box) { return -1; } + if (!cmpd_box) { + if (isKnownUncompressedFrameConfigurationBoxProfile(uncC_box)) { + return 8; + } else { + return -1; + } + } int luma_bits = 0; int alternate_channel_bits = 0; From b224783292110e1284ccaa34dd766f4ef44225f9 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Fri, 28 Jun 2024 11:26:12 +0200 Subject: [PATCH 065/129] Add initial TIFF decoder for heif-enc. --- examples/CMakeLists.txt | 7 ++ examples/decoder_tiff.cc | 167 ++++++++++++++++++++++++++++++++++++ examples/decoder_tiff.h | 34 ++++++++ examples/heif_enc.cc | 24 +++++- scripts/install-ci-linux.sh | 1 + 5 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 examples/decoder_tiff.cc create mode 100644 examples/decoder_tiff.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index bdda0eb9bc..e0c0bf0783 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -122,3 +122,10 @@ if (TARGET PNG::PNG) install(TARGETS heif-thumbnailer RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES heif-thumbnailer.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) endif () + +find_package(TIFF) +if (TIFF_FOUND) + target_link_libraries(heif-enc PRIVATE TIFF::TIFF) + target_sources(heif-enc PRIVATE decoder_tiff.cc decoder_tiff.h) + target_compile_definitions(heif-enc PUBLIC HAVE_LIBTIFF=1) +endif () diff --git a/examples/decoder_tiff.cc b/examples/decoder_tiff.cc new file mode 100644 index 0000000000..13e59814f5 --- /dev/null +++ b/examples/decoder_tiff.cc @@ -0,0 +1,167 @@ +/* + libheif example application "heif". + + MIT License + + Copyright (c) 2024 Joachim Bauch + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include +#include + +#include + +extern "C" { +#include +#include +} + +#include "decoder_tiff.h" + +InputImage loadTIFF(const char* filename) { + std::unique_ptr tifPtr(TIFFOpen(filename, "r"), [](TIFF* tif) { TIFFClose(tif); }); + if (!tifPtr) { + std::cerr << "Can't open " << filename << "\n"; + exit(1); + } + + TIFF* tif = tifPtr.get(); + if (TIFFIsTiled(tif)) { + // TODO: Implement this. + std::cerr << "Tiled TIFF images are not supported.\n"; + exit(1); + } + + InputImage input_image; + + uint16_t shortv, bpp, bps, config, format; + uint32_t width, height; + uint32_t row; + if (TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &shortv) && shortv == PHOTOMETRIC_PALETTE) { + std::cerr << "Palette TIFF images are not supported.\n"; + exit(1); + } + + if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) || + !TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) { + std::cerr << "Can't read width and/or height from TIFF image.\n"; + exit(1); + } + + TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &config); + TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &bpp); + if (bpp != 1 && bpp != 3 && bpp != 4) { + std::cerr << "Unsupported TIFF samples per pixel: " << bpp << "\n"; + exit(1); + } + + TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps); + if (bps != 8) { + std::cerr << "Unsupported TIFF bits per sample: " << bps << "\n"; + exit(1); + } + + if (TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &format) && format != SAMPLEFORMAT_UINT) { + std::cerr << "Unsupported TIFF sample format: " << format << "\n"; + exit(1); + } + + struct heif_error err; + struct heif_image* image = nullptr; + heif_colorspace colorspace = bpp == 1 ? heif_colorspace_monochrome : heif_colorspace_RGB; + heif_chroma chroma = bpp == 1 ? heif_chroma_monochrome : heif_chroma_interleaved_RGB; + if (bpp == 4) { + chroma = heif_chroma_interleaved_RGBA; + } + + err = heif_image_create((int) width, (int) height, colorspace, chroma, &image); + (void) err; + // TODO: handle error + + switch (config) { + case PLANARCONFIG_CONTIG: + { + heif_channel channel = heif_channel_interleaved; + heif_image_add_plane(image, channel, (int) width, (int) height, bpp*8); + + int y_stride; + uint8_t* py = heif_image_get_plane(image, channel, &y_stride); + + tdata_t buf = _TIFFmalloc(TIFFScanlineSize(tif)); + for (row = 0; row < height; row++) { + TIFFReadScanline(tif, buf, row, 0); + memcpy(py, buf, width*bpp); + py += y_stride; + } + _TIFFfree(buf); + } + break; + case PLANARCONFIG_SEPARATE: + { + heif_channel channel = heif_channel_interleaved; + heif_image_add_plane(image, channel, (int) width, (int) height, bpp*8); + + int y_stride; + uint8_t* py = heif_image_get_plane(image, channel, &y_stride); + + if (bpp == 4) { + TIFFRGBAImage img; + char emsg[1024] = { 0 }; + if (!TIFFRGBAImageBegin(&img, tif, 1, emsg)) { + heif_image_release(image); + std::cerr << "Could not get RGBA image: " << emsg << "\n"; + exit(1); + } + + uint32_t* buf = static_cast(_TIFFmalloc(width*bpp)); + for (row = 0; row < height; row++) { + TIFFReadRGBAStrip(tif, row, buf); + memcpy(py, buf, width*bpp); + py += y_stride; + } + _TIFFfree(buf); + TIFFRGBAImageEnd(&img); + } else { + uint8_t* buf = static_cast(_TIFFmalloc(TIFFScanlineSize(tif))); + for (uint16_t i = 0; i < bpp; i++) { + uint8_t* dest = py+i; + for (row = 0; row < height; row++) { + TIFFReadScanline(tif, buf, row, i); + for (uint32_t x = 0; x < width; x++, dest+=bpp) { + *dest = buf[x]; + } + dest += (y_stride - width*bpp); + } + } + _TIFFfree(buf); + } + } + break; + default: + heif_image_release(image); + std::cerr << "Unsupported planar config: " << config << "\n"; + exit(1); + } + + input_image.image = std::shared_ptr(image, + [](heif_image* img) { heif_image_release(img); }); + return input_image; +} diff --git a/examples/decoder_tiff.h b/examples/decoder_tiff.h new file mode 100644 index 0000000000..44a26fe5af --- /dev/null +++ b/examples/decoder_tiff.h @@ -0,0 +1,34 @@ +/* + libheif example application "heif". + + MIT License + + Copyright (c) 2024 Joachim Bauch + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef LIBHEIF_DECODER_TIFF_H +#define LIBHEIF_DECODER_TIFF_H + +#include "decoder.h" + +InputImage loadTIFF(const char* filename); + +#endif //LIBHEIF_DECODER_TIFF_H diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc index 5115840c5b..2510fef2da 100644 --- a/examples/heif_enc.cc +++ b/examples/heif_enc.cc @@ -48,6 +48,10 @@ #include "decoder_png.h" #endif +#if HAVE_LIBTIFF +#include "decoder_tiff.h" +#endif + #include "decoder_y4m.h" #include @@ -211,6 +215,17 @@ InputImage loadPNG(const char* filename, int output_bit_depth) #endif +#if !HAVE_LIBTIFF +InputImage loadTIFF(const char* filename) +{ + std::cerr << "Cannot load TIFF because libtiff support was not compiled.\n"; + exit(1); + + return {}; +} +#endif + + void list_encoder_parameters(heif_encoder* encoder) { std::cerr << "Parameters for encoder `" << heif_encoder_get_name(encoder) << "`:\n"; @@ -629,7 +644,7 @@ int main(int argc, char** argv) return 5; } - if ((force_enc_av1f ? 1 : 0) + (force_enc_vvc ? 1 : 0) + (force_enc_uncompressed ? 1 : 0) + (force_enc_jpeg ? 1 : 0) + + if ((force_enc_av1f ? 1 : 0) + (force_enc_vvc ? 1 : 0) + (force_enc_uncompressed ? 1 : 0) + (force_enc_jpeg ? 1 : 0) + (force_enc_jpeg2000 ? 1 : 0) > 1) { std::cerr << "Choose at most one output compression format.\n"; } @@ -787,13 +802,15 @@ int main(int argc, char** argv) enum { - PNG, JPEG, Y4M + PNG, JPEG, Y4M, TIFF } filetype = JPEG; if (suffix == "png") { filetype = PNG; } else if (suffix == "y4m") { filetype = Y4M; + } else if (suffix == "tif" || suffix == "tiff") { + filetype = TIFF; } InputImage input_image; @@ -803,6 +820,9 @@ int main(int argc, char** argv) else if (filetype == Y4M) { input_image = loadY4M(input_filename.c_str()); } + else if (filetype == TIFF) { + input_image = loadTIFF(input_filename.c_str()); + } else { input_image = loadJPEG(input_filename.c_str()); } diff --git a/scripts/install-ci-linux.sh b/scripts/install-ci-linux.sh index 4eb65ce2e6..97a644eeef 100755 --- a/scripts/install-ci-linux.sh +++ b/scripts/install-ci-linux.sh @@ -107,6 +107,7 @@ if [ ! -z "$WITH_GRAPHICS" ]; then libgdk-pixbuf2.0-dev \ libjpeg-dev \ libpng-dev \ + libtiff-dev \ " fi From 7a81491b338646bd3de24c1b632f25aeb1888f13 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 4 Jul 2024 11:19:08 +0200 Subject: [PATCH 066/129] Extract EXIF data from TIFF images. --- examples/decoder_tiff.cc | 227 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/examples/decoder_tiff.cc b/examples/decoder_tiff.cc index 13e59814f5..6c7ae9c6b4 100644 --- a/examples/decoder_tiff.cc +++ b/examples/decoder_tiff.cc @@ -26,6 +26,8 @@ #include #include +#include +#include #include @@ -36,6 +38,223 @@ extern "C" { #include "decoder_tiff.h" +static bool seekTIFF(TIFF* tif, off_t offset, int whence) { + TIFFSeekProc seekProc = TIFFGetSeekProc(tif); + if (!seekProc) { + return false; + } + + thandle_t handle = TIFFClientdata(tif); + if (!handle) { + return false; + } + + return seekProc(handle, offset, whence) != static_cast(-1); +} + +static bool readTIFF(TIFF* tif, void* dest, size_t size) { + TIFFReadWriteProc readProc = TIFFGetReadProc(tif); + if (!readProc) { + return false; + } + + thandle_t handle = TIFFClientdata(tif); + if (!handle) { + return false; + } + + tmsize_t result = readProc(handle, dest, size); + if (result < 0 || static_cast(result) != size) { + return false; + } + + return true; +} + +static bool readTIFFUint16(TIFF* tif, uint16_t* dest) { + if (!readTIFF(tif, &dest, 2)) { + return false; + } + + if (TIFFIsByteSwapped(tif)) { + TIFFSwabShort(dest); + } + return true; +} + +static bool readTIFFUint32(TIFF* tif, uint32_t* dest) { + if (!readTIFF(tif, &dest, 4)) { + return false; + } + + if (TIFFIsByteSwapped(tif)) { + TIFFSwabLong(dest); + } + return true; +} + +class ExifTags { + public: + ~ExifTags() = default; + + void Encode(std::vector* dest); + + static std::unique_ptr Parse(TIFF* tif); + + private: + class Tag { + public: + uint16_t tag; + uint16_t type; + uint32_t len; + + uint32_t offset; + std::vector data; + }; + + ExifTags(uint16_t count); + void writeUint16(std::vector* dest, uint16_t value); + void writeUint32(std::vector* dest, uint32_t value); + void writeUint32(std::vector* dest, size_t pos, uint32_t value); + void writeData(std::vector* dest, const std::vector& value); + + std::vector> tags_; +}; + +ExifTags::ExifTags(uint16_t count) { + tags_.reserve(count); +} + +// static +std::unique_ptr ExifTags::Parse(TIFF* tif) { + toff_t exif_offset; + if (!TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_offset)) { + // Image doesn't contain EXIF data. + return nullptr; + } + + if (!seekTIFF(tif, exif_offset, SEEK_SET)) { + return nullptr; + } + + uint16_t count; + if (!readTIFFUint16(tif, &count)) { + return nullptr; + } + + if (count == 0) { + return nullptr; + } + + std::unique_ptr tags(new ExifTags(count)); + for (uint16_t i = 0; i < count; i++) { + std::unique_ptr tag(new Tag()); + if (!readTIFFUint16(tif, &tag->tag)) { + return nullptr; + } + + if (!readTIFFUint16(tif, &tag->type) || tag->type > TIFF_IFD8) { + return nullptr; + } + + if (TIFFDataWidth(static_cast(tag->type)) == 0) { + return nullptr; + } + + if (!readTIFFUint32(tif, &tag->len)) { + return nullptr; + } + + if (!readTIFFUint32(tif, &tag->offset)) { + return nullptr; + } + + tags->tags_.push_back(std::move(tag)); + } + + for (const auto& tag : tags->tags_) { + size_t size = tag->len * TIFFDataWidth(static_cast(tag->type)); + if (size <= 4) { + continue; + } + + if (!seekTIFF(tif, tag->offset, SEEK_SET)) { + return nullptr; + } + + tag->data.resize(size); + if (!readTIFF(tif, tag->data.data(), size)) { + return nullptr; + } + } + + return tags; +} + +void ExifTags::writeUint16(std::vector* dest, uint16_t value) { + dest->resize(dest->size() + sizeof(value)); + void* d = dest->data() + dest->size() - sizeof(value); + memcpy(d, &value, sizeof(value)); +} + +void ExifTags::writeUint32(std::vector* dest, uint32_t value) { + dest->resize(dest->size() + sizeof(value)); + writeUint32(dest, dest->size() - sizeof(value), value); +} + +void ExifTags::writeUint32(std::vector* dest, size_t pos, uint32_t value) { + void* d = dest->data() + pos; + memcpy(d, &value, sizeof(value)); +} + +void ExifTags::writeData(std::vector* dest, const std::vector& value) { + dest->resize(dest->size() + value.size()); + void* d = dest->data() + dest->size() - value.size(); + memcpy(d, value.data(), value.size()); +} + +void ExifTags::Encode(std::vector* dest) { + if (tags_.empty()) { + return; + } + +#if HOST_BIGENDIAN + dest->push_back('M'); + dest->push_back('M'); +#else + dest->push_back('I'); + dest->push_back('I'); +#endif + writeUint16(dest, 42); + // Offset of IFD0. + writeUint32(dest, 8); + + writeUint16(dest, static_cast(tags_.size())); + for (const auto& tag : tags_) { + writeUint16(dest, tag->tag); + writeUint16(dest, tag->type); + writeUint32(dest, tag->len); + writeUint32(dest, tag->offset); + } + // No IFD1 dictionary. + writeUint32(dest, 0); + + // Update offsets of tags that have their data stored separately. + for (size_t i = 0; i < tags_.size(); i++) { + const auto& tag = tags_[i]; + size_t size = tag->data.size(); + if (size <= 4) { + continue; + } + + // StartOfTags + (TagIndex * sizeof(Tag)) + OffsetOfTagData + size_t pos = 10 + (i * 12) + 8; + size_t offset = dest->size(); + writeUint32(dest, pos, static_cast(offset)); + writeData(dest, tag->data); + } +} + InputImage loadTIFF(const char* filename) { std::unique_ptr tifPtr(TIFFOpen(filename, "r"), [](TIFF* tif) { TIFFClose(tif); }); if (!tifPtr) { @@ -163,5 +382,13 @@ InputImage loadTIFF(const char* filename) { input_image.image = std::shared_ptr(image, [](heif_image* img) { heif_image_release(img); }); + + // Unfortunately libtiff doesn't provide a way to read a raw dictionary. + // Therefore we manually parse the EXIF data, extract the tags and encode + // them for use in the HEIF image. + std::unique_ptr tags = ExifTags::Parse(tif); + if (tags) { + tags->Encode(&input_image.exif); + } return input_image; } From e6c99674f8c80c33e5cdba34d75eb9a816bd841b Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 4 Jul 2024 11:21:44 +0200 Subject: [PATCH 067/129] CI: Install libtiff on Windows. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index f1922f9b81..da31437d1e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,6 +17,7 @@ install: - vcpkg install libde265:%arch%-windows - vcpkg install x265:%arch%-windows - vcpkg install dav1d:%arch%-windows + - vcpkg install tiff:%arch%-windows - cd c:\tools\vcpkg - vcpkg integrate install - cd %APPVEYOR_BUILD_FOLDER% From f7bcbdc246cff17c2cb4282f2afefdbf495c7ae9 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 4 Jul 2024 12:21:07 +0200 Subject: [PATCH 068/129] add cmake output whether libtiff was found (#1205) --- examples/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e0c0bf0783..6a27f630a8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -125,7 +125,10 @@ endif () find_package(TIFF) if (TIFF_FOUND) + message("libtiff found, building heif-enc with TIFF input support") target_link_libraries(heif-enc PRIVATE TIFF::TIFF) target_sources(heif-enc PRIVATE decoder_tiff.cc decoder_tiff.h) target_compile_definitions(heif-enc PUBLIC HAVE_LIBTIFF=1) +else () + message("libtiff NOT found, building heif-enc without TIFF input support") endif () From b429be04c950085d93d6e9ac2696ba57a432cb83 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Fri, 28 Jun 2024 10:40:15 +0200 Subject: [PATCH 069/129] Limit total size of image instead of width and height individually. --- libheif/codecs/jpeg2000.h | 4 +- libheif/codecs/mask_image.cc | 19 +++------ libheif/codecs/mask_image.h | 5 +-- libheif/codecs/uncompressed_image.cc | 22 ++++------ libheif/codecs/uncompressed_image.h | 5 +-- libheif/context.cc | 64 +++++++++++++--------------- libheif/context.h | 17 +++++--- libheif/plugins/decoder_dav1d.cc | 2 +- libheif/security_limits.h | 3 +- 9 files changed, 61 insertions(+), 80 deletions(-) diff --git a/libheif/codecs/jpeg2000.h b/libheif/codecs/jpeg2000.h index e1b00486ab..42ff72c669 100644 --- a/libheif/codecs/jpeg2000.h +++ b/libheif/codecs/jpeg2000.h @@ -324,11 +324,9 @@ class Jpeg2000ImageCodec { public: -// static Error decode_jpeg2000_image(const std::shared_ptr& heif_file, +// static Error decode_jpeg2000_image(const HeifContext* context, // heif_item_id ID, // std::shared_ptr& img, -// uint32_t maximum_image_width_limit, -// uint32_t maximum_image_height_limit, // const std::vector& uncompressed_data); static Error encode_jpeg2000_image(const std::shared_ptr& heif_file, diff --git a/libheif/codecs/mask_image.cc b/libheif/codecs/mask_image.cc index fc977818b4..4d8e57d5f6 100644 --- a/libheif/codecs/mask_image.cc +++ b/libheif/codecs/mask_image.cc @@ -55,15 +55,13 @@ Error Box_mskC::write(StreamWriter& writer) const } -Error MaskImageCodec::decode_mask_image(const std::shared_ptr& heif_file, +Error MaskImageCodec::decode_mask_image(const HeifContext* context, heif_item_id ID, std::shared_ptr& img, - uint32_t maximum_image_width_limit, - uint32_t maximum_image_height_limit, const std::vector& data) { std::vector> item_properties; - Error error = heif_file->get_properties(ID, item_properties); + Error error = context->get_heif_file()->get_properties(ID, item_properties); if (error) { return error; } @@ -76,16 +74,11 @@ Error MaskImageCodec::decode_mask_image(const std::shared_ptr& h if (ispe) { width = ispe->get_width(); height = ispe->get_height(); - - if (width >= maximum_image_width_limit || height >= maximum_image_height_limit) { - std::stringstream sstr; - sstr << "Image size " << width << "x" << height << " exceeds the maximum image size " - << maximum_image_width_limit << "x" << maximum_image_height_limit << "\n"; - - return Error(heif_error_Memory_allocation_error, - heif_suberror_Security_limit_exceeded, - sstr.str()); + error = context->check_resolution(width, height); + if (error) { + return error; } + found_ispe = true; } diff --git a/libheif/codecs/mask_image.h b/libheif/codecs/mask_image.h index a05842128d..b8e67c2e80 100644 --- a/libheif/codecs/mask_image.h +++ b/libheif/codecs/mask_image.h @@ -70,11 +70,9 @@ class Box_mskC : public FullBox class MaskImageCodec { public: - static Error decode_mask_image(const std::shared_ptr& heif_file, + static Error decode_mask_image(const HeifContext* context, heif_item_id ID, std::shared_ptr& img, - uint32_t maximum_image_width_limit, - uint32_t maximum_image_height_limit, const std::vector& data); static Error encode_mask_image(const std::shared_ptr& heif_file, const std::shared_ptr& src_image, @@ -84,4 +82,3 @@ class MaskImageCodec }; #endif //LIBHEIF_MASK_IMAGE_H - diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index bd2318e637..ff627eeb57 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -28,6 +28,7 @@ #include #include "common_utils.h" +#include "context.h" #include "error.h" #include "libheif/heif.h" #include "uncompressed.h" @@ -559,7 +560,7 @@ class AbstractDecoder std::vector channelList; - void buildChannelList(std::shared_ptr& img) { + void buildChannelList(std::shared_ptr& img) { for (Box_uncC::Component component : m_uncC->get_components()) { ChannelListEntry entry = buildChannelListEntry(component, img); channelList.push_back(entry); @@ -857,11 +858,9 @@ static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std:: } } -Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptr& heif_file, +Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* context, heif_item_id ID, std::shared_ptr& img, - uint32_t maximum_image_width_limit, - uint32_t maximum_image_height_limit, const std::vector& uncompressed_data) { if (uncompressed_data.empty()) { @@ -873,7 +872,7 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptr> item_properties; - Error error = heif_file->get_properties(ID, item_properties); + Error error = context->get_heif_file()->get_properties(ID, item_properties); if (error) { printf("failed to get properties\n"); return error; @@ -889,16 +888,11 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptrget_width(); height = ispe->get_height(); - - if (width >= maximum_image_width_limit || height >= maximum_image_height_limit) { - std::stringstream sstr; - sstr << "Image size " << width << "x" << height << " exceeds the maximum image size " - << maximum_image_width_limit << "x" << maximum_image_height_limit << "\n"; - printf("way too big\n"); - return Error(heif_error_Memory_allocation_error, - heif_suberror_Security_limit_exceeded, - sstr.str()); + error = context->check_resolution(width, height); + if (error) { + return error; } + found_ispe = true; } diff --git a/libheif/codecs/uncompressed_image.h b/libheif/codecs/uncompressed_image.h index 742f9a76f8..755fac7c11 100644 --- a/libheif/codecs/uncompressed_image.h +++ b/libheif/codecs/uncompressed_image.h @@ -31,17 +31,16 @@ #include #include +class HeifContext; class UncompressedImageCodec { public: static int get_luma_bits_per_pixel_from_configuration_unci(const HeifFile& heif_file, heif_item_id imageID); - static Error decode_uncompressed_image(const std::shared_ptr& heif_file, + static Error decode_uncompressed_image(const HeifContext* context, heif_item_id ID, std::shared_ptr& img, - uint32_t maximum_image_width_limit, - uint32_t maximum_image_height_limit, const std::vector& uncompressed_data); static Error encode_uncompressed_image(const std::shared_ptr& heif_file, diff --git a/libheif/context.cc b/libheif/context.cc index b123e86d8c..0b6df3ad6b 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -399,8 +399,7 @@ void ImageOverlay::get_offset(size_t image_index, int32_t* x, int32_t* y) const HeifContext::HeifContext() { - m_maximum_image_width_limit = MAX_IMAGE_WIDTH; - m_maximum_image_height_limit = MAX_IMAGE_HEIGHT; + m_maximum_image_size_limit = MAX_IMAGE_SIZE; reset_to_empty_heif(); } @@ -457,6 +456,23 @@ void HeifContext::reset_to_empty_heif() m_primary_image.reset(); } +Error HeifContext::check_resolution(uint32_t width, uint32_t height) const { + // --- check whether the image size is "too large" + uint32_t max_width_height = static_cast(std::numeric_limits::max()); + if ((width > max_width_height || height > max_width_height) || + (height != 0 && width > m_maximum_image_size_limit / height)) { + std::stringstream sstr; + sstr << "Image size " << width << "x" << height << " exceeds the maximum image size " + << m_maximum_image_size_limit << "\n"; + + return Error(heif_error_Memory_allocation_error, + heif_suberror_Security_limit_exceeded, + sstr.str()); + } + + return Error::Ok; +} + std::shared_ptr HeifContext::add_region_item(uint32_t reference_width, uint32_t reference_height) { std::shared_ptr box = m_heif_file->add_new_infe_box("rgan"); @@ -595,16 +611,9 @@ Error HeifContext::interpret_heif_file() // --- check whether the image size is "too large" - - if (width > m_maximum_image_width_limit || - height > m_maximum_image_height_limit) { - std::stringstream sstr; - sstr << "Image size " << width << "x" << height << " exceeds the maximum image size " - << m_maximum_image_width_limit << "x" << m_maximum_image_height_limit << "\n"; - - return Error(heif_error_Memory_allocation_error, - heif_suberror_Security_limit_exceeded, - sstr.str()); + err = check_resolution(width, height); + if (err) { + return err; } image->set_resolution(width, height); @@ -1510,11 +1519,9 @@ Error HeifContext::decode_image_planar(heif_item_id ID, if (error) { return error; } - error = UncompressedImageCodec::decode_uncompressed_image(m_heif_file, + error = UncompressedImageCodec::decode_uncompressed_image(this, ID, img, - m_maximum_image_width_limit, - m_maximum_image_height_limit, data); if (error) { return error; @@ -1528,11 +1535,9 @@ Error HeifContext::decode_image_planar(heif_item_id ID, std::cout << "mski error 1" << std::endl; return error; } - error = MaskImageCodec::decode_mask_image(m_heif_file, + error = MaskImageCodec::decode_mask_image(this, ID, img, - m_maximum_image_width_limit, - m_maximum_image_height_limit, data); if (error) { return error; @@ -1784,17 +1789,11 @@ Error HeifContext::decode_full_grid_image(heif_item_id ID, // --- generate image of full output size - if (w >= m_maximum_image_width_limit || h >= m_maximum_image_height_limit) { - std::stringstream sstr; - sstr << "Image size " << w << "x" << h << " exceeds the maximum image size " - << m_maximum_image_width_limit << "x" << m_maximum_image_height_limit << "\n"; - - return Error(heif_error_Memory_allocation_error, - heif_suberror_Security_limit_exceeded, - sstr.str()); + err = check_resolution(w, h); + if (err) { + return err; } - img = std::make_shared(); img->create(w, h, heif_colorspace_RGB, @@ -2134,14 +2133,9 @@ Error HeifContext::decode_overlay_image(heif_item_id ID, uint32_t w = overlay.get_canvas_width(); uint32_t h = overlay.get_canvas_height(); - if (w >= m_maximum_image_width_limit || h >= m_maximum_image_height_limit) { - std::stringstream sstr; - sstr << "Image size " << w << "x" << h << " exceeds the maximum image size " - << m_maximum_image_width_limit << "x" << m_maximum_image_height_limit << "\n"; - - return Error(heif_error_Memory_allocation_error, - heif_suberror_Security_limit_exceeded, - sstr.str()); + err = check_resolution(w, h); + if (err) { + return err; } // TODO: seems we always have to compose this in RGB since the background color is an RGB value diff --git a/libheif/context.h b/libheif/context.h index 3928d53937..b177f47652 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -70,10 +70,11 @@ class HeifContext : public ErrorBuffer void set_max_decoding_threads(int max_threads) { m_max_decoding_threads = max_threads; } + // Sets the maximum size of both width and height of an image. The total limit + // of the image size (width * height) will be "maximum_size * maximum_size". void set_maximum_image_size_limit(int maximum_size) { - m_maximum_image_width_limit = maximum_size; - m_maximum_image_height_limit = maximum_size; + m_maximum_image_size_limit = int64_t(maximum_size) * maximum_size; } Error read(const std::shared_ptr& reader); @@ -97,6 +98,10 @@ class HeifContext : public ErrorBuffer m_aux_images.clear(); } + Error check_resolution(uint32_t w, uint32_t h) const { + return m_heif_context->check_resolution(w, h); + } + void set_resolution(int w, int h) { m_width = w; @@ -341,7 +346,9 @@ class HeifContext : public ErrorBuffer Box_cmex::ExtrinsicMatrix m_extrinsic_matrix{}; }; - std::shared_ptr get_heif_file() { return m_heif_file; } + Error check_resolution(uint32_t width, uint32_t height) const; + + std::shared_ptr get_heif_file() const { return m_heif_file; } std::vector> get_top_level_images() { return m_top_level_images; } @@ -522,8 +529,8 @@ class HeifContext : public ErrorBuffer int m_max_decoding_threads = 4; - uint32_t m_maximum_image_width_limit; - uint32_t m_maximum_image_height_limit; + // Maximum image size in pixels (width * height). + uint64_t m_maximum_image_size_limit; std::vector> m_region_items; diff --git a/libheif/plugins/decoder_dav1d.cc b/libheif/plugins/decoder_dav1d.cc index de68644125..5a0afcd8f6 100644 --- a/libheif/plugins/decoder_dav1d.cc +++ b/libheif/plugins/decoder_dav1d.cc @@ -90,7 +90,7 @@ struct heif_error dav1d_new_decoder(void** dec) dav1d_default_settings(&decoder->settings); - decoder->settings.frame_size_limit = MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT; + decoder->settings.frame_size_limit = MAX_IMAGE_SIZE; decoder->settings.all_layers = 0; if (dav1d_open(&decoder->context, &decoder->settings) != 0) { diff --git a/libheif/security_limits.h b/libheif/security_limits.h index f1c177c0a1..302138127f 100644 --- a/libheif/security_limits.h +++ b/libheif/security_limits.h @@ -31,8 +31,7 @@ static const int MAX_COLOR_PROFILE_SIZE = 100 * 1024 * 1024; // 100 MB // Artificial limit to avoid allocating too much memory. // 32768^2 = 1.5 GB as YUV-4:2:0 or 4 GB as RGB32 -static const int MAX_IMAGE_WIDTH = 32768; -static const int MAX_IMAGE_HEIGHT = 32768; +static const int64_t MAX_IMAGE_SIZE = 32768 * 32768; // Maximum nesting level of boxes in input files. // We put a limit on this to avoid unlimited stack usage by malicious input files. From 0e0e7370595effd4c86fcd0d2051eab1f9192d0b Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Mon, 1 Jul 2024 08:57:25 +0200 Subject: [PATCH 070/129] Cast to correct types for comparisons. Fixes compiler errors like the following with clang-12: /path/to/libheif/libheif/api/libheif/heif.cc:1673:10: error: result of comparison of constant 4294967295 with expression of type 'uint16_t' (aka 'unsigned short') is always false [-Werror,-Wtautological-constant-out-of-range-compare] cp > std::numeric_limits::type>::max()) { ~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /path/to/libheif/libheif/api/libheif/heif.cc:1714:10: error: result of comparison of constant 4294967295 with expression of type 'uint16_t' (aka 'unsigned short') is always false [-Werror,-Wtautological-constant-out-of-range-compare] tc > std::numeric_limits::type>::max()) { ~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /path/to/libheif/libheif/api/libheif/heif.cc:1751:10: error: result of comparison of constant 4294967295 with expression of type 'uint16_t' (aka 'unsigned short') is always false [-Werror,-Wtautological-constant-out-of-range-compare] mc > std::numeric_limits::type>::max()) { ~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 errors generated. --- libheif/api/libheif/heif.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index d702fb7798..980b0434b0 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -1669,8 +1669,8 @@ static const std::set::type> struct heif_error heif_nclx_color_profile_set_color_primaries(heif_color_profile_nclx* nclx, uint16_t cp) { - if (cp < std::numeric_limits::type>::min() || - cp > std::numeric_limits::type>::max()) { + if (static_cast::type>(cp) < std::numeric_limits::type>::min() || + static_cast::type>(cp) > std::numeric_limits::type>::max()) { return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_color_primaries).error_struct(nullptr); } @@ -1710,8 +1710,8 @@ static const std::set::type>::min() || - tc > std::numeric_limits::type>::max()) { + if (static_cast::type>(tc) < std::numeric_limits::type>::min() || + static_cast::type>(tc) > std::numeric_limits::type>::max()) { return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_transfer_characteristics).error_struct(nullptr); } @@ -1747,8 +1747,8 @@ static const std::set::t struct heif_error heif_nclx_color_profile_set_matrix_coefficients(struct heif_color_profile_nclx* nclx, uint16_t mc) { - if (mc < std::numeric_limits::type>::min() || - mc > std::numeric_limits::type>::max()) { + if (static_cast::type>(mc) < std::numeric_limits::type>::min() || + static_cast::type>(mc) > std::numeric_limits::type>::max()) { return Error(heif_error_Invalid_input, heif_suberror_Unknown_NCLX_matrix_coefficients).error_struct(nullptr); } From fba9ee80ac3b73b3b03c969201ee4f0ac17f6534 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Mon, 1 Jul 2024 09:16:39 +0200 Subject: [PATCH 071/129] Add fuzzing example that caused overflow. --- ...crash-20ca2625096a205937b809a7841e7f019f0b2dc6 | Bin 0 -> 276 bytes scripts/check-licenses.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 fuzzing/data/corpus/crash-20ca2625096a205937b809a7841e7f019f0b2dc6 diff --git a/fuzzing/data/corpus/crash-20ca2625096a205937b809a7841e7f019f0b2dc6 b/fuzzing/data/corpus/crash-20ca2625096a205937b809a7841e7f019f0b2dc6 new file mode 100644 index 0000000000000000000000000000000000000000..d49c028be01109f94bff824e764aa859a572d07c GIT binary patch literal 276 zcmXAjF-rqM5QX2~1tGzxV6YGgrZ9vQqISU|f?#cHxh%V6@HV$3m!QoNun|iOAwS5! zA!wU4HYr3h&cU>>-)OXh}be2ZE{z$#jT@4s!gw z5IPjFNoxyG3X`FsehR*>NODJE6htp6m|V0$<6wLL1;ETw5!>{Z7q>#P@0*_cElvFk eyyoUCS_c%o76n-TRH~km2(jM1fm$(IRp1Z(7e}c8 literal 0 HcmV?d00001 diff --git a/scripts/check-licenses.sh b/scripts/check-licenses.sh index 70227e125a..cd39ecda27 100755 --- a/scripts/check-licenses.sh +++ b/scripts/check-licenses.sh @@ -21,7 +21,7 @@ set -eu # echo "Checking licenses..." -CHECK_RESULT=`/usr/bin/licensecheck --recursive --ignore 'emscripten|libde265|README\.md|post\.js|/.git/|clusterfuzz-testcase-.*' .` +CHECK_RESULT=`/usr/bin/licensecheck --recursive --ignore 'emscripten|libde265|README\.md|post\.js|/.git/|clusterfuzz-testcase-.*|fuzzing/data/.*' .` FOUND= while read -r line; do From 2e9c361db7b1a445867d81b8496f2aa5bdbc301d Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 4 Jul 2024 12:40:28 +0200 Subject: [PATCH 072/129] add cmake output which input formats are supported for heif-enc (#1205) --- examples/CMakeLists.txt | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6a27f630a8..3567b3c4bf 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -125,10 +125,27 @@ endif () find_package(TIFF) if (TIFF_FOUND) - message("libtiff found, building heif-enc with TIFF input support") target_link_libraries(heif-enc PRIVATE TIFF::TIFF) target_sources(heif-enc PRIVATE decoder_tiff.cc decoder_tiff.h) target_compile_definitions(heif-enc PUBLIC HAVE_LIBTIFF=1) +endif () + +message("") +message("=== Active input formats for heif-enc ===") +if (JPEG_FOUND) + message("JPEG: active") +else () + message("JPEG: ------ (libjpeg not found)") +endif () +if (PNG_FOUND) + message("PNG: active") else () - message("libtiff NOT found, building heif-enc without TIFF input support") + message("PNG: ------ (libpng not found)") endif () +if (TIFF_FOUND) + message("TIFF: active") +else () + message("TIFF: ------ (libtiff not found)") +endif () +message("") + From d432310e3a82c7337ff7cb433f02aa8fc0e6e13d Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 4 Jul 2024 12:47:30 +0200 Subject: [PATCH 073/129] AOM encoder: set maximum number of threads to 64 (#1212) --- libheif/plugins/encoder_aom.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc index 445cf9e53c..8b6abd27fb 100644 --- a/libheif/plugins/encoder_aom.cc +++ b/libheif/plugins/encoder_aom.cc @@ -241,7 +241,7 @@ static void aom_init_parameters() p->has_default = true; p->integer.have_minimum_maximum = true; p->integer.minimum = 1; - p->integer.maximum = 16; + p->integer.maximum = 64; p->integer.valid_values = NULL; p->integer.num_valid_values = 0; d[i++] = p++; From 29bc4c32514b1fc720b29dc1b0ac6cea4f7bf2b5 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Thu, 4 Jul 2024 13:44:10 +0200 Subject: [PATCH 074/129] Do not check maximum image size while merely parsing the file. Do the test when actually decoding the image (#1220) --- libheif/context.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libheif/context.cc b/libheif/context.cc index 0b6df3ad6b..379ea65617 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -609,13 +609,6 @@ Error HeifContext::interpret_heif_file() uint32_t width = ispe->get_width(); uint32_t height = ispe->get_height(); - - // --- check whether the image size is "too large" - err = check_resolution(width, height); - if (err) { - return err; - } - image->set_resolution(width, height); ispe_read = true; } @@ -1362,6 +1355,16 @@ Error HeifContext::decode_image_planar(heif_item_id ID, Error error; + // --- check whether image size exceeds maximum (according to 'ispe') + + auto ispe = m_heif_file->get_property(ID); + if (ispe) { + error = check_resolution(ispe->get_width(), ispe->get_height()); + if (error) { + return error; + } + } + // --- decode image, depending on its type if (image_type == "hvc1" || From 8ddaa7458fdd470d93210fd9f3a875ecf2d8b171 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 4 Jul 2024 13:58:34 +0200 Subject: [PATCH 075/129] aom: Use all available CPUs for encoding by default. --- libheif/plugins/encoder_aom.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc index 8b6abd27fb..0eeb9177c6 100644 --- a/libheif/plugins/encoder_aom.cc +++ b/libheif/plugins/encoder_aom.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "encoder_aom.h" @@ -237,7 +238,12 @@ static void aom_init_parameters() p->version = 2; p->name = kParam_threads; p->type = heif_encoder_parameter_type_integer; - p->integer.default_value = 4; + unsigned int threads = std::thread::hardware_concurrency(); + if (threads == 0) { + // Could not autodetect, use previous default value. + threads = 4; + } + p->integer.default_value = threads; p->has_default = true; p->integer.have_minimum_maximum = true; p->integer.minimum = 1; From 73f23a1cc7b6d5f23e533c6bc883cd5dc9303ae7 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 4 Jul 2024 14:14:33 +0200 Subject: [PATCH 076/129] Fix filename in fuzzing corpus so it gets parsed during regression testing. --- ...h-20ca2625096a205937b809a7841e7f019f0b2dc6.heic} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename fuzzing/data/corpus/{crash-20ca2625096a205937b809a7841e7f019f0b2dc6 => crash-20ca2625096a205937b809a7841e7f019f0b2dc6.heic} (100%) diff --git a/fuzzing/data/corpus/crash-20ca2625096a205937b809a7841e7f019f0b2dc6 b/fuzzing/data/corpus/crash-20ca2625096a205937b809a7841e7f019f0b2dc6.heic similarity index 100% rename from fuzzing/data/corpus/crash-20ca2625096a205937b809a7841e7f019f0b2dc6 rename to fuzzing/data/corpus/crash-20ca2625096a205937b809a7841e7f019f0b2dc6.heic From b34e7ace854eaf50c27365a206d66c7e8810d489 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 4 Jul 2024 14:27:37 +0200 Subject: [PATCH 077/129] Bring back overflow check removed in 29bc4c32514b1fc720b29dc1b0ac6cea4f7bf2b5 --- libheif/context.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libheif/context.cc b/libheif/context.cc index 379ea65617..b3666d3282 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -609,6 +609,17 @@ Error HeifContext::interpret_heif_file() uint32_t width = ispe->get_width(); uint32_t height = ispe->get_height(); + uint32_t max_width_height = static_cast(std::numeric_limits::max()); + if (width >= max_width_height || height >= max_width_height) { + std::stringstream sstr; + sstr << "Image size " << width << "x" << height << " exceeds the maximum image size " + << m_maximum_image_size_limit << "\n"; + + return Error(heif_error_Memory_allocation_error, + heif_suberror_Security_limit_exceeded, + sstr.str()); + } + image->set_resolution(width, height); ispe_read = true; } From c954e20a5340608b563d912a0b6e11949a5b4073 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 4 Jul 2024 11:45:21 +0200 Subject: [PATCH 078/129] CI: Enable more codecs when compiling on Windows. --- appveyor.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index da31437d1e..52bd7f20f7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,10 +14,15 @@ environment: - arch: arm64 install: - - vcpkg install libde265:%arch%-windows - - vcpkg install x265:%arch%-windows + - vcpkg install aom:%arch%-windows - vcpkg install dav1d:%arch%-windows + - vcpkg install ffmpeg[avcodec]:%arch%-windows + - vcpkg install libde265:%arch%-windows + - vcpkg install libjpeg-turbo:%arch%-windows + - vcpkg install libpng:%arch%-windows - vcpkg install tiff:%arch%-windows + - vcpkg install x265:%arch%-windows + - vcpkg install zlib:%arch%-windows - cd c:\tools\vcpkg - vcpkg integrate install - cd %APPVEYOR_BUILD_FOLDER% @@ -25,7 +30,7 @@ install: before_build: - mkdir build - cd build - - cmake .. -A %arch% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake + - cmake .. -A %arch% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DWITH_DAV1D=ON -DWITH_AOM_DECODER=ON -DWITH_AOM_ENCODER=ON -DWITH_JPEG_DECODER=ON -DWITH_JPEG_ENCODER=ON -DWITH_UNCOMPRESSED_CODEC=ON -DWITH_DEFLATE_HEADER_COMPRESSION=ON -DWITH_FFMPEG_DECODER=ON build: verbosity: normal From eb4ce123cb7cf337a13572d4c62a956de5002c18 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 4 Jul 2024 16:15:39 +0200 Subject: [PATCH 079/129] CI: Only install x265 on x64, doesn't exist on arm64. --- appveyor.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 52bd7f20f7..6d0c48608a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,10 +4,6 @@ image: Visual Studio 2022 configuration: Release cache: c:\tools\vcpkg\installed\ -matrix: - allow_failures: - - arch: arm64 # libde265 currently doesn't support arm64 on vcpkg - environment: matrix: - arch: x64 @@ -21,7 +17,7 @@ install: - vcpkg install libjpeg-turbo:%arch%-windows - vcpkg install libpng:%arch%-windows - vcpkg install tiff:%arch%-windows - - vcpkg install x265:%arch%-windows + - ps: If (${env:arch} -eq "x64") { vcpkg install x265:${env:arch}-windows } - vcpkg install zlib:%arch%-windows - cd c:\tools\vcpkg - vcpkg integrate install From bd5748fcc65c3b89d827836d178a85ba233ec78d Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Fri, 28 Jun 2024 22:22:46 +1000 Subject: [PATCH 080/129] uncompressed: add sanity limit check on number of tile rows and cols --- libheif/codecs/uncompressed_box.cc | 15 +++- tests/uncompressed_box.cc | 136 +++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 2 deletions(-) diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index b6afa359c0..c113aba13d 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -27,6 +27,7 @@ #include #include "libheif/heif.h" +#include "security_limits.h" #include "uncompressed.h" #include "uncompressed_box.h" @@ -260,9 +261,19 @@ Error Box_uncC::parse(BitstreamRange& range) m_tile_align_size = range.read32(); - m_num_tile_cols = range.read32() + 1; + uint32_t num_tile_cols_minus_one = range.read32(); + uint32_t num_tile_rows_minus_one = range.read32(); + if ((num_tile_cols_minus_one >= UINT32_MAX) || (num_tile_rows_minus_one >= UINT32_MAX)) { + std::stringstream sstr; + sstr << "Tiling size " << ((uint64_t)num_tile_cols_minus_one + 1) << " x " << ((uint64_t)num_tile_rows_minus_one + 1) << " exceeds the maximum allowed size " + << UINT32_MAX << " x " << UINT32_MAX; + return Error(heif_error_Memory_allocation_error, + heif_suberror_Security_limit_exceeded, + sstr.str()); + } + m_num_tile_cols = num_tile_cols_minus_one + 1; - m_num_tile_rows = range.read32() + 1; + m_num_tile_rows = num_tile_rows_minus_one + 1; } return range.get_error(); } diff --git a/tests/uncompressed_box.cc b/tests/uncompressed_box.cc index 630e79b1f3..100113abe7 100644 --- a/tests/uncompressed_box.cc +++ b/tests/uncompressed_box.cc @@ -216,3 +216,139 @@ TEST_CASE( "uncC" ) std::string dump_output = uncC->dump(indent); REQUIRE(dump_output == "Box: uncC -----\nsize: 0 (header size: 0)\nprofile: 1919378017 (rgba)\ncomponent_index: 0\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\ncomponent_index: 1\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\ncomponent_index: 2\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\ncomponent_index: 3\ncomponent_bit_depth: 8\ncomponent_format: unsigned\ncomponent_align_size: 0\nsampling_type: no subsampling\ninterleave_type: pixel\nblock_size: 0\ncomponents_little_endian: 0\nblock_pad_lsb: 0\nblock_little_endian: 0\nblock_reversed: 0\npad_unknown: 0\npixel_size: 0\nrow_align_size: 0\ntile_align_size: 0\nnum_tile_cols: 1\nnum_tile_rows: 1\n"); } + +TEST_CASE("uncC_parse") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C', + 0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a', + 0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00, + 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02 + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("uncC")); + REQUIRE(box->get_type_string() == "uncC"); + std::shared_ptr uncC = std::dynamic_pointer_cast(box); + REQUIRE(uncC->get_number_of_tile_columns() == 2); + REQUIRE(uncC->get_number_of_tile_rows() == 3); + Indent indent; + std::string dumpResult = box->dump(indent); + REQUIRE(dumpResult == "Box: uncC -----\n" + "size: 64 (header size: 12)\n" + "profile: 1919378017 (rgba)\n" + "component_index: 0\n" + "component_bit_depth: 8\n" + "component_format: unsigned\n" + "component_align_size: 0\n" + "component_index: 1\n" + "component_bit_depth: 8\n" + "component_format: unsigned\n" + "component_align_size: 0\n" + "component_index: 2\n" + "component_bit_depth: 8\n" + "component_format: unsigned\n" + "component_align_size: 0\n" + "component_index: 3\n" + "component_bit_depth: 8\n" + "component_format: unsigned\n" + "component_align_size: 0\n" + "sampling_type: no subsampling\n" + "interleave_type: pixel\n" + "block_size: 0\n" + "components_little_endian: 0\n" + "block_pad_lsb: 0\n" + "block_little_endian: 0\n" + "block_reversed: 0\n" + "pad_unknown: 0\n" + "pixel_size: 0\n" + "row_align_size: 0\n" + "tile_align_size: 0\n" + "num_tile_cols: 2\n" + "num_tile_rows: 3\n"); +} + +TEST_CASE("uncC_parse_no_overflow") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C', + 0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a', + 0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00, + 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("uncC")); + REQUIRE(box->get_type_string() == "uncC"); + std::shared_ptr uncC = std::dynamic_pointer_cast(box); + REQUIRE(uncC->get_number_of_tile_columns() == 4294967295); + REQUIRE(uncC->get_number_of_tile_rows() == 4294967295); +} + +TEST_CASE("uncC_parse_excess_tile_cols") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C', + 0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a', + 0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00, + 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(range.error() == 0); + REQUIRE(error.error_code == 6); + REQUIRE(error.sub_error_code == 1000); + REQUIRE(error.message == "Tiling size 4294967296 x 32768 exceeds the maximum allowed size 4294967295 x 4294967295"); +} + +TEST_CASE("uncC_parse_excess_tile_rows") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x40, 'u', 'n', 'c', 'C', + 0x00, 0x00, 0x00, 0x00, 'r', 'g', 'b', 'a', + 0x00, 0x00, 0x00, 0x04, 0, 0, 7, 0x00, + 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x02, + 0x07, 0x00, 0x00, 0x00, 0x03, 0x07, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(range.error() == 0); + REQUIRE(error.error_code == 6); + REQUIRE(error.sub_error_code == 1000); + REQUIRE(error.message == "Tiling size 32768 x 4294967296 exceeds the maximum allowed size 4294967295 x 4294967295"); +} From cc31d573fedbd293ea89f2e396e5d60cc2e8aa59 Mon Sep 17 00:00:00 2001 From: Ezra Huang Date: Wed, 3 Jul 2024 14:33:15 -0600 Subject: [PATCH 081/129] fix wrong include directories for libde265 and aom in build-emscripten.sh Currently when running the build-emscripten.sh script from another directory (as the script suggests) the base path for libde265 set in $LIBARY_LINKER_FLAGS and $LIBRARY_INCLUDE_FLAGS for get set to (if the src dir of the project is / and the build dir is /buildjs) /libde265-1.0.12 instead of /buildjs/libde265-1.0.12. This causes the compiler to fail to find libde265. This isn't caught in ./scripts/run-ci.sh because the script (on line 246) has "source ./emscripten/emsdk/emsdk_env.sh && USE_WASM=0 ./build-emscripten.sh ." using the same directory as both the source directory and the build directory. make the same fixes for aom --- build-emscripten.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build-emscripten.sh b/build-emscripten.sh index f9449c2888..dfc0124b32 100755 --- a/build-emscripten.sh +++ b/build-emscripten.sh @@ -46,9 +46,10 @@ if [ "$ENABLE_LIBDE265" = "1" ]; then emmake make -j${CORES} cd .. fi - CONFIGURE_ARGS_LIBDE265="-DLIBDE265_INCLUDE_DIR=${DIR}/libde265-${LIBDE265_VERSION} -DLIBDE265_LIBRARY=-L${DIR}/libde265-${LIBDE265_VERSION}/libde265/.libs" + LIBDE265_DIR="$(pwd)/libde265-${LIBDE265_VERSION}" + CONFIGURE_ARGS_LIBDE265="-DLIBDE265_INCLUDE_DIR=${LIBDE265_DIR} -DLIBDE265_LIBRARY=-L${LIBDE265_DIR}/libde265/.libs" LIBRARY_LINKER_FLAGS="$LIBRARY_LINKER_FLAGS -lde265" - LIBRARY_INCLUDE_FLAGS="$LIBRARY_INCLUDE_FLAGS -L${DIR}/libde265-${LIBDE265_VERSION}/libde265/.libs" + LIBRARY_INCLUDE_FLAGS="$LIBRARY_INCLUDE_FLAGS -L${LIBDE265_DIR}/libde265/.libs" fi CONFIGURE_ARGS_AOM="" @@ -80,9 +81,10 @@ if [ "$ENABLE_AOM" = "1" ]; then cd .. fi - CONFIGURE_ARGS_AOM="-DAOM_INCLUDE_DIR=${DIR}/aom-${AOM_VERSION}/aom-source -DAOM_LIBRARY=-L${DIR}/aom-${AOM_VERSION}" + AOM_DIR="$(pwd)/aom-${AOM_VERSION}" + CONFIGURE_ARGS_AOM="-DAOM_INCLUDE_DIR=${AOM_DIR}/aom-source -DAOM_LIBRARY=-L${AOM_DIR}" LIBRARY_LINKER_FLAGS="$LIBRARY_LINKER_FLAGS -laom" - LIBRARY_INCLUDE_FLAGS="$LIBRARY_INCLUDE_FLAGS -L${DIR}/aom-${AOM_VERSION}" + LIBRARY_INCLUDE_FLAGS="$LIBRARY_INCLUDE_FLAGS -L${AOM_DIR}" fi EXTRA_EXE_LINKER_FLAGS="-lembind" From 34ece2d79c293fb51c65924f9b58231ff8a012c9 Mon Sep 17 00:00:00 2001 From: Ezra Huang Date: Wed, 3 Jul 2024 14:33:51 -0600 Subject: [PATCH 082/129] upgrade libde265 version to 1.0.15 from 1.0.12 --- build-emscripten.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-emscripten.sh b/build-emscripten.sh index dfc0124b32..3e4fbeee50 100755 --- a/build-emscripten.sh +++ b/build-emscripten.sh @@ -20,7 +20,7 @@ SRCDIR=$1 CORES="${CORES:-`nproc --all`}" ENABLE_LIBDE265="${ENABLE_LIBDE265:-1}" -LIBDE265_VERSION="${LIBDE265_VERSION:-1.0.12}" +LIBDE265_VERSION="${LIBDE265_VERSION:-1.0.15}" ENABLE_AOM="${ENABLE_AOM:-0}" AOM_VERSION="${AOM_VERSION:-3.6.1}" STANDALONE="${STANDALONE:-0}" From 8d447f34da996c60becdf2e23aa50eb721283542 Mon Sep 17 00:00:00 2001 From: Ezra Huang Date: Wed, 3 Jul 2024 15:02:45 -0600 Subject: [PATCH 083/129] make build-emscripten script compatible with emsdk versions >=3.1.55 The --memory-init-file emcc flag was removed in version 3.1.55 so it has to be removed. Another emscripten option to generate typescript definitions was also added so this is added to the build-emscripten script. --- build-emscripten.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/build-emscripten.sh b/build-emscripten.sh index 3e4fbeee50..9e9ccaee2e 100755 --- a/build-emscripten.sh +++ b/build-emscripten.sh @@ -26,6 +26,8 @@ AOM_VERSION="${AOM_VERSION:-3.6.1}" STANDALONE="${STANDALONE:-0}" DEBUG="${DEBUG:-0}" USE_WASM="${USE_WASM:-1}" +USE_TYPESCRIPT="${USE_TYPESCRIPT:-1}" +USE_UNSAFE_EVAL="${USE_UNSAFE_EVAL:-1}" echo "Build using ${CORES} CPU cores" @@ -110,7 +112,12 @@ EXPORTED_FUNCTIONS=$($EMSDK/upstream/bin/llvm-nm $LIBHEIFA --format=just-symbols echo "Running Emscripten..." -BUILD_FLAGS="-lembind -o libheif.js --post-js ${SRCDIR}/post.js -sWASM=$USE_WASM" +BUILD_FLAGS="-lembind -o libheif.js --post-js ${SRCDIR}/post.js -sWASM=$USE_WASM -sDYNAMIC_EXECUTION=$USE_UNSAFE_EVAL" + +if [ "$USE_TYPESCRIPT" = "1" ]; then + BUILD_FLAGS="$BUILD_FLAGS --emit-tsd libheif.d.ts" +fi + RELEASE_BUILD_FLAGS="-O3" if [ "$STANDALONE" = "1" ]; then @@ -130,7 +137,6 @@ emcc -Wl,--whole-archive "$LIBHEIFA" -Wl,--no-whole-archive \ -sEXPORT_NAME="libheif" \ -sWASM_ASYNC_COMPILATION=0 \ -sALLOW_MEMORY_GROWTH \ - --memory-init-file 0 \ -std=c++11 \ $LIBRARY_INCLUDE_FLAGS \ $LIBRARY_LINKER_FLAGS \ From 5ef70d821b3e5360d6b2ba1f852f679b3db224ce Mon Sep 17 00:00:00 2001 From: Ezra Huang Date: Fri, 5 Jul 2024 08:51:37 -0600 Subject: [PATCH 084/129] CI: upgrade emscripten to v3.1.61 --- .github/workflows/emscripten.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml index 0b36c40a00..157a7cbba3 100644 --- a/.github/workflows/emscripten.yml +++ b/.github/workflows/emscripten.yml @@ -9,7 +9,7 @@ on: jobs: emscripten: env: - EMSCRIPTEN_VERSION: 3.1.47 + EMSCRIPTEN_VERSION: 3.1.61 runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 From 7c9729ea8d0f319ab423f8e4e6de121fd78596d0 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Sat, 6 Jul 2024 23:54:14 +0200 Subject: [PATCH 085/129] check for zero width/height images (clusterfuzz issue 70099) --- libheif/context.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libheif/context.cc b/libheif/context.cc index b3666d3282..3b6a373fb4 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -470,6 +470,12 @@ Error HeifContext::check_resolution(uint32_t width, uint32_t height) const { sstr.str()); } + if (width==0 || height==0) { + return Error(heif_error_Memory_allocation_error, + heif_suberror_Invalid_image_size, + "zero width or height"); + } + return Error::Ok; } From a4cfe72d101138750046b531fffb9e319b729359 Mon Sep 17 00:00:00 2001 From: flyyee <8mrgil@gmail.com> Date: Sun, 7 Jul 2024 17:37:45 +0800 Subject: [PATCH 086/129] Added patch to check that overlay's offsets are valid --- libheif/pixelimage.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc index 9fd69366d7..f22e40f4d6 100644 --- a/libheif/pixelimage.cc +++ b/libheif/pixelimage.cc @@ -829,6 +829,26 @@ Error HeifPixelImage::overlay(std::shared_ptr& overlay, int dx, "Overlay image outside of left or top canvas border"); } + // verify that the destination points are within the bounds of the image's dimensions + if (out_x0 < 0 || + out_x0 > out_w || + out_y0 < 0 || + out_y0 > out_h) { + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_overlay_data, + "Overlay image has invalid offsets"); + } + + // verify that the source points are within the bounds of the image's dimensions + if (in_x0 < 0 || + in_x0 > in_w || + in_y0 < 0 || + in_y0 > in_h) { + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_overlay_data, + "Overlay image has invalid offsets"); + } + for (int y = in_y0; y < in_h; y++) { if (!has_alpha) { memcpy(out_p + out_x0 + (out_y0 + y - in_y0) * out_stride, From 6bed0f023ddf8a27dcb38d72385692f8c12f3062 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sun, 7 Jul 2024 11:32:24 +0100 Subject: [PATCH 087/129] Sanity-check tile dimensions/resolution Prevents integer overflow --- libheif/context.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libheif/context.cc b/libheif/context.cc index 3b6a373fb4..b3e6f944ea 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -1914,6 +1914,10 @@ Error HeifContext::decode_full_grid_image(heif_item_id ID, const std::shared_ptr tileImg = iter->second; int src_width = tileImg->get_width(); int src_height = tileImg->get_height(); + err = check_resolution(src_width, src_height); + if (err) { + return err; + } #if ENABLE_PARALLEL_TILE_DECODING if (m_max_decoding_threads > 0) From d60b2c67e259439c861478e704e8d57d97084a45 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 8 Jul 2024 10:37:27 +0200 Subject: [PATCH 088/129] more checks on valid grid image tile sizes (#1228) --- libheif/context.cc | 57 ++++++++++++++++++++++++++++++---------------- libheif/context.h | 2 +- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/libheif/context.cc b/libheif/context.cc index b3e6f944ea..09fd6fa78e 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -1878,7 +1878,7 @@ Error HeifContext::decode_full_grid_image(heif_item_id ID, img->add_plane(heif_channel_B, w, h, bpp); } - int y0 = 0; + uint32_t y0 = 0; int reference_idx = 0; #if ENABLE_PARALLEL_TILE_DECODING @@ -1886,7 +1886,7 @@ Error HeifContext::decode_full_grid_image(heif_item_id ID, struct tile_data { heif_item_id tileID; - int x_origin, y_origin; + uint32_t x_origin, y_origin; }; std::deque tiles; @@ -1896,29 +1896,49 @@ Error HeifContext::decode_full_grid_image(heif_item_id ID, std::deque > errs; #endif - for (int y = 0; y < grid.get_rows(); y++) { - int x0 = 0; - int tile_height = 0; + uint32_t tile_width=0; + uint32_t tile_height=0; - for (int x = 0; x < grid.get_columns(); x++) { + for (uint32_t y = 0; y < grid.get_rows(); y++) { + uint32_t x0 = 0; + + for (uint32_t x = 0; x < grid.get_columns(); x++) { heif_item_id tileID = image_references[reference_idx]; auto iter = m_all_images.find(tileID); if (iter == m_all_images.end()) { - return Error(heif_error_Invalid_input, - heif_suberror_Missing_grid_images, - "Nonexistent grid image referenced"); + return {heif_error_Invalid_input, + heif_suberror_Missing_grid_images, + "Nonexistent grid image referenced"}; } const std::shared_ptr tileImg = iter->second; - int src_width = tileImg->get_width(); - int src_height = tileImg->get_height(); + uint32_t src_width = tileImg->get_width(); + uint32_t src_height = tileImg->get_height(); err = check_resolution(src_width, src_height); if (err) { return err; } + if (src_width < grid.get_width() / grid.get_columns() || + src_height < grid.get_height() / grid.get_rows()) { + return {heif_error_Invalid_input, + heif_suberror_Invalid_grid_data, + "Grid tiles do not cover whole image"}; + } + + if (x==0 && y==0) { + // remember size of first tile and compare all other tiles against this + tile_width = src_width; + tile_height = src_height; + } + else if (src_width != tile_width || src_height != tile_height) { + return {heif_error_Invalid_input, + heif_suberror_Invalid_grid_data, + "Grid tiles have different sizes"}; + } + #if ENABLE_PARALLEL_TILE_DECODING if (m_max_decoding_threads > 0) tiles[x + y * grid.get_columns()] = tile_data{tileID, x0, y0}; @@ -1927,14 +1947,13 @@ Error HeifContext::decode_full_grid_image(heif_item_id ID, if (1) #endif { - Error err = decode_and_paste_tile_image(tileID, img, x0, y0, options); + err = decode_and_paste_tile_image(tileID, img, x0, y0, options); if (err) { return err; } } x0 += src_width; - tile_height = src_height; // TODO: check that all tiles have the same height reference_idx++; } @@ -1990,7 +2009,7 @@ Error HeifContext::decode_full_grid_image(heif_item_id ID, Error HeifContext::decode_and_paste_tile_image(heif_item_id tileID, const std::shared_ptr& img, - int x0, int y0, + uint32_t x0, uint32_t y0, const heif_decoding_options& options) const { std::shared_ptr tile_img; @@ -2000,16 +2019,14 @@ Error HeifContext::decode_and_paste_tile_image(heif_item_id tileID, return err; } - const int w = img->get_width(); - const int h = img->get_height(); + const uint32_t w = img->get_width(); + const uint32_t h = img->get_height(); // --- copy tile into output image - int src_width = tile_img->get_width(); - int src_height = tile_img->get_height(); - assert(src_width >= 0); - assert(src_height >= 0); + uint32_t src_width = tile_img->get_width(); + uint32_t src_height = tile_img->get_height(); heif_chroma chroma = img->get_chroma_format(); diff --git a/libheif/context.h b/libheif/context.h index b177f47652..af1d7d23dd 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -545,7 +545,7 @@ class HeifContext : public ErrorBuffer Error decode_and_paste_tile_image(heif_item_id tileID, const std::shared_ptr& out_image, - int x0, int y0, + uint32_t x0, uint32_t y0, const heif_decoding_options& options) const; Error decode_derived_image(heif_item_id ID, From 45c704a82cd8cb587b6ac01f65c12a7a75c785fe Mon Sep 17 00:00:00 2001 From: flyyee <8mrgil@gmail.com> Date: Mon, 8 Jul 2024 16:50:15 +0800 Subject: [PATCH 089/129] Changed offset checks against image dimensions to use >= --- libheif/pixelimage.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc index f22e40f4d6..3a54dd4117 100644 --- a/libheif/pixelimage.cc +++ b/libheif/pixelimage.cc @@ -831,9 +831,9 @@ Error HeifPixelImage::overlay(std::shared_ptr& overlay, int dx, // verify that the destination points are within the bounds of the image's dimensions if (out_x0 < 0 || - out_x0 > out_w || + out_x0 >= out_w || out_y0 < 0 || - out_y0 > out_h) { + out_y0 >= out_h) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_overlay_data, "Overlay image has invalid offsets"); @@ -841,9 +841,9 @@ Error HeifPixelImage::overlay(std::shared_ptr& overlay, int dx, // verify that the source points are within the bounds of the image's dimensions if (in_x0 < 0 || - in_x0 > in_w || + in_x0 >= in_w || in_y0 < 0 || - in_y0 > in_h) { + in_y0 >= in_h) { return Error(heif_error_Invalid_input, heif_suberror_Invalid_overlay_data, "Overlay image has invalid offsets"); From a3ed1b1eb178c5d651d6ac619c8da3d71ac2be36 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 8 Jul 2024 12:26:24 +0200 Subject: [PATCH 090/129] rewrite overlay image area intersection (#1226) --- libheif/context.cc | 12 +++- libheif/pixelimage.cc | 131 +++++++++++++++++++++++------------------- libheif/pixelimage.h | 2 +- 3 files changed, 81 insertions(+), 64 deletions(-) diff --git a/libheif/context.cc b/libheif/context.cc index 09fd6fa78e..929a2b322b 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -326,9 +326,9 @@ Error ImageOverlay::parse(size_t num_images, const std::vector& data) std::stringstream sstr; sstr << "Overlay image data version " << ((int) m_version) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); + return {heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()}; } int field_len = ((m_flags & 1) ? 4 : 2); @@ -346,6 +346,12 @@ Error ImageOverlay::parse(size_t num_images, const std::vector& data) m_width = readvec(data, ptr, field_len); m_height = readvec(data, ptr, field_len); + if (m_width==0 || m_height==0) { + return {heif_error_Invalid_input, + heif_suberror_Invalid_overlay_data, + "Overlay image with zero width or height."}; + } + m_offsets.resize(num_images); for (size_t i = 0; i < num_images; i++) { diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc index 3a54dd4117..c4793460ae 100644 --- a/libheif/pixelimage.cc +++ b/libheif/pixelimage.cc @@ -748,7 +748,20 @@ Error HeifPixelImage::fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_ } -Error HeifPixelImage::overlay(std::shared_ptr& overlay, int dx, int dy) +uint32_t negate_negative_int32(int32_t x) +{ + assert(x <= 0); + + if (x == INT32_MIN) { + return static_cast(INT32_MAX) + 1; + } + else { + return static_cast(-x); + } +} + + +Error HeifPixelImage::overlay(std::shared_ptr& overlay, int32_t dx, int32_t dy) { std::set channels = overlay->get_channel_set(); @@ -773,90 +786,88 @@ Error HeifPixelImage::overlay(std::shared_ptr& overlay, int dx, in_p = overlay->get_plane(channel, &in_stride); out_p = get_plane(channel, &out_stride); - int in_w = overlay->get_width(channel); - int in_h = overlay->get_height(channel); - assert(in_w >= 0); - assert(in_h >= 0); + uint32_t in_w = overlay->get_width(channel); + uint32_t in_h = overlay->get_height(channel); - int out_w = get_width(channel); - int out_h = get_height(channel); - assert(out_w >= 0); - assert(out_h >= 0); + uint32_t out_w = get_width(channel); + uint32_t out_h = get_height(channel); - // overlay image extends past the right border -> cut width for copy - if (dx + in_w > out_w) { - in_w = out_w - dx; - } + // top-left points where to start copying in source and destination + uint32_t in_x0; + uint32_t in_y0; + uint32_t out_x0; + uint32_t out_y0; - // overlay image extends past the bottom border -> cut height for copy - if (dy + in_h > out_h) { - in_h = out_h - dy; + if (dx > 0 && static_cast(dx) >= out_w) { + // the overlay image is completely outside the right border -> skip overlaying + return Error::Ok; + } + else if (dx < 0 && in_w <= negate_negative_int32(dx)) { + // the overlay image is completely outside the left border -> skip overlaying + return Error::Ok; } - // overlay image completely outside right or bottom border -> do not copy - if (in_w < 0 || in_h < 0) { - return Error(heif_error_Invalid_input, - heif_suberror_Overlay_image_outside_of_canvas, - "Overlay image outside of right or bottom canvas border"); + if (dx < 0) { + // overlay image started partially outside of left border + + in_x0 = negate_negative_int32(dx); + out_x0 = 0; + in_w = in_w - in_x0; // in_x0 < in_w because in_w > -dx = in_x0 + } + else { + in_x0 = 0; + out_x0 = static_cast(dx); } + // we know that dx >= 0 && dx < out_w - // calculate top-left point where to start copying in source and destination - int in_x0 = 0; - int in_y0 = 0; - int out_x0 = dx; - int out_y0 = dy; + if (static_cast(dx) > UINT32_MAX - in_w || + dx + in_w > out_w) { + // overlay image extends partially outside of right border - // overlay image started outside of left border - // -> move start into the image and start at left output column - if (dx < 0) { - in_x0 = -dx; - out_x0 = 0; + in_w = out_w - static_cast(dx); // we know that dx < out_w from first condition + } + + + if (dy > 0 && static_cast(dy) >= out_h) { + // the overlay image is completely outside the bottom border -> skip overlaying + return Error::Ok; + } + else if (dy < 0 && in_h <= negate_negative_int32(dy)) { + // the overlay image is completely outside the top border -> skip overlaying + return Error::Ok; } - // overlay image started outside of top border - // -> move start into the image and start at top output row if (dy < 0) { - in_y0 = -dy; + // overlay image started partially outside of top border + + in_y0 = negate_negative_int32(dy); out_y0 = 0; + in_h = in_h - in_y0; // in_y0 < in_h because in_h > -dy = in_y0 } - - // if overlay image is completely outside at left border, do not copy anything. - if (in_w <= in_x0 || - in_h <= in_y0) { - return Error(heif_error_Invalid_input, - heif_suberror_Overlay_image_outside_of_canvas, - "Overlay image outside of left or top canvas border"); + else { + in_y0 = 0; + out_y0 = static_cast(dy); } - // verify that the destination points are within the bounds of the image's dimensions - if (out_x0 < 0 || - out_x0 >= out_w || - out_y0 < 0 || - out_y0 >= out_h) { - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_overlay_data, - "Overlay image has invalid offsets"); - } + // we know that dy >= 0 && dy < out_h + + if (static_cast(dy) > UINT32_MAX - in_h || + dy + in_h > out_h) { + // overlay image extends partially outside of bottom border - // verify that the source points are within the bounds of the image's dimensions - if (in_x0 < 0 || - in_x0 >= in_w || - in_y0 < 0 || - in_y0 >= in_h) { - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_overlay_data, - "Overlay image has invalid offsets"); + in_h = out_h - static_cast(dy); // we know that dy < out_h from first condition } - for (int y = in_y0; y < in_h; y++) { + + for (uint32_t y = in_y0; y < in_h; y++) { if (!has_alpha) { memcpy(out_p + out_x0 + (out_y0 + y - in_y0) * out_stride, in_p + in_x0 + y * in_stride, in_w - in_x0); } else { - for (int x = in_x0; x < in_w; x++) { + for (uint32_t x = in_x0; x < in_w; x++) { uint8_t* outptr = &out_p[out_x0 + (out_y0 + y - in_y0) * out_stride + x]; uint8_t in_val = in_p[in_x0 + y * in_stride + x]; uint8_t alpha_val = alpha_p[in_x0 + y * in_stride + x]; diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h index d050776cb8..096cb01dad 100644 --- a/libheif/pixelimage.h +++ b/libheif/pixelimage.h @@ -109,7 +109,7 @@ class HeifPixelImage : public std::enable_shared_from_this, Error fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_t a); - Error overlay(std::shared_ptr& overlay, int dx, int dy); + Error overlay(std::shared_ptr& overlay, int32_t dx, int32_t dy); Error scale_nearest_neighbor(std::shared_ptr& output, int width, int height) const; From 6826a9d7d808351f4a510c3939af9924ee31c950 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 8 Jul 2024 19:09:15 +0200 Subject: [PATCH 091/129] AOM encoder: default number of threads should not exceed maximum (#1221) --- libheif/plugins/encoder_aom.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc index 0eeb9177c6..849f8f3d35 100644 --- a/libheif/plugins/encoder_aom.cc +++ b/libheif/plugins/encoder_aom.cc @@ -238,16 +238,17 @@ static void aom_init_parameters() p->version = 2; p->name = kParam_threads; p->type = heif_encoder_parameter_type_integer; - unsigned int threads = std::thread::hardware_concurrency(); + p->has_default = true; + p->integer.have_minimum_maximum = true; + p->integer.minimum = 1; + p->integer.maximum = 64; + int threads = static_cast(std::thread::hardware_concurrency()); if (threads == 0) { // Could not autodetect, use previous default value. threads = 4; } + threads = std::min(threads, p->integer.maximum); p->integer.default_value = threads; - p->has_default = true; - p->integer.have_minimum_maximum = true; - p->integer.minimum = 1; - p->integer.maximum = 64; p->integer.valid_values = NULL; p->integer.num_valid_values = 0; d[i++] = p++; From 2de879aadb52c7d2f661c0067faa6b853330a9f1 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 10:21:19 +0200 Subject: [PATCH 092/129] error if read box version is not supported --- libheif/box.cc | 80 ++++++++++++++++++++++++++++++++++++++++++++++ libheif/box.h | 1 + libheif/context.cc | 14 +++++--- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index 645dc4b27a..4aed5d46e6 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1049,6 +1049,10 @@ Error Box_meta::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() != 0) { + return unsupported_version_error("meta"); + } + /* uint64_t boxSizeLimit; if (get_box_size() == BoxHeader::size_until_end_of_file) { @@ -1073,10 +1077,25 @@ std::string Box_meta::dump(Indent& indent) const } +Error FullBox::unsupported_version_error(const char* box) const +{ + std::stringstream sstr; + sstr << box << " box data version " << ((int) m_version) << " is not implemented yet"; + + return {heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()}; +} + + Error Box_hdlr::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() != 0) { + return unsupported_version_error("hdlr"); + } + m_pre_defined = range.read32(); m_handler_type = range.read32(); @@ -1125,6 +1144,11 @@ Error Box_pitm::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() > 1) { + return unsupported_version_error("pitm"); + } + + if (get_version() == 0) { m_item_ID = range.read16(); } @@ -1179,6 +1203,10 @@ Error Box_iloc::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() > 2) { + return unsupported_version_error("iloc"); + } + const int version = get_version(); uint16_t values4 = range.read16(); @@ -1708,6 +1736,11 @@ Error Box_infe::parse(BitstreamRange& range) { parse_full_box_header(range); + // only versions 2,3 are required by HEIF + if (get_version() > 3) { + return unsupported_version_error("infe"); + } + if (get_version() <= 1) { m_item_ID = range.read16(); m_item_protection_index = range.read16(); @@ -1855,6 +1888,10 @@ Error Box_iinf::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() > 1) { + return unsupported_version_error("iinf"); + } + int nEntries_size = (get_version() > 0) ? 4 : 2; uint32_t item_count; @@ -1966,6 +2003,10 @@ Error Box_pixi::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() != 0) { + return unsupported_version_error("pixi"); + } + StreamReader::grow_status status; uint8_t num_channels = range.read8(); status = range.wait_for_available_bytes(num_channels); @@ -2273,6 +2314,10 @@ Error Box_ispe::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() != 0) { + return unsupported_version_error("ispe"); + } + m_image_width = range.read32(); m_image_height = range.read32(); @@ -2321,6 +2366,12 @@ Error Box_ipma::parse(BitstreamRange& range) { parse_full_box_header(range); + // TODO: is there any specification of allowed values for the ipma version in the HEIF standards? + + if (get_version() > 1) { + return unsupported_version_error("ipma"); + } + uint32_t entry_cnt = range.read32(); for (uint32_t i = 0; i < entry_cnt && !range.error() && !range.eof(); i++) { Entry entry; @@ -2503,6 +2554,10 @@ Error Box_auxC::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() != 0) { + return unsupported_version_error("auxC"); + } + m_aux_type = range.read_string(); while (!range.eof()) { @@ -2790,6 +2845,10 @@ Error Box_iref::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() > 1) { + return unsupported_version_error("iref"); + } + while (!range.eof()) { Reference ref; @@ -3198,6 +3257,10 @@ Error Box_dref::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() != 0) { + return unsupported_version_error("dref"); + } + uint32_t nEntities = range.read32(); /* @@ -3241,6 +3304,10 @@ Error Box_url::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() > 0) { + return unsupported_version_error("url"); + } + m_location = range.read_string(); return range.get_error(); @@ -3262,6 +3329,11 @@ std::string Box_url::dump(Indent& indent) const Error Box_udes::parse(BitstreamRange& range) { parse_full_box_header(range); + + if (get_version() > 0) { + return unsupported_version_error("udes"); + } + m_lang = range.read_string(); m_name = range.read_string(); m_description = range.read_string(); @@ -3347,6 +3419,10 @@ Error Box_cmin::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() > 0) { + return unsupported_version_error("cmin"); + } + m_denominatorShift = (get_flags() & 0x1F00) >> 8; uint32_t denominator = (1U << m_denominatorShift); @@ -3530,6 +3606,10 @@ Error Box_cmex::parse(BitstreamRange& range) { parse_full_box_header(range); + if (get_version() > 0) { + return unsupported_version_error("cmex"); + } + m_matrix = ExtrinsicMatrix{}; if (get_flags() & pos_x_present) { diff --git a/libheif/box.h b/libheif/box.h index 61f736c797..2e88faac62 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -261,6 +261,7 @@ class FullBox : public Box Error write_header(StreamWriter&, size_t total_size, bool data64bit = false) const override; + Error unsupported_version_error(const char* box) const; private: uint8_t m_version = 0; diff --git a/libheif/context.cc b/libheif/context.cc index 929a2b322b..ecb5e5f5ea 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -178,7 +178,13 @@ Error ImageGrid::parse(const std::vector& data) } uint8_t version = data[0]; - (void) version; // version is unused + if (version != 0) { + std::stringstream sstr; + sstr << "Grid image version " << ((int)version) << " is not supported"; + return {heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()}; + } uint8_t flags = data[1]; int field_size = ((flags & 1) ? 32 : 16); @@ -312,7 +318,7 @@ class ImageOverlay Error ImageOverlay::parse(size_t num_images, const std::vector& data) { Error eofError(heif_error_Invalid_input, - heif_suberror_Invalid_grid_data, + heif_suberror_Invalid_overlay_data, "Overlay image data incomplete"); if (data.size() < 2 + 4 * 2) { @@ -320,8 +326,6 @@ Error ImageOverlay::parse(size_t num_images, const std::vector& data) } m_version = data[0]; - m_flags = data[1]; - if (m_version != 0) { std::stringstream sstr; sstr << "Overlay image data version " << ((int) m_version) << " is not implemented yet"; @@ -331,6 +335,8 @@ Error ImageOverlay::parse(size_t num_images, const std::vector& data) sstr.str()}; } + m_flags = data[1]; + int field_len = ((m_flags & 1) ? 4 : 2); int ptr = 2; From b97d0c2b2353c8c132f334729fc75e2d47d3763d Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 10:52:07 +0200 Subject: [PATCH 093/129] fix iinf box version in example image --- examples/example.heic | Bin 718114 -> 718114 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/example.heic b/examples/example.heic index 91d5dc14972ff7489be4e10d6aab5f7c60ec748f..829384037820e545467a4af49aa6414c2b0f2885 100644 GIT binary patch delta 44 zcmV+{0Mq}X>MNq^E0DJV0fD%MxB-Q@0)@B(g}4NTxCMo{28Fl>g}4ZXxCynm3TpJr CqZAkb delta 44 zcmV+{0Mq}X>MNq^E0DJV0)e=NxB-Q@0)@B(g}4NTxCMo{28Fl>g}4ZXxCynm3TpJr C#uOO< From e8e7f0c3c9ca88a6f73c5c6263455f9782d8619b Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 12:18:16 +0200 Subject: [PATCH 094/129] when reading HEVC SEI, make sure that there is enough input data --- libheif/codecs/hevc.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libheif/codecs/hevc.cc b/libheif/codecs/hevc.cc index 4c23510a22..aea9ef65ad 100644 --- a/libheif/codecs/hevc.cc +++ b/libheif/codecs/hevc.cc @@ -427,6 +427,12 @@ Error decode_hevc_aux_sei_messages(const std::vector& data, // Read this and the NAL size directly on the array data. BitReader reader(data.data(), (int) data.size()); + if (reader.get_bits_remaining() < 32) { + return {heif_error_Invalid_input, + heif_suberror_End_of_data, + "HEVC SEI NAL too short"}; + } + uint32_t len = (uint32_t) reader.get_bits(32); if (len > data.size() - 4) { @@ -435,8 +441,15 @@ Error decode_hevc_aux_sei_messages(const std::vector& data, while (reader.get_current_byte_index() < (int) len) { int currPos = reader.get_current_byte_index(); + BitReader sei_reader(data.data() + currPos, (int) data.size() - currPos); + if (sei_reader.get_bits_remaining() < 32+8) { + return {heif_error_Invalid_input, + heif_suberror_End_of_data, + "HEVC SEI NAL too short"}; + } + uint32_t nal_size = (uint32_t) sei_reader.get_bits(32); (void) nal_size; @@ -448,6 +461,12 @@ Error decode_hevc_aux_sei_messages(const std::vector& data, if (nal_type == 39 || nal_type == 40) { + if (sei_reader.get_bits_remaining() < 16) { + return {heif_error_Invalid_input, + heif_suberror_End_of_data, + "HEVC SEI NAL too short"}; + } + // TODO: loading of multi-byte sei headers uint8_t payload_id = (uint8_t) (sei_reader.get_bits(8)); uint8_t payload_size = (uint8_t) (sei_reader.get_bits(8)); From 98c1d7c7e86363c8b9d193d3bc86d6b4e48b7fad Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 12:25:45 +0200 Subject: [PATCH 095/129] fill_RGB_16bit() for very large images --- libheif/pixelimage.cc | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc index c4793460ae..336e33c5bb 100644 --- a/libheif/pixelimage.cc +++ b/libheif/pixelimage.cc @@ -713,9 +713,9 @@ Error HeifPixelImage::fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_ "Can currently only fill images with 8 bits per pixel"); } - int h = plane.m_height; + size_t h = plane.m_height; - int stride = plane.stride; + size_t stride = plane.stride; uint8_t* data = plane.mem; uint16_t val16; @@ -741,7 +741,22 @@ Error HeifPixelImage::fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_ auto val8 = static_cast(val16 >> 8U); - memset(data, val8, stride * h); + + // memset() even when h * stride > sizeof(size_t) + + if (std::numeric_limits::max() / stride > h) { + // can fill in one step + memset(data, val8, stride * h); + } + else { + // fill line by line + auto* p = data; + + for (size_t y=0;y Date: Tue, 9 Jul 2024 12:30:37 +0200 Subject: [PATCH 096/129] include --- libheif/pixelimage.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc index 336e33c5bb..257f54025e 100644 --- a/libheif/pixelimage.cc +++ b/libheif/pixelimage.cc @@ -25,6 +25,7 @@ #include #include #include +#include heif_chroma chroma_from_subsampling(int h, int v) From c0e4d242f157ed6386cf0bfcb85374cec80af837 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Fri, 28 Jun 2024 21:29:11 +1000 Subject: [PATCH 097/129] uncompressed: implement generic compression decode --- CMakeLists.txt | 11 +- README.md | 6 +- cmake/modules/FindBrotli.cmake | 26 + go/heif/heif.go | 6 + libheif/CMakeLists.txt | 17 +- libheif/api/libheif/heif.h | 10 + libheif/api/libheif/heif_emscripten.h | 3 + libheif/bitstream.cc | 25 + libheif/bitstream.h | 2 + libheif/box.cc | 8 + libheif/codecs/uncompressed_box.cc | 87 ++++ libheif/codecs/uncompressed_box.h | 68 ++- libheif/codecs/uncompressed_image.cc | 29 +- .../{metadata_compression.h => compression.h} | 21 +- libheif/compression_brotli.cc | 84 ++++ ...ata_compression.cc => compression_zlib.cc} | 37 +- libheif/context.cc | 2 +- libheif/error.cc | 8 + libheif/file.cc | 462 ++++++++++++------ libheif/file.h | 19 +- scripts/install-ci-linux.sh | 7 + tests/CMakeLists.txt | 1 + tests/data/rgb_generic_compressed_brotli.heif | Bin 0 -> 554 bytes tests/data/rgb_generic_compressed_defl.heif | Bin 0 -> 631 bytes .../rgb_generic_compressed_tile_deflate.heif | Bin 0 -> 692 bytes tests/data/rgb_generic_compressed_zlib.heif | Bin 0 -> 647 bytes .../rgb_generic_compressed_zlib_rows.heif | Bin 0 -> 2582 bytes .../rgb_generic_compressed_zlib_tiled.heif | Bin 0 -> 770 bytes tests/uncompressed_box.cc | 213 ++++++++ tests/uncompressed_decode.h | 10 + ...uncompressed_decode_generic_compression.cc | 216 ++++++++ tests/uncompressed_encode.cc | 3 + 32 files changed, 1188 insertions(+), 193 deletions(-) create mode 100644 cmake/modules/FindBrotli.cmake rename libheif/{metadata_compression.h => compression.h} (67%) create mode 100644 libheif/compression_brotli.cc rename libheif/{metadata_compression.cc => compression_zlib.cc} (72%) create mode 100644 tests/data/rgb_generic_compressed_brotli.heif create mode 100644 tests/data/rgb_generic_compressed_defl.heif create mode 100644 tests/data/rgb_generic_compressed_tile_deflate.heif create mode 100644 tests/data/rgb_generic_compressed_zlib.heif create mode 100644 tests/data/rgb_generic_compressed_zlib_rows.heif create mode 100644 tests/data/rgb_generic_compressed_zlib_tiled.heif create mode 100644 tests/uncompressed_decode_generic_compression.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 562a115d95..2a51616eec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -395,9 +395,18 @@ endif() if (LIBSHARPYUV_FOUND) list(APPEND REQUIRES_PRIVATE "libsharpyuv") endif() -if (WITH_DEFLATE_HEADER_COMPRESSION) +if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) list(APPEND REQUIRES_PRIVATE "zlib") endif() +if (WITH_UNCOMPRESSED_CODEC) + find_package(Brotli) + if (Brotli_FOUND) + message("Brotli found") + list(APPEND REQUIRES_PRIVATE "libbrotli") + else() + message("Brotli not found") + endif() +endif() list(JOIN REQUIRES_PRIVATE " " REQUIRES_PRIVATE) diff --git a/README.md b/README.md index 677d892168..31f177aa33 100644 --- a/README.md +++ b/README.md @@ -159,8 +159,8 @@ The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODE Further options are: -* `WITH_UNCOMPRESSED_CODEC`: enable support for uncompressed images according to ISO/IEC 23001-17:2023. This is *experimental* - and not available as a dynamic plugin. +* `WITH_UNCOMPRESSED_CODEC`: enable support for uncompressed images according to ISO/IEC 23001-17:2024. This is *experimental* + and not available as a dynamic plugin. When enabled, it adds a dependency to `zlib`, and optionally will use `brotli`. * `WITH_DEFLATE_HEADER_COMPRESSION`: enables support for compressed metadata. When enabled, it adds a dependency to `zlib`. Note that header compression is not widely supported yet. * `WITH_LIBSHARPYUV`: enables high-quality YCbCr/RGB color space conversion algorithms (requires `libsharpyuv`, @@ -170,7 +170,7 @@ Further options are: * `PLUGIN_DIRECTORY`: the directory where libheif will search for dynamic plugins when the environment variable `LIBHEIF_PLUGIN_PATH` is not set. * `WITH_REDUCED_VISIBILITY`: only export those symbols into the library that are public API. - Has to be turned off for running the tests. + Has to be turned off for running some tests. ### macOS diff --git a/cmake/modules/FindBrotli.cmake b/cmake/modules/FindBrotli.cmake new file mode 100644 index 0000000000..d0ea34c892 --- /dev/null +++ b/cmake/modules/FindBrotli.cmake @@ -0,0 +1,26 @@ +include(FindPackageHandleStandardArgs) + +find_path(BROTLI_DEC_INCLUDE_DIR "brotli/decode.h") +find_path(BROTLI_ENC_INCLUDE_DIR "brotli/encode.h") + +find_library(BROTLI_COMMON_LIB NAMES brotlicommon) +find_library(BROTLI_DEC_LIB NAMES brotlidec) +find_library(BROTLI_ENC_LIB NAMES brotlienc) + +find_package_handle_standard_args(Brotli + FOUND_VAR + Brotli_FOUND + REQUIRED_VARS + BROTLI_COMMON_LIB + BROTLI_DEC_INCLUDE_DIR + BROTLI_DEC_LIB + BROTLI_ENC_INCLUDE_DIR + BROTLI_ENC_LIB + FAIL_MESSAGE + "Did not find Brotli" +) + + +set(HAVE_BROTLI ${Brotli_FOUND}) +set(BROTLI_INCLUDE_DIRS ${BROTLI_DEC_INCLUDE_DIR} ${BROTLI_ENC_INCLUDE_DIR}) +set(BROTLI_LIBS "${BROTLICOMMON_LIBRARY}" "${BROTLI_DEC_LIB}" "${BROTLI_ENC_LIB}") \ No newline at end of file diff --git a/go/heif/heif.go b/go/heif/heif.go index 71e531e967..b71b2eadf4 100644 --- a/go/heif/heif.go +++ b/go/heif/heif.go @@ -269,6 +269,8 @@ const ( SuberrorCameraExtrinsicMatrixUndefined = C.heif_suberror_Camera_extrinsic_matrix_undefined + SuberrorDecompressionInvalidData = C.heif_suberror_Decompression_invalid_data + // --- Memory_allocation_error --- // A security limit preventing unreasonable memory allocations was exceeded by the input file. @@ -276,6 +278,8 @@ const ( // security limits further. SuberrorSecurityLimitExceeded = C.heif_suberror_Security_limit_exceeded + CompressionInitialisationError = C.heif_suberror_Compression_initialisation_error + // --- Usage_error --- // An item ID was used that is not present in the file. @@ -331,6 +335,8 @@ const ( SuberrorUnsupportedDataVersion = C.heif_suberror_Unsupported_data_version + SuberrorUnsupportedGenericCompressionMethod = C.heif_suberror_Unsupported_generic_compression_method + // The conversion of the source image to the requested chroma / colorspace is not supported. SuberrorUnsupportedColorConversion = C.heif_suberror_Unsupported_color_conversion diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 4555cabc21..401d0769a8 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -33,8 +33,9 @@ set(libheif_sources init.h logging.h logging.cc - metadata_compression.cc - metadata_compression.h + compression.h + compression_brotli.cc + compression_zlib.cc common_utils.cc common_utils.h region.cc @@ -133,11 +134,21 @@ else () message("Not compiling 'libsharpyuv'") endif () -if (WITH_DEFLATE_HEADER_COMPRESSION) +if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) find_package(ZLIB REQUIRED) target_link_libraries(heif PRIVATE ZLIB::ZLIB) + target_compile_definitions(heif PRIVATE WITH_ZLIB_COMPRESSION=1) +endif () + +if (WITH_DEFLATE_HEADER_COMPRESSION) target_compile_definitions(heif PRIVATE WITH_DEFLATE_HEADER_COMPRESSION=1) endif () + +if (HAVE_BROTLI) + target_compile_definitions(heif PUBLIC HAVE_BROTLI=1) + target_include_directories(heif PRIVATE ${BROTLI_INCLUDE_DIRS}) + target_link_libraries(heif PRIVATE ${BROTLI_LIBS}) +endif() if (ENABLE_MULTITHREADING_SUPPORT) find_package(Threads) diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index a560c3a77b..b38da0a60c 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -241,6 +241,11 @@ enum heif_suberror_code heif_suberror_No_vvcC_box = 141, + // icbr is only needed in some situations, this error is for those cases + heif_suberror_No_icbr_box = 142, + + // Decompressing generic compression or header compression data failed (e.g. bitstream corruption) + heif_suberror_Decompression_invalid_data = 150, // --- Memory_allocation_error --- @@ -249,6 +254,9 @@ enum heif_suberror_code // security limits further. heif_suberror_Security_limit_exceeded = 1000, + // There was an error from the underlying compression / decompression library. + // One possibility is lack of resources (e.g. memory). + heif_suberror_Compression_initialisation_error = 1001, // --- Usage_error --- @@ -297,6 +305,8 @@ enum heif_suberror_code heif_suberror_Unsupported_header_compression_method = 3005, + // Generically compressed data used an unsupported compression method + heif_suberror_Unsupported_generic_compression_method = 3006, // --- Encoder_plugin_error --- diff --git a/libheif/api/libheif/heif_emscripten.h b/libheif/api/libheif/heif_emscripten.h index a04124babb..cc43c65c98 100644 --- a/libheif/api/libheif/heif_emscripten.h +++ b/libheif/api/libheif/heif_emscripten.h @@ -331,6 +331,8 @@ EMSCRIPTEN_BINDINGS(libheif) { emscripten::enum_("heif_suberror_code") .value("heif_suberror_Unspecified", heif_suberror_Unspecified) .value("heif_suberror_Cannot_write_output_data", heif_suberror_Cannot_write_output_data) + .value("heif_suberror_Compression_initialisation_error", heif_suberror_Compression_initialisation_error) + .value("heif_suberror_Decompression_invalid_data", heif_suberror_Decompression_invalid_data) .value("heif_suberror_Encoder_initialization", heif_suberror_Encoder_initialization) .value("heif_suberror_Encoder_encoding", heif_suberror_Encoder_encoding) .value("heif_suberror_Encoder_cleanup", heif_suberror_Encoder_cleanup) @@ -386,6 +388,7 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_Unsupported_codec", heif_suberror_Unsupported_codec) .value("heif_suberror_Unsupported_image_type", heif_suberror_Unsupported_image_type) .value("heif_suberror_Unsupported_data_version", heif_suberror_Unsupported_data_version) + .value("heif_suberror_Unsupported_generic_compression_method", heif_suberror_Unsupported_generic_compression_method) .value("heif_suberror_Unsupported_color_conversion", heif_suberror_Unsupported_color_conversion) .value("heif_suberror_Unsupported_item_construction_method", heif_suberror_Unsupported_item_construction_method) .value("heif_suberror_Unsupported_header_compression_method", heif_suberror_Unsupported_header_compression_method) diff --git a/libheif/bitstream.cc b/libheif/bitstream.cc index 80fcd02e68..c49a3f08cf 100644 --- a/libheif/bitstream.cc +++ b/libheif/bitstream.cc @@ -235,6 +235,31 @@ uint32_t BitstreamRange::read32() (buf[3])); } +uint64_t BitstreamRange::read64() +{ + if (!prepare_read(8)) { + return 0; + } + + uint8_t buf[8]; + + auto istr = get_istream(); + bool success = istr->read((char*) buf, 8); + + if (!success) { + set_eof_while_reading(); + return 0; + } + + return (uint64_t) (((uint64_t)buf[0] << 56) | + ((uint64_t)buf[1] << 48) | + ((uint64_t)buf[2] << 40) | + ((uint64_t)buf[3] << 32) | + (buf[4] << 24) | + (buf[5] << 16) | + (buf[6] << 8) | + (buf[7])); +} int32_t BitstreamRange::read32s() { diff --git a/libheif/bitstream.h b/libheif/bitstream.h index ed1de3d37b..a0188ef35e 100644 --- a/libheif/bitstream.h +++ b/libheif/bitstream.h @@ -150,6 +150,8 @@ class BitstreamRange int32_t read32s(); + uint64_t read64(); + std::string read_string(); bool read(uint8_t* data, size_t n); diff --git a/libheif/box.cc b/libheif/box.cc index 4aed5d46e6..2140dc4faf 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -611,6 +611,14 @@ Error Box::read(BitstreamRange& range, std::shared_ptr* result) case fourcc("uncC"): box = std::make_shared(); break; + + case fourcc("cmpC"): + box = std::make_shared(); + break; + + case fourcc("icbr"): + box = std::make_shared(); + break; #endif // --- JPEG 2000 diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index c113aba13d..19cb8745b3 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -323,6 +323,7 @@ std::string Box_uncC::dump(Indent& indent) const return sstr.str(); } + Error Box_uncC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); @@ -361,3 +362,89 @@ Error Box_uncC::write(StreamWriter& writer) const return Error::Ok; } + + +Error Box_cmpC::parse(BitstreamRange& range) +{ + parse_full_box_header(range); + compression_type = range.read32(); + uint8_t v = range.read8(); + must_decompress_individual_entities = ((v & 0x80) == 0x80); + compressed_range_type = (v & 0x7f); + return range.get_error(); +} + + +std::string Box_cmpC::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + sstr << indent << "compression_type: " << to_fourcc(compression_type) << "\n"; + sstr << indent << "must_decompress_individual_entities: " << must_decompress_individual_entities << "\n"; + sstr << indent << "compressed_entity_type: " << (int)compressed_range_type << "\n"; + return sstr.str(); +} + +Error Box_cmpC::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + writer.write32(compression_type); + uint8_t v = must_decompress_individual_entities ? 0x80 : 0x00; + v |= (compressed_range_type & 0x7F); + writer.write8(v); + + prepend_header(writer, box_start); + + return Error::Ok; +} + + +Error Box_icbr::parse(BitstreamRange& range) +{ + parse_full_box_header(range); + uint32_t num_ranges = range.read32(); + for (uint32_t r = 0; r < num_ranges; r++) { + struct ByteRange byteRange; + if (get_version() == 1) { + byteRange.range_offset = range.read64(); + byteRange.range_size = range.read64(); + } else if (get_version() == 0) { + byteRange.range_offset = range.read32(); + byteRange.range_size = range.read32(); + } + m_ranges.push_back(byteRange); + } + return range.get_error(); +} + + +std::string Box_icbr::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + sstr << indent << "num_ranges: " << m_ranges.size() << "\n"; + for (ByteRange range: m_ranges) { + sstr << indent << "range_offset: " << range.range_offset << ", range_size: " << range.range_size << "\n"; + } + return sstr.str(); +} + +Error Box_icbr::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + writer.write32((uint32_t)m_ranges.size()); + for (ByteRange range: m_ranges) { + if (get_version() == 1) { + writer.write64(range.range_offset); + writer.write64(range.range_size); + } else if (get_version() == 0) { + writer.write32((uint32_t)range.range_offset); + writer.write32((uint32_t)range.range_size); + } + } + prepend_header(writer, box_start); + + return Error::Ok; +} \ No newline at end of file diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed_box.h index 98ae3da60b..39a52a6b49 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -76,7 +76,8 @@ class Box_cmpd : public Box class Box_uncC : public FullBox { public: - Box_uncC() { + Box_uncC() + { set_short_type(fourcc("uncC")); } @@ -220,4 +221,69 @@ class Box_uncC : public FullBox uint32_t m_num_tile_rows = 1; }; +/** + * Generic compression box (cmpC). + * + * This is from ISO/IEC 23001-17 Amd 2. + */ +class Box_cmpC : public FullBox +{ +public: + Box_cmpC() + { + set_short_type(fourcc("cmpC")); + } + + std::string dump(Indent&) const override; + + uint32_t get_compression_type() const { return compression_type; } + bool get_must_decompress_individual_entities() const { return must_decompress_individual_entities; } + uint8_t get_compressed_range_type() const { return compressed_range_type; } + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + + uint32_t compression_type; + bool must_decompress_individual_entities; + uint8_t compressed_range_type; +}; + +/** + * Item compressed byte range info (icbr). + * + * This is from ISO/IEC 23001-17 Amd 2. + */ +class Box_icbr : public FullBox +{ +public: + Box_icbr() + { + set_short_type(fourcc("icbr")); + } + + struct ByteRange + { + uint64_t range_offset; + uint64_t range_size; + }; + + const std::vector& get_ranges() const { return m_ranges; } + + void add_component(const ByteRange& range) + { + m_ranges.push_back(range); + } + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + + std::vector m_ranges; +}; + #endif //LIBHEIF_UNCOMPRESSED_BOX_H diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index ff627eeb57..1145eb9496 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -29,6 +29,7 @@ #include "common_utils.h" #include "context.h" +#include "compression.h" #include "error.h" #include "libheif/heif.h" #include "uncompressed.h" @@ -861,9 +862,9 @@ static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std:: Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* context, heif_item_id ID, std::shared_ptr& img, - const std::vector& uncompressed_data) + const std::vector& source_data) { - if (uncompressed_data.empty()) { + if (source_data.empty()) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Uncompressed image data is empty"}; @@ -883,6 +884,9 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte bool found_ispe = false; std::shared_ptr cmpd; std::shared_ptr uncC; + std::shared_ptr cmpC; + std::shared_ptr icbr; + for (const auto& prop : item_properties) { auto ispe = std::dynamic_pointer_cast(prop); if (ispe) { @@ -905,6 +909,17 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte if (maybe_uncC) { uncC = maybe_uncC; } + + auto maybe_cmpC = std::dynamic_pointer_cast(prop); + if (maybe_cmpC) { + cmpC = maybe_cmpC; + } + + auto maybe_icbr = std::dynamic_pointer_cast(prop); + if (maybe_icbr) { + icbr = maybe_icbr; + } + } @@ -919,6 +934,10 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte heif_suberror_Unsupported_data_version, "Missing required uncC box for uncompressed codec"); } + if (!cmpd) { + printf("No cmpd\n"); + } + // printf("uncC version: %d\n", uncC->get_version()); if (!cmpd && (uncC->get_version() !=1)) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, @@ -958,9 +977,9 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte AbstractDecoder *decoder = makeDecoder(width, height, cmpd, uncC); if (decoder != nullptr) { - Error result = decoder->decode(uncompressed_data, img); - delete decoder; - return result; + Error result = decoder->decode(source_data, img); + delete decoder; + return result; } else { printf("bad interleave mode - we should have detected this earlier: %d\n", uncC->get_interleave_type()); std::stringstream sstr; diff --git a/libheif/metadata_compression.h b/libheif/compression.h similarity index 67% rename from libheif/metadata_compression.h rename to libheif/compression.h index 3780620b92..bff089ff52 100644 --- a/libheif/metadata_compression.h +++ b/libheif/compression.h @@ -17,17 +17,28 @@ * You should have received a copy of the GNU Lesser General Public License * along with libheif. If not, see . */ -#ifndef LIBHEIF_METADATA_COMPRESSION_H -#define LIBHEIF_METADATA_COMPRESSION_H +#ifndef LIBHEIF_COMPRESSION_H +#define LIBHEIF_COMPRESSION_H #include #include #include -#if WITH_DEFLATE_HEADER_COMPRESSION +#include + +#if WITH_ZLIB_COMPRESSION std::vector deflate(const uint8_t* input, size_t size); -std::vector inflate(const std::vector&); +Error inflate_zlib(const std::vector&compressed_input, std::vector *output); + +Error inflate_deflate(const std::vector&, std::vector *output); + +#endif + +#if HAVE_BROTLI + +Error inflate_brotli(const std::vector &compressed_input, std::vector *output); + #endif -#endif //LIBHEIF_METADATA_COMPRESSION_H +#endif //LIBHEIF_COMPRESSION_H diff --git a/libheif/compression_brotli.cc b/libheif/compression_brotli.cc new file mode 100644 index 0000000000..69de5bf767 --- /dev/null +++ b/libheif/compression_brotli.cc @@ -0,0 +1,84 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Brad Hards + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "compression.h" + +#if HAVE_BROTLI + +const size_t BUF_SIZE = (1 << 18); +#include +#include +#include +#include +#include + +#include "error.h" + +Error inflate_brotli(const std::vector &compressed_input, std::vector *output) +{ + BrotliDecoderResult result = BROTLI_DECODER_RESULT_ERROR; + std::vector buffer(BUF_SIZE, 0); + size_t available_in = compressed_input.size(); + const std::uint8_t *next_in = reinterpret_cast(compressed_input.data()); + size_t available_out = buffer.size(); + std::uint8_t *next_output = buffer.data(); + BrotliDecoderState *state = BrotliDecoderCreateInstance(0, 0, 0); + + while (true) + { + result = BrotliDecoderDecompressStream(state, &available_in, &next_in, &available_out, &next_output, 0); + + if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) + { + output->insert(output->end(), buffer.data(), buffer.data() + std::distance(buffer.data(), next_output)); + available_out = buffer.size(); + next_output = buffer.data(); + } + else if (result == BROTLI_DECODER_RESULT_SUCCESS) + { + output->insert(output->end(), buffer.data(), buffer.data() + std::distance(buffer.data(), next_output)); + break; + } + else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) + { + std::stringstream sstr; + sstr << "Error performing brotli inflate - insufficient data.\n"; + return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str()); + } + else if (result == BROTLI_DECODER_RESULT_ERROR) + { + const char* errorMessage = BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state)); + std::stringstream sstr; + sstr << "Error performing brotli inflate - " << errorMessage << "\n"; + return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str()); + } + else + { + const char* errorMessage = BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state)); + std::stringstream sstr; + sstr << "Unknown error performing brotli inflate - " << errorMessage << "\n"; + return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str()); + } + } + BrotliDecoderDestroyInstance(state); + return Error::Ok; +} + +#endif \ No newline at end of file diff --git a/libheif/metadata_compression.cc b/libheif/compression_zlib.cc similarity index 72% rename from libheif/metadata_compression.cc rename to libheif/compression_zlib.cc index b738d1f62d..af84ebe352 100644 --- a/libheif/metadata_compression.cc +++ b/libheif/compression_zlib.cc @@ -19,12 +19,14 @@ */ -#include "metadata_compression.h" +#include "compression.h" -#if WITH_DEFLATE_HEADER_COMPRESSION +#if WITH_ZLIB_COMPRESSION + #include #include +#include std::vector deflate(const uint8_t* input, size_t size) { @@ -78,9 +80,8 @@ std::vector deflate(const uint8_t* input, size_t size) } -std::vector inflate(const std::vector& compressed_input) +Error do_inflate(const std::vector& compressed_input, int windowSize, std::vector *output) { - std::vector output; // decompress data with zlib @@ -102,10 +103,11 @@ std::vector inflate(const std::vector& compressed_input) int err = -1; - err = inflateInit(&strm); + err = inflateInit2(&strm, windowSize); if (err != Z_OK) { - // TODO: return error - return {}; + std::stringstream sstr; + sstr << "Error initialising zlib inflate: " << strm.msg << " (" << err << ")\n"; + return Error(heif_error_Memory_allocation_error, heif_suberror_Compression_initialisation_error, sstr.str()); } do { @@ -118,19 +120,28 @@ std::vector inflate(const std::vector& compressed_input) // -> do nothing } else if (err == Z_NEED_DICT || err == Z_DATA_ERROR || err == Z_STREAM_ERROR) { - // TODO: return error - return {}; + std::stringstream sstr; + sstr << "Error performing zlib inflate: " << strm.msg << " (" << err << ")\n"; + return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str()); } - // append decoded data to output - - output.insert(output.end(), dst, dst + outBufferSize - strm.avail_out); + output->insert(output->end(), dst, dst + outBufferSize - strm.avail_out); } while (err != Z_STREAM_END); inflateEnd(&strm); - return output; + return Error::Ok; +} + +Error inflate_zlib(const std::vector& compressed_input, std::vector *output) +{ + return do_inflate(compressed_input, 15, output); +} + +Error inflate_deflate(const std::vector& compressed_input, std::vector *output) +{ + return do_inflate(compressed_input, -15, output); } #endif diff --git a/libheif/context.cc b/libheif/context.cc index ecb5e5f5ea..419ffd38b5 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -40,7 +40,7 @@ #include "pixelimage.h" #include "libheif/api_structs.h" #include "security_limits.h" -#include "metadata_compression.h" +#include "compression.h" #include "color-conversion/colorconversion.h" #include "plugin_registry.h" #include "codecs/hevc.h" diff --git a/libheif/error.cc b/libheif/error.cc index 2735c12981..1caa5348fc 100644 --- a/libheif/error.cc +++ b/libheif/error.cc @@ -168,12 +168,18 @@ const char* Error::get_error_string(heif_suberror_code err) return "Camera extrinsic matrix undefined"; case heif_suberror_Invalid_J2K_codestream: return "Invalid JPEG 2000 codestream"; + case heif_suberror_Decompression_invalid_data: + return "Invalid data in generic compression inflation"; + case heif_suberror_No_icbr_box: + return "No 'icbr' box"; // --- Memory_allocation_error --- case heif_suberror_Security_limit_exceeded: return "Security limit exceeded"; + case heif_suberror_Compression_initialisation_error: + return "Compression initialisation method error"; // --- Usage_error --- @@ -210,6 +216,8 @@ const char* Error::get_error_string(heif_suberror_code err) return "Unsupported item construction method"; case heif_suberror_Unsupported_header_compression_method: return "Unsupported header compression method"; + case heif_suberror_Unsupported_generic_compression_method: + return "Unsupported generic compression method"; // --- Encoder_plugin_error -- diff --git a/libheif/file.cc b/libheif/file.cc index ea66d5279a..770c35e6ca 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -22,13 +22,15 @@ #include "box.h" #include "libheif/heif.h" #include "libheif/heif_properties.h" -#include "metadata_compression.h" +#include "compression.h" #include "codecs/jpeg2000.h" #include "codecs/jpeg.h" #include "codecs/vvc.h" +#include "codecs/uncompressed_box.h" #include #include +#include #include #include #include @@ -49,6 +51,7 @@ #if WITH_UNCOMPRESSED_CODEC #include "codecs/uncompressed_image.h" #endif +#include // TODO: make this a decoder option #define STRICT_PARSING false @@ -780,211 +783,353 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* sstr.str()); } - Error error = Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_codec); if (item_type == "hvc1") { // --- --- --- HEVC - - // --- get properties for this image - - std::vector> properties; - Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); - if (err) { - return err; - } - - // --- get codec configuration - - std::shared_ptr hvcC_box; - for (auto& prop : properties) { - if (prop->get_short_type() == fourcc("hvcC")) { - hvcC_box = std::dynamic_pointer_cast(prop); - if (hvcC_box) { - break; + return get_compressed_image_data_hvc1(ID, data, item); + } + else if (item_type == "vvc1") { + // --- --- --- VVC + return get_compressed_image_data_vvc(ID, data, item); + } + else if (item_type == "av01") { + return get_compressed_image_data_av1(ID, data, item); + } + else if (item_type == "jpeg" || + (item_type == "mime" && get_content_type(ID) == "image/jpeg")) { + return get_compressed_image_data_jpeg(ID, data, item); + } + else if (item_type == "j2k1") { + return get_compressed_image_data_jpeg2000(ID, item, data); + } +#if WITH_UNCOMPRESSED_CODEC + else if (item_type == "unci") { + return get_compressed_image_data_uncompressed(ID, data, item); + } +#endif + else if (true || // fallback case for all kinds of generic metadata (e.g. 'iptc') + item_type == "grid" || + item_type == "iovl" || + item_type == "Exif" || + (item_type == "mime" && content_type == "application/rdf+xml")) { + Error error; + bool read_uncompressed = true; + if (item_type == "mime") { + std::string encoding = infe_box->get_content_encoding(); + if (encoding == "deflate") { +#if WITH_DEFLATE_HEADER_COMPRESSION + read_uncompressed = false; + std::vector compressed_data; + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, &compressed_data); + if (error) { + return error; } + error = inflate_zlib(compressed_data, data); + if (error) { + return error; + } +#else + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method, + encoding); +#endif } } - if (!hvcC_box) { - // Should always have an hvcC box, because we are checking this in - // heif_context::interpret_heif_file() - assert(false); - return Error(heif_error_Invalid_input, - heif_suberror_No_hvcC_box); - } - else if (!hvcC_box->get_headers(data)) { - return Error(heif_error_Invalid_input, - heif_suberror_No_item_data); + if (read_uncompressed) { + return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); } + } + return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_codec); +} - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); +// generic compression and uncompressed, per 23001-17 +const Error HeifFile::get_compressed_image_data_uncompressed(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const +{ + std::vector> properties; + Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); + if (err) { + return err; } - else if (item_type == "vvc1") { - // --- --- --- VVC - // --- get properties for this image + // --- get codec configuration - std::vector> properties; - Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); - if (err) { - return err; + std::shared_ptr cmpC_box; + std::shared_ptr icbr_box; + for (auto& prop : properties) { + if (prop->get_short_type() == fourcc("cmpC")) { + cmpC_box = std::dynamic_pointer_cast(prop); } - - // --- get codec configuration - - std::shared_ptr vvcC_box; - for (auto& prop : properties) { - if (prop->get_short_type() == fourcc("vvcC")) { - vvcC_box = std::dynamic_pointer_cast(prop); - if (vvcC_box) { - break; - } - } + if (prop->get_short_type() == fourcc("icbr")) { + icbr_box = std::dynamic_pointer_cast(prop); } - - if (!vvcC_box) { - // Should always have an vvcC box, because we are checking this in - // heif_context::interpret_heif_file() - assert(false); - return Error(heif_error_Invalid_input, - heif_suberror_No_vvcC_box); + if (cmpC_box && icbr_box) { + break; } - else if (!vvcC_box->get_headers(data)) { + } + if (!cmpC_box) { + // assume no generic compression + return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + } + if (!cmpC_box->get_must_decompress_individual_entities()) { + std::vector compressed_data; + m_iloc_box->read_data(*item, m_input_stream, m_idat_box, &compressed_data); + return do_decompress_data(cmpC_box, compressed_data, data); + } else { + if (!icbr_box) { + std::stringstream sstr; + sstr << "cannot decode unci item requiring entity decompression without icbr box" << std::endl; return Error(heif_error_Invalid_input, - heif_suberror_No_item_data); + heif_suberror_No_icbr_box, + sstr.str()); + } + if (item->construction_method == 0) { + for (Box_icbr::ByteRange range: icbr_box->get_ranges()) { + // TODO: check errors + bool success = m_input_stream->seek(range.range_offset); + if (!success) { + return Error{heif_error_Invalid_input, heif_suberror_End_of_data, "error while seeking to generically compressed data"}; + } + std::vector compressed_range_bytes(range.range_size); + success = m_input_stream->read((char*) compressed_range_bytes.data(), static_cast(compressed_range_bytes.size())); + if (!success) { + return Error{heif_error_Invalid_input, heif_suberror_End_of_data, "error while reading generically compressed data"}; + } + std::vector uncompressed_range_data; + Error err = do_decompress_data(cmpC_box, compressed_range_bytes, &uncompressed_range_data); + if (err) { + return err; + } + data->insert(data->end(), uncompressed_range_data.data(), uncompressed_range_data.data() + uncompressed_range_data.size()); + } + return Error::Ok; + } else { + // TODO: implement... + std::stringstream sstr; + sstr << "cannot decode unci item from idat yet" << std::endl; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); } - - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); } - else if (item_type == "av01") { - // --- --- --- AV1 + return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_codec); +} - // --- get properties for this image +const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, std::vector compressed_data, std::vector *data) const +{ + if (cmpC_box->get_compression_type() == fourcc("brot")) { +#if HAVE_BROTLI + return inflate_brotli(compressed_data, data); +#else + std::stringstream sstr; + sstr << "cannot decode unci item with brotli compression - not enabled" << std::endl; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_generic_compression_method, + sstr.str()); +#endif + } else if (cmpC_box->get_compression_type() == fourcc("zlib")) { + return inflate_zlib(compressed_data, data); + } else if (cmpC_box->get_compression_type() == fourcc("defl")) { + return inflate_deflate(compressed_data, data); + } else { + std::stringstream sstr; + sstr << "cannot decode unci item with unsupported compression type: " << cmpC_box->get_compression_type() << std::endl; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_generic_compression_method, + sstr.str()); + } +} - std::vector> properties; - Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); - if (err) { - return err; - } +const Error HeifFile::get_compressed_image_data_hvc1(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const +{ + // --- get properties for this image + std::vector> properties; + Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); + if (err) + { + return err; + } - // --- get codec configuration + // --- get codec configuration - std::shared_ptr av1C_box; - for (auto& prop : properties) { - if (prop->get_short_type() == fourcc("av1C")) { - av1C_box = std::dynamic_pointer_cast(prop); - if (av1C_box) { - break; - } + std::shared_ptr hvcC_box; + for (auto &prop : properties) + { + if (prop->get_short_type() == fourcc("hvcC")) + { + hvcC_box = std::dynamic_pointer_cast(prop); + if (hvcC_box) + { + break; } } + } - if (!av1C_box) { - // Should always have an hvcC box, because we are checking this in - // heif_context::interpret_heif_file() - return Error(heif_error_Invalid_input, - heif_suberror_No_av1C_box); - } - else if (!av1C_box->get_headers(data)) { - return Error(heif_error_Invalid_input, - heif_suberror_No_item_data); - } - - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + if (!hvcC_box) + { + // Should always have an hvcC box, because we are checking this in + // heif_context::interpret_heif_file() + assert(false); + return Error(heif_error_Invalid_input, + heif_suberror_No_hvcC_box); + } + else if (!hvcC_box->get_headers(data)) + { + return Error(heif_error_Invalid_input, + heif_suberror_No_item_data); } - else if (item_type == "jpeg" || - (item_type == "mime" && get_content_type(ID) == "image/jpeg")) { - // --- check if 'jpgC' is present + return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); +} - std::vector> properties; - Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); - if (err) { - return err; - } +const Error HeifFile::get_compressed_image_data_vvc(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const +{ - // --- get codec configuration + // --- get properties for this image - std::shared_ptr jpgC_box; - for (auto& prop : properties) { - if (prop->get_short_type() == fourcc("jpgC")) { - jpgC_box = std::dynamic_pointer_cast(prop); - if (jpgC_box) { - *data = jpgC_box->get_data(); - break; - } + std::vector> properties; + Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); + if (err) + { + return err; + } + + // --- get codec configuration + + std::shared_ptr vvcC_box; + for (auto &prop : properties) + { + if (prop->get_short_type() == fourcc("vvcC")) + { + vvcC_box = std::dynamic_pointer_cast(prop); + if (vvcC_box) + { + break; } } + } + + if (!vvcC_box) + { + assert(false); + return Error(heif_error_Invalid_input, + heif_suberror_No_vvcC_box); + } + else if (!vvcC_box->get_headers(data)) + { + return Error(heif_error_Invalid_input, + heif_suberror_No_item_data); + } + + return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); +} + +const Error HeifFile::get_compressed_image_data_av1(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const +{ + // --- --- --- AV1 + + // --- get properties for this image - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + std::vector> properties; + Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); + if (err) + { + return err; } - else if (item_type == "j2k1") { - std::vector> properties; - Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); - if (err) { - return err; - } - // --- get codec configuration + // --- get codec configuration - std::shared_ptr j2kH_box; - for (auto& prop : properties) { - if (prop->get_short_type() == fourcc("j2kH")) { - j2kH_box = std::dynamic_pointer_cast(prop); - if (j2kH_box) { - break; - } + std::shared_ptr av1C_box; + for (auto &prop : properties) + { + if (prop->get_short_type() == fourcc("av1C")) + { + av1C_box = std::dynamic_pointer_cast(prop); + if (av1C_box) + { + break; } } + } - if (!j2kH_box) { - // Should always have an j2kH box, because we are checking this in - // heif_context::interpret_heif_file() + if (!av1C_box) + { + return Error(heif_error_Invalid_input, + heif_suberror_No_av1C_box); + } + else if (!av1C_box->get_headers(data)) + { + return Error(heif_error_Invalid_input, + heif_suberror_No_item_data); + } - //TODO - Correctly Find the j2kH box - // return Error(heif_error_Invalid_input, - // heif_suberror_Unspecified); - } - // else if (!j2kH_box->get_headers(data)) { - // return Error(heif_error_Invalid_input, - // heif_suberror_No_item_data); - // } + return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); +} - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); +const Error HeifFile::get_compressed_image_data_jpeg2000(heif_item_id ID, const Box_iloc::Item *item, std::vector *data) const +{ + std::vector> properties; + Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); + if (err) + { + return err; } - else if (true || // fallback case for all kinds of generic metadata (e.g. 'iptc') - item_type == "grid" || - item_type == "iovl" || - item_type == "Exif" || - (item_type == "mime" && content_type == "application/rdf+xml")) { - bool read_uncompressed = true; - if (item_type == "mime") { - std::string encoding = infe_box->get_content_encoding(); - if (encoding == "deflate") { -#if WITH_DEFLATE_HEADER_COMPRESSION - read_uncompressed = false; - std::vector compressed_data; - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, &compressed_data); - *data = inflate(compressed_data); -#else - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_header_compression_method, - encoding); -#endif + // --- get codec configuration + + std::shared_ptr j2kH_box; + for (auto &prop : properties) + { + if (prop->get_short_type() == fourcc("j2kH")) + { + j2kH_box = std::dynamic_pointer_cast(prop); + if (j2kH_box) + { + break; } } + } - if (read_uncompressed) { - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); - } + if (!j2kH_box) + { + // TODO - Correctly Find the j2kH box + // return Error(heif_error_Invalid_input, + // heif_suberror_Unspecified); } + // else if (!j2kH_box->get_headers(data)) { + // return Error(heif_error_Invalid_input, + // heif_suberror_No_item_data); + // } - if (error != Error::Ok) { - return error; + return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); +} + +const Error HeifFile::get_compressed_image_data_jpeg(heif_item_id ID, std::vector * data, const Box_iloc::Item *item) const +{ + // --- check if 'jpgC' is present + std::vector> properties; + Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties); + if (err) + { + return err; } - return Error::Ok; + // --- get codec configuration + + std::shared_ptr jpgC_box; + for (auto &prop : properties) + { + if (prop->get_short_type() == fourcc("jpgC")) + { + jpgC_box = std::dynamic_pointer_cast(prop); + if (jpgC_box) + { + *data = jpgC_box->get_data(); + break; + } + } + } + + return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); } @@ -1075,8 +1220,7 @@ Error HeifFile::get_item_data(heif_item_id ID, std::vector* out_data, h switch (compression) { #if WITH_DEFLATE_HEADER_COMPRESSION case heif_metadata_compression_deflate: - *out_data = inflate(compressed_data); - return Error::Ok; + return inflate_zlib(compressed_data, out_data); #endif default: return {heif_error_Unsupported_filetype, heif_suberror_Unsupported_header_compression_method}; diff --git a/libheif/file.h b/libheif/file.h index a489af9a87..75095c438b 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -26,6 +26,7 @@ #include "codecs/avif.h" #include "codecs/hevc.h" #include "codecs/vvc.h" +#include "codecs/uncompressed_box.h" #include #include @@ -84,9 +85,9 @@ class HeifFile std::string get_item_uri_type(heif_item_id ID) const; - Error get_compressed_image_data(heif_item_id ID, std::vector* out_data) const; + Error get_compressed_image_data(heif_item_id ID, std::vector *out_data) const; - Error get_item_data(heif_item_id ID, std::vector* out_data, heif_metadata_compression* out_compression) const; + Error get_item_data(heif_item_id ID, std::vector *out_data, heif_metadata_compression *out_compression) const; std::shared_ptr get_ftyp_box() { return m_ftyp_box; } @@ -251,6 +252,20 @@ class HeifFile std::unordered_set& parent_items) const; int jpeg_get_bits_per_pixel(heif_item_id imageID) const; + + const Error get_compressed_image_data_hvc1(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; + + const Error get_compressed_image_data_vvc(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; + + const Error get_compressed_image_data_uncompressed(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; + + const Error do_decompress_data(std::shared_ptr &cmpC_box, std::vector compressed_data, std::vector *data) const; + + const Error get_compressed_image_data_av1(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; + + const Error get_compressed_image_data_jpeg2000(heif_item_id ID, const Box_iloc::Item *item, std::vector *data) const; + + const Error get_compressed_image_data_jpeg(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; }; #endif diff --git a/scripts/install-ci-linux.sh b/scripts/install-ci-linux.sh index 97a644eeef..cdb5f981e9 100755 --- a/scripts/install-ci-linux.sh +++ b/scripts/install-ci-linux.sh @@ -111,6 +111,13 @@ if [ ! -z "$WITH_GRAPHICS" ]; then " fi +if [ ! -z "$WITH_UNCOMPRESSED_CODEC" ]; then + INSTALL_PACKAGES="$INSTALL_PACKAGES \ + libbrotli-dev \ + zlib-dev \ + " +fi + if [ "$MINGW" == "32" ]; then sudo dpkg --add-architecture i386 # https://github.com/actions/runner-images/issues/4589 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 82152065b9..afa9fbea03 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -48,6 +48,7 @@ endif() if (WITH_UNCOMPRESSED_CODEC) add_libheif_test(uncompressed_decode) + add_libheif_test(uncompressed_decode_generic_compression) add_libheif_test(uncompressed_decode_mono) add_libheif_test(uncompressed_decode_rgb) add_libheif_test(uncompressed_decode_rgb16) diff --git a/tests/data/rgb_generic_compressed_brotli.heif b/tests/data/rgb_generic_compressed_brotli.heif new file mode 100644 index 0000000000000000000000000000000000000000..8771ddc8114aa76b2e368cc7790175a820dbd28b GIT binary patch literal 554 zcmZQzV30{GsVvCNOfzHv0uT+vMx}X~X$%aEKDntSi6C(xP|QflDS|K=7z#3zOJHmm z%~z0Fk_+Z=0ZIGJ%sikGK)?vZhCog#6Nm?tE6q#JbWhDoEy_$z%*m-#NY2kKC`v6Z zPEAqp%uP&B1u4_W%*js%>2z@c8UQj70zg7Qz<3HsBmnWM%z~l;8Z24s~}K9M&IjxWc?3ZNZi~T3pNjEogo{`^dCIhRN}1m!ueWNIENBl4R(xoDAf6 tR53HiK2P7(pBTl=Aa|6d(u$d3i6z5_*E_H7eaXYH;}DCa=3HQ?0|1sjWNQEb literal 0 HcmV?d00001 diff --git a/tests/data/rgb_generic_compressed_defl.heif b/tests/data/rgb_generic_compressed_defl.heif new file mode 100644 index 0000000000000000000000000000000000000000..acb484a43cb9fd1379505fae75bd3740301e3d74 GIT binary patch literal 631 zcmZQzV30{GsVvCNOfzHv0uT+vMx}X~X$%aEKDntSi6C(xP|QflDS|K=7z#3zOJHmm z%~z0Fk_+Z=0ZIGJ%sikGK)?vZhCog#6Nm?tE6q#JbWhDoEy_$z%*m-#NY2kKC`v6Z zPEAqp%uP&B1u4_W%*js%>2z@c8UQj70zg7Qz<3HstOep#nFU1!K)NflAUPjM3nk|k zqyTAV1{MZZ1~wqe8fcv}kY-2+S_zh6U zgLsZZVl}>e}4Tt{_snakKj;VUsb>27+mi6pPc#=@7i2z@c8UQj70zg7Qz<3HsyaM6Of}#Q-eJZmcIUh(1CFd5T z0BL3h76w)ZHXzFyXq_{VW=IEG36^1C2U3h6f(b-m0%o8P$Rd%<;sUU1fb0e!_5fl* zpao##Qc}}$nt%cUnaN48aN&TuOa@3l1Ij5vtq=#&azMT;khTWWKsHk(kd_D16;S>( z2+bgoS&*9uWP z!C=MLCV9aA!F*r-x7QqjavV&Ci*D|pa_qrFu}2Dx``cEv9v4|~n(5L13q@}aLNqJ< zo_16`Z!(!0@_ p!_nz7(-a2z@c8UQj70zg7Qz<3Hs>;>XgnFU1!K)NflAUPjM3nk|k zqyTAV1{MZZ1~wqe8fcv}kY-2+S_zh6U*huOd_IXP@WiuMf#@wy_BqWK!R=f3})_x3T%yz|dL|E#f_fBrfDuWFCoKpM(D z|1;gA+8)mT-+%uff95`3xIp~#<$qV!XS{z5RsZ>Caoufu8O3l8xRUvwFV|oAr~kGN oWIR~e{&W7HbN^pxgWIzHbMXHO|JtwEFR6qpvVU%Wk;gj%0Q=3c*Z=?k literal 0 HcmV?d00001 diff --git a/tests/data/rgb_generic_compressed_zlib_rows.heif b/tests/data/rgb_generic_compressed_zlib_rows.heif new file mode 100644 index 0000000000000000000000000000000000000000..c0507f6cd93ab78a4fab30e06c99d1b7c4f8acdd GIT binary patch literal 2582 zcmdVZT}TvB6bJCLpROTluE7@4ZV_lNfg(Z}td(T-U`7y*uSWK-5 zqK6)WLc(4Q!azbO5JCugFep*5AcG$ILMnnnLNhyO=fBZQ@4FZ7{ASL*bMDNYGelHn zqz5ch8MVYsW1%Bvv`d$1H7cIeGCLwN) zSt4&ck#kvDDT|#|mX_p!3eB`)tc%>_Aupdb@^S}R>f$A{BUHpV!mqX-c~_2mwzIsxrw$&1r(gmO!5i=%l>A%x{ss66uG@MmU^Uzak3)mq#$RU; z-hxx`Ih4FSuSlvwtb(#1a@_{c!V!2IK7z8(GXJx!PxgV|-^a16d*A_h5=vf~mx0&e zILq(vn}&1nJzTXdE`z(^Uf9ZRB`WTMgYYJN0H48kQ1+Maqkyu^+&{;a@3}BEJaUoa zrAA866(e~gW-OZSA6?rBh9hIOp~(8=!yqkPK65AkVpfO(K5{L?F_~$XLX=XK=3{$4h_|`v~PcbbrQ})|W?x_5DCt{sw0AV{kC=x+sf$R67X38x!#|nb%;mRN_@6AOfj^L$6bS$T literal 0 HcmV?d00001 diff --git a/tests/data/rgb_generic_compressed_zlib_tiled.heif b/tests/data/rgb_generic_compressed_zlib_tiled.heif new file mode 100644 index 0000000000000000000000000000000000000000..4b905aa465a9303c4eedf64b75c49c3c22844c40 GIT binary patch literal 770 zcmZQzV30{GsVvCNOfzHv0uT+vMx}X~X$%aEJ-MkRi6C(xP|QflDS|K=7z#3zOJHmm z%~z0Fk_+Z=0ZIGJ%sikGK)?vZhCog#6Nm?tE6q#JbWhDoEy_$z%*m-#NY2kKC`v6Z zPEAqp%uP&B1u4_W%*js%>2z@c8UQj70zg7Qz<3IXEr5(ynFU1!K>Ac>L2^Ej7D~=7 zNCDE!3@i++3~WG_HPAX|AkB~tv=S`Czz(DsK?D|5dyZU!w$p7u{n3u9vyaKAbYADFSz|r8a_~!m8#}XRe zA26I;ksC0*5;yZn~51*$w~2n+@uCWYVAuZdN&sW2Sa%Jkt9gZR2% zK44=&l7|fb%)XaWFd^c_MV2zz(6|2>Hmvyjzm$J_-d}l;n+$n@idr20uY2`IyP5GE zQzG|EA;Vwx*1PJR7X9D6__z8Jb}?=nHLy|?XRSZ5QTmc<(wF+OlK(9(|I^JoZ!mXm zmdXOD1bLL>0MO={M-&(H3vur~7x0T=B76JQ|FPlU+b=Oa@(`I0RCmRZ5vYz~;aC4{ dVN#FfoH)`Lr24P41A}DU>i^A`n1m97Dgm`$*F^vT literal 0 HcmV?d00001 diff --git a/tests/uncompressed_box.cc b/tests/uncompressed_box.cc index 100113abe7..8b2205b027 100644 --- a/tests/uncompressed_box.cc +++ b/tests/uncompressed_box.cc @@ -352,3 +352,216 @@ TEST_CASE("uncC_parse_excess_tile_rows") { REQUIRE(error.sub_error_code == 1000); REQUIRE(error.message == "Tiling size 32768 x 4294967296 exceeds the maximum allowed size 4294967295 x 4294967295"); } + +TEST_CASE("cmpC_defl") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x11, 'c', 'm', 'p', 'C', + 0x00, 0x00, 0x00, 0x00, 'd', 'e', 'f', 'l', + 0x00 + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("cmpC")); + REQUIRE(box->get_type_string() == "cmpC"); + std::shared_ptr cmpC = std::dynamic_pointer_cast(box); + REQUIRE(cmpC != nullptr); + REQUIRE(cmpC->get_compression_type() == fourcc("defl")); + REQUIRE(cmpC->get_must_decompress_individual_entities() == false); + REQUIRE(cmpC->get_compressed_range_type() == 0); + + StreamWriter writer; + Error err = cmpC->write(writer); + REQUIRE(err.error_code == heif_error_Ok); + const std::vector written = writer.get_data(); + REQUIRE(written == byteArray); + + Indent indent; + std::string dump_output = cmpC->dump(indent); + REQUIRE(dump_output == "Box: cmpC -----\nsize: 17 (header size: 12)\ncompression_type: defl\nmust_decompress_individual_entities: 0\ncompressed_entity_type: 0\n"); + +} + + +TEST_CASE("cmpC_zlib") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x11, 'c', 'm', 'p', 'C', + 0x00, 0x00, 0x00, 0x00, 'z', 'l', 'i', 'b', + 0x82 + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("cmpC")); + REQUIRE(box->get_type_string() == "cmpC"); + std::shared_ptr cmpC = std::dynamic_pointer_cast(box); + REQUIRE(cmpC != nullptr); + REQUIRE(cmpC->get_compression_type() == fourcc("zlib")); + REQUIRE(cmpC->get_must_decompress_individual_entities() == true); + REQUIRE(cmpC->get_compressed_range_type() == 2); + + StreamWriter writer; + Error err = cmpC->write(writer); + REQUIRE(err.error_code == heif_error_Ok); + const std::vector written = writer.get_data(); + REQUIRE(written == byteArray); + + Indent indent; + std::string dump_output = cmpC->dump(indent); + REQUIRE(dump_output == "Box: cmpC -----\nsize: 17 (header size: 12)\ncompression_type: zlib\nmust_decompress_individual_entities: 1\ncompressed_entity_type: 2\n"); + +} + +TEST_CASE("cmpC_brot") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x11, 'c', 'm', 'p', 'C', + 0x00, 0x00, 0x00, 0x00, 'b', 'r', 'o', 't', + 0x81 + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("cmpC")); + REQUIRE(box->get_type_string() == "cmpC"); + std::shared_ptr cmpC = std::dynamic_pointer_cast(box); + REQUIRE(cmpC != nullptr); + REQUIRE(cmpC->get_compression_type() == fourcc("brot")); + REQUIRE(cmpC->get_must_decompress_individual_entities() == true); + REQUIRE(cmpC->get_compressed_range_type() == 1); + + StreamWriter writer; + Error err = cmpC->write(writer); + REQUIRE(err.error_code == heif_error_Ok); + const std::vector written = writer.get_data(); + REQUIRE(written == byteArray); + + Indent indent; + std::string dump_output = cmpC->dump(indent); + REQUIRE(dump_output == "Box: cmpC -----\nsize: 17 (header size: 12)\ncompression_type: brot\nmust_decompress_individual_entities: 1\ncompressed_entity_type: 1\n"); + + } + +TEST_CASE("icbr_empty") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x10, 'i', 'c', 'b', 'r', + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("icbr")); + REQUIRE(box->get_type_string() == "icbr"); + std::shared_ptr icbr = std::dynamic_pointer_cast(box); + REQUIRE(icbr != nullptr); + REQUIRE(icbr->get_ranges().size() == 0); + + StreamWriter writer; + Error err = icbr->write(writer); + REQUIRE(err.error_code == heif_error_Ok); + const std::vector written = writer.get_data(); + REQUIRE(written == byteArray); + + Indent indent; + std::string dump_output = icbr->dump(indent); + REQUIRE(dump_output == "Box: icbr -----\nsize: 16 (header size: 12)\nnum_ranges: 0\n"); +} + +TEST_CASE("icbr_version0") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x20, 'i', 'c', 'b', 'r', + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x0a, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x02, 0x03, 0x0a, 0x00, 0x04, 0x05, 0x07 + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("icbr")); + REQUIRE(box->get_type_string() == "icbr"); + std::shared_ptr icbr = std::dynamic_pointer_cast(box); + REQUIRE(icbr != nullptr); + REQUIRE(icbr->get_ranges().size() == 2); + REQUIRE(icbr->get_version() == 0); + + StreamWriter writer; + Error err = icbr->write(writer); + REQUIRE(err.error_code == heif_error_Ok); + const std::vector written = writer.get_data(); + REQUIRE(written == byteArray); + + Indent indent; + std::string dump_output = icbr->dump(indent); + REQUIRE(dump_output == "Box: icbr -----\nsize: 32 (header size: 12)\nnum_ranges: 2\nrange_offset: 2563, range_size: 66051\nrange_offset: 131850, range_size: 263431\n"); +} + +TEST_CASE("icbr_version1") { + std::vector byteArray{ + 0x00, 0x00, 0x00, 0x30, 'i', 'c', 'b', 'r', + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x05, 0x07 + }; + + auto reader = std::make_shared(byteArray.data(), + byteArray.size(), false); + + BitstreamRange range(reader, byteArray.size()); + std::shared_ptr box; + Error error = Box::read(range, &box); + REQUIRE(error == Error::Ok); + REQUIRE(range.error() == 0); + + REQUIRE(box->get_short_type() == fourcc("icbr")); + REQUIRE(box->get_type_string() == "icbr"); + std::shared_ptr icbr = std::dynamic_pointer_cast(box); + REQUIRE(icbr != nullptr); + REQUIRE(icbr->get_ranges().size() == 2); + REQUIRE(icbr->get_version() == 1); + + StreamWriter writer; + Error err = icbr->write(writer); + REQUIRE(err.error_code == heif_error_Ok); + const std::vector written = writer.get_data(); + REQUIRE(written == byteArray); + + Indent indent; + std::string dump_output = icbr->dump(indent); + REQUIRE(dump_output == "Box: icbr -----\nsize: 48 (header size: 12)\nnum_ranges: 2\nrange_offset: 2563, range_size: 66051\nrange_offset: 131850, range_size: 263431\n"); +} diff --git a/tests/uncompressed_decode.h b/tests/uncompressed_decode.h index 4b12486676..48ea344e4b 100644 --- a/tests/uncompressed_decode.h +++ b/tests/uncompressed_decode.h @@ -46,6 +46,16 @@ "uncompressed_pix_R8G8B8_bsz0_psz10_tiled.heif", \ "uncompressed_pix_R8G8B8_bsz0_psz5_tiled.heif" +#if HAVE_BROTLI + #define BROTLI_FILES "rgb_generic_compressed_brotli.heif", +#else + #define BROTLI_FILES +#endif + +#define FILES_GENERIC_COMPRESSED \ + "rgb_generic_compressed_defl.heif", BROTLI_FILES \ + "rgb_generic_compressed_tile_deflate.heif", "rgb_generic_compressed_zlib.heif", \ + "rgb_generic_compressed_zlib_rows.heif", "rgb_generic_compressed_zlib_tiled.heif" #define FILES_16BIT_RGB \ "uncompressed_comp_B16R16G16.heif", "uncompressed_comp_B16R16G16_tiled.heif", \ diff --git a/tests/uncompressed_decode_generic_compression.cc b/tests/uncompressed_decode_generic_compression.cc new file mode 100644 index 0000000000..47460ca7ae --- /dev/null +++ b/tests/uncompressed_decode_generic_compression.cc @@ -0,0 +1,216 @@ +/* + libheif integration tests for uncompressed decoder + + MIT License + + Copyright (c) 2023 Brad Hards + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#include "catch.hpp" +#include "libheif/heif.h" +#include "libheif/api_structs.h" +#include +#include +#include "test_utils.h" +#include + +#include "uncompressed_decode.h" + +void check_image_size(struct heif_context *&context) { + heif_image_handle *handle = get_primary_image_handle(context); + heif_image *img = get_primary_image(handle); + + REQUIRE(heif_image_has_channel(img, heif_channel_Y) == 0); + REQUIRE(heif_image_has_channel(img, heif_channel_Cb) == 0); + REQUIRE(heif_image_has_channel(img, heif_channel_Cr) == 0); + REQUIRE(heif_image_has_channel(img, heif_channel_R) == 1); + REQUIRE(heif_image_has_channel(img, heif_channel_G) == 1); + REQUIRE(heif_image_has_channel(img, heif_channel_B) == 1); + REQUIRE(heif_image_has_channel(img, heif_channel_Alpha) == 0); + REQUIRE(heif_image_has_channel(img, heif_channel_interleaved) == 0); + int width = heif_image_get_primary_width(img); + REQUIRE(width == 128); + int height = heif_image_get_primary_height(img); + REQUIRE(height == 72); + width = heif_image_get_width(img, heif_channel_R); + REQUIRE(width == 128); + height = heif_image_get_height(img, heif_channel_R); + REQUIRE(height == 72); + width = heif_image_get_width(img, heif_channel_G); + REQUIRE(width == 128); + height = heif_image_get_height(img, heif_channel_G); + REQUIRE(height == 72); + width = heif_image_get_width(img, heif_channel_B); + REQUIRE(width == 128); + height = heif_image_get_height(img, heif_channel_B); + REQUIRE(height == 72); + + int pixel_depth = heif_image_get_bits_per_pixel(img, heif_channel_R); + REQUIRE(pixel_depth == 8); + pixel_depth = heif_image_get_bits_per_pixel(img, heif_channel_G); + REQUIRE(pixel_depth == 8); + pixel_depth = heif_image_get_bits_per_pixel(img, heif_channel_B); + REQUIRE(pixel_depth == 8); + + int pixel_range = heif_image_get_bits_per_pixel_range(img, heif_channel_R); + REQUIRE(pixel_range == 8); + pixel_range = heif_image_get_bits_per_pixel_range(img, heif_channel_G); + REQUIRE(pixel_range == 8); + pixel_range = heif_image_get_bits_per_pixel_range(img, heif_channel_B); + REQUIRE(pixel_range == 8); + + heif_image_release(img); + heif_image_handle_release(handle); +} + +TEST_CASE("check image size") { + auto file = GENERATE(FILES_GENERIC_COMPRESSED); + auto context = get_context_for_test_file(file); + INFO("file name: " << file); + check_image_size(context); + heif_context_free(context); +} + + +void check_image_content(struct heif_context *&context) { + heif_image_handle *handle = get_primary_image_handle(context); + heif_image *img = get_primary_image(handle); + + int stride; + const uint8_t *img_plane = + heif_image_get_plane_readonly(img, heif_channel_R, &stride); + REQUIRE(stride == 128); + for (int row = 0; row < 24; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 255); + REQUIRE(((int)(img_plane[stride * row + 31])) == 255); + REQUIRE(((int)(img_plane[stride * row + 32])) == 0); + REQUIRE(((int)(img_plane[stride * row + 63])) == 0); + REQUIRE(((int)(img_plane[stride * row + 64])) == 0); + REQUIRE(((int)(img_plane[stride * row + 95])) == 0); + REQUIRE(((int)(img_plane[stride * row + 96])) == 0); + REQUIRE(((int)(img_plane[stride * row + 127])) == 0); + } + for (int row = 24; row < 48; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 255); + REQUIRE(((int)(img_plane[stride * row + 31])) == 255); + REQUIRE(((int)(img_plane[stride * row + 32])) == 64); + REQUIRE(((int)(img_plane[stride * row + 63])) == 64); + REQUIRE(((int)(img_plane[stride * row + 64])) == 0); + REQUIRE(((int)(img_plane[stride * row + 95])) == 0); + REQUIRE(((int)(img_plane[stride * row + 96])) == 255); + REQUIRE(((int)(img_plane[stride * row + 127])) == 255); + } + for (int row = 48; row < 72; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 192); + REQUIRE(((int)(img_plane[stride * row + 31])) == 192); + REQUIRE(((int)(img_plane[stride * row + 32])) == 255); + REQUIRE(((int)(img_plane[stride * row + 63])) == 255); + REQUIRE(((int)(img_plane[stride * row + 64])) == 255); + REQUIRE(((int)(img_plane[stride * row + 95])) == 255); + REQUIRE(((int)(img_plane[stride * row + 96])) == 255); + REQUIRE(((int)(img_plane[stride * row + 127])) == 255); + } + + img_plane = heif_image_get_plane_readonly(img, heif_channel_G, &stride); + REQUIRE(stride == 128); + for (int row = 0; row < 24; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 0); + REQUIRE(((int)(img_plane[stride * row + 31])) == 0); + REQUIRE(((int)(img_plane[stride * row + 32])) == 255); + REQUIRE(((int)(img_plane[stride * row + 63])) == 255); + REQUIRE(((int)(img_plane[stride * row + 64])) == 0); + REQUIRE(((int)(img_plane[stride * row + 95])) == 0); + REQUIRE(((int)(img_plane[stride * row + 96])) == 0); + REQUIRE(((int)(img_plane[stride * row + 127])) == 0); + } + for (int row = 24; row < 48; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 255); + REQUIRE(((int)(img_plane[stride * row + 31])) == 255); + REQUIRE(((int)(img_plane[stride * row + 32])) == 64); + REQUIRE(((int)(img_plane[stride * row + 63])) == 64); + REQUIRE(((int)(img_plane[stride * row + 64])) == 255); + REQUIRE(((int)(img_plane[stride * row + 95])) == 255); + REQUIRE(((int)(img_plane[stride * row + 96])) == 0); + REQUIRE(((int)(img_plane[stride * row + 127])) == 0); + } + for (int row = 48; row < 72; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 192); + REQUIRE(((int)(img_plane[stride * row + 31])) == 192); + REQUIRE(((int)(img_plane[stride * row + 32])) == 255); + REQUIRE(((int)(img_plane[stride * row + 63])) == 255); + REQUIRE(((int)(img_plane[stride * row + 64])) == 175); + REQUIRE(((int)(img_plane[stride * row + 95])) == 175); + REQUIRE(((int)(img_plane[stride * row + 96])) == 200); + REQUIRE(((int)(img_plane[stride * row + 127])) == 200); + } + + img_plane = heif_image_get_plane_readonly(img, heif_channel_B, &stride); + REQUIRE(stride == 128); + for (int row = 0; row < 24; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 0); + REQUIRE(((int)(img_plane[stride * row + 31])) == 0); + REQUIRE(((int)(img_plane[stride * row + 32])) == 0); + REQUIRE(((int)(img_plane[stride * row + 63])) == 0); + REQUIRE(((int)(img_plane[stride * row + 64])) == 255); + REQUIRE(((int)(img_plane[stride * row + 95])) == 255); + REQUIRE(((int)(img_plane[stride * row + 96])) == 0); + REQUIRE(((int)(img_plane[stride * row + 127])) == 0); + } + for (int row = 24; row < 48; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 255); + REQUIRE(((int)(img_plane[stride * row + 31])) == 255); + REQUIRE(((int)(img_plane[stride * row + 32])) == 64); + REQUIRE(((int)(img_plane[stride * row + 63])) == 64); + REQUIRE(((int)(img_plane[stride * row + 64])) == 255); + REQUIRE(((int)(img_plane[stride * row + 95])) == 255); + REQUIRE(((int)(img_plane[stride * row + 96])) == 255); + REQUIRE(((int)(img_plane[stride * row + 127])) == 255); + } + for (int row = 48; row < 72; row++) { + INFO("row: " << row); + REQUIRE(((int)(img_plane[stride * row + 0])) == 192); + REQUIRE(((int)(img_plane[stride * row + 31])) == 192); + REQUIRE(((int)(img_plane[stride * row + 32])) == 0); + REQUIRE(((int)(img_plane[stride * row + 63])) == 0); + REQUIRE(((int)(img_plane[stride * row + 64])) == 175); + REQUIRE(((int)(img_plane[stride * row + 95])) == 175); + REQUIRE(((int)(img_plane[stride * row + 96])) == 0); + REQUIRE(((int)(img_plane[stride * row + 127])) == 0); + } + + heif_image_release(img); + heif_image_handle_release(handle); +} + +TEST_CASE("check image content") { + auto file = GENERATE(FILES_GENERIC_COMPRESSED); + auto context = get_context_for_test_file(file); + INFO("file name: " << file); + check_image_content(context); + heif_context_free(context); +} diff --git a/tests/uncompressed_encode.cc b/tests/uncompressed_encode.cc index b3326dc4d4..c7d2e6e99b 100644 --- a/tests/uncompressed_encode.cc +++ b/tests/uncompressed_encode.cc @@ -565,6 +565,8 @@ struct heif_image *createImage_RGBA_planar() return image; } +#include + static void do_encode(heif_image* input_image, const char* filename, bool check_decode, uint8_t prefer_uncC_short_form = 0) { REQUIRE(input_image != nullptr); @@ -607,6 +609,7 @@ static void do_encode(heif_image* input_image, const char* filename, bool check_ REQUIRE(height == heif_image_get_primary_height(input_image)); heif_image* decode_image; err = heif_decode_image(decode_image_handle, &decode_image, heif_colorspace_undefined, heif_chroma_undefined, NULL); + std::cout << "error message: " << err.message << std::endl; REQUIRE(err.code == heif_error_Ok); // REQUIRE(heif_image_has_channel(input_image, heif_channel_Y) == heif_image_has_channel(decode_image, heif_channel_Y)); REQUIRE(heif_image_has_channel(input_image, heif_channel_Cb) == heif_image_has_channel(decode_image, heif_channel_Cb)); From 6a20e4ae976937f89376b36f58301ca7f12101a7 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sat, 6 Jul 2024 14:26:35 +1000 Subject: [PATCH 098/129] uncompressed: add missing emscripten enum --- libheif/api/libheif/heif_emscripten.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libheif/api/libheif/heif_emscripten.h b/libheif/api/libheif/heif_emscripten.h index cc43c65c98..5b015f1288 100644 --- a/libheif/api/libheif/heif_emscripten.h +++ b/libheif/api/libheif/heif_emscripten.h @@ -400,7 +400,8 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_No_ispe_property", heif_suberror_No_ispe_property) .value("heif_suberror_Camera_intrinsic_matrix_undefined", heif_suberror_Camera_intrinsic_matrix_undefined) .value("heif_suberror_Camera_extrinsic_matrix_undefined", heif_suberror_Camera_extrinsic_matrix_undefined) - .value("heif_suberror_Invalid_J2K_codestream", heif_suberror_Invalid_J2K_codestream); + .value("heif_suberror_Invalid_J2K_codestream", heif_suberror_Invalid_J2K_codestream) + .value("heif_suberror_No_icbr_box", heif_suberror_No_icbr_box); emscripten::enum_("heif_compression_format") .value("heif_compression_undefined", heif_compression_undefined) From f4344a0737876db1106ad5459cb6bb61c5aa391d Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sat, 6 Jul 2024 14:32:39 +1000 Subject: [PATCH 099/129] uncompressed add missing go enum --- go/heif/heif.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go/heif/heif.go b/go/heif/heif.go index b71b2eadf4..85751e6be7 100644 --- a/go/heif/heif.go +++ b/go/heif/heif.go @@ -325,6 +325,8 @@ const ( SuberrorNoVcCBox = C.heif_suberror_No_vvcC_box + SuberrorNoIcbrBox = C.heif_suberror_No_icbr_box + // --- Unsupported_feature --- // Image was coded with an unsupported compression method. From 3aa63af8e410b8815e2fd2c0e0250659bdab4c94 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sat, 6 Jul 2024 14:40:14 +1000 Subject: [PATCH 100/129] uncompressed: fix up go formatting --- go/heif/heif.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/heif/heif.go b/go/heif/heif.go index 85751e6be7..85706b84cb 100644 --- a/go/heif/heif.go +++ b/go/heif/heif.go @@ -326,7 +326,7 @@ const ( SuberrorNoVcCBox = C.heif_suberror_No_vvcC_box SuberrorNoIcbrBox = C.heif_suberror_No_icbr_box - + // --- Unsupported_feature --- // Image was coded with an unsupported compression method. From ee76649688a32a3ba5ab97db5e7fc88a8836db5b Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sat, 6 Jul 2024 15:48:39 +1000 Subject: [PATCH 101/129] uncompressed: check icbr version --- libheif/codecs/uncompressed_box.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index 19cb8745b3..6cd2dce779 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -412,6 +412,8 @@ Error Box_icbr::parse(BitstreamRange& range) } else if (get_version() == 0) { byteRange.range_offset = range.read32(); byteRange.range_size = range.read32(); + } else { + return Error(heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icbr version"); } m_ranges.push_back(byteRange); } From 3ad82921560c735e6699d6dbe64e2836ba8d7b2f Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sat, 6 Jul 2024 15:53:30 +1000 Subject: [PATCH 102/129] uncompressed: try harder to make zlib optional --- libheif/file.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libheif/file.cc b/libheif/file.cc index 770c35e6ca..5d0dd71e09 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -927,9 +927,25 @@ const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, st sstr.str()); #endif } else if (cmpC_box->get_compression_type() == fourcc("zlib")) { +#if WITH_ZLIB_COMPRESSION return inflate_zlib(compressed_data, data); +#else + std::stringstream sstr; + sstr << "cannot decode unci item with zlib compression - not enabled" << std::endl; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_generic_compression_method, + sstr.str()); +#endif } else if (cmpC_box->get_compression_type() == fourcc("defl")) { +#if WITH_ZLIB_COMPRESSION return inflate_deflate(compressed_data, data); +#else + std::stringstream sstr; + sstr << "cannot decode unci item with deflate compression - not enabled" << std::endl; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_generic_compression_method, + sstr.str()); +#endif } else { std::stringstream sstr; sstr << "cannot decode unci item with unsupported compression type: " << cmpC_box->get_compression_type() << std::endl; From 25d0c77e728c5c34e2e90f203812e9ef6e219709 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sat, 6 Jul 2024 17:02:57 +1000 Subject: [PATCH 103/129] uncompressed: minor editorial cleanups --- libheif/CMakeLists.txt | 2 +- libheif/codecs/uncompressed_box.cc | 3 +-- libheif/codecs/uncompressed_box.h | 7 +++---- libheif/codecs/uncompressed_image.cc | 10 +++------- libheif/compression_brotli.cc | 2 +- libheif/file.cc | 2 -- libheif/file.h | 4 ++-- tests/uncompressed_decode_generic_compression.cc | 2 +- tests/uncompressed_encode.cc | 3 --- 9 files changed, 12 insertions(+), 23 deletions(-) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 401d0769a8..bb40fc94ba 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -143,7 +143,7 @@ endif () if (WITH_DEFLATE_HEADER_COMPRESSION) target_compile_definitions(heif PRIVATE WITH_DEFLATE_HEADER_COMPRESSION=1) endif () - + if (HAVE_BROTLI) target_compile_definitions(heif PUBLIC HAVE_BROTLI=1) target_include_directories(heif PRIVATE ${BROTLI_INCLUDE_DIRS}) diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index 6cd2dce779..04f3677c4b 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -323,7 +323,6 @@ std::string Box_uncC::dump(Indent& indent) const return sstr.str(); } - Error Box_uncC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); @@ -449,4 +448,4 @@ Error Box_icbr::write(StreamWriter& writer) const prepend_header(writer, box_start); return Error::Ok; -} \ No newline at end of file +} diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed_box.h index 39a52a6b49..e6a7d17751 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -76,8 +76,7 @@ class Box_cmpd : public Box class Box_uncC : public FullBox { public: - Box_uncC() - { + Box_uncC() { set_short_type(fourcc("uncC")); } @@ -239,7 +238,7 @@ class Box_cmpC : public FullBox uint32_t get_compression_type() const { return compression_type; } bool get_must_decompress_individual_entities() const { return must_decompress_individual_entities; } uint8_t get_compressed_range_type() const { return compressed_range_type; } - + Error write(StreamWriter& writer) const override; protected: @@ -252,7 +251,7 @@ class Box_cmpC : public FullBox /** * Item compressed byte range info (icbr). - * + * * This is from ISO/IEC 23001-17 Amd 2. */ class Box_icbr : public FullBox diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 1145eb9496..a1fcf7ca11 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -934,10 +934,6 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte heif_suberror_Unsupported_data_version, "Missing required uncC box for uncompressed codec"); } - if (!cmpd) { - printf("No cmpd\n"); - } - // printf("uncC version: %d\n", uncC->get_version()); if (!cmpd && (uncC->get_version() !=1)) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, @@ -977,9 +973,9 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte AbstractDecoder *decoder = makeDecoder(width, height, cmpd, uncC); if (decoder != nullptr) { - Error result = decoder->decode(source_data, img); - delete decoder; - return result; + Error result = decoder->decode(source_data, img); + delete decoder; + return result; } else { printf("bad interleave mode - we should have detected this earlier: %d\n", uncC->get_interleave_type()); std::stringstream sstr; diff --git a/libheif/compression_brotli.cc b/libheif/compression_brotli.cc index 69de5bf767..82a34980cd 100644 --- a/libheif/compression_brotli.cc +++ b/libheif/compression_brotli.cc @@ -81,4 +81,4 @@ Error inflate_brotli(const std::vector &compressed_input, std::vector #include -#include #include #include #include @@ -51,7 +50,6 @@ #if WITH_UNCOMPRESSED_CODEC #include "codecs/uncompressed_image.h" #endif -#include // TODO: make this a decoder option #define STRICT_PARSING false diff --git a/libheif/file.h b/libheif/file.h index 75095c438b..a82b318c29 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -85,9 +85,9 @@ class HeifFile std::string get_item_uri_type(heif_item_id ID) const; - Error get_compressed_image_data(heif_item_id ID, std::vector *out_data) const; + Error get_compressed_image_data(heif_item_id ID, std::vector* out_data) const; - Error get_item_data(heif_item_id ID, std::vector *out_data, heif_metadata_compression *out_compression) const; + Error get_item_data(heif_item_id ID, std::vector *out_data, heif_metadata_compression* out_compression) const; std::shared_ptr get_ftyp_box() { return m_ftyp_box; } diff --git a/tests/uncompressed_decode_generic_compression.cc b/tests/uncompressed_decode_generic_compression.cc index 47460ca7ae..6a6f24fe8b 100644 --- a/tests/uncompressed_decode_generic_compression.cc +++ b/tests/uncompressed_decode_generic_compression.cc @@ -166,7 +166,7 @@ void check_image_content(struct heif_context *&context) { REQUIRE(((int)(img_plane[stride * row + 96])) == 200); REQUIRE(((int)(img_plane[stride * row + 127])) == 200); } - + img_plane = heif_image_get_plane_readonly(img, heif_channel_B, &stride); REQUIRE(stride == 128); for (int row = 0; row < 24; row++) { diff --git a/tests/uncompressed_encode.cc b/tests/uncompressed_encode.cc index c7d2e6e99b..b3326dc4d4 100644 --- a/tests/uncompressed_encode.cc +++ b/tests/uncompressed_encode.cc @@ -565,8 +565,6 @@ struct heif_image *createImage_RGBA_planar() return image; } -#include - static void do_encode(heif_image* input_image, const char* filename, bool check_decode, uint8_t prefer_uncC_short_form = 0) { REQUIRE(input_image != nullptr); @@ -609,7 +607,6 @@ static void do_encode(heif_image* input_image, const char* filename, bool check_ REQUIRE(height == heif_image_get_primary_height(input_image)); heif_image* decode_image; err = heif_decode_image(decode_image_handle, &decode_image, heif_colorspace_undefined, heif_chroma_undefined, NULL); - std::cout << "error message: " << err.message << std::endl; REQUIRE(err.code == heif_error_Ok); // REQUIRE(heif_image_has_channel(input_image, heif_channel_Y) == heif_image_has_channel(decode_image, heif_channel_Y)); REQUIRE(heif_image_has_channel(input_image, heif_channel_Cb) == heif_image_has_channel(decode_image, heif_channel_Cb)); From 86299ef300f037698e5cd0917e4aa552e3033ce0 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 9 Jul 2024 21:36:02 +1000 Subject: [PATCH 104/129] uncompressed: add version checks for cmpC and icbr --- libheif/codecs/uncompressed_box.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index 04f3677c4b..cbfd268d52 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -366,6 +366,11 @@ Error Box_uncC::write(StreamWriter& writer) const Error Box_cmpC::parse(BitstreamRange& range) { parse_full_box_header(range); + + if (get_version() != 0) { + return unsupported_version_error("cmpC"); + } + compression_type = range.read32(); uint8_t v = range.read8(); must_decompress_individual_entities = ((v & 0x80) == 0x80); @@ -402,6 +407,11 @@ Error Box_cmpC::write(StreamWriter& writer) const Error Box_icbr::parse(BitstreamRange& range) { parse_full_box_header(range); + + if ((get_version() != 0) && (get_version() != 1)) { + return unsupported_version_error("icbr"); + } + uint32_t num_ranges = range.read32(); for (uint32_t r = 0; r < num_ranges; r++) { struct ByteRange byteRange; From 85956fb22a60bc5bed3da54f592350e45470935d Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 9 Jul 2024 21:50:40 +1000 Subject: [PATCH 105/129] uncompressed: rename ZLIB compression preprocessor define --- libheif/CMakeLists.txt | 2 +- libheif/compression.h | 2 +- libheif/compression_zlib.cc | 2 +- libheif/file.cc | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index bb40fc94ba..cb7b92712b 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -137,7 +137,7 @@ endif () if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) find_package(ZLIB REQUIRED) target_link_libraries(heif PRIVATE ZLIB::ZLIB) - target_compile_definitions(heif PRIVATE WITH_ZLIB_COMPRESSION=1) + target_compile_definitions(heif PRIVATE HAVE_ZLIB=1) endif () if (WITH_DEFLATE_HEADER_COMPRESSION) diff --git a/libheif/compression.h b/libheif/compression.h index bff089ff52..6da2bb125b 100644 --- a/libheif/compression.h +++ b/libheif/compression.h @@ -26,7 +26,7 @@ #include -#if WITH_ZLIB_COMPRESSION +#if HAVE_ZLIB std::vector deflate(const uint8_t* input, size_t size); Error inflate_zlib(const std::vector&compressed_input, std::vector *output); diff --git a/libheif/compression_zlib.cc b/libheif/compression_zlib.cc index af84ebe352..affd7762e6 100644 --- a/libheif/compression_zlib.cc +++ b/libheif/compression_zlib.cc @@ -22,7 +22,7 @@ #include "compression.h" -#if WITH_ZLIB_COMPRESSION +#if HAVE_ZLIB #include #include diff --git a/libheif/file.cc b/libheif/file.cc index 88cf40f337..c719ba7787 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -925,7 +925,7 @@ const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, st sstr.str()); #endif } else if (cmpC_box->get_compression_type() == fourcc("zlib")) { -#if WITH_ZLIB_COMPRESSION +#if HAVE_ZLIB return inflate_zlib(compressed_data, data); #else std::stringstream sstr; @@ -935,7 +935,7 @@ const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, st sstr.str()); #endif } else if (cmpC_box->get_compression_type() == fourcc("defl")) { -#if WITH_ZLIB_COMPRESSION +#if HAVE_ZLIB return inflate_deflate(compressed_data, data); #else std::stringstream sstr; From 1cb2c0ed0bfec7ba975128c2e3397e8c620f3a0e Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 15:58:21 +0200 Subject: [PATCH 106/129] rename data compression functions (zlib/deflate/brotli) --- libheif/compression.h | 8 ++++---- libheif/compression_brotli.cc | 2 +- libheif/compression_zlib.cc | 6 +++--- libheif/context.cc | 4 ++-- libheif/file.cc | 18 +++++++++--------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libheif/compression.h b/libheif/compression.h index 6da2bb125b..d4e11a5667 100644 --- a/libheif/compression.h +++ b/libheif/compression.h @@ -27,17 +27,17 @@ #include #if HAVE_ZLIB -std::vector deflate(const uint8_t* input, size_t size); +std::vector compress_zlib(const uint8_t* input, size_t size); -Error inflate_zlib(const std::vector&compressed_input, std::vector *output); +Error decompress_zlib(const std::vector&compressed_input, std::vector *output); -Error inflate_deflate(const std::vector&, std::vector *output); +Error decompress_deflate(const std::vector& compressed_input, std::vector *output); #endif #if HAVE_BROTLI -Error inflate_brotli(const std::vector &compressed_input, std::vector *output); +Error decompress_brotli(const std::vector &compressed_input, std::vector *output); #endif diff --git a/libheif/compression_brotli.cc b/libheif/compression_brotli.cc index 82a34980cd..0f8904b2ee 100644 --- a/libheif/compression_brotli.cc +++ b/libheif/compression_brotli.cc @@ -31,7 +31,7 @@ const size_t BUF_SIZE = (1 << 18); #include "error.h" -Error inflate_brotli(const std::vector &compressed_input, std::vector *output) +Error decompress_brotli(const std::vector &compressed_input, std::vector *output) { BrotliDecoderResult result = BROTLI_DECODER_RESULT_ERROR; std::vector buffer(BUF_SIZE, 0); diff --git a/libheif/compression_zlib.cc b/libheif/compression_zlib.cc index affd7762e6..d09602e04c 100644 --- a/libheif/compression_zlib.cc +++ b/libheif/compression_zlib.cc @@ -28,7 +28,7 @@ #include #include -std::vector deflate(const uint8_t* input, size_t size) +std::vector compress_zlib(const uint8_t* input, size_t size) { std::vector output; @@ -135,12 +135,12 @@ Error do_inflate(const std::vector& compressed_input, int windowSize, s return Error::Ok; } -Error inflate_zlib(const std::vector& compressed_input, std::vector *output) +Error decompress_zlib(const std::vector& compressed_input, std::vector *output) { return do_inflate(compressed_input, 15, output); } -Error inflate_deflate(const std::vector& compressed_input, std::vector *output) +Error decompress_deflate(const std::vector& compressed_input, std::vector *output) { return do_inflate(compressed_input, -15, output); } diff --git a/libheif/context.cc b/libheif/context.cc index 419ffd38b5..08be19d211 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -3812,8 +3812,8 @@ Error HeifContext::add_generic_metadata(const std::shared_ptr& master_ima std::vector data_array; if (compression == heif_metadata_compression_deflate) { #if WITH_DEFLATE_HEADER_COMPRESSION - data_array = deflate((const uint8_t*) data, size); - metadata_infe_box->set_content_encoding("deflate"); + data_array = compress_zlib((const uint8_t*) data, size); + metadata_infe_box->set_content_encoding("compress_zlib"); #else return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_header_compression_method); diff --git a/libheif/file.cc b/libheif/file.cc index c719ba7787..bda9c7ffa4 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -813,7 +813,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* bool read_uncompressed = true; if (item_type == "mime") { std::string encoding = infe_box->get_content_encoding(); - if (encoding == "deflate") { + if (encoding == "compress_zlib") { #if WITH_DEFLATE_HEADER_COMPRESSION read_uncompressed = false; std::vector compressed_data; @@ -821,7 +821,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* if (error) { return error; } - error = inflate_zlib(compressed_data, data); + error = decompress_zlib(compressed_data, data); if (error) { return error; } @@ -916,7 +916,7 @@ const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, st { if (cmpC_box->get_compression_type() == fourcc("brot")) { #if HAVE_BROTLI - return inflate_brotli(compressed_data, data); + return decompress_brotli(compressed_data, data); #else std::stringstream sstr; sstr << "cannot decode unci item with brotli compression - not enabled" << std::endl; @@ -926,7 +926,7 @@ const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, st #endif } else if (cmpC_box->get_compression_type() == fourcc("zlib")) { #if HAVE_ZLIB - return inflate_zlib(compressed_data, data); + return decompress_zlib(compressed_data, data); #else std::stringstream sstr; sstr << "cannot decode unci item with zlib compression - not enabled" << std::endl; @@ -936,7 +936,7 @@ const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, st #endif } else if (cmpC_box->get_compression_type() == fourcc("defl")) { #if HAVE_ZLIB - return inflate_deflate(compressed_data, data); + return decompress_deflate(compressed_data, data); #else std::stringstream sstr; sstr << "cannot decode unci item with deflate compression - not enabled" << std::endl; @@ -1205,7 +1205,7 @@ Error HeifFile::get_item_data(heif_item_id ID, std::vector* out_data, h return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, out_data); } - else if (encoding == "deflate") { + else if (encoding == "compress_zlib") { compression = heif_metadata_compression_deflate; } else { @@ -1234,7 +1234,7 @@ Error HeifFile::get_item_data(heif_item_id ID, std::vector* out_data, h switch (compression) { #if WITH_DEFLATE_HEADER_COMPRESSION case heif_metadata_compression_deflate: - return inflate_zlib(compressed_data, out_data); + return decompress_zlib(compressed_data, out_data); #endif default: return {heif_error_Unsupported_filetype, heif_suberror_Unsupported_header_compression_method}; @@ -1649,8 +1649,8 @@ Error HeifFile::set_item_data(const std::shared_ptr& item, const uint8 std::vector data_array; if (compression == heif_metadata_compression_deflate) { #if WITH_DEFLATE_HEADER_COMPRESSION - data_array = deflate((const uint8_t*) data, size); - item->set_content_encoding("deflate"); + data_array = compress_zlib((const uint8_t*) data, size); + item->set_content_encoding("compress_zlib"); #else return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_header_compression_method); From ebf6c61260307b0514c134232f79b006a8960b49 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 16:34:31 +0200 Subject: [PATCH 107/129] metadata compression cleanup - support for brotli metadata decompression - rename deflate,zlib compression methods - replace WITH_DEFLATE_HEADER_COMPRESSION with WITH_HEADER_COMPRESSION --- CMakeLists.txt | 4 +-- CMakePresets.json | 6 ++-- README.md | 2 +- appveyor.yml | 2 +- examples/CMakeLists.txt | 4 +++ examples/heif_enc.cc | 2 ++ libheif/CMakeLists.txt | 6 +--- libheif/api/libheif/heif.h | 10 +++--- libheif/compression.h | 8 +++-- libheif/compression_zlib.cc | 15 ++++++-- libheif/context.cc | 13 +++++-- libheif/file.cc | 68 ++++++++++++++++++++++++++++++++++--- 12 files changed, 112 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a51616eec..3c4b2dce48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -395,7 +395,7 @@ endif() if (LIBSHARPYUV_FOUND) list(APPEND REQUIRES_PRIVATE "libsharpyuv") endif() -if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) +if (WITH_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) list(APPEND REQUIRES_PRIVATE "zlib") endif() if (WITH_UNCOMPRESSED_CODEC) @@ -430,7 +430,7 @@ option(WITH_GDK_PIXBUF "Build gdk-pixbuf plugin" ON) option(WITH_REDUCED_VISIBILITY "Reduced symbol visibility in library" ON) -option(WITH_DEFLATE_HEADER_COMPRESSION OFF) +option(WITH_HEADER_COMPRESSION OFF) option(ENABLE_MULTITHREADING_SUPPORT "Switch off for platforms without multithreading support" ON) option(ENABLE_PARALLEL_TILE_DECODING "Will launch multiple decoders to decode tiles in parallel (requires ENABLE_MULTITHREADING_SUPPORT)" ON) diff --git a/CMakePresets.json b/CMakePresets.json index 845adf4eec..947c37ec8d 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -45,7 +45,7 @@ "WITH_FFMPEG_DECODER_PLUGIN" : "OFF", "WITH_REDUCED_VISIBILITY" : "OFF", - "WITH_DEFLATE_HEADER_COMPRESSION" : "ON", + "WITH_HEADER_COMPRESSION" : "ON", "WITH_LIBSHARPYUV" : "ON", "WITH_EXAMPLES": "ON", "WITH_FUZZERS": "OFF" @@ -91,7 +91,7 @@ "WITH_FFMPEG_DECODER_PLUGIN" : "ON", "WITH_REDUCED_VISIBILITY" : "ON", - "WITH_DEFLATE_HEADER_COMPRESSION" : "ON", + "WITH_HEADER_COMPRESSION" : "ON", "WITH_LIBSHARPYUV" : "ON", "WITH_EXAMPLES": "ON", "WITH_FUZZERS": "OFF" @@ -123,7 +123,7 @@ "WITH_FFMPEG_DECODER" : "OFF", "WITH_REDUCED_VISIBILITY" : "ON", - "WITH_DEFLATE_HEADER_COMPRESSION" : "OFF", + "WITH_HEADER_COMPRESSION" : "OFF", "WITH_LIBSHARPYUV" : "ON", "WITH_EXAMPLES": "ON", "WITH_FUZZERS": "OFF" diff --git a/README.md b/README.md index 31f177aa33..d6c1835c88 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ Further options are: * `WITH_UNCOMPRESSED_CODEC`: enable support for uncompressed images according to ISO/IEC 23001-17:2024. This is *experimental* and not available as a dynamic plugin. When enabled, it adds a dependency to `zlib`, and optionally will use `brotli`. -* `WITH_DEFLATE_HEADER_COMPRESSION`: enables support for compressed metadata. When enabled, it adds a dependency to `zlib`. +* `WITH_HEADER_COMPRESSION`: enables support for compressed metadata. When enabled, it adds a dependency to `zlib`. Note that header compression is not widely supported yet. * `WITH_LIBSHARPYUV`: enables high-quality YCbCr/RGB color space conversion algorithms (requires `libsharpyuv`, e.g. from the `third-party` directory). diff --git a/appveyor.yml b/appveyor.yml index 6d0c48608a..1f62cc8e53 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,7 +26,7 @@ install: before_build: - mkdir build - cd build - - cmake .. -A %arch% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DWITH_DAV1D=ON -DWITH_AOM_DECODER=ON -DWITH_AOM_ENCODER=ON -DWITH_JPEG_DECODER=ON -DWITH_JPEG_ENCODER=ON -DWITH_UNCOMPRESSED_CODEC=ON -DWITH_DEFLATE_HEADER_COMPRESSION=ON -DWITH_FFMPEG_DECODER=ON + - cmake .. -A %arch% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DWITH_DAV1D=ON -DWITH_AOM_DECODER=ON -DWITH_AOM_ENCODER=ON -DWITH_JPEG_DECODER=ON -DWITH_JPEG_ENCODER=ON -DWITH_UNCOMPRESSED_CODEC=ON -DWITH_HEADER_COMPRESSION=ON -DWITH_FFMPEG_DECODER=ON build: verbosity: normal diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3567b3c4bf..298775b2e7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -55,6 +55,10 @@ target_link_libraries(heif-enc PRIVATE heif) install(TARGETS heif-enc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES heif-enc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +if (WITH_HEADER_COMPRESSION) + target_compile_definitions(heif-enc PRIVATE WITH_HEADER_COMPRESSION=1) +endif () + add_executable(heif-test ${getopt_sources} heif_test.cc diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc index 2510fef2da..1329d58965 100644 --- a/examples/heif_enc.cc +++ b/examples/heif_enc.cc @@ -185,7 +185,9 @@ void show_help(const char* argv0) << " --full_range_flag nclx profile: full range flag, default: 1\n" << " --enable-two-colr-boxes will write both an ICC and an nclx color profile if both are present\n" << " --premultiplied-alpha input image has premultiplied alpha\n" +#if WITH_HEADER_COMPRESSION << " --enable-metadata-compression enable XMP metadata compression (experimental)\n" +#endif << " -C,--chroma-downsampling ALGO force chroma downsampling algorithm (nn = nearest-neighbor / average / sharp-yuv)\n" << " (sharp-yuv makes edges look sharper when using YUV420 with bilinear chroma upsampling)\n" << " --benchmark measure encoding time, PSNR, and output file size\n" diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index cb7b92712b..5da6952ccd 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -134,16 +134,12 @@ else () message("Not compiling 'libsharpyuv'") endif () -if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) +if (WITH_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) find_package(ZLIB REQUIRED) target_link_libraries(heif PRIVATE ZLIB::ZLIB) target_compile_definitions(heif PRIVATE HAVE_ZLIB=1) endif () -if (WITH_DEFLATE_HEADER_COMPRESSION) - target_compile_definitions(heif PRIVATE WITH_DEFLATE_HEADER_COMPRESSION=1) -endif () - if (HAVE_BROTLI) target_compile_definitions(heif PUBLIC HAVE_BROTLI=1) target_include_directories(heif PRIVATE ${BROTLI_INCLUDE_DIRS}) diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index b38da0a60c..44ded7482c 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -2169,10 +2169,12 @@ struct heif_error heif_context_encode_thumbnail(struct heif_context*, enum heif_metadata_compression { - heif_metadata_compression_off, - heif_metadata_compression_auto, - heif_metadata_compression_deflate, - heif_metadata_compression_unknown + heif_metadata_compression_off = 0, + heif_metadata_compression_auto = 1, + heif_metadata_compression_unknown = 2, // only used when reading unknown method from input file + heif_metadata_compression_deflate = 3, + heif_metadata_compression_zlib = 4, // do not use for header data + heif_metadata_compression_brotli = 5 }; // Assign 'thumbnail_image' as the thumbnail image of 'master_image'. diff --git a/libheif/compression.h b/libheif/compression.h index d4e11a5667..dd7dad45cf 100644 --- a/libheif/compression.h +++ b/libheif/compression.h @@ -29,15 +29,17 @@ #if HAVE_ZLIB std::vector compress_zlib(const uint8_t* input, size_t size); -Error decompress_zlib(const std::vector&compressed_input, std::vector *output); +std::vector compress_deflate(const uint8_t* input, size_t size); -Error decompress_deflate(const std::vector& compressed_input, std::vector *output); +Error decompress_zlib(const std::vector& compressed_input, std::vector* output); + +Error decompress_deflate(const std::vector& compressed_input, std::vector* output); #endif #if HAVE_BROTLI -Error decompress_brotli(const std::vector &compressed_input, std::vector *output); +Error decompress_brotli(const std::vector& compressed_input, std::vector* output); #endif diff --git a/libheif/compression_zlib.cc b/libheif/compression_zlib.cc index d09602e04c..8efab59a0b 100644 --- a/libheif/compression_zlib.cc +++ b/libheif/compression_zlib.cc @@ -28,7 +28,7 @@ #include #include -std::vector compress_zlib(const uint8_t* input, size_t size) +std::vector compress(const uint8_t* input, size_t size, int windowSize) { std::vector output; @@ -50,7 +50,7 @@ std::vector compress_zlib(const uint8_t* input, size_t size) strm.zfree = Z_NULL; strm.opaque = Z_NULL; - int err = deflateInit(&strm, Z_DEFAULT_COMPRESSION); + int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowSize, 8, Z_DEFAULT_STRATEGY); if (err != Z_OK) { return {}; // TODO: return error } @@ -135,6 +135,17 @@ Error do_inflate(const std::vector& compressed_input, int windowSize, s return Error::Ok; } +std::vector compress_zlib(const uint8_t* input, size_t size) +{ + return compress(input, size, 15); +} + +std::vector compress_deflate(const uint8_t* input, size_t size) +{ + return compress(input, size, -15); +} + + Error decompress_zlib(const std::vector& compressed_input, std::vector *output) { return do_inflate(compressed_input, 15, output); diff --git a/libheif/context.cc b/libheif/context.cc index 08be19d211..3e0586e0f1 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -3810,13 +3810,22 @@ Error HeifContext::add_generic_metadata(const std::shared_ptr& master_ima std::vector data_array; - if (compression == heif_metadata_compression_deflate) { -#if WITH_DEFLATE_HEADER_COMPRESSION + if (compression == heif_metadata_compression_zlib) { +#if HAVE_ZLIB data_array = compress_zlib((const uint8_t*) data, size); metadata_infe_box->set_content_encoding("compress_zlib"); #else return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_header_compression_method); +#endif + } + else if (compression == heif_metadata_compression_deflate) { +#if HAVE_ZLIB + data_array = compress_zlib((const uint8_t*) data, size); + metadata_infe_box->set_content_encoding("deflate"); +#else + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method); #endif } else { diff --git a/libheif/file.cc b/libheif/file.cc index bda9c7ffa4..b3c7a6cd7f 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -814,7 +814,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* if (item_type == "mime") { std::string encoding = infe_box->get_content_encoding(); if (encoding == "compress_zlib") { -#if WITH_DEFLATE_HEADER_COMPRESSION +#if HAVE_ZLIB read_uncompressed = false; std::vector compressed_data; error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, &compressed_data); @@ -829,6 +829,42 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_header_compression_method, encoding); +#endif + } + else if (encoding == "deflate") { +#if HAVE_ZLIB + read_uncompressed = false; + std::vector compressed_data; + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, &compressed_data); + if (error) { + return error; + } + error = decompress_deflate(compressed_data, data); + if (error) { + return error; + } +#else + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method, + encoding); +#endif + } + else if (encoding == "br") { +#if HAVE_BROTLI + read_uncompressed = false; + std::vector compressed_data; + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, &compressed_data); + if (error) { + return error; + } + error = decompress_brotli(compressed_data, data); + if (error) { + return error; + } +#else + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method, + encoding); #endif } } @@ -1206,8 +1242,14 @@ Error HeifFile::get_item_data(heif_item_id ID, std::vector* out_data, h return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, out_data); } else if (encoding == "compress_zlib") { + compression = heif_metadata_compression_zlib; + } + else if (encoding == "deflate") { compression = heif_metadata_compression_deflate; } + else if (encoding == "br") { + compression = heif_metadata_compression_brotli; + } else { compression = heif_metadata_compression_unknown; } @@ -1232,9 +1274,15 @@ Error HeifFile::get_item_data(heif_item_id ID, std::vector* out_data, h // decompress the data switch (compression) { -#if WITH_DEFLATE_HEADER_COMPRESSION - case heif_metadata_compression_deflate: +#if HAVE_ZLIB + case heif_metadata_compression_zlib: return decompress_zlib(compressed_data, out_data); + case heif_metadata_compression_deflate: + return decompress_deflate(compressed_data, out_data); +#endif +#if HAVE_BROTLI + case heif_metadata_compression_brotli: + return decompress_brotli(compressed_data, out_data); #endif default: return {heif_error_Unsupported_filetype, heif_suberror_Unsupported_header_compression_method}; @@ -1647,8 +1695,8 @@ Error HeifFile::set_item_data(const std::shared_ptr& item, const uint8 std::vector data_array; - if (compression == heif_metadata_compression_deflate) { -#if WITH_DEFLATE_HEADER_COMPRESSION + if (compression == heif_metadata_compression_zlib) { +#if HAVE_ZLIB data_array = compress_zlib((const uint8_t*) data, size); item->set_content_encoding("compress_zlib"); #else @@ -1656,6 +1704,16 @@ Error HeifFile::set_item_data(const std::shared_ptr& item, const uint8 heif_suberror_Unsupported_header_compression_method); #endif } + else if (compression == heif_metadata_compression_deflate) { +#if HAVE_ZLIB + data_array = compress_deflate((const uint8_t*) data, size); + item->set_content_encoding("compress_zlib"); +#else + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method); +#endif + } + // TODO: brotli else { // uncompressed data, plain copy From d2e1188814b0389ed368ec7fd877a6bf91485884 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 16:45:06 +0200 Subject: [PATCH 108/129] zlib is optional for both metadata and 23001-17 --- CMakeLists.txt | 11 ++++++++--- libheif/CMakeLists.txt | 5 ++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c4b2dce48..12c4039e03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -396,9 +396,14 @@ if (LIBSHARPYUV_FOUND) list(APPEND REQUIRES_PRIVATE "libsharpyuv") endif() if (WITH_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) - list(APPEND REQUIRES_PRIVATE "zlib") -endif() -if (WITH_UNCOMPRESSED_CODEC) + find_package(ZLIB) + if (ZLIB_FOUND) + message("zlib found") + list(APPEND REQUIRES_PRIVATE "zlib") + else() + message("zlib not found") + endif() + find_package(Brotli) if (Brotli_FOUND) message("Brotli found") diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 5da6952ccd..98f88d9f4c 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -134,10 +134,9 @@ else () message("Not compiling 'libsharpyuv'") endif () -if (WITH_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) - find_package(ZLIB REQUIRED) - target_link_libraries(heif PRIVATE ZLIB::ZLIB) +if (HAVE_ZLIB) target_compile_definitions(heif PRIVATE HAVE_ZLIB=1) + target_link_libraries(heif PRIVATE ZLIB::ZLIB) endif () if (HAVE_BROTLI) From 595b0e43723d76cb6642062989b1b22d5ed4eedb Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 17:01:05 +0200 Subject: [PATCH 109/129] fix ZLIB_FOUND variable --- libheif/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 98f88d9f4c..b638a21725 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -134,12 +134,12 @@ else () message("Not compiling 'libsharpyuv'") endif () -if (HAVE_ZLIB) +if (ZLIB_FOUND) target_compile_definitions(heif PRIVATE HAVE_ZLIB=1) target_link_libraries(heif PRIVATE ZLIB::ZLIB) endif () -if (HAVE_BROTLI) +if (Brotli_FOUND) target_compile_definitions(heif PUBLIC HAVE_BROTLI=1) target_include_directories(heif PRIVATE ${BROTLI_INCLUDE_DIRS}) target_link_libraries(heif PRIVATE ${BROTLI_LIBS}) From dd838046386f3513106fca23676dee8274e1d69c Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 9 Jul 2024 17:13:31 +0200 Subject: [PATCH 110/129] fix compilation without WITH_UNCOMPRESSED_CODEC (fixes #1229) --- libheif/file.cc | 3 +++ libheif/file.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/libheif/file.cc b/libheif/file.cc index b3c7a6cd7f..92bdf615bf 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -876,6 +876,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_codec); } +#if WITH_UNCOMPRESSED_CODEC // generic compression and uncompressed, per 23001-17 const Error HeifFile::get_compressed_image_data_uncompressed(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const { @@ -988,6 +989,7 @@ const Error HeifFile::do_decompress_data(std::shared_ptr &cmpC_box, st sstr.str()); } } +#endif const Error HeifFile::get_compressed_image_data_hvc1(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const { @@ -1031,6 +1033,7 @@ const Error HeifFile::get_compressed_image_data_hvc1(heif_item_id ID, std::vecto return m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); } + const Error HeifFile::get_compressed_image_data_vvc(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const { diff --git a/libheif/file.h b/libheif/file.h index a82b318c29..e6c8224446 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -257,9 +257,11 @@ class HeifFile const Error get_compressed_image_data_vvc(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; +#if WITH_UNCOMPRESSED_CODEC const Error get_compressed_image_data_uncompressed(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; const Error do_decompress_data(std::shared_ptr &cmpC_box, std::vector compressed_data, std::vector *data) const; +#endif const Error get_compressed_image_data_av1(heif_item_id ID, std::vector *data, const Box_iloc::Item *item) const; From baa8e09463d6e92568faf23a6324df04268ca713 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Wed, 10 Jul 2024 09:06:09 +1000 Subject: [PATCH 111/129] compression: add (internal) api documentation for compression --- libheif/compression.h | 52 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/libheif/compression.h b/libheif/compression.h index dd7dad45cf..16490241ec 100644 --- a/libheif/compression.h +++ b/libheif/compression.h @@ -27,18 +27,68 @@ #include #if HAVE_ZLIB +/** + * Compress data using zlib method. + * + * This is the RFC 1950 format. + * + * @param input pointer to the data to be compressed + * @param size the length of the input array in bytes + * @return the corresponding compressed data + */ std::vector compress_zlib(const uint8_t* input, size_t size); +/** + * Compress data using deflate method. + * + * This is the RFC 1951 format. + * + * @param input pointer to the data to be compressed + * @param size the length of the input array in bytes + * @return the corresponding compressed data + */ std::vector compress_deflate(const uint8_t* input, size_t size); +/** + * Decompress zlib compressed data. + * + * This is assumed to be in RFC 1950 format, which is the normal zlib format. + * + * @param compressed_input the compressed data to be decompressed + * @param output pointer to the resulting vector of decompressed data + * @return success (Ok) or an error on failure (usually corrupt data) + * + * @sa decompress_deflate + * @sa compress_zlib + */ Error decompress_zlib(const std::vector& compressed_input, std::vector* output); +/** + * Decompress "deflate" compressed data. + * + * This is assumed to be in RFC 1951 format, which is the deflate format. + * + * @param compressed_input the compressed data to be decompressed + * @param output pointer to the resulting vector of decompressed data + * @return success (Ok) or an error on failure (usually corrupt data) + * + * @sa decompress_zlib + * @sa compress_deflate + */ Error decompress_deflate(const std::vector& compressed_input, std::vector* output); #endif #if HAVE_BROTLI - +/** + * Decompress Brotli compressed data. + * + * Brotli is described at https://brotli.org/ + * + * @param compressed_input the compressed data to be decompressed + * @param output pointer to the resulting vector of decompressed data + * @return success (Ok) or an error on failure (usually corrupt data) + */ Error decompress_brotli(const std::vector& compressed_input, std::vector* output); #endif From f466dfeffb5c502a2127ce2ea1a0fcb2a0d16c6c Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Wed, 10 Jul 2024 11:31:40 +1000 Subject: [PATCH 112/129] uncompressed: fix parse fuzz failure --- libheif/codecs/uncompressed_box.cc | 3 ++ tests/CMakeLists.txt | 1 + tests/icbr_box.cc | 51 ++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 tests/icbr_box.cc diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index cbfd268d52..2b6f30930a 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -424,6 +424,9 @@ Error Box_icbr::parse(BitstreamRange& range) } else { return Error(heif_error_Usage_error, heif_suberror_Unsupported_parameter, "Unsupported icbr version"); } + if (range.get_error() != Error::Ok) { + return range.get_error(); + } m_ranges.push_back(byteRange); } return range.get_error(); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index afa9fbea03..daa5060c46 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,6 +20,7 @@ if (WITH_REDUCED_VISIBILITY) else() add_libheif_test(box_equals) add_libheif_test(conversion) + add_libheif_test(icbr_box) add_libheif_test(idat) add_libheif_test(jpeg2000) add_libheif_test(avc_box) diff --git a/tests/icbr_box.cc b/tests/icbr_box.cc new file mode 100644 index 0000000000..29f2d8b02b --- /dev/null +++ b/tests/icbr_box.cc @@ -0,0 +1,51 @@ +/* + libheif icbr unit tests + + MIT License + + Copyright (c) 2024 Brad Hards + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "catch.hpp" +#include "box.h" +#include +#include + +TEST_CASE("icbr bad") { + std::vector testData{0x00, 0x00, 0x00, 0x00, 'i', 'c', 'b', 'r', + 0x00, 0x11, 0x69, 0x6c, 0x6f, 0x9d, 0xf4, 0x89,}; + auto reader = std::make_shared(testData.data(), + testData.size(), false); + + BitstreamRange range(reader, testData.size()); + for (;;) { + std::shared_ptr box; + Error error = Box::read(range, &box); + if (error != Error::Ok || range.error()) { + break; + } + + box->get_type(); + box->get_type_string(); + Indent indent; + box->dump(indent); + } +} From 9496e961c97df9f2a0e60523b6a6a47d6956bccf Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Wed, 10 Jul 2024 11:52:47 +1000 Subject: [PATCH 113/129] additional fuzzer fix for 64 bit parsing --- libheif/bitstream.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libheif/bitstream.cc b/libheif/bitstream.cc index c49a3f08cf..d649e18a55 100644 --- a/libheif/bitstream.cc +++ b/libheif/bitstream.cc @@ -255,10 +255,10 @@ uint64_t BitstreamRange::read64() ((uint64_t)buf[1] << 48) | ((uint64_t)buf[2] << 40) | ((uint64_t)buf[3] << 32) | - (buf[4] << 24) | - (buf[5] << 16) | - (buf[6] << 8) | - (buf[7])); + ((uint64_t)buf[4] << 24) | + ((uint64_t)buf[5] << 16) | + ((uint64_t)buf[6] << 8) | + ((uint64_t)buf[7])); } int32_t BitstreamRange::read32s() From 8cd90a9a8d920c797a64f19a33645b2285c42e75 Mon Sep 17 00:00:00 2001 From: dnd29 Date: Wed, 10 Jul 2024 15:18:34 +0900 Subject: [PATCH 114/129] fix heif_dec no-colons option for windows modify no-colons option only replace in auxType --- examples/heif_dec.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/heif_dec.cc b/examples/heif_dec.cc index eaff6e79fc..da6e1533bd 100644 --- a/examples/heif_dec.cc +++ b/examples/heif_dec.cc @@ -631,17 +631,17 @@ int main(int argc, char** argv) heif_image_handle_release_auxiliary_type(aux_handle, &auxTypeC); + if (option_no_colons) { + std::replace(auxType.begin(), auxType.end(), ':', '_'); + } + std::ostringstream s; s << numbered_output_filename_stem; s << "-" + auxType + "."; s << output_filename_suffix; std::string auxFilename = s.str(); - - if (option_no_colons) { - std::replace(auxFilename.begin(), auxFilename.end(), ':', '_'); - } - + written = encoder->Encode(aux_handle, aux_image, auxFilename); if (!written) { fprintf(stderr, "could not write auxiliary image\n"); From f3d2217c62093e7d3d75cb17b455bfaa7251046a Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Wed, 10 Jul 2024 16:36:05 +1000 Subject: [PATCH 115/129] uncompressed: fix box dump when profile is 0 --- libheif/codecs/uncompressed_box.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index cbfd268d52..27a69b856b 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -288,8 +288,8 @@ std::string Box_uncC::dump(Indent& indent) const sstr << indent << "profile: " << m_profile; if (m_profile != 0) { sstr << " (" << to_fourcc(m_profile) << ")"; - sstr << "\n"; } + sstr << "\n"; if (get_version() == 0) { for (const auto& component : m_components) { sstr << indent << "component_index: " << component.component_index << "\n"; From eb97cd658461985e8a8c745337e6634cdd37feb3 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 10 Jul 2024 11:03:11 +0200 Subject: [PATCH 116/129] rewrite parsing of 'grpl' and add 'pymd' --- libheif/box.cc | 118 +++++++++++++++++++++++++++++++++++++------------ libheif/box.h | 38 +++++++++++++--- 2 files changed, 121 insertions(+), 35 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index 2140dc4faf..3798ad4f25 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -543,6 +543,15 @@ Error Box::read(BitstreamRange& range, std::shared_ptr* result) box = std::make_shared(); break; + case fourcc("pymd"): + box = std::make_shared(); + break; + + case fourcc("altr"): + case fourcc("ster"): + box = std::make_shared(); + break; + case fourcc("dinf"): box = std::make_shared(); break; @@ -3191,58 +3200,109 @@ Error Box_grpl::parse(BitstreamRange& range) { //parse_full_box_header(range); - //return read_children(range); + return read_children(range); // should we pass the parsing context 'grpl' or are the box types unique? +} - while (!range.eof()) { - EntityGroup group; - Error err = group.header.parse_header(range); - if (err != Error::Ok) { - return err; - } - err = group.header.parse_full_box_header(range); - if (err != Error::Ok) { - return err; - } +std::string Box_grpl::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + sstr << dump_children(indent); + return sstr.str(); +} - group.group_id = range.read32(); - uint32_t nEntities = range.read32(); - for (uint32_t i = 0; i < nEntities; i++) { - if (range.eof()) { - break; - } - group.entity_ids.push_back(range.read32()); +Error Box_EntityToGroup::parse(BitstreamRange& range) +{ + Error err = parse_full_box_header(range); + if (err != Error::Ok) { + return err; + } + + group_id = range.read32(); + uint32_t nEntities = range.read32(); + for (uint32_t i = 0; i < nEntities; i++) { + if (range.eof()) { + break; } - m_entity_groups.push_back(group); + entity_ids.push_back(range.read32()); } - return range.get_error(); + return Error::Ok; } - -std::string Box_grpl::dump(Indent& indent) const +std::string Box_EntityToGroup::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); - for (const auto& group : m_entity_groups) { - sstr << indent << "group type: " << group.header.get_type_string() << "\n" - << indent << "| group id: " << group.group_id << "\n" - << indent << "| entity IDs: "; + sstr << indent << "group id: " << group_id << "\n" + << indent << "entity IDs: "; - for (uint32_t id : group.entity_ids) { - sstr << id << " "; + bool first = true; + for (uint32_t id : entity_ids) { + if (first) { + first = false; + } + else { + sstr << ' '; } - sstr << "\n"; + sstr << id; } + sstr << "\n"; + return sstr.str(); } + +Error Box_pymd::parse(BitstreamRange& range) +{ + Error err = Box_EntityToGroup::parse(range); + if (err) { + return err; + } + + tile_size_x = range.read16(); + tile_size_y = range.read16(); + + for (size_t i = 0; i < entity_ids.size(); i++) { + LayerInfo layer{}; + layer.layer_binning = range.read16(); + layer.tiles_in_layer_row_minus1 = range.read16(); + layer.tiles_in_layer_column_minus1 = range.read16(); + + m_layer_infos.push_back(layer); + } + + return Error::Ok; +} + +std::string Box_pymd::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box_EntityToGroup::dump(indent); + + sstr << indent << "tile size: " << tile_size_x << "x" << tile_size_y << "\n"; + + int layerNr = 0; + for (const auto& layer : m_layer_infos) { + sstr << indent << "layer " << layerNr << ":\n" + << indent << "| binning: " << layer.layer_binning << "\n" + << indent << "| tiles: " << (layer.tiles_in_layer_row_minus1 + 1) << "x" << (layer.tiles_in_layer_column_minus1 + 1) << "\n"; + + layerNr++; + } + + return sstr.str(); +} + + + Error Box_dinf::parse(BitstreamRange& range) { //parse_full_box_header(range); diff --git a/libheif/box.h b/libheif/box.h index 2e88faac62..7bcf895280 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -868,19 +868,45 @@ class Box_grpl : public Box protected: Error parse(BitstreamRange& range) override; +}; - struct EntityGroup - { - FullBox header; - uint32_t group_id; - std::vector entity_ids; +class Box_EntityToGroup : public FullBox +{ +public: + std::string dump(Indent&) const override; + +protected: + uint32_t group_id; + std::vector entity_ids; + + Error parse(BitstreamRange& range) override; +}; + + +class Box_pymd : public Box_EntityToGroup +{ +public: + std::string dump(Indent&) const override; + +protected: + uint16_t tile_size_x; + uint16_t tile_size_y; + + struct LayerInfo { + uint16_t layer_binning; + uint16_t tiles_in_layer_row_minus1; + uint16_t tiles_in_layer_column_minus1; }; - std::vector m_entity_groups; + std::vector m_layer_infos; + + Error parse(BitstreamRange& range) override; }; + + class Box_dinf : public Box { public: From 8a48bf8f182932a949e178e398124186adee09d9 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 10 Jul 2024 11:13:05 +0200 Subject: [PATCH 117/129] implement Box_ster (stereo entity group) --- libheif/box.cc | 35 ++++++++++++++++++++++++++++++++++- libheif/box.h | 14 ++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/libheif/box.cc b/libheif/box.cc index 3798ad4f25..870f235db2 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -548,10 +548,13 @@ Error Box::read(BitstreamRange& range, std::shared_ptr* result) break; case fourcc("altr"): - case fourcc("ster"): box = std::make_shared(); break; + case fourcc("ster"): + box = std::make_shared(); + break; + case fourcc("dinf"): box = std::make_shared(); break; @@ -3259,6 +3262,36 @@ std::string Box_EntityToGroup::dump(Indent& indent) const } +Error Box_ster::parse(BitstreamRange& range) +{ + Error err = Box_EntityToGroup::parse(range); + if (err) { + return err; + } + + if (entity_ids.size() != 2) { + return {heif_error_Invalid_input, + heif_suberror_Invalid_box_size, + "'ster' entity group does not exists of exactly two images"}; + } + + return Error::Ok; +} + + +std::string Box_ster::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + sstr << indent << "group id: " << group_id << "\n" + << indent << "left image ID " << entity_ids[0] << "\n" + << indent << "right image ID " << entity_ids[1] << "\n"; + + return sstr.str(); +} + + Error Box_pymd::parse(BitstreamRange& range) { diff --git a/libheif/box.h b/libheif/box.h index 7bcf895280..ee70d904d0 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -884,6 +884,20 @@ class Box_EntityToGroup : public FullBox }; +class Box_ster : public Box_EntityToGroup +{ +public: + std::string dump(Indent&) const override; + + heif_item_id get_left_image() const { return entity_ids[0]; } + heif_item_id get_right_image() const { return entity_ids[1]; } + +protected: + + Error parse(BitstreamRange& range) override; +}; + + class Box_pymd : public Box_EntityToGroup { public: From 537d66852629e23ca778ef717343be8f657c0e79 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 10 Jul 2024 11:23:32 +0200 Subject: [PATCH 118/129] do not enforce iinf box version<2 as there are many images in circulation with a wrong version field --- libheif/box.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index 870f235db2..50f52e9306 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1908,7 +1908,8 @@ Error Box_iinf::parse(BitstreamRange& range) { parse_full_box_header(range); - if (get_version() > 1) { + // TODO: there are several images in circulation that have an iinf version=2. We should not enforce this with a hard error. + if (false && get_version() > 1) { return unsupported_version_error("iinf"); } @@ -3285,8 +3286,8 @@ std::string Box_ster::dump(Indent& indent) const sstr << Box::dump(indent); sstr << indent << "group id: " << group_id << "\n" - << indent << "left image ID " << entity_ids[0] << "\n" - << indent << "right image ID " << entity_ids[1] << "\n"; + << indent << "left image ID: " << entity_ids[0] << "\n" + << indent << "right image ID: " << entity_ids[1] << "\n"; return sstr.str(); } From 97e8c246e4449a4a1725baeffb355faf043a27ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milo=C5=A1=20Komar=C4=8Devi=C4=87?= <4973094+kmilos@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:07:24 +0200 Subject: [PATCH 119/129] Fix libbrotlidec pkg-config requires --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 12c4039e03..92e955153f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -407,7 +407,7 @@ if (WITH_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC) find_package(Brotli) if (Brotli_FOUND) message("Brotli found") - list(APPEND REQUIRES_PRIVATE "libbrotli") + list(APPEND REQUIRES_PRIVATE "libbrotlidec") else() message("Brotli not found") endif() From a4560c2f6b6d37a103408b03c9db47a548df363b Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Wed, 10 Jul 2024 15:22:33 +0200 Subject: [PATCH 120/129] tiff: Fix wrong type for "seekTIFF" function. --- examples/decoder_tiff.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/decoder_tiff.cc b/examples/decoder_tiff.cc index 6c7ae9c6b4..b927cb9807 100644 --- a/examples/decoder_tiff.cc +++ b/examples/decoder_tiff.cc @@ -38,7 +38,7 @@ extern "C" { #include "decoder_tiff.h" -static bool seekTIFF(TIFF* tif, off_t offset, int whence) { +static bool seekTIFF(TIFF* tif, toff_t offset, int whence) { TIFFSeekProc seekProc = TIFFGetSeekProc(tif); if (!seekProc) { return false; From 66bdcdcdedf5100e04b52a95afd317addd23f2f3 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Wed, 10 Jul 2024 15:13:46 +0200 Subject: [PATCH 121/129] fix 32bit compiler warnings in box.cc (#1235) --- libheif/bitstream.cc | 2 +- libheif/box.cc | 90 +++++++++++++++++++++++++++----------------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/libheif/bitstream.cc b/libheif/bitstream.cc index d649e18a55..fda64157ed 100644 --- a/libheif/bitstream.cc +++ b/libheif/bitstream.cc @@ -72,7 +72,7 @@ StreamReader_memory::StreamReader_memory(const uint8_t* data, size_t size, bool { if (copy) { m_owned_data = new uint8_t[m_length]; - memcpy(m_owned_data, data, m_length); + memcpy(m_owned_data, data, size); m_data = m_owned_data; } diff --git a/libheif/box.cc b/libheif/box.cc index 50f52e9306..9b969bfa75 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -414,12 +414,15 @@ Error Box::parse(BitstreamRange& range) } else { uint64_t content_size = get_box_size() - get_header_size(); - if (range.prepare_read(content_size)) { - if (content_size > MAX_BOX_SIZE) { - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_box_size); - } + assert(MAX_BOX_SIZE <= SIZE_MAX); + + if (content_size > MAX_BOX_SIZE) { + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_box_size); + } + + if (range.prepare_read(static_cast(content_size))) { range.get_istream()->seek_cur(get_box_size() - get_header_size()); } } @@ -691,49 +694,57 @@ Error Box::read(BitstreamRange& range, std::shared_ptr* result) box->set_short_header(hdr); - if (hdr.has_fixed_box_size() && hdr.get_box_size() < hdr.get_header_size()) { - std::stringstream sstr; - sstr << "Box size (" << hdr.get_box_size() << " bytes) smaller than header size (" - << hdr.get_header_size() << " bytes)"; - - // Sanity check. - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_box_size, - sstr.str()); - } - - if (range.get_nesting_level() > MAX_BOX_NESTING_LEVEL) { return Error(heif_error_Memory_allocation_error, heif_suberror_Security_limit_exceeded, "Security limit for maximum nesting of boxes has been exceeded"); } - if (hdr.has_fixed_box_size()) { - auto status = range.wait_for_available_bytes(hdr.get_box_size() - hdr.get_header_size()); - if (status != StreamReader::size_reached) { - // TODO: return recoverable error at timeout - return Error(heif_error_Invalid_input, - heif_suberror_End_of_data); + // Sanity checks + if (hdr.get_box_size() < hdr.get_header_size()) { + std::stringstream sstr; + sstr << "Box size (" << hdr.get_box_size() << " bytes) smaller than header size (" + << hdr.get_header_size() << " bytes)"; + + return {heif_error_Invalid_input, + heif_suberror_Invalid_box_size, + sstr.str()}; } - } - // Security check: make sure that box size does not exceed int64 size. + // this is >= 0 because of above condition + auto nBytes = static_cast(hdr.get_box_size() - hdr.get_header_size()); + if (nBytes > SIZE_MAX) { + return {heif_error_Memory_allocation_error, + heif_suberror_Invalid_box_size, + "Box size too large"}; + } - if (hdr.get_box_size() > (uint64_t) std::numeric_limits::max()) { - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_box_size); + // Security check: make sure that box size does not exceed int64 size. + + if (hdr.get_box_size() > (uint64_t) std::numeric_limits::max()) { + return {heif_error_Invalid_input, + heif_suberror_Invalid_box_size}; + } + + // --- wait for data to arrive + + auto status = range.wait_for_available_bytes(static_cast(nBytes)); + if (status != StreamReader::size_reached) { + // TODO: return recoverable error at timeout + return {heif_error_Invalid_input, + heif_suberror_End_of_data}; + } } - int64_t box_size = static_cast(hdr.get_box_size()); + auto box_size = static_cast(hdr.get_box_size()); int64_t box_size_without_header = hdr.has_fixed_box_size() ? (box_size - hdr.get_header_size()) : (int64_t)range.get_remaining_bytes(); // Box size may not be larger than remaining bytes in parent box. if ((int64_t)range.get_remaining_bytes() < box_size_without_header) { - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_box_size); + return {heif_error_Invalid_input, + heif_suberror_Invalid_box_size}; } @@ -928,13 +939,21 @@ Error Box_other::parse(BitstreamRange& range) if (has_fixed_box_size()) { size_t len; if (get_box_size() >= get_header_size()) { - len = get_box_size() - get_header_size(); + auto len64 = get_box_size() - get_header_size(); + if (len64 > MAX_BOX_SIZE) { + return {heif_error_Invalid_input, + heif_suberror_Security_limit_exceeded, + "Box size too large"}; + } + + len = static_cast(len64); + m_data.resize(len); range.read(m_data.data(), len); } else { - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_box_size); + return {heif_error_Invalid_input, + heif_suberror_Invalid_box_size}; } } else { @@ -971,7 +990,8 @@ std::string Box_other::dump(Indent& indent) const size_t len = 0; if (get_box_size() >= get_header_size()) { - len = get_box_size() - get_header_size(); + // We can cast because if it does not fit, it would fail during parsing. + len = static_cast(get_box_size() - get_header_size()); } else { sstr << indent << "invalid box size " << get_box_size() << " (smaller than header)\n"; From d067859445df99fe14dbfdb0932e1e977d9f57b8 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 11 Jul 2024 12:10:58 +0200 Subject: [PATCH 122/129] Fix coverity build. - Run "cmake" before building. - Enable more codecs. - Build on PRs (but don't upload to Coverity). --- .github/workflows/coverity.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index fd5ab0b603..bab1fe3bbc 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -3,6 +3,8 @@ name: coverity on: push: branches: [ coverity ] + pull_request: + branches: [ master ] jobs: scan: @@ -10,8 +12,10 @@ jobs: env: TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} WITH_AOM: 1 + WITH_DAV1D: 1 WITH_GRAPHICS: 1 WITH_LIBDE265: 1 + WITH_RAV1E: 1 WITH_X265: 1 steps: - uses: actions/checkout@v4 @@ -21,7 +25,7 @@ jobs: with: path: | coverity_tool.tar.gz - key: ${{ runner.os }} + key: coverity_tool-${{ runner.os }} - name: Download Coverity build tool run: | @@ -39,10 +43,13 @@ jobs: - name: Build with Coverity build tool run: | - export PATH=`pwd`/coverity_tool/bin:$PATH - cov-build --dir cov-int make + export PATH=$(pwd)/coverity_tool/bin:$PATH + export PKG_CONFIG_PATH="$(pwd)/libde265/dist/lib/pkgconfig/:$(pwd)/third-party/rav1e/dist/lib/pkgconfig/:$(pwd)/third-party/dav1d/dist/lib/x86_64-linux-gnu/pkgconfig/" + cmake --preset=develop . + cov-build --dir cov-int make -j$(nproc) - name: Submit build result to Coverity Scan + if: github.ref == 'refs/heads/coverity' run: | tar czvf libheif.tar.gz cov-int curl --form token=$TOKEN \ From 9b5defa7ff2e985b909420b5559d0e7b7968d7c1 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 11 Jul 2024 13:46:44 +0200 Subject: [PATCH 123/129] CI: Fail if third-party build fails. --- scripts/install-ci-linux.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/install-ci-linux.sh b/scripts/install-ci-linux.sh index cdb5f981e9..c2f6509683 100755 --- a/scripts/install-ci-linux.sh +++ b/scripts/install-ci-linux.sh @@ -199,7 +199,7 @@ if [ "$WITH_DAV1D" = "1" ]; then export PATH="$PATH:$HOME/.local/bin" cd third-party - sh dav1d.cmd # dav1d does not support this option anymore: -Denable_avx512=false + sh -e dav1d.cmd # dav1d does not support this option anymore: -Denable_avx512=false cd .. fi @@ -208,6 +208,6 @@ if [ "$WITH_RAV1E" = "1" ]; then export PATH="$PATH:$HOME/.cargo/bin" cd third-party - sh rav1e.cmd + sh -e rav1e.cmd cd .. fi From 9d62fb4437584de7303a4d47dd240a790aea0481 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 11 Jul 2024 14:01:42 +0200 Subject: [PATCH 124/129] appveyor: Build multithreaded. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 1f62cc8e53..031fa958d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,6 +29,7 @@ before_build: - cmake .. -A %arch% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DWITH_DAV1D=ON -DWITH_AOM_DECODER=ON -DWITH_AOM_ENCODER=ON -DWITH_JPEG_DECODER=ON -DWITH_JPEG_ENCODER=ON -DWITH_UNCOMPRESSED_CODEC=ON -DWITH_HEADER_COMPRESSION=ON -DWITH_FFMPEG_DECODER=ON build: + parallel: true verbosity: normal artifacts: From e8ca0915efc02e09167409b89a5961738ddcb7e5 Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 11 Jul 2024 15:12:21 +0200 Subject: [PATCH 125/129] Bump rav1e to 0.6 to fix compile error. --- third-party/rav1e.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/rav1e.cmd b/third-party/rav1e.cmd index 6b4c3a8cc8..371f1a0685 100644 --- a/third-party/rav1e.cmd +++ b/third-party/rav1e.cmd @@ -13,7 +13,7 @@ : # Also, the error that "The target windows-msvc is not supported yet" can safely be ignored provided that rav1e/target/release : # contains rav1e.h and rav1e.lib. -git clone -b 0.5 --depth 1 https://github.com/xiph/rav1e.git +git clone -b 0.6 --depth 1 https://github.com/xiph/rav1e.git cd rav1e cargo install cargo-c From 2832a8d37c1b72c03cbaae97e0c668cb8abda200 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Tue, 16 Jul 2024 01:08:25 +0200 Subject: [PATCH 126/129] fix memory leaks in Brotli decoder (oss-fuzz #70383) --- libheif/compression_brotli.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libheif/compression_brotli.cc b/libheif/compression_brotli.cc index 0f8904b2ee..198e2562a5 100644 --- a/libheif/compression_brotli.cc +++ b/libheif/compression_brotli.cc @@ -39,11 +39,12 @@ Error decompress_brotli(const std::vector &compressed_input, std::vecto const std::uint8_t *next_in = reinterpret_cast(compressed_input.data()); size_t available_out = buffer.size(); std::uint8_t *next_output = buffer.data(); - BrotliDecoderState *state = BrotliDecoderCreateInstance(0, 0, 0); + + std::unique_ptr state(BrotliDecoderCreateInstance(0, 0, 0), BrotliDecoderDestroyInstance); while (true) { - result = BrotliDecoderDecompressStream(state, &available_in, &next_in, &available_out, &next_output, 0); + result = BrotliDecoderDecompressStream(state.get(), &available_in, &next_in, &available_out, &next_output, 0); if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) { @@ -64,20 +65,20 @@ Error decompress_brotli(const std::vector &compressed_input, std::vecto } else if (result == BROTLI_DECODER_RESULT_ERROR) { - const char* errorMessage = BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state)); + const char* errorMessage = BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state.get())); std::stringstream sstr; sstr << "Error performing brotli inflate - " << errorMessage << "\n"; return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str()); } else { - const char* errorMessage = BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state)); + const char* errorMessage = BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state.get())); std::stringstream sstr; sstr << "Unknown error performing brotli inflate - " << errorMessage << "\n"; return Error(heif_error_Invalid_input, heif_suberror_Decompression_invalid_data, sstr.str()); } } - BrotliDecoderDestroyInstance(state); + return Error::Ok; } From 0c47c2a67dd918f33e5c8e17c8469a983fbe35c8 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 16 Jul 2024 20:42:40 +1000 Subject: [PATCH 127/129] rename property raw data implementation to match header --- libheif/api/libheif/heif_properties.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libheif/api/libheif/heif_properties.cc b/libheif/api/libheif/heif_properties.cc index 8452f09888..e076429a23 100644 --- a/libheif/api/libheif/heif_properties.cc +++ b/libheif/api/libheif/heif_properties.cc @@ -372,10 +372,10 @@ struct heif_error heif_item_get_property_raw_size(const struct heif_context* con } -struct heif_error heif_item_get_property_uuid(const struct heif_context* context, - heif_item_id itemId, - heif_property_id propertyId, - uint8_t* data_out) +struct heif_error heif_item_get_property_raw_data(const struct heif_context* context, + heif_item_id itemId, + heif_property_id propertyId, + uint8_t* data_out) { if (!context || !data_out) { return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; From 0a0fec6d88955b67317c629f7a5f3262019923cd Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Fri, 19 Jul 2024 16:51:11 +1000 Subject: [PATCH 128/129] libheif: add API to get UUID for user extension boxes --- libheif/api/libheif/heif_properties.cc | 76 ++++++++++++++------- libheif/api/libheif/heif_properties.h | 18 +++++ tests/CMakeLists.txt | 1 + tests/extended_type.cc | 93 ++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 24 deletions(-) create mode 100644 tests/extended_type.cc diff --git a/libheif/api/libheif/heif_properties.cc b/libheif/api/libheif/heif_properties.cc index e076429a23..5f99d28d4e 100644 --- a/libheif/api/libheif/heif_properties.cc +++ b/libheif/api/libheif/heif_properties.cc @@ -334,16 +334,11 @@ struct heif_error heif_item_add_raw_property(const struct heif_context* context, return heif_error_success; } - -struct heif_error heif_item_get_property_raw_size(const struct heif_context* context, - heif_item_id itemId, - heif_property_id propertyId, - size_t* size_out) +static struct heif_error find_property(const struct heif_context* context, + heif_item_id itemId, + heif_property_id propertyId, + std::shared_ptr *box_other) { - if (!context || !size_out) { - return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; - } - auto file = context->context->get_heif_file(); std::vector> properties; @@ -357,7 +352,24 @@ struct heif_error heif_item_get_property_raw_size(const struct heif_context* con } auto box = properties[propertyId - 1]; - auto box_other = std::dynamic_pointer_cast(box); + *box_other = std::dynamic_pointer_cast(box); + return heif_error_success; +} + + +struct heif_error heif_item_get_property_raw_size(const struct heif_context* context, + heif_item_id itemId, + heif_property_id propertyId, + size_t* size_out) +{ + if (!context || !size_out) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; + } + std::shared_ptr box_other; + struct heif_error err = find_property(context, itemId, propertyId, &box_other); + if (err.code) { + return err; + } // TODO: every Box (not just Box_other) should have a get_raw_data() method. if (box_other == nullptr) { @@ -381,22 +393,12 @@ struct heif_error heif_item_get_property_raw_data(const struct heif_context* con return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; } - auto file = context->context->get_heif_file(); - - std::vector> properties; - Error err = file->get_properties(itemId, properties); - if (err) { - return err.error_struct(context->context.get()); - } - - if (propertyId - 1 < 0 || propertyId - 1 >= properties.size()) { - return {heif_error_Usage_error, heif_suberror_Invalid_property, "property index out of range"}; + std::shared_ptr box_other; + struct heif_error err = find_property(context, itemId, propertyId, &box_other); + if (err.code) { + return err; } - - auto box = properties[propertyId - 1]; - auto box_other = std::dynamic_pointer_cast(box); - // TODO: every Box (not just Box_other) should have a get_raw_data() method. if (box_other == nullptr) { return {heif_error_Usage_error, heif_suberror_Invalid_property, "this property is not read as a raw box"}; @@ -409,3 +411,29 @@ struct heif_error heif_item_get_property_raw_data(const struct heif_context* con return heif_error_success; } + +struct heif_error heif_item_get_property_extended_type(const struct heif_context* context, + heif_item_id itemId, + heif_property_id propertyId, + uint8_t* extended_type) +{ + if (!context || !extended_type) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL argument passed in"}; + } + + std::shared_ptr box_other; + struct heif_error err = find_property(context, itemId, propertyId, &box_other); + if (err.code) { + return err; + } + + if (box_other == nullptr) { + return {heif_error_Usage_error, heif_suberror_Invalid_property, "this property is not read as a raw box"}; + } + + auto uuid = box_other->get_uuid_type(); + + std::copy(uuid.begin(), uuid.end(), extended_type); + + return heif_error_success; +} \ No newline at end of file diff --git a/libheif/api/libheif/heif_properties.h b/libheif/api/libheif/heif_properties.h index 4f54561871..cfbe3d3b78 100644 --- a/libheif/api/libheif/heif_properties.h +++ b/libheif/api/libheif/heif_properties.h @@ -167,6 +167,24 @@ struct heif_error heif_item_get_property_raw_data(const struct heif_context* con heif_property_id propertyId, uint8_t* out_data); +/** + * Get the extended type for an extended "uuid" box. + * + * This provides the UUID for the extended box. + * + * This method should only be called on properties of type `heif_item_property_type_uuid`. + * + * @param context the heif_context containing the HEIF file + * @param itemId the image item id to which this property belongs. + * @param propertyID the property index (1-based) to get the extended type for + * @param extended_type output of the call, must be a pointer to at least 16-bytes. + * @return heif_error_success or an error indicating the failure + */ +LIBHEIF_API +struct heif_error heif_item_get_property_extended_type(const struct heif_context* context, + heif_item_id itemId, + heif_property_id propertyId, + uint8_t* extended_type); #ifdef __cplusplus } #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index daa5060c46..5265110d65 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -33,6 +33,7 @@ endif() # --- tests that only access the public API add_libheif_test(encode) +add_libheif_test(extended_type) add_libheif_test(region) if (WITH_OPENJPH_ENCODER) diff --git a/tests/extended_type.cc b/tests/extended_type.cc new file mode 100644 index 0000000000..eacab47565 --- /dev/null +++ b/tests/extended_type.cc @@ -0,0 +1,93 @@ +/* + libheif integration tests for extended type (uuid) boxes + + MIT License + + Copyright (c) 2024 Brad Hards + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "catch.hpp" +#include "libheif/api_structs.h" +#include "libheif/heif.h" +#include "test-config.h" +#include "test_utils.h" +#include +#include +#include + + +TEST_CASE("make extended type") { + heif_image *input_image = createImage_RGB_planar(); + heif_init(nullptr); + heif_context *ctx = heif_context_alloc(); + heif_encoder *encoder; + struct heif_error err; + err = heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder); + REQUIRE(err.code == heif_error_Ok); + + struct heif_encoding_options *options = heif_encoding_options_alloc(); + + heif_image_handle *output_image_handle; + + err = heif_context_encode_image(ctx, input_image, encoder, nullptr, &output_image_handle); + REQUIRE(err.code == heif_error_Ok); + + heif_item_id itemId; + err = heif_context_get_primary_image_ID(ctx, &itemId); + REQUIRE(err.code == heif_error_Ok); + + const uint8_t uuid[] = {0x13, 0x7a, 0x17, 0x42, 0x75, 0xac, 0x47, 0x47, 0x82, 0xbc, 0x65, 0x95, 0x76, 0xe8, 0x67, 0x5b}; + std::vector body {0x00, 0x00, 0x00, 0x01, 0xfa, 0xde, 0x99, 0x04}; + heif_property_id propertyId; + err = heif_item_add_raw_property(ctx, itemId, heif_item_property_type_uuid, &uuid[0], body.data(), body.size(), 0, &propertyId); + REQUIRE(err.code == heif_error_Ok); + REQUIRE(propertyId == 4); + err = heif_context_write_to_file(ctx, "with_uuid.heif"); + REQUIRE(err.code == heif_error_Ok); + + uint8_t extended_type[16]; + err = heif_item_get_property_extended_type(ctx, itemId, propertyId, &extended_type[0]); + REQUIRE(err.code == heif_error_Ok); + for (int i = 0; i < 16; i++) { + REQUIRE(extended_type[i] == uuid[i]); + } + + size_t size = 0; + err = heif_item_get_property_raw_size(ctx, itemId, propertyId, &size); + REQUIRE(err.code == heif_error_Ok); + REQUIRE(size == 8); + + uint8_t data[8]; + err = heif_item_get_property_raw_data(ctx, itemId, propertyId, &data[0]); + REQUIRE(err.code == heif_error_Ok); + for (int i = 0; i < 8; i++) { + REQUIRE(data[i] == body[i]); + } + + heif_image_handle_release(output_image_handle); + heif_encoding_options_free(options); + heif_encoder_release(encoder); + heif_image_release(input_image); + + heif_context_free(ctx); + heif_deinit(); +} + From 42fc168a78d780fb38e30abc28f9a8e6633367b4 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Mon, 22 Jan 2024 22:16:26 +1100 Subject: [PATCH 129/129] wip: openjph HT decoder --- CMakeLists.txt | 4 +- libheif/context.cc | 21 ++- libheif/plugin_registry.cc | 8 + libheif/plugins/CMakeLists.txt | 4 + libheif/plugins/decoder_openjpeg.cc | 2 +- libheif/plugins/decoder_openjph.cc | 238 ++++++++++++++++++++++++++++ libheif/plugins/decoder_openjph.h | 35 ++++ 7 files changed, 303 insertions(+), 9 deletions(-) create mode 100644 libheif/plugins/decoder_openjph.cc create mode 100644 libheif/plugins/decoder_openjph.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 92e955153f..26fcb1b4a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,7 +224,7 @@ endif () # openjph plugin_option(OPENJPH_ENCODER "OpenJPH HT-J2K encoder" OFF ON) -# plugin_option(OPENJPH_DECODER "OpenJPH HT-J2K decoder" OFF ON) +plugin_option(OPENJPH_DECODER "OpenJPH HT-J2K decoder" OFF ON) if (WITH_OPENJPH_ENCODER OR WITH_OPENJPH_DECODER) find_package(OPENJPH) endif() @@ -250,7 +250,7 @@ plugin_compilation_info(JPEG_DECODER JPEG "JPEG decoder") plugin_compilation_info(JPEG_ENCODER JPEG "JPEG encoder") plugin_compilation_info(OpenJPEG_DECODER OpenJPEG "OpenJPEG J2K decoder") plugin_compilation_info(OpenJPEG_ENCODER OpenJPEG "OpenJPEG J2K encoder") -# plugin_compilation_info(OPENJPH_DECODER OPENJPH "OpenJPH HT-J2K decoder") +plugin_compilation_info(OPENJPH_DECODER OPENJPH "OpenJPH HT-J2K decoder") plugin_compilation_info(OPENJPH_ENCODER OPENJPH "OpenJPH HT-J2K encoder") plugin_compilation_info(UVG266_ENCODER UVG266 "uvg266 VVC enc. (experimental)") plugin_compilation_info(VVENC vvenc "vvenc VVC enc. (experimental)") diff --git a/libheif/context.cc b/libheif/context.cc index 3e0586e0f1..d95c029e93 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -1403,6 +1403,12 @@ Error HeifContext::decode_image_planar(heif_item_id ID, image_type == "jpeg" || (image_type == "mime" && m_heif_file->get_content_type(ID) == "image/jpeg")) { + std::vector data; + error = m_heif_file->get_compressed_image_data(ID, &data); + if (error) { + return error; + } + heif_compression_format compression = heif_compression_undefined; if (image_type == "hvc1") { compression = heif_compression_HEVC; @@ -1418,7 +1424,14 @@ Error HeifContext::decode_image_planar(heif_item_id ID, compression = heif_compression_JPEG; } else if (image_type == "j2k1") { - compression = heif_compression_JPEG2000; + JPEG2000MainHeader j2k_header; + j2k_header.setHeaderData(data); + j2k_header.doParse(); + if (j2k_header.hasHighThroughputExtension()) { + compression = heif_compression_HTJ2K; + } else { + compression = heif_compression_JPEG2000; + } } const struct heif_decoder_plugin* decoder_plugin = get_decoder(compression, options.decoder_id); @@ -1426,11 +1439,7 @@ Error HeifContext::decode_image_planar(heif_item_id ID, return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed); } - std::vector data; - error = m_heif_file->get_compressed_image_data(ID, &data); - if (error) { - return error; - } + void* decoder; struct heif_error err = decoder_plugin->new_decoder(&decoder); diff --git a/libheif/plugin_registry.cc b/libheif/plugin_registry.cc index 38e4594f30..7ffae06438 100644 --- a/libheif/plugin_registry.cc +++ b/libheif/plugin_registry.cc @@ -100,6 +100,10 @@ #include "plugins/encoder_openjph.h" #endif +#if HAVE_OPENJPH_DECODER +#include "plugins/decoder_openjph.h" +#endif + std::set s_decoder_plugins; std::multiset, @@ -203,6 +207,10 @@ void register_default_plugins() register_encoder(get_encoder_plugin_openjph()); #endif +#if HAVE_OPENJPH_DECODER + register_decoder(get_decoder_plugin_openjph()); +#endif + #if WITH_UNCOMPRESSED_CODEC register_encoder(get_encoder_plugin_uncompressed()); #endif diff --git a/libheif/plugins/CMakeLists.txt b/libheif/plugins/CMakeLists.txt index 67f3e74ead..b856202a7e 100644 --- a/libheif/plugins/CMakeLists.txt +++ b/libheif/plugins/CMakeLists.txt @@ -96,6 +96,10 @@ set(OPENJPH_ENCODER_sources encoder_openjph.cc encoder_openjph.h) set(OPENJPH_ENCODER_extra_plugin_sources) plugin_compilation(jphenc OPENJPH OPENJPH_FOUND OPENJPH_ENCODER OPENJPH_ENCODER) +set(OPENJPH_DECODER_sources decoder_openjph.cc decoder_openjph.h) +set(OPENJPH_DECODER_extra_plugin_sources) +plugin_compilation(jphdec OPENJPH OPENJPH_FOUND OPENJPH_DECODER OPENJPH_DECODER) + set(UVG266_sources encoder_uvg266.cc encoder_uvg266.h) set(UVG266_extra_plugin_sources) plugin_compilation(uvg266 UVG266 UVG266_FOUND UVG266 UVG266) diff --git a/libheif/plugins/decoder_openjpeg.cc b/libheif/plugins/decoder_openjpeg.cc index 7e801bff00..255c9601e2 100644 --- a/libheif/plugins/decoder_openjpeg.cc +++ b/libheif/plugins/decoder_openjpeg.cc @@ -62,7 +62,7 @@ static void openjpeg_deinit_plugin() static int openjpeg_does_support_format(enum heif_compression_format format) { - if (format == heif_compression_JPEG2000) { + if ((format == heif_compression_JPEG2000) || (format == heif_compression_HTJ2K)) { return OPENJPEG_PLUGIN_PRIORITY; } else { diff --git a/libheif/plugins/decoder_openjph.cc b/libheif/plugins/decoder_openjph.cc new file mode 100644 index 0000000000..342acf2738 --- /dev/null +++ b/libheif/plugins/decoder_openjph.cc @@ -0,0 +1,238 @@ +/* + * OpenJPH High Throughput JPEG 2000 decoder. + * + * Copyright (c) 2024 Brad Hards + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + */ + +#include "libheif/heif.h" +#include "libheif/heif_plugin.h" +#include "decoder_openjph.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +struct openjph_dec_context +{ + std::vector data; + bool strict_decoding = false; +}; + + +static const int OPENJPH_DEC_PLUGIN_PRIORITY = 100; + +static void openjph_dec_init_plugin() +{ +} + +static void openjph_dec_deinit_plugin() +{ +} + +static int openjph_dec_does_support_format(enum heif_compression_format format) +{ + if (format == heif_compression_HTJ2K) { + return OPENJPH_DEC_PLUGIN_PRIORITY; + } else { + return 0; + } +} + +struct heif_error openjph_dec_new_decoder(void **dec) +{ + struct openjph_dec_context *decoder_context = new openjph_dec_context(); + *dec = decoder_context; + return heif_error_ok; +} + +void openjph_dec_free_decoder(void *decoder_raw) +{ + openjph_dec_context *decoder_context = (openjph_dec_context *)decoder_raw; + delete decoder_context; +} + +void openjph_dec_set_strict_decoding(void *decoder_raw, int flag) +{ + openjph_dec_context *decoder_context = (openjph_dec_context *)decoder_raw; + decoder_context->strict_decoding = flag; +} + +struct heif_error openjph_dec_push_data(void *decoder_raw, const void *frame_data, size_t frame_size) +{ + openjph_dec_context *decoder_context = (openjph_dec_context *)decoder_raw; + const uint8_t* data = (const uint8_t*)frame_data; + decoder_context->data.insert(decoder_context->data.end(), data, data + frame_size); + return heif_error_ok; +} + +struct heif_error openjph_dec_decode_image(void *decoder_raw, struct heif_image **out_img) +{ + openjph_dec_context *decoder_context = (openjph_dec_context *)decoder_raw; + ojph::codestream codestream; + ojph::mem_infile input; + input.open(decoder_context->data.data(), decoder_context->data.size()); + if (!(decoder_context->strict_decoding)) { + codestream.enable_resilience(); + } + codestream.read_headers(&input); + codestream.create(); + + struct heif_image* heif_img = nullptr; + + ojph::param_siz siz = codestream.access_siz(); + ojph::point imageExtent = siz.get_image_extent(); + ojph::point imageOffset = siz.get_image_offset(); + uint32_t width = imageExtent.x - imageOffset.x; + uint32_t height = imageExtent.y - imageOffset.y; + + // TODO: work out colorspace and chroma correctly + heif_colorspace colourspace = heif_colorspace_undefined; + heif_chroma chroma = heif_chroma_undefined; + if (siz.get_num_components() == 3) { + colourspace = heif_colorspace_YCbCr; + chroma = heif_chroma_444; + // check components after the first one + for (unsigned int i = 1; i < siz.get_num_components(); ++i) { + ojph::point downsampling = siz.get_downsampling(i); + if ((downsampling.x == 1) && (downsampling.y == 1)) { + // 4:4:4 + if (chroma != heif_chroma_444) { + struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "mismatched chroma format"}; + return err; + } + } else if ((downsampling.x == 2) && (downsampling.y == 1)) { + // 4:2:2 + if ((chroma == heif_chroma_444) && (i == 1)) { + chroma = heif_chroma_422; + } else if (chroma != heif_chroma_422) { + struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "mismatched chroma format"}; + return err; + } + } else if ((downsampling.x == 2) && (downsampling.y == 2)) { + // 4:2:0 + if ((chroma == heif_chroma_444) && (i == 1)) { + chroma = heif_chroma_420; + } else if (chroma != heif_chroma_420) { + struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "mismatched chroma format"}; + return err; + } + + } + } + } else if (siz.get_num_components() == 1) { + colourspace = heif_colorspace_monochrome; + chroma = heif_chroma_monochrome; + } else { + struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "unsupported number of components"}; + return err; + + } + + struct heif_error err = heif_image_create(width, height, colourspace, chroma, &heif_img); + if (err.code != heif_error_Ok) { + assert(heif_img == nullptr); + return err; + } + + // TODO: map component to channel + if (colourspace == heif_colorspace_monochrome) { + heif_image_add_plane(heif_img, heif_channel_Y, width, height, siz.get_bit_depth(0)); + // TODO: make image + struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "unsupported monochrome image"}; + return err; + } else { + if (codestream.is_planar()) { + heif_channel channels[] = {heif_channel_Y, heif_channel_Cb, heif_channel_Cr}; + for (uint32_t componentIndex = 0; componentIndex < siz.get_num_components(); ++componentIndex) { + uint32_t component_width = siz.get_recon_width(componentIndex); + uint32_t component_height = siz.get_recon_height(componentIndex); + uint32_t bit_depth = siz.get_bit_depth(componentIndex); + heif_channel channel = channels[componentIndex]; + heif_image_add_plane(heif_img, channel, component_width, component_height, bit_depth); + int planeStride; + uint8_t* plane = heif_image_get_plane(heif_img, channel, &planeStride); + for (uint32_t rowIndex = 0; rowIndex < component_height; rowIndex++) { + uint32_t comp_num; + ojph::line_buf *line = codestream.pull(comp_num); + const int32_t *cursor = line->i32; + for (uint32_t colIndex = 0; colIndex < component_width; ++colIndex) { + int v = *cursor; + // TODO: this only works for the 8 bit case + plane[rowIndex * planeStride + colIndex] = (uint8_t) v; + cursor++; + } + } + } + } else { + struct heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "unsupported interleaved image"}; + return err; + } + } + *out_img = heif_img; + return heif_error_ok; +} + +static const int MAX_PLUGIN_NAME_LENGTH = 80; +static char plugin_name[MAX_PLUGIN_NAME_LENGTH]; + +const char *openjph_dec_plugin_name() +{ + snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, + "OpenJPH %s.%s.%s", + OJPH_INT_TO_STRING(OPENJPH_VERSION_MAJOR), + OJPH_INT_TO_STRING(OPENJPH_VERSION_MINOR), + OJPH_INT_TO_STRING(OPENJPH_VERSION_PATCH)); + plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0; + + return plugin_name; +} + +static const struct heif_decoder_plugin decoder_openjph +{ + 3, + openjph_dec_plugin_name, + openjph_dec_init_plugin, + openjph_dec_deinit_plugin, + openjph_dec_does_support_format, + openjph_dec_new_decoder, + openjph_dec_free_decoder, + openjph_dec_push_data, + openjph_dec_decode_image, + openjph_dec_set_strict_decoding, + "openjph" +}; + +const struct heif_decoder_plugin *get_decoder_plugin_openjph() +{ + return &decoder_openjph; +} + +#if PLUGIN_OPENJPH_DECODER +heif_plugin_info plugin_info{ + 1, + heif_plugin_type_decoder, + &decoder_openjph}; +#endif diff --git a/libheif/plugins/decoder_openjph.h b/libheif/plugins/decoder_openjph.h new file mode 100644 index 0000000000..f78442f7de --- /dev/null +++ b/libheif/plugins/decoder_openjph.h @@ -0,0 +1,35 @@ +/* + * OpenJPH High Throughput JPEG 2000 decoder. + * + * Copyright (c) 2024 Brad Hards + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_HEIF_DECODER_OPENJPH_H +#define LIBHEIF_HEIF_DECODER_OPENJPH_H + +#include "common_utils.h" + +const struct heif_decoder_plugin* get_decoder_plugin_openjph(); + +#if PLUGIN_OPENJPH_DECODER +extern "C" { +MAYBE_UNUSED LIBHEIF_API extern heif_plugin_info plugin_info; +} +#endif + +#endif