From 721ef0d7e583b81580ac368c3fdaaea6201bd605 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 14:53:14 +0200 Subject: [PATCH 01/12] unci: write empty unci image (to be filled with tiled) --- libheif/api/libheif/heif.cc | 33 +++++++ libheif/api/libheif/heif.h | 21 ++++ libheif/codecs/uncompressed_box.cc | 36 +++++++ libheif/codecs/uncompressed_box.h | 6 +- libheif/codecs/uncompressed_image.cc | 141 +++++++++++++++++++++++---- libheif/codecs/uncompressed_image.h | 14 +++ libheif/context.cc | 8 ++ libheif/context.h | 5 + 8 files changed, 243 insertions(+), 21 deletions(-) diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index 9801ee1778..29b54e1c8c 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -37,6 +37,10 @@ #include #include +#if WITH_UNCOMPRESSED_CODEC +#include "codecs/uncompressed_image.h" +#endif + #if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_STANDALONE_WASM__) #include "heif_emscripten.h" #endif @@ -3472,6 +3476,35 @@ struct heif_error heif_context_add_tild_image_tile(struct heif_context* ctx, } +struct heif_error heif_context_add_unci_image(struct heif_context* ctx, + const struct heif_unci_image_parameters* parameters, + const struct heif_encoding_options* encoding_options, + const heif_image* prototype, + struct heif_image_handle** out_unci_image_handle) +{ +#if WITH_UNCOMPRESSED_CODEC + Result> unciImageResult; + unciImageResult = ctx->context->add_unci_item(parameters, encoding_options, prototype->image); + + if (unciImageResult.error != Error::Ok) { + return unciImageResult.error.error_struct(ctx->context.get()); + } + + if (out_unci_image_handle) { + *out_unci_image_handle = new heif_image_handle; + (*out_unci_image_handle)->image = unciImageResult.value; + (*out_unci_image_handle)->context = ctx->context; + } + + return heif_error_success; +#else + return {heif_error_Unsupported_feature, + heif_suberror_Unspecified, + "support for uncompressed images (ISO23001-17) has been disabled."}; +#endif +} + + struct heif_error heif_context_assign_thumbnail(struct heif_context* ctx, const struct heif_image_handle* master_image, diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index 1a90f02a1c..7eb74ed0d8 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -2464,6 +2464,27 @@ struct heif_error heif_context_add_tild_image_tile(struct heif_context* ctx, const struct heif_image* image, struct heif_encoder* encoder); +struct heif_unci_image_parameters { + int version; + + // --- version 1 + + uint32_t image_width; + uint32_t image_height; + + uint32_t tile_width; + uint32_t tile_height; + + // TODO: interleave type, padding +}; + + +LIBHEIF_API +struct heif_error heif_context_add_unci_image(struct heif_context* ctx, + const struct heif_unci_image_parameters* parameters, + const struct heif_encoding_options* encoding_options, + const struct heif_image* prototype, + struct heif_image_handle** out_unci_image_handle); // offsets[] should either be NULL (all offsets==0) or an array of size 2*nImages with x;y offset pairs. // If background_rgba is NULL, the background is transparent. diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index 7f51904e4c..db9e1c757e 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -326,6 +326,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); @@ -366,6 +367,41 @@ Error Box_uncC::write(StreamWriter& writer) const } +uint64_t Box_uncC::compute_tile_data_size_bytes(uint32_t image_width, uint32_t image_height) const +{ + if (m_profile != 0) { + switch (m_profile) { + case fourcc("rgba"): + return 4 * image_width / m_num_tile_cols * image_height / m_num_tile_rows; + + case fourcc("rgb3"): + return 3 * image_width / m_num_tile_cols * image_height / m_num_tile_rows; + + default: + assert(false); + return 0; + } + } + + switch (m_interleave_type) { + case interleave_mode_component: + case interleave_mode_pixel: { + uint32_t bytes_per_pixel = 0; + + for (const auto& comp : m_components) { + assert(comp.component_bit_depth % 8 == 0); // TODO: component sizes that are no multiples of bytes + bytes_per_pixel += comp.component_bit_depth / 8; + } + + return bytes_per_pixel * image_width / m_num_tile_cols * image_height / m_num_tile_rows; + } + default: + assert(false); + return 0; + } +} + + Error Box_cmpC::parse(BitstreamRange& range) { parse_full_box_header(range); diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed_box.h index 0e4085b641..b2e6c2189a 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -201,10 +201,14 @@ class Box_uncC : public FullBox m_num_tile_rows = num_tile_rows; } + uint32_t get_number_of_tiles() const { return m_num_tile_rows * m_num_tile_rows; } + + uint64_t compute_tile_data_size_bytes(uint32_t image_width, uint32_t image_height) const; + protected: Error parse(BitstreamRange& range) override; - uint32_t m_profile = 0; // not compliant to any profile + uint32_t m_profile = 0; // 0 = not compliant to any profile std::vector m_components; uint8_t m_sampling_type = sampling_mode_no_subsampling; // no subsampling diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 305f3e7cd7..3b08f1e563 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -1564,8 +1564,14 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte return Error::Ok; } -Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptr& uncC, const std::shared_ptr& image) +Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, + std::shared_ptr& uncC, + const std::shared_ptr& image, + const heif_unci_image_parameters* parameters) { + uint32_t nTileColumns = parameters->image_width / parameters->tile_width; + uint32_t nTileRows = parameters->image_height / parameters->tile_height; + const heif_colorspace colourspace = image->get_colorspace(); if (colourspace == heif_colorspace_YCbCr) { if (!(image->has_channel(heif_channel_Y) && image->has_channel(heif_channel_Cb) && image->has_channel(heif_channel_Cr))) @@ -1617,8 +1623,8 @@ Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptrset_pixel_size(0); uncC->set_row_align_size(0); uncC->set_tile_align_size(0); - uncC->set_number_of_tile_columns(1); - uncC->set_number_of_tile_rows(1); + uncC->set_number_of_tile_columns(nTileColumns); + uncC->set_number_of_tile_rows(nTileRows); } else if (colourspace == heif_colorspace_RGB) { @@ -1713,8 +1719,8 @@ Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptrset_pixel_size(0); uncC->set_row_align_size(0); uncC->set_tile_align_size(0); - uncC->set_number_of_tile_columns(1); - uncC->set_number_of_tile_rows(1); + uncC->set_number_of_tile_columns(nTileColumns); + uncC->set_number_of_tile_rows(nTileRows); } else if (colourspace == heif_colorspace_monochrome) { @@ -1745,8 +1751,8 @@ Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptrset_pixel_size(0); uncC->set_row_align_size(0); uncC->set_tile_align_size(0); - uncC->set_number_of_tile_columns(1); - uncC->set_number_of_tile_rows(1); + uncC->set_number_of_tile_columns(nTileColumns); + uncC->set_number_of_tile_rows(nTileRows); } else { @@ -1758,7 +1764,7 @@ Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, std::shared_ptr& uncC, const std::shared_ptr& image) +static void maybe_make_minimised_uncC(std::shared_ptr& uncC, const std::shared_ptr& image) { uncC->set_version(0); if (image->get_colorspace() != heif_colorspace_RGB) { @@ -1809,30 +1815,66 @@ Result> ImageItem_uncompressed::decode_compresse } -Result ImageItem_uncompressed::encode(const std::shared_ptr& src_image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) +struct unciHeaders { - CodedImageData codedImageData; + std::shared_ptr uncC; + std::shared_ptr cmpd; +}; + + +static Result generate_headers(const std::shared_ptr& src_image, + const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* options) +{ + unciHeaders headers; -#if WITH_UNCOMPRESSED_CODEC std::shared_ptr uncC = std::make_shared(); - if (options.prefer_uncC_short_form) { + if (options && options->prefer_uncC_short_form) { maybe_make_minimised_uncC(uncC, src_image); } + if (uncC->get_version() == 1) { - codedImageData.properties.push_back(uncC); + headers.uncC = uncC; } else { std::shared_ptr cmpd = std::make_shared(); - Error error = fill_cmpd_and_uncC(cmpd, uncC, src_image); + Error error = fill_cmpd_and_uncC(cmpd, uncC, src_image, parameters); if (error) { return error; } - codedImageData.properties.push_back(cmpd); - codedImageData.properties.push_back(uncC); + headers.cmpd = cmpd; + headers.uncC = uncC; + } + + return headers; +} + + +Result ImageItem_uncompressed::encode(const std::shared_ptr& src_image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ + heif_unci_image_parameters parameters{}; + parameters.image_width = src_image->get_width(); + parameters.image_height = src_image->get_height(); + parameters.tile_width = parameters.image_width; + parameters.tile_height = parameters.image_height; + + Result genHeadersResult = generate_headers(src_image, ¶meters, &options); + if (genHeadersResult.error) { + return genHeadersResult.error; + } + + const unciHeaders& headers = *genHeadersResult; + + CodedImageData codedImageData; + if (headers.uncC) { + codedImageData.properties.push_back(headers.uncC); + } + if (headers.cmpd) { + codedImageData.properties.push_back(headers.cmpd); } std::vector data; @@ -1952,12 +1994,71 @@ Result ImageItem_uncompressed::encode(const std::shar heif_suberror_Unsupported_data_version, "Unsupported colourspace"); } -#endif return codedImageData; } +Result> ImageItem_uncompressed::add_unci_item(HeifContext* ctx, + const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* encoding_options, + const std::shared_ptr& prototype) +{ + // Create 'tild' Item + + auto file = ctx->get_heif_file(); + + heif_item_id unci_id = ctx->get_heif_file()->add_new_image("unci"); + auto unci_image = std::make_shared(ctx, unci_id); + ctx->insert_new_image(unci_id, unci_image); + + + // Generate headers + + Result genHeadersResult = generate_headers(prototype, parameters, encoding_options); + if (genHeadersResult.error) { + return genHeadersResult.error; + } + + const unciHeaders& headers = *genHeadersResult; + + if (headers.uncC) { + file->add_property(unci_id, headers.uncC, true); + } + + if (headers.cmpd) { + file->add_property(unci_id, headers.cmpd, true); + } + + // Add `ispe` property + + file->add_ispe_property(unci_id, + static_cast(parameters->image_width), + static_cast(parameters->image_height)); + + // Create empty image + + uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width, parameters->image_height); + + std::cout << "tile size: " << tile_size << "\n"; + + std::vector dummydata; + dummydata.resize(tile_size); + + for (uint64_t i = 0; i < tile_size; i++) { + const int construction_method = 0; // 0=mdat 1=idat + file->append_iloc_data(unci_id, dummydata, construction_method); + } + + + // Set Brands + //m_heif_file->set_brand(encoder->plugin->compression_format, + // out_grid_image->is_miaf_compatible()); + + return {unci_image}; +} + + int ImageItem_uncompressed::get_luma_bits_per_pixel() const { int bpp = UncompressedImageCodec::get_luma_bits_per_pixel_from_configuration_unci(*get_file(), get_id()); diff --git a/libheif/codecs/uncompressed_image.h b/libheif/codecs/uncompressed_image.h index 694755d2b3..9c967e4064 100644 --- a/libheif/codecs/uncompressed_image.h +++ b/libheif/codecs/uncompressed_image.h @@ -93,10 +93,24 @@ class ImageItem_uncompressed : public ImageItem Result> decode_compressed_image(const struct heif_decoding_options& options, bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override; + // --- encoding + Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, enum heif_image_input_class input_class) override; + + static Result> add_unci_item(HeifContext* ctx, + const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* encoding_options, + const std::shared_ptr& prototype); + +private: + /* + Result generate_headers(const std::shared_ptr& src_image, + const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* options); + */ }; #endif //LIBHEIF_UNCOMPRESSED_IMAGE_H diff --git a/libheif/context.cc b/libheif/context.cc index 43be547cbb..43ac13aaab 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -1385,6 +1385,14 @@ Error HeifContext::add_tild_image_tile(heif_item_id tild_id, uint32_t tile_x, ui } +Result> HeifContext::add_unci_item(const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* encoding_options, + const std::shared_ptr& prototype) +{ + return ImageItem_uncompressed::add_unci_item(this, parameters, encoding_options, prototype); +} + + void HeifContext::set_primary_image(const std::shared_ptr& image) { // update heif context diff --git a/libheif/context.h b/libheif/context.h index 364869d42b..bd5f3dcf72 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -51,6 +51,7 @@ class ImageOverlay; class ImageItem; class ImageItem_Overlay; class ImageItem_Tild; +class ImageItem_uncompressed; // This is a higher-level view than HeifFile. @@ -161,6 +162,10 @@ class HeifContext : public ErrorBuffer const std::shared_ptr& image, struct heif_encoder* encoder); + Result> add_unci_item(const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* encoding_options, + const std::shared_ptr& prototype); + void set_primary_image(const std::shared_ptr& image); bool is_primary_image_set() const { return m_primary_image != nullptr; } From 27699745f3d6f86a05dbebfc16c6b2fc6b7a97cc Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 15:00:46 +0200 Subject: [PATCH 02/12] fix compilation when unci support is disabled --- libheif/context.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libheif/context.cc b/libheif/context.cc index 43ac13aaab..736123db17 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -1389,7 +1389,13 @@ Result> HeifContext::add_unci_item(const const struct heif_encoding_options* encoding_options, const std::shared_ptr& prototype) { +#if WITH_UNCOMPRESSED_CODEC return ImageItem_uncompressed::add_unci_item(this, parameters, encoding_options, prototype); +#else + return {heif_error_Unsupported_feature, + heif_suberror_Unspecified, + "support for uncompressed images has been disabled"}; +#endif } From 027152eafa59e07284f4a372d64cf69966136a8b Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 15:54:15 +0200 Subject: [PATCH 03/12] unci: write individual tiles --- libheif/api/libheif/heif.cc | 31 +++++-- libheif/api/libheif/heif.h | 10 +-- libheif/box.cc | 26 +++++- libheif/box.h | 2 +- libheif/codecs/uncompressed_box.cc | 8 +- libheif/codecs/uncompressed_box.h | 2 +- libheif/codecs/uncompressed_image.cc | 116 ++++++++++++++++++--------- libheif/codecs/uncompressed_image.h | 2 + libheif/context.cc | 6 +- 9 files changed, 141 insertions(+), 62 deletions(-) diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index 29b54e1c8c..c749362ec8 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -3465,14 +3465,29 @@ struct heif_error heif_context_add_tild_image(struct heif_context* ctx, } -struct heif_error heif_context_add_tild_image_tile(struct heif_context* ctx, - struct heif_image_handle* tild_image, - uint32_t tile_x, uint32_t tile_y, - const struct heif_image* image, - struct heif_encoder* encoder) -{ - Error err = ctx->context->add_tild_image_tile(tild_image->image->get_id(), tile_x, tile_y, image->image, encoder); - return err.error_struct(ctx->context.get()); +struct heif_error heif_context_add_image_tile(struct heif_context* ctx, + struct heif_image_handle* tiled_image, + uint32_t tile_x, uint32_t tile_y, + const struct heif_image* image, + struct heif_encoder* encoder) +{ + if (tiled_image->image->get_infe_type() == std::string{"tili"}) { + Error err = ctx->context->add_tild_image_tile(tiled_image->image->get_id(), tile_x, tile_y, image->image, encoder); + return err.error_struct(ctx->context.get()); + } +#if WITH_UNCOMPRESSED_CODEC + else if (auto unci = std::dynamic_pointer_cast(tiled_image->image)) { + Error err = unci->add_image_tile(tile_x, tile_y, image->image); + return err.error_struct(ctx->context.get()); + } +#endif + else { + return { + heif_error_Usage_error, + heif_suberror_Unspecified, + "Cannot add tile to a non-tiled image" + }; + } } diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index 7eb74ed0d8..f4f1a03c64 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -2458,11 +2458,11 @@ struct heif_error heif_context_add_tild_image(struct heif_context* ctx, struct heif_image_handle** out_tild_image_handle); LIBHEIF_API -struct heif_error heif_context_add_tild_image_tile(struct heif_context* ctx, - struct heif_image_handle* tild_image, - uint32_t tile_x, uint32_t tile_y, - const struct heif_image* image, - struct heif_encoder* encoder); +struct heif_error heif_context_add_image_tile(struct heif_context* ctx, + struct heif_image_handle* tild_image, + uint32_t tile_x, uint32_t tile_y, + const struct heif_image* image, + struct heif_encoder* encoder); struct heif_unci_image_parameters { int version; diff --git a/libheif/box.cc b/libheif/box.cc index 92c609d039..e5c228bda4 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1701,12 +1701,11 @@ Error Box_iloc::append_data(heif_item_id item_ID, Error Box_iloc::replace_data(heif_item_id item_ID, - uint64_t offset, + uint64_t output_offset, const std::vector& data, uint8_t construction_method) { assert(construction_method == 0); // TODO - assert(offset == 0); // TODO // check whether this item ID already exists @@ -1718,8 +1717,27 @@ Error Box_iloc::replace_data(heif_item_id item_ID, } assert(idx != m_items.size()); - assert(m_items[idx].extents[0].data.size() >= data.size()); // TODO - memcpy(m_items[idx].extents[0].data.data(), data.data(), data.size()); + + uint64_t data_start = 0; + for (auto& extent : m_items[idx].extents) { + if (output_offset >= extent.data.size()) { + output_offset -= extent.data.size(); + } + else { + uint64_t write_n = std::min(extent.data.size() - output_offset, + data.size() - data_start); + assert(write_n > 0); + + memcpy(extent.data.data() + output_offset, data.data() + data_start, write_n); + + data_start += write_n; + output_offset = 0; + } + + if (data_start == data.size()) { + break; + } + } return Error::Ok; } diff --git a/libheif/box.h b/libheif/box.h index af8a403349..4dcd5c2b35 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -533,7 +533,7 @@ class Box_iloc : public FullBox uint8_t construction_method = 0); Error replace_data(heif_item_id item_ID, - uint64_t offset, + uint64_t output_offset, const std::vector& data, uint8_t construction_method); diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index db9e1c757e..525b8e6f69 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -367,15 +367,15 @@ Error Box_uncC::write(StreamWriter& writer) const } -uint64_t Box_uncC::compute_tile_data_size_bytes(uint32_t image_width, uint32_t image_height) const +uint64_t Box_uncC::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const { if (m_profile != 0) { switch (m_profile) { case fourcc("rgba"): - return 4 * image_width / m_num_tile_cols * image_height / m_num_tile_rows; + return 4 * tile_width * tile_height; case fourcc("rgb3"): - return 3 * image_width / m_num_tile_cols * image_height / m_num_tile_rows; + return 3 * tile_width * tile_height; default: assert(false); @@ -393,7 +393,7 @@ uint64_t Box_uncC::compute_tile_data_size_bytes(uint32_t image_width, uint32_t i bytes_per_pixel += comp.component_bit_depth / 8; } - return bytes_per_pixel * image_width / m_num_tile_cols * image_height / m_num_tile_rows; + return bytes_per_pixel * tile_width * tile_height; } default: assert(false); diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed_box.h index b2e6c2189a..d02eba2d4e 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -203,7 +203,7 @@ class Box_uncC : public FullBox uint32_t get_number_of_tiles() const { return m_num_tile_rows * m_num_tile_rows; } - uint64_t compute_tile_data_size_bytes(uint32_t image_width, uint32_t image_height) const; + uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const; protected: Error parse(BitstreamRange& range) override; diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 3b08f1e563..097b35001d 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -1851,32 +1851,8 @@ static Result generate_headers(const std::shared_ptr ImageItem_uncompressed::encode(const std::shared_ptr& src_image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) +Result> encode_image_tile(const std::shared_ptr& src_image) { - heif_unci_image_parameters parameters{}; - parameters.image_width = src_image->get_width(); - parameters.image_height = src_image->get_height(); - parameters.tile_width = parameters.image_width; - parameters.tile_height = parameters.image_height; - - Result genHeadersResult = generate_headers(src_image, ¶meters, &options); - if (genHeadersResult.error) { - return genHeadersResult.error; - } - - const unciHeaders& headers = *genHeadersResult; - - CodedImageData codedImageData; - if (headers.uncC) { - codedImageData.properties.push_back(headers.uncC); - } - if (headers.cmpd) { - codedImageData.properties.push_back(headers.cmpd); - } - std::vector data; if (src_image->get_colorspace() == heif_colorspace_YCbCr) @@ -1887,7 +1863,7 @@ Result ImageItem_uncompressed::encode(const std::shar uint32_t src_stride; uint32_t src_width = src_image->get_width(channel); uint32_t src_height = src_image->get_height(channel); - uint8_t* src_data = src_image->get_plane(channel, &src_stride); + const uint8_t* src_data = src_image->get_plane(channel, &src_stride); uint64_t out_size = src_width * src_height; data.resize(data.size() + out_size); for (uint32_t y = 0; y < src_height; y++) { @@ -1896,7 +1872,7 @@ Result ImageItem_uncompressed::encode(const std::shar offset += out_size; } - codedImageData.append(data.data(), data.size()); + return data; } else if (src_image->get_colorspace() == heif_colorspace_RGB) { @@ -1911,14 +1887,14 @@ Result ImageItem_uncompressed::encode(const std::shar for (heif_channel channel : channels) { uint32_t src_stride; - uint8_t* src_data = src_image->get_plane(channel, &src_stride); + const uint8_t* src_data = src_image->get_plane(channel, &src_stride); uint64_t out_size = src_image->get_height() * src_stride; data.resize(data.size() + out_size); memcpy(data.data() + offset, src_data, out_size); offset += out_size; } - codedImageData.append(data.data(), data.size()); + return data; } else if ((src_image->get_chroma_format() == heif_chroma_interleaved_RGB) || (src_image->get_chroma_format() == heif_chroma_interleaved_RGBA) || @@ -1948,14 +1924,14 @@ Result ImageItem_uncompressed::encode(const std::shar } uint32_t src_stride; - uint8_t* src_data = src_image->get_plane(heif_channel_interleaved, &src_stride); + const uint8_t* src_data = src_image->get_plane(heif_channel_interleaved, &src_stride); uint64_t out_size = src_image->get_height() * src_image->get_width() * bytes_per_pixel; data.resize(out_size); for (uint32_t y = 0; y < src_image->get_height(); y++) { memcpy(data.data() + y * src_image->get_width() * bytes_per_pixel, src_data + src_stride * y, src_image->get_width() * bytes_per_pixel); } - codedImageData.append(data.data(), data.size()); + return data; } else { @@ -1979,14 +1955,14 @@ Result ImageItem_uncompressed::encode(const std::shar for (heif_channel channel : channels) { uint32_t src_stride; - uint8_t* src_data = src_image->get_plane(channel, &src_stride); + const uint8_t* src_data = src_image->get_plane(channel, &src_stride); uint64_t out_size = src_image->get_height() * src_stride; data.resize(data.size() + out_size); memcpy(data.data() + offset, src_data, out_size); offset += out_size; } - codedImageData.append(data.data(), data.size()); + return data; } else { @@ -1995,6 +1971,48 @@ Result ImageItem_uncompressed::encode(const std::shar "Unsupported colourspace"); } +} + + +Result ImageItem_uncompressed::encode(const std::shared_ptr& src_image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ + heif_unci_image_parameters parameters{}; + parameters.image_width = src_image->get_width(); + parameters.image_height = src_image->get_height(); + parameters.tile_width = parameters.image_width; + parameters.tile_height = parameters.image_height; + + + // --- generate configuration property boxes + + Result genHeadersResult = generate_headers(src_image, ¶meters, &options); + if (genHeadersResult.error) { + return genHeadersResult.error; + } + + const unciHeaders& headers = *genHeadersResult; + + CodedImageData codedImageData; + if (headers.uncC) { + codedImageData.properties.push_back(headers.uncC); + } + if (headers.cmpd) { + codedImageData.properties.push_back(headers.cmpd); + } + + + // --- encode image + + Result> codedBitstreamResult = encode_image_tile(src_image); + if (codedBitstreamResult.error) { + return codedBitstreamResult.error; + } + + codedImageData.bitstream = *codedBitstreamResult; + return codedImageData; } @@ -2038,7 +2056,8 @@ Result> ImageItem_uncompressed::add_unci // Create empty image - uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width, parameters->image_height); + uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width / headers.uncC->get_number_of_tile_columns(), + parameters->image_height / headers.uncC->get_number_of_tile_rows()); std::cout << "tile size: " << tile_size << "\n"; @@ -2052,13 +2071,38 @@ Result> ImageItem_uncompressed::add_unci // Set Brands - //m_heif_file->set_brand(encoder->plugin->compression_format, - // out_grid_image->is_miaf_compatible()); + ctx->get_heif_file()->set_brand(heif_compression_uncompressed, unci_image->is_miaf_compatible()); return {unci_image}; } +Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image) +{ + std::shared_ptr uncC = get_file()->get_property(get_id()); + assert(uncC); + + uint32_t tile_width = image->get_width(); + uint32_t tile_height = image->get_height(); + + uint64_t tile_data_size = uncC->compute_tile_data_size_bytes(tile_width, tile_height); + + tile_x /= tile_width; + tile_y /= tile_height; + + uint32_t tile_idx = tile_y * uncC->get_number_of_tile_columns() + tile_x; + + Result> codedBitstreamResult = encode_image_tile(image); + if (codedBitstreamResult.error) { + return codedBitstreamResult.error; + } + + get_file()->replace_iloc_data(get_id(), tile_idx * tile_data_size, *codedBitstreamResult, 0); + + return Error::Ok; +} + + int ImageItem_uncompressed::get_luma_bits_per_pixel() const { int bpp = UncompressedImageCodec::get_luma_bits_per_pixel_from_configuration_unci(*get_file(), get_id()); diff --git a/libheif/codecs/uncompressed_image.h b/libheif/codecs/uncompressed_image.h index 9c967e4064..115bb72b85 100644 --- a/libheif/codecs/uncompressed_image.h +++ b/libheif/codecs/uncompressed_image.h @@ -105,6 +105,8 @@ class ImageItem_uncompressed : public ImageItem const struct heif_encoding_options* encoding_options, const std::shared_ptr& prototype); + Error add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image); + private: /* Result generate_headers(const std::shared_ptr& src_image, diff --git a/libheif/context.cc b/libheif/context.cc index 736123db17..4d5efb54a5 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -1392,9 +1392,9 @@ Result> HeifContext::add_unci_item(const #if WITH_UNCOMPRESSED_CODEC return ImageItem_uncompressed::add_unci_item(this, parameters, encoding_options, prototype); #else - return {heif_error_Unsupported_feature, - heif_suberror_Unspecified, - "support for uncompressed images has been disabled"}; + return Error{heif_error_Unsupported_feature, + heif_suberror_Unspecified, + "support for uncompressed images has been disabled"}; #endif } From 5e9e9d9720acd513337fe582320b87bd3c3289cd Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 16:47:50 +0200 Subject: [PATCH 04/12] unci: fix writing component interleaved tiles --- libheif/codecs/uncompressed_image.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 097b35001d..854a9bdebf 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -1888,9 +1888,13 @@ Result> encode_image_tile(const std::shared_ptrget_plane(channel, &src_stride); - uint64_t out_size = src_image->get_height() * src_stride; + uint64_t out_size = src_image->get_height() * src_image->get_width(); + data.resize(data.size() + out_size); - memcpy(data.data() + offset, src_data, out_size); + for (uint32_t y = 0; y < src_image->get_height(); y++) { + memcpy(data.data() + offset + y * src_image->get_width(), src_data + y * src_stride, src_image->get_width()); + } + offset += out_size; } @@ -2059,8 +2063,6 @@ Result> ImageItem_uncompressed::add_unci uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width / headers.uncC->get_number_of_tile_columns(), parameters->image_height / headers.uncC->get_number_of_tile_rows()); - std::cout << "tile size: " << tile_size << "\n"; - std::vector dummydata; dummydata.resize(tile_size); From 7cc1f27434f479b6b889a1705b9cfd60b2937ddd Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 16:59:14 +0200 Subject: [PATCH 05/12] unci: check tiling parameters --- libheif/codecs/uncompressed_image.cc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 854a9bdebf..5d665b1776 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -2026,7 +2026,16 @@ Result> ImageItem_uncompressed::add_unci const struct heif_encoding_options* encoding_options, const std::shared_ptr& prototype) { - // Create 'tild' Item + // Check input parameters + + if (parameters->image_width % parameters->tile_width != 0 || + parameters->image_height % parameters->tile_height != 0) { + return Error{heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + "ISO 23001-17 image size must be an integer multiple of the tile size."}; + } + + // Create 'unci' Item auto file = ctx->get_heif_file(); From abcd58fcd88ce19f72d05563f777b84684a13385 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 17:23:25 +0200 Subject: [PATCH 06/12] unci: writing tiles with compression --- libheif/api/libheif/heif.h | 22 ++++---- libheif/codecs/uncompressed_box.cc | 12 ++--- libheif/codecs/uncompressed_box.h | 17 ++++--- libheif/codecs/uncompressed_image.cc | 76 ++++++++++++++++++++++++---- libheif/codecs/uncompressed_image.h | 2 + 5 files changed, 97 insertions(+), 32 deletions(-) diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index f4f1a03c64..5d7920e922 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -520,6 +520,16 @@ enum heif_channel_datatype heif_channel_datatype_complex_number = 4 }; +enum heif_metadata_compression +{ + 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 +}; + // ========================= library initialization ====================== struct heif_init_params @@ -2475,6 +2485,8 @@ struct heif_unci_image_parameters { uint32_t tile_width; uint32_t tile_height; + enum heif_metadata_compression compression; // TODO + // TODO: interleave type, padding }; @@ -2518,16 +2530,6 @@ struct heif_error heif_context_encode_thumbnail(struct heif_context*, int bbox_size, struct heif_image_handle** out_thumb_image_handle); -enum heif_metadata_compression -{ - 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'. LIBHEIF_API struct heif_error heif_context_assign_thumbnail(struct heif_context*, diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index 525b8e6f69..c17b26d660 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -410,8 +410,8 @@ Error Box_cmpC::parse(BitstreamRange& range) return unsupported_version_error("cmpC"); } - compression_type = range.read32(); - compressed_unit_type = range.read8(); + m_compression_type = range.read32(); + m_compressed_unit_type = range.read8(); return range.get_error(); } @@ -420,8 +420,8 @@ 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 << "compressed_entity_type: " << (int)compressed_unit_type << "\n"; + sstr << indent << "m_compression_type: " << to_fourcc(m_compression_type) << "\n"; + sstr << indent << "compressed_entity_type: " << (int)m_compressed_unit_type << "\n"; return sstr.str(); } @@ -429,8 +429,8 @@ Error Box_cmpC::write(StreamWriter& writer) const { size_t box_start = reserve_box_header_space(writer); - writer.write32(compression_type); - writer.write8(compressed_unit_type); + writer.write32(m_compression_type); + writer.write8(m_compressed_unit_type); prepend_header(writer, box_start); diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed_box.h index d02eba2d4e..f870d779a2 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -250,16 +250,21 @@ class Box_cmpC : public FullBox std::string dump(Indent&) const override; - uint32_t get_compression_type() const { return compression_type; } - heif_cmpC_compressed_unit_type get_compressed_unit_type() const { return (heif_cmpC_compressed_unit_type)compressed_unit_type; } + uint32_t get_compression_type() const { return m_compression_type; } + + heif_cmpC_compressed_unit_type get_compressed_unit_type() const { return (heif_cmpC_compressed_unit_type) m_compressed_unit_type; } + + void set_compression_type(uint32_t type) { m_compression_type = type; } + + void set_compressed_unit_type(heif_cmpC_compressed_unit_type type) { m_compressed_unit_type = type; } Error write(StreamWriter& writer) const override; protected: Error parse(BitstreamRange& range) override; - uint32_t compression_type; - uint8_t compressed_unit_type; + uint32_t m_compression_type; + uint8_t m_compressed_unit_type; }; /** @@ -279,8 +284,8 @@ class Box_icef : public FullBox struct CompressedUnitInfo { - uint64_t unit_offset; - uint64_t unit_size; + uint64_t unit_offset = 0; + uint64_t unit_size = 0; }; const std::vector& get_units() const { return m_unit_infos; } diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 5d665b1776..b42cb5929c 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -2067,19 +2067,39 @@ Result> ImageItem_uncompressed::add_unci static_cast(parameters->image_width), static_cast(parameters->image_height)); - // Create empty image + if (parameters->compression != heif_metadata_compression_off) { + auto icef = std::make_shared(); + auto cmpC = std::make_shared(); + cmpC->set_compressed_unit_type(heif_cmpC_compressed_unit_type_image_tile); - uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width / headers.uncC->get_number_of_tile_columns(), - parameters->image_height / headers.uncC->get_number_of_tile_rows()); - - std::vector dummydata; - dummydata.resize(tile_size); + if (parameters->compression == heif_metadata_compression_deflate) { + cmpC->set_compression_type(fourcc("defl")); + } + else if (parameters->compression == heif_metadata_compression_zlib) { + cmpC->set_compression_type(fourcc("zlib")); + } + else { + assert(false); + } - for (uint64_t i = 0; i < tile_size; i++) { - const int construction_method = 0; // 0=mdat 1=idat - file->append_iloc_data(unci_id, dummydata, construction_method); + file->add_property(unci_id, cmpC, true); + file->add_property(unci_id, icef, true); } + // Create empty image. If we use compression, we append the data piece by piece. + + if (parameters->compression == heif_metadata_compression_off) { + uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width / headers.uncC->get_number_of_tile_columns(), + parameters->image_height / headers.uncC->get_number_of_tile_rows()); + + std::vector dummydata; + dummydata.resize(tile_size); + + for (uint64_t i = 0; i < tile_size; i++) { + const int construction_method = 0; // 0=mdat 1=idat + file->append_iloc_data(unci_id, dummydata, construction_method); + } + } // Set Brands ctx->get_heif_file()->set_brand(heif_compression_uncompressed, unci_image->is_miaf_compatible()); @@ -2108,7 +2128,43 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c return codedBitstreamResult.error; } - get_file()->replace_iloc_data(get_id(), tile_idx * tile_data_size, *codedBitstreamResult, 0); + std::shared_ptr cmpC = get_file()->get_property(get_id()); + std::shared_ptr icef = get_file()->get_property(get_id()); + + if (!icef || !cmpC) { + assert(!icef); + assert(!cmpC); + + // uncompressed + + get_file()->replace_iloc_data(get_id(), tile_idx * tile_data_size, *codedBitstreamResult, 0); + } + else { + std::vector compressed_data; + const std::vector& raw_data = codedBitstreamResult.value; + + uint32_t compr = cmpC->get_compression_type(); + switch (compr) { + case fourcc("defl"): + compressed_data = compress_deflate(raw_data.data(), raw_data.size()); + break; + case fourcc("zlib"): + compressed_data = compress_zlib(raw_data.data(), raw_data.size()); + break; + default: + assert(false); + break; + } + + get_file()->append_iloc_data(get_id(), compressed_data, 0); + + Box_icef::CompressedUnitInfo unit_info; + unit_info.unit_offset = m_next_tile_write_pos; + unit_info.unit_size = compressed_data.size(); + icef->add_component(unit_info); + + m_next_tile_write_pos += compressed_data.size(); + } return Error::Ok; } diff --git a/libheif/codecs/uncompressed_image.h b/libheif/codecs/uncompressed_image.h index 115bb72b85..8da7dc9d88 100644 --- a/libheif/codecs/uncompressed_image.h +++ b/libheif/codecs/uncompressed_image.h @@ -113,6 +113,8 @@ class ImageItem_uncompressed : public ImageItem const heif_unci_image_parameters* parameters, const struct heif_encoding_options* options); */ + + uint64_t m_next_tile_write_pos = 0; }; #endif //LIBHEIF_UNCOMPRESSED_IMAGE_H From 7d65ebf1a8a06e6399078ffc83f41d3d56f26fee Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 17:38:35 +0200 Subject: [PATCH 07/12] unci: fix search/replace typo --- 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 c17b26d660..4448308f0a 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -420,7 +420,7 @@ std::string Box_cmpC::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); - sstr << indent << "m_compression_type: " << to_fourcc(m_compression_type) << "\n"; + sstr << indent << "compression_type: " << to_fourcc(m_compression_type) << "\n"; sstr << indent << "compressed_entity_type: " << (int)m_compressed_unit_type << "\n"; return sstr.str(); } From 8344ef0cdecdcb6a250f4f997639cc930d6a6b7d Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 17:50:04 +0200 Subject: [PATCH 08/12] add brotli compression method --- libheif/compression.h | 1 + libheif/compression_brotli.cc | 42 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/libheif/compression.h b/libheif/compression.h index 16490241ec..085a71cc6c 100644 --- a/libheif/compression.h +++ b/libheif/compression.h @@ -91,6 +91,7 @@ Error decompress_deflate(const std::vector& compressed_input, std::vect */ Error decompress_brotli(const std::vector& compressed_input, std::vector* output); +std::vector compress_brotli(const uint8_t* input, size_t size); #endif #endif //LIBHEIF_COMPRESSION_H diff --git a/libheif/compression_brotli.cc b/libheif/compression_brotli.cc index 198e2562a5..c3a7f7bb2d 100644 --- a/libheif/compression_brotli.cc +++ b/libheif/compression_brotli.cc @@ -24,6 +24,7 @@ const size_t BUF_SIZE = (1 << 18); #include +#include #include #include #include @@ -31,6 +32,7 @@ const size_t BUF_SIZE = (1 << 18); #include "error.h" + Error decompress_brotli(const std::vector &compressed_input, std::vector *output) { BrotliDecoderResult result = BROTLI_DECODER_RESULT_ERROR; @@ -82,4 +84,44 @@ Error decompress_brotli(const std::vector &compressed_input, std::vecto return Error::Ok; } + +std::vector compress_brotli(const uint8_t* input, size_t size) +{ + std::unique_ptr state(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr), BrotliEncoderDestroyInstance); + + size_t available_in = size; + const uint8_t* next_in = input; + + std::vector tmp(BUF_SIZE); + size_t available_out = BUF_SIZE; + uint8_t* next_out = tmp.data(); + + std::vector result; + + for (;;) { + BROTLI_BOOL success = BrotliEncoderCompressStream(state.get(), + BROTLI_OPERATION_FINISH, + &available_in, + &next_in, + &available_out, + &next_out, + nullptr); + if (!success) { + return {}; + } + + if (next_out != tmp.data()) { + result.insert(result.end(), tmp.data(), tmp.data() + std::distance(tmp.data(), next_out)); + available_out = BUF_SIZE; + next_out = tmp.data(); + } + + if (BrotliEncoderIsFinished(state.get())) { + break; + } + } + + return result; +} + #endif From 35976b0b33781a4b2596d2c8d72f728d55ab6487 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 17:50:30 +0200 Subject: [PATCH 09/12] unci: save tiles with brotli compression --- libheif/codecs/uncompressed_image.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index b42cb5929c..e82097667c 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -2078,6 +2078,9 @@ Result> ImageItem_uncompressed::add_unci else if (parameters->compression == heif_metadata_compression_zlib) { cmpC->set_compression_type(fourcc("zlib")); } + else if (parameters->compression == heif_metadata_compression_brotli) { + cmpC->set_compression_type(fourcc("brot")); + } else { assert(false); } @@ -2151,6 +2154,9 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c case fourcc("zlib"): compressed_data = compress_zlib(raw_data.data(), raw_data.size()); break; + case fourcc("brot"): + compressed_data = compress_brotli(raw_data.data(), raw_data.size()); + break; default: assert(false); break; From 9f094550bb86dc628f824e16cfb0bea5476f3dbe Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 17:55:08 +0200 Subject: [PATCH 10/12] only compile brotli support if available --- libheif/codecs/uncompressed_image.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index e82097667c..14e789eb49 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -2078,9 +2078,11 @@ Result> ImageItem_uncompressed::add_unci else if (parameters->compression == heif_metadata_compression_zlib) { cmpC->set_compression_type(fourcc("zlib")); } +#if HAVE_BROTLI else if (parameters->compression == heif_metadata_compression_brotli) { cmpC->set_compression_type(fourcc("brot")); } +#endif else { assert(false); } @@ -2154,9 +2156,11 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c case fourcc("zlib"): compressed_data = compress_zlib(raw_data.data(), raw_data.size()); break; +#if HAVE_BROTLI case fourcc("brot"): compressed_data = compress_brotli(raw_data.data(), raw_data.size()); break; +#endif default: assert(false); break; From 6bcdc780cd242e8edbcd15c08d5f250ce53d9939 Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 19:33:23 +0200 Subject: [PATCH 11/12] unci: support writing tiles in arbitrary order and return tiling info --- libheif/api/libheif/heif.cc | 7 +++++++ libheif/codecs/uncompressed_box.h | 9 ++++++++ libheif/codecs/uncompressed_image.cc | 31 ++++++++++++++++++++++------ libheif/codecs/uncompressed_image.h | 2 ++ libheif/file.cc | 10 +++++++++ libheif/file.h | 4 +++- 6 files changed, 56 insertions(+), 7 deletions(-) diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index c749362ec8..8eb0e51327 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -877,6 +877,13 @@ struct heif_image_tiling heif_image_handle_get_image_tiling(const struct heif_im return tildItem->get_heif_image_tiling(); } +#if WITH_UNCOMPRESSED_CODEC + std::shared_ptr unciItem = std::dynamic_pointer_cast(handle->image); + if (unciItem) { + return unciItem->get_heif_image_tiling(); + } +#endif + return tiling; } diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed_box.h index f870d779a2..f1a81585fd 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -295,6 +295,15 @@ class Box_icef : public FullBox m_unit_infos.push_back(unit_info); } + void set_component(uint32_t tile_idx, const CompressedUnitInfo& unit_info) + { + if (tile_idx >= m_unit_infos.size()) { + m_unit_infos.resize(tile_idx+1); + } + + m_unit_infos[tile_idx] = unit_info; + } + std::string dump(Indent&) const override; Error write(StreamWriter& writer) const override; diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed_image.cc index 14e789eb49..b219511bdf 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -1426,7 +1426,7 @@ Error UncompressedImageCodec::decode_uncompressed_image_tile(const HeifContext* Error result = decoder->decode_tile(context, ID, img, 0, 0, ispe->get_width(), ispe->get_height(), - tile_x0 / tile_width, tile_y0 / tile_height); + tile_x0, tile_y0); delete decoder; return result; } @@ -2088,7 +2088,7 @@ Result> ImageItem_uncompressed::add_unci } file->add_property(unci_id, cmpC, true); - file->add_property(unci_id, icef, true); + file->add_property_without_deduplication(unci_id, icef, true); // icef is empty. A normal add_property() would lead to a wrong deduplication. } // Create empty image. If we use compression, we append the data piece by piece. @@ -2123,9 +2123,6 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c uint64_t tile_data_size = uncC->compute_tile_data_size_bytes(tile_width, tile_height); - tile_x /= tile_width; - tile_y /= tile_height; - uint32_t tile_idx = tile_y * uncC->get_number_of_tile_columns() + tile_x; Result> codedBitstreamResult = encode_image_tile(image); @@ -2171,7 +2168,7 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c Box_icef::CompressedUnitInfo unit_info; unit_info.unit_offset = m_next_tile_write_pos; unit_info.unit_size = compressed_data.size(); - icef->add_component(unit_info); + icef->set_component(tile_idx, unit_info); m_next_tile_write_pos += compressed_data.size(); } @@ -2206,3 +2203,25 @@ void ImageItem_uncompressed::get_tile_size(uint32_t& w, uint32_t& h) const w = ispe->get_width() / uncC->get_number_of_tile_columns(); h = ispe->get_height() / uncC->get_number_of_tile_rows(); } + + +heif_image_tiling ImageItem_uncompressed::get_heif_image_tiling() const +{ + heif_image_tiling tiling{}; + + auto ispe = get_file()->get_property(get_id()); + auto uncC = get_file()->get_property(get_id()); + assert(ispe && uncC); + + tiling.num_columns = uncC->get_number_of_tile_columns(); + tiling.num_rows = uncC->get_number_of_tile_rows(); + + tiling.tile_width = ispe->get_width() / tiling.num_columns; + tiling.tile_height = ispe->get_height() / tiling.num_rows; + + tiling.image_width = ispe->get_width(); + tiling.image_height = ispe->get_height(); + tiling.number_of_extra_dimensions = 0; + + return tiling; +} diff --git a/libheif/codecs/uncompressed_image.h b/libheif/codecs/uncompressed_image.h index 8da7dc9d88..78e87727b7 100644 --- a/libheif/codecs/uncompressed_image.h +++ b/libheif/codecs/uncompressed_image.h @@ -93,6 +93,8 @@ class ImageItem_uncompressed : public ImageItem Result> decode_compressed_image(const struct heif_decoding_options& options, bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const override; + heif_image_tiling get_heif_image_tiling() const; + // --- encoding Result encode(const std::shared_ptr& image, diff --git a/libheif/file.cc b/libheif/file.cc index 624151e029..6ce7f1ca9c 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -901,6 +901,16 @@ heif_property_id HeifFile::add_property(heif_item_id id, const std::shared_ptr& property, bool essential) +{ + int index = m_ipco_box->append_child_box(property); + + m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{essential, uint16_t(index + 1)}); + + return index + 1; +} + + void HeifFile::add_orientation_properties(heif_item_id id, heif_orientation orientation) { // Note: ISO/IEC 23000-22:2019(E) (MIAF) 7.3.6.7 requires the following order: diff --git a/libheif/file.h b/libheif/file.h index bf463b9ac8..143cbc6105 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -161,9 +161,11 @@ class HeifFile void add_pixi_property(heif_item_id id, uint8_t c1, uint8_t c2 = 0, uint8_t c3 = 0); - // TODO: can be remove the 'essential' parameter and take this from the box? Or is that depending on the context? + // TODO: can we remove the 'essential' parameter and take this from the box? Or is that depending on the context? heif_property_id add_property(heif_item_id id, const std::shared_ptr& property, bool essential); + heif_property_id add_property_without_deduplication(heif_item_id id, const std::shared_ptr& property, bool essential); + Result add_infe(const char* item_type, const uint8_t* data, size_t size); Result add_infe_mime(const char* content_type, heif_metadata_compression content_encoding, const uint8_t* data, size_t size); From dcd23997c854de2538c74ab0db54e26d3e1a3d8e Mon Sep 17 00:00:00 2001 From: Dirk Farin Date: Mon, 23 Sep 2024 19:40:21 +0200 Subject: [PATCH 12/12] move unci implementation files into its own directory --- libheif/CMakeLists.txt | 8 ++++---- libheif/api/libheif/heif.cc | 2 +- libheif/box.cc | 2 +- libheif/codecs/image_item.cc | 2 +- .../{uncompressed_box.cc => uncompressed/unc_boxes.cc} | 4 ++-- .../{uncompressed_box.h => uncompressed/unc_boxes.h} | 8 ++++---- .../{uncompressed_image.cc => uncompressed/unc_image.cc} | 8 ++++---- .../{uncompressed_image.h => uncompressed/unc_image.h} | 6 +++--- .../codecs/{uncompressed.h => uncompressed/unc_types.h} | 6 +++--- libheif/context.cc | 2 +- libheif/file.cc | 4 ++-- libheif/file.h | 2 +- tests/uncompressed_box.cc | 4 ++-- 13 files changed, 29 insertions(+), 29 deletions(-) rename libheif/codecs/{uncompressed_box.cc => uncompressed/unc_boxes.cc} (99%) rename libheif/codecs/{uncompressed_box.h => uncompressed/unc_boxes.h} (98%) rename libheif/codecs/{uncompressed_image.cc => uncompressed/unc_image.cc} (99%) rename libheif/codecs/{uncompressed_image.h => uncompressed/unc_image.h} (97%) rename libheif/codecs/{uncompressed.h => uncompressed/unc_types.h} (98%) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index f426f830c5..88dc899600 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -170,10 +170,10 @@ endif () if (WITH_UNCOMPRESSED_CODEC) target_compile_definitions(heif PUBLIC WITH_UNCOMPRESSED_CODEC=1) target_sources(heif PRIVATE - codecs/uncompressed_box.h - codecs/uncompressed_box.cc - codecs/uncompressed_image.h - codecs/uncompressed_image.cc) + codecs/uncompressed/unc_boxes.h + codecs/uncompressed/unc_boxes.cc + codecs/uncompressed/unc_image.h + codecs/uncompressed/unc_image.cc) endif () write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion) diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index 8eb0e51327..9b79ac25e7 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -38,7 +38,7 @@ #include #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed_image.h" +#include "codecs/uncompressed/unc_image.h" #endif #if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_STANDALONE_WASM__) diff --git a/libheif/box.cc b/libheif/box.cc index e5c228bda4..b409e0c0bb 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -43,7 +43,7 @@ #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed_box.h" +#include "codecs/uncompressed/unc_boxes.h" #endif #ifndef M_PI diff --git a/libheif/codecs/image_item.cc b/libheif/codecs/image_item.cc index 9fa5b05220..14b8d1bd94 100644 --- a/libheif/codecs/image_item.cc +++ b/libheif/codecs/image_item.cc @@ -39,7 +39,7 @@ #include #if WITH_UNCOMPRESSED_CODEC -#include +#include "codecs/uncompressed/unc_image.h" #endif diff --git a/libheif/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed/unc_boxes.cc similarity index 99% rename from libheif/codecs/uncompressed_box.cc rename to libheif/codecs/uncompressed/unc_boxes.cc index 4448308f0a..bd31c0172c 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed/unc_boxes.cc @@ -29,8 +29,8 @@ #include "libheif/heif.h" #include "security_limits.h" -#include "uncompressed.h" -#include "uncompressed_box.h" +#include "unc_types.h" +#include "unc_boxes.h" /** diff --git a/libheif/codecs/uncompressed_box.h b/libheif/codecs/uncompressed/unc_boxes.h similarity index 98% rename from libheif/codecs/uncompressed_box.h rename to libheif/codecs/uncompressed/unc_boxes.h index f1a81585fd..4bb5a2cceb 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed/unc_boxes.h @@ -19,12 +19,12 @@ */ -#ifndef LIBHEIF_UNCOMPRESSED_BOX_H -#define LIBHEIF_UNCOMPRESSED_BOX_H +#ifndef LIBHEIF_UNC_BOXES_H +#define LIBHEIF_UNC_BOXES_H #include "box.h" #include "bitstream.h" -#include "uncompressed.h" +#include "unc_types.h" #include #include @@ -318,4 +318,4 @@ class Box_icef : public FullBox const uint8_t get_required_size_code(uint64_t size) const; }; -#endif //LIBHEIF_UNCOMPRESSED_BOX_H +#endif //LIBHEIF_UNC_BOXES_H diff --git a/libheif/codecs/uncompressed_image.cc b/libheif/codecs/uncompressed/unc_image.cc similarity index 99% rename from libheif/codecs/uncompressed_image.cc rename to libheif/codecs/uncompressed/unc_image.cc index b219511bdf..ff62e3f801 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed/unc_image.cc @@ -31,10 +31,10 @@ #include "compression.h" #include "error.h" #include "libheif/heif.h" -#include "uncompressed.h" -#include "uncompressed_box.h" -#include "uncompressed_image.h" -#include +#include "unc_types.h" +#include "unc_boxes.h" +#include "unc_image.h" +#include "codecs/image_item.h" static bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr& uncC) { diff --git a/libheif/codecs/uncompressed_image.h b/libheif/codecs/uncompressed/unc_image.h similarity index 97% rename from libheif/codecs/uncompressed_image.h rename to libheif/codecs/uncompressed/unc_image.h index 78e87727b7..1e91033a8e 100644 --- a/libheif/codecs/uncompressed_image.h +++ b/libheif/codecs/uncompressed/unc_image.h @@ -19,8 +19,8 @@ */ -#ifndef LIBHEIF_UNCOMPRESSED_IMAGE_H -#define LIBHEIF_UNCOMPRESSED_IMAGE_H +#ifndef LIBHEIF_UNC_IMAGE_H +#define LIBHEIF_UNC_IMAGE_H #include "pixelimage.h" #include "file.h" @@ -119,4 +119,4 @@ class ImageItem_uncompressed : public ImageItem uint64_t m_next_tile_write_pos = 0; }; -#endif //LIBHEIF_UNCOMPRESSED_IMAGE_H +#endif //LIBHEIF_UNC_IMAGE_H diff --git a/libheif/codecs/uncompressed.h b/libheif/codecs/uncompressed/unc_types.h similarity index 98% rename from libheif/codecs/uncompressed.h rename to libheif/codecs/uncompressed/unc_types.h index d92bfb0a58..1bcedf7316 100644 --- a/libheif/codecs/uncompressed.h +++ b/libheif/codecs/uncompressed/unc_types.h @@ -19,8 +19,8 @@ */ -#ifndef LIBHEIF_UNCOMPRESSED_H -#define LIBHEIF_UNCOMPRESSED_H +#ifndef LIBHEIF_UNC_TYPES_H +#define LIBHEIF_UNC_TYPES_H #include @@ -282,4 +282,4 @@ enum heif_uncompressed_interleave_mode -#endif //LIBHEIF_UNCOMPRESSED_H +#endif //LIBHEIF_UNC_TYPES_H diff --git a/libheif/context.cc b/libheif/context.cc index 4d5efb54a5..a32ba7ae03 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -55,7 +55,7 @@ #include "codecs/tild.h" #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed_image.h" +#include "codecs/uncompressed/unc_image.h" #endif diff --git a/libheif/file.cc b/libheif/file.cc index 6ce7f1ca9c..9dafded950 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -26,7 +26,7 @@ #include "codecs/jpeg2000.h" #include "codecs/jpeg.h" #include "codecs/vvc.h" -#include "codecs/uncompressed_box.h" +#include "codecs/uncompressed/unc_boxes.h" #include #include @@ -48,7 +48,7 @@ #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed_image.h" +#include "codecs/uncompressed/unc_image.h" #endif // TODO: make this a decoder option diff --git a/libheif/file.h b/libheif/file.h index 143cbc6105..3a6d189667 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -26,7 +26,7 @@ #include "codecs/avif.h" #include "codecs/hevc.h" #include "codecs/vvc.h" -#include "codecs/uncompressed_box.h" +#include "codecs/uncompressed/unc_boxes.h" #include "file_layout.h" #include diff --git a/tests/uncompressed_box.cc b/tests/uncompressed_box.cc index 967d480350..76c0f2b535 100644 --- a/tests/uncompressed_box.cc +++ b/tests/uncompressed_box.cc @@ -27,8 +27,8 @@ #include "catch.hpp" #include "box.h" #include "libheif/heif.h" -#include "codecs/uncompressed.h" -#include "codecs/uncompressed_box.h" +#include "codecs/uncompressed/unc_types.h" +#include "codecs/uncompressed/unc_boxes.h" #include #include