From 889d0b524b743bf019a07d57422a92e1fe2bc133 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Fri, 28 Jun 2024 21:29:11 +1000 Subject: [PATCH] uncompressed: wip on generic compression --- CMakeLists.txt | 11 ++- README.md | 8 +- cmake/modules/FindBrotli.cmake | 26 ++++++ go/heif/heif.go | 6 ++ libheif/CMakeLists.txt | 17 +++- libheif/api/libheif/heif.h | 7 ++ libheif/api/libheif/heif_emscripten.h | 3 + libheif/codecs/uncompressed_box.cc | 85 +++++++++++++++++++ libheif/codecs/uncompressed_box.h | 79 ++++++++++++++++- libheif/codecs/uncompressed_image.cc | 76 +++++++++++++++-- .../{metadata_compression.h => compression.h} | 21 +++-- libheif/compression_brotli.cc | 84 ++++++++++++++++++ ...ata_compression.cc => compression_zlib.cc} | 35 +++++--- libheif/context.cc | 2 +- libheif/error.cc | 6 ++ libheif/file.cc | 13 ++- tests/uncompressed_encode.cc | 3 + 17 files changed, 446 insertions(+), 36 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%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3676450446e..b93de7e9b26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -379,9 +379,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 e9064f79319..79369f6b590 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 @@ -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 diff --git a/cmake/modules/FindBrotli.cmake b/cmake/modules/FindBrotli.cmake new file mode 100644 index 00000000000..d0ea34c892c --- /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 71e531e967f..b71b2eadf45 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 e606666153b..6d9e76a246f 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 a560c3a77b6..37984560eba 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -241,6 +241,8 @@ enum heif_suberror_code heif_suberror_No_vvcC_box = 141, + // Decompressing generic compression or header compression data failed (e.g. bitstream corruption) + heif_suberror_Decompression_invalid_data = 150, // --- Memory_allocation_error --- @@ -249,6 +251,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 +302,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 cb219f24bc2..f9ec62dc070 100644 --- a/libheif/api/libheif/heif_emscripten.h +++ b/libheif/api/libheif/heif_emscripten.h @@ -303,6 +303,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) @@ -358,6 +360,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/codecs/uncompressed_box.cc b/libheif/codecs/uncompressed_box.cc index b6afa359c00..f75b2cee260 100644 --- a/libheif/codecs/uncompressed_box.cc +++ b/libheif/codecs/uncompressed_box.cc @@ -312,6 +312,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); @@ -350,3 +351,87 @@ 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(); + can_decompress_contiguous_ranges = ((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 << "can_compress_contiguous_ranges: " << can_decompress_contiguous_ranges << "\n"; + sstr << indent << "compressed_range_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 = can_decompress_contiguous_ranges ? 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) { + // TODO + } 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) { + // TODO + } 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 98ae3da60b2..2be5ddf8c4a 100644 --- a/libheif/codecs/uncompressed_box.h +++ b/libheif/codecs/uncompressed_box.h @@ -76,7 +76,22 @@ class Box_cmpd : public Box class Box_uncC : public FullBox { public: - Box_uncC() { + Box_uncC() : + m_profile(0), + m_sampling_type(sampling_mode_no_subsampling), + m_interleave_type(interleave_mode_component), + 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) + { set_short_type(fourcc("uncC")); } @@ -220,4 +235,66 @@ 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; } + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + + uint32_t compression_type; + bool can_decompress_contiguous_ranges; + 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 c40b659ec9a..e02e0c8170f 100644 --- a/libheif/codecs/uncompressed_image.cc +++ b/libheif/codecs/uncompressed_image.cc @@ -28,6 +28,7 @@ #include #include "common_utils.h" +#include "compression.h" #include "error.h" #include "libheif/heif.h" #include "uncompressed.h" @@ -855,9 +856,9 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptr& img, uint32_t maximum_image_width_limit, uint32_t maximum_image_height_limit, - 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"}; @@ -877,6 +878,9 @@ Error UncompressedImageCodec::decode_uncompressed_image(const 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) { @@ -904,6 +908,17 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptr(prop); + if (maybe_cmpC) { + cmpC = maybe_cmpC; + } + + auto maybe_icbr = std::dynamic_pointer_cast(prop); + if (maybe_icbr) { + icbr = maybe_icbr; + } + } @@ -918,6 +933,10 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptrget_version()); if (!cmpd && (uncC->get_version() !=1)) { return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, @@ -957,9 +976,56 @@ Error UncompressedImageCodec::decode_uncompressed_image(const std::shared_ptrdecode(uncompressed_data, img); - delete decoder; - return result; + if (!cmpC) { + Error result = decoder->decode(source_data, img); + delete decoder; + return result; + } else { +#if WITH_ZLIB_COMPRESSION + if (cmpC->get_compression_type() == fourcc("zlib")) { + std::vector inflated_data; + Error error = inflate_zlib(source_data, &inflated_data); + if (error) { + delete decoder; + return error; + } + error = decoder->decode(inflated_data, img); + delete decoder; + return error; + } else if (cmpC->get_compression_type() == fourcc("defl")) { + std::vector inflated_data; + Error error = inflate_deflate(source_data, &inflated_data); + if (error) { + delete decoder; + return error; + } + error = decoder->decode(inflated_data, img); + delete decoder; + return error; + } else +#endif +#if HAVE_BROTLI + if (cmpC->get_compression_type() == fourcc("brot")) { + std::vector inflated_data; + Error error = inflate_brotli(source_data, &inflated_data); + if (error) { + delete decoder; + return error; + } + error = decoder->decode(inflated_data, img); + delete decoder; + return error; + } else +#endif + { + std::stringstream sstr; + sstr << "unsupported uncC compression type: " << to_fourcc(cmpC->get_compression_type()) << std::endl; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + + } + } } 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 3780620b925..bff089ff527 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 00000000000..69de5bf7673 --- /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 b738d1f62db..cce70cf6e31 100644 --- a/libheif/metadata_compression.cc +++ b/libheif/compression_zlib.cc @@ -19,10 +19,11 @@ */ -#include "metadata_compression.h" +#include "compression.h" -#if WITH_DEFLATE_HEADER_COMPRESSION +#if WITH_ZLIB_COMPRESSION + #include #include @@ -78,9 +79,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 +102,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 +119,29 @@ 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 ef6edad258d..6c5cbd17d8f 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 2735c129817..71f0e245bcc 100644 --- a/libheif/error.cc +++ b/libheif/error.cc @@ -168,12 +168,16 @@ 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"; // --- 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 +214,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 ea66d5279a6..98a40047490 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -22,7 +22,7 @@ #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" @@ -966,7 +966,13 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* 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); + 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, @@ -1075,8 +1081,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/tests/uncompressed_encode.cc b/tests/uncompressed_encode.cc index b3326dc4d49..c7d2e6e99b1 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));