diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 2b9ca52f79..55b9460412 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -188,6 +188,8 @@ if (WITH_UNCOMPRESSED_CODEC) codecs/uncompressed/unc_boxes.cc codecs/uncompressed/unc_image.h codecs/uncompressed/unc_image.cc + codecs/uncompressed/unc_codec.h + codecs/uncompressed/unc_codec.cc codecs/uncompressed/unc_dec.h codecs/uncompressed/unc_dec.cc) endif () diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc new file mode 100644 index 0000000000..48f25a355a --- /dev/null +++ b/libheif/codecs/uncompressed/unc_codec.cc @@ -0,0 +1,1668 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include "common_utils.h" +#include "context.h" +#include "compression.h" +#include "error.h" +#include "libheif/heif.h" +#include "unc_types.h" +#include "unc_boxes.h" +#include "unc_codec.h" +#include "unc_dec.h" + + +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(const std::shared_ptr& uncC, + const 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; + if ((component_type > 7) && (component_type != component_type_padded)) { + std::stringstream sstr; + sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + if ((component.component_bit_depth > 8) && (component.component_bit_depth != 16)) { + std::stringstream sstr; + sstr << "Uncompressed image with component_bit_depth " << ((int) component.component_bit_depth) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + if (component.component_format != component_format_unsigned) { + std::stringstream sstr; + sstr << "Uncompressed image with component_format " << ((int) component.component_format) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + if (component.component_align_size > 2) { + std::stringstream sstr; + sstr << "Uncompressed image with component_align_size " << ((int) component.component_align_size) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + } + if ((uncC->get_sampling_type() != sampling_mode_no_subsampling) + && (uncC->get_sampling_type() != sampling_mode_422) + && (uncC->get_sampling_type() != sampling_mode_420) + ) { + std::stringstream sstr; + sstr << "Uncompressed sampling_type of " << ((int) uncC->get_sampling_type()) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + if ((uncC->get_interleave_type() != interleave_mode_component) + && (uncC->get_interleave_type() != interleave_mode_pixel) + && (uncC->get_interleave_type() != interleave_mode_mixed) + && (uncC->get_interleave_type() != interleave_mode_row) + && (uncC->get_interleave_type() != interleave_mode_tile_component) + ) { + std::stringstream sstr; + sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.3 + if (uncC->get_sampling_type() == sampling_mode_422) { + // We check Y Cb and Cr appear in the chroma test + // TODO: error for tile width not multiple of 2 + if ((uncC->get_interleave_type() != interleave_mode_component) + && (uncC->get_interleave_type() != interleave_mode_mixed) + && (uncC->get_interleave_type() != interleave_mode_multi_y)) + { + std::stringstream sstr; + sstr << "YCbCr 4:2:2 subsampling is only valid with component, mixed or multi-Y interleave mode (ISO/IEC 23001-17 5.2.1.5.3)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) { + if (uncC->get_row_align_size() % 2 != 0) { + std::stringstream sstr; + sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + } + if (uncC->get_tile_align_size() != 0) { + if (uncC->get_tile_align_size() % 2 != 0) { + std::stringstream sstr; + sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + } + } + // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.4 + if (uncC->get_sampling_type() == sampling_mode_422) { + // We check Y Cb and Cr appear in the chroma test + // TODO: error for tile width not multiple of 2 + if ((uncC->get_interleave_type() != interleave_mode_component) + && (uncC->get_interleave_type() != interleave_mode_mixed)) + { + std::stringstream sstr; + sstr << "YCbCr 4:2:0 subsampling is only valid with component or mixed interleave mode (ISO/IEC 23001-17 5.2.1.5.4)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) { + if (uncC->get_row_align_size() % 2 != 0) { + std::stringstream sstr; + sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.4)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + } + if (uncC->get_tile_align_size() != 0) { + if (uncC->get_tile_align_size() % 4 != 0) { + std::stringstream sstr; + sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 4 (ISO/IEC 23001-17 5.2.1.5.3)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + } + } + if ((uncC->get_interleave_type() == interleave_mode_mixed) && (uncC->get_sampling_type() == sampling_mode_no_subsampling)) + { + std::stringstream sstr; + sstr << "Interleave interleave mode is not valid with subsampling mode (ISO/IEC 23001-17 5.2.1.6.4)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + if ((uncC->get_interleave_type() == interleave_mode_multi_y) + && ((uncC->get_sampling_type() != sampling_mode_422) && (uncC->get_sampling_type() != sampling_mode_411))) + { + std::stringstream sstr; + sstr << "Multi-Y interleave mode is only valid with 4:2:2 and 4:1:1 subsampling modes (ISO/IEC 23001-17 5.2.1.6.7)."; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + // TODO: throw error if mixed and Cb and Cr are not adjacent. + + if (uncC->get_block_size() != 0) { + std::stringstream sstr; + sstr << "Uncompressed block_size of " << ((int) uncC->get_block_size()) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + if (uncC->is_components_little_endian()) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Uncompressed components_little_endian == 1 is not implemented yet"); + } + if (uncC->is_block_pad_lsb()) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Uncompressed block_pad_lsb == 1 is not implemented yet"); + } + if (uncC->is_block_little_endian()) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Uncompressed block_little_endian == 1 is not implemented yet"); + } + if (uncC->is_block_reversed()) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Uncompressed block_reversed == 1 is not implemented yet"); + } + if ((uncC->get_pixel_size() != 0) && ((uncC->get_interleave_type() != interleave_mode_pixel) && (uncC->get_interleave_type() != interleave_mode_multi_y))) { + std::stringstream sstr; + sstr << "Uncompressed pixel_size of " << ((int) uncC->get_pixel_size()) << " is only valid with interleave_type 1 or 5 (ISO/IEC 23001-17 5.2.1.7)"; + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + sstr.str()); + } + return Error::Ok; +} + + +Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr& uncC, + const std::shared_ptr& cmpd, + heif_chroma* out_chroma, heif_colorspace* out_colourspace) +{ + *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; + + 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; + + if (component_type > component_type_max_valid) { + std::stringstream sstr; + sstr << "a component_type > " << component_type_max_valid << " is not supported"; + return { heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, sstr.str()}; + } + if (component_type == component_type_padded) { + // not relevant for determining chroma + continue; + } + componentSet |= (1 << component_type); + } + + if (componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue)) || + componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue) | (1 << component_type_alpha))) { + *out_chroma = heif_chroma_444; + *out_colourspace = heif_colorspace_RGB; + } + + if (componentSet == ((1 << component_type_Y) | (1 << component_type_Cb) | (1 << component_type_Cr))) { + switch (uncC->get_sampling_type()) { + case sampling_mode_no_subsampling: + *out_chroma = heif_chroma_444; + break; + case sampling_mode_422: + *out_chroma = heif_chroma_422; + break; + case sampling_mode_420: + *out_chroma = heif_chroma_420; + break; + } + *out_colourspace = heif_colorspace_YCbCr; + } + + if (componentSet == ((1 << component_type_monochrome)) || componentSet == ((1 << component_type_monochrome) | (1 << component_type_alpha))) { + // mono or mono + alpha input, mono output. + *out_chroma = heif_chroma_monochrome; + *out_colourspace = heif_colorspace_monochrome; + } + + // TODO: more combinations + + if (*out_chroma == heif_chroma_undefined) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Could not determine chroma"); + } + else if (*out_colourspace == heif_colorspace_undefined) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Could not determine colourspace"); + } + else { + return Error::Ok; + } +} + +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_R; + return true; + case 1: + *channel = heif_channel_G; + return true; + case 2: + *channel = heif_channel_B; + return true; + case 3: + *channel = heif_channel_Alpha; + 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; + return true; + case component_type_Y: + *channel = heif_channel_Y; + return true; + case component_type_Cb: + *channel = heif_channel_Cb; + return true; + case component_type_Cr: + *channel = heif_channel_Cr; + return true; + case component_type_red: + *channel = heif_channel_R; + return true; + case component_type_green: + *channel = heif_channel_G; + return true; + case component_type_blue: + *channel = heif_channel_B; + return true; + case component_type_alpha: + *channel = heif_channel_Alpha; + return true; + case component_type_padded: + return false; + default: + return false; + } +} + + +template T nAlignmentSkipBytes(uint32_t alignment, T size) { + if (alignment==0) { + return 0; + } + + T residual = size % alignment; + if (residual==0) { + return 0; + } + + return alignment - residual; +} + + +class UncompressedBitReader : public BitReader + { + public: + UncompressedBitReader(const std::vector& data) : BitReader(data.data(), (int)data.size()) + {} + + void markPixelStart() { + m_pixelStartOffset = get_current_byte_index(); + } + + void markRowStart() { + m_rowStartOffset = get_current_byte_index(); + } + + void markTileStart() { + m_tileStartOffset = get_current_byte_index(); + } + + inline void handlePixelAlignment(uint32_t pixel_size) { + if (pixel_size != 0) { + uint32_t bytes_in_pixel = get_current_byte_index() - m_pixelStartOffset; + uint32_t padding = pixel_size - bytes_in_pixel; + skip_bytes(padding); + } + } + + void handleRowAlignment(uint32_t alignment) { + skip_to_byte_boundary(); + if (alignment != 0) { + uint32_t bytes_in_row = get_current_byte_index() - m_rowStartOffset; + uint32_t residual = bytes_in_row % alignment; + if (residual != 0) { + uint32_t padding = alignment - residual; + skip_bytes(padding); + } + } + } + + void handleTileAlignment(uint32_t alignment) { + if (alignment != 0) { + uint32_t bytes_in_tile = get_current_byte_index() - m_tileStartOffset; + uint32_t residual = bytes_in_tile % alignment; + if (residual != 0) { + uint32_t tile_padding = alignment - residual; + skip_bytes(tile_padding); + } + } + } + + private: + int m_pixelStartOffset; + int m_rowStartOffset; + int m_tileStartOffset; +}; + + +class AbstractDecoder +{ +public: + virtual ~AbstractDecoder() = default; + + virtual Error decode_tile(const HeifContext* context, + heif_item_id item_id, + std::shared_ptr& img, + uint32_t out_x0, uint32_t out_y0, + uint32_t image_width, uint32_t image_height, + uint32_t tile_x, uint32_t tile_y) { assert(false); return Error{heif_error_Unsupported_feature, + heif_suberror_Unsupported_image_type, + "unci tile decoding not supported for this image type"};} + +protected: + AbstractDecoder(uint32_t width, uint32_t height, const std::shared_ptr cmpd, const std::shared_ptr uncC): + m_width(width), + m_height(height), + m_cmpd(std::move(cmpd)), + m_uncC(std::move(uncC)) + { + m_tile_height = m_height / m_uncC->get_number_of_tile_rows(); + m_tile_width = m_width / m_uncC->get_number_of_tile_columns(); + + assert(m_tile_width > 0); + assert(m_tile_height > 0); + } + + const uint32_t m_width; + const uint32_t m_height; + const std::shared_ptr m_cmpd; + const std::shared_ptr m_uncC; + // TODO: see if we can make this const + uint32_t m_tile_height; + uint32_t m_tile_width; + + class ChannelListEntry + { + public: + uint32_t get_bytes_per_tile() const + { + return bytes_per_tile_row_src * tile_height; + } + + inline uint64_t getDestinationRowOffset(uint32_t tile_row, uint32_t tile_y) const { + uint64_t dst_row_number = tile_row * tile_height + tile_y; + return dst_row_number * dst_plane_stride; + } + + heif_channel channel = heif_channel_Y; + uint8_t* dst_plane; + uint8_t* other_chroma_dst_plane; + uint32_t dst_plane_stride; + uint32_t other_chroma_dst_plane_stride; + uint32_t tile_width; + uint32_t tile_height; + uint32_t bytes_per_component_sample; + uint16_t bits_per_component_sample; + uint8_t component_alignment; + uint32_t bytes_per_tile_row_src; + bool use_channel; + }; + + std::vector channelList; + +public: + void buildChannelList(std::shared_ptr& img) { + for (Box_uncC::Component component : m_uncC->get_components()) { + ChannelListEntry entry = buildChannelListEntry(component, img); + channelList.push_back(entry); + } + } + +protected: + void processComponentSample(UncompressedBitReader &srcBits, const ChannelListEntry &entry, uint64_t dst_row_offset, uint32_t tile_column, uint32_t tile_x) { + uint64_t dst_col_number = tile_column * entry.tile_width + tile_x; + uint64_t dst_column_offset = dst_col_number * entry.bytes_per_component_sample; + int val = srcBits.get_bits(entry.bits_per_component_sample); + memcpy(entry.dst_plane + dst_row_offset + dst_column_offset, &val, entry.bytes_per_component_sample); + } + + // Handles the case where a row consists of a single component type + // Not valid for Pixel interleave + // Not valid for the Cb/Cr channels in Mixed Interleave + // Not valid for multi-Y pixel interleave + void processComponentRow(ChannelListEntry &entry, UncompressedBitReader &srcBits, uint64_t dst_row_offset, uint32_t tile_column) + { + for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { + if (entry.component_alignment != 0) { + srcBits.skip_to_byte_boundary(); + int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; + srcBits.skip_bits(numPadBits); + } + processComponentSample(srcBits, entry, dst_row_offset, tile_column, tile_x); + } + srcBits.skip_to_byte_boundary(); + } + + void processComponentTileSample(UncompressedBitReader &srcBits, const ChannelListEntry &entry, uint64_t dst_offset, uint32_t tile_x) { + uint64_t dst_sample_offset = tile_x * entry.bytes_per_component_sample; + int val = srcBits.get_bits(entry.bits_per_component_sample); + memcpy(entry.dst_plane + dst_offset + dst_sample_offset, &val, entry.bytes_per_component_sample); + } + + // Handles the case where a row consists of a single component type + // Not valid for Pixel interleave + // Not valid for the Cb/Cr channels in Mixed Interleave + // Not valid for multi-Y pixel interleave + void processComponentTileRow(ChannelListEntry &entry, UncompressedBitReader &srcBits, uint64_t dst_offset) + { + for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { + if (entry.component_alignment != 0) { + srcBits.skip_to_byte_boundary(); + int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; + srcBits.skip_bits(numPadBits); + } + processComponentTileSample(srcBits, entry, dst_offset, tile_x); + } + srcBits.skip_to_byte_boundary(); + } + +private: + ChannelListEntry buildChannelListEntry(Box_uncC::Component component, std::shared_ptr &img) { + ChannelListEntry entry; + 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; + if ((entry.channel == heif_channel_Cb) || (entry.channel == heif_channel_Cr)) { + if (m_uncC->get_sampling_type() == sampling_mode_422) { + entry.tile_width /= 2; + } else if (m_uncC->get_sampling_type() == sampling_mode_420) { + entry.tile_width /= 2; + entry.tile_height /= 2; + } + if (entry.channel == heif_channel_Cb) { + entry.other_chroma_dst_plane = img->get_plane(heif_channel_Cr, &(entry.other_chroma_dst_plane_stride)); + } else if (entry.channel == heif_channel_Cr) { + entry.other_chroma_dst_plane = img->get_plane(heif_channel_Cb, &(entry.other_chroma_dst_plane_stride)); + } + } + entry.bits_per_component_sample = component.component_bit_depth; + entry.component_alignment = component.component_align_size; + entry.bytes_per_component_sample = (component.component_bit_depth + 7) / 8; + entry.bytes_per_tile_row_src = entry.tile_width * entry.bytes_per_component_sample; + return entry; + } + +protected: + + // generic compression and uncompressed, per 23001-17 + const Error get_compressed_image_data_uncompressed(const HeifContext* context, heif_item_id ID, + std::vector *data, + uint64_t range_start_offset, uint64_t range_size, + uint32_t tile_idx, + const Box_iloc::Item *item) const + { + // --- get codec configuration + + std::shared_ptr cmpC_box = context->get_heif_file()->get_property(ID); + std::shared_ptr icef_box = context->get_heif_file()->get_property(ID); + + if (!cmpC_box) { + // assume no generic compression + return context->get_heif_file()->append_data_from_iloc(ID, *data, range_start_offset, range_size); + } + + if (icef_box && cmpC_box->get_compressed_unit_type() == heif_cmpC_compressed_unit_type_image_tile) { + const auto& units = icef_box->get_units(); + if (tile_idx >= units.size()) { + return {heif_error_Invalid_input, + heif_suberror_Unspecified, + "no icef-box entry for tile index"}; + } + + const auto unit = units[tile_idx]; + + // get all data and decode all + std::vector compressed_bytes; + Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes, unit.unit_offset, unit.unit_size); + if (err) { + return err; + } + + // decompress only the unit + err = do_decompress_data(cmpC_box, compressed_bytes, data); + if (err) { + return err; + } + } + else if (icef_box) { + // get all data and decode all + std::vector compressed_bytes; + Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size); + if (err) { + return err; + } + + for (Box_icef::CompressedUnitInfo unit_info : icef_box->get_units()) { + auto unit_start = compressed_bytes.begin() + unit_info.unit_offset; + auto unit_end = unit_start + unit_info.unit_size; + std::vector compressed_unit_data = std::vector(unit_start, unit_end); + std::vector uncompressed_unit_data; + err = do_decompress_data(cmpC_box, compressed_unit_data, &uncompressed_unit_data); + if (err) { + return err; + } + data->insert(data->end(), uncompressed_unit_data.data(), uncompressed_unit_data.data() + uncompressed_unit_data.size()); + } + + // cut out the range that we actually need + memcpy(data->data(), data->data() + range_start_offset, range_size); + data->resize(range_size); + } + else { + // get all data and decode all + std::vector compressed_bytes; + Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size); + if (err) { + return err; + } + + // Decode as a single blob + err = do_decompress_data(cmpC_box, compressed_bytes, data); + if (err) { + return err; + } + + // cut out the range that we actually need + memcpy(data->data(), data->data() + range_start_offset, range_size); + data->resize(range_size); + } + + return Error::Ok; + } + + const Error 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 decompress_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")) { +#if HAVE_ZLIB + return decompress_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 HAVE_ZLIB + return decompress_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; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_generic_compression_method, + sstr.str()); + } + } +}; + + +class ComponentInterleaveDecoder : public AbstractDecoder +{ +public: + ComponentInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): + AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) + {} + + Error decode_tile(const HeifContext* context, + heif_item_id image_id, + std::shared_ptr& img, + uint32_t out_x0, uint32_t out_y0, + uint32_t image_width, uint32_t image_height, + uint32_t tile_x, uint32_t tile_y) override + { + if (m_tile_width == 0) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: ComponentInterleaveDecoder tile_width=0"}; + } + + // --- compute which file range we need to read for the tile + + uint64_t total_tile_size = 0; + + for (ChannelListEntry& entry : channelList) { + uint32_t bits_per_component = entry.bits_per_component_sample; + if (entry.component_alignment > 0) { + uint32_t bytes_per_component = (bits_per_component + 7)/8; + bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); + bits_per_component = bytes_per_component * 8; + } + + uint32_t bytes_per_tile_row = (bits_per_component * entry.tile_width + 7)/8; + bytes_per_tile_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_tile_row); + uint64_t bytes_per_tile = bytes_per_tile_row * entry.tile_height; + total_tile_size += bytes_per_tile; + } + + if (m_uncC->get_tile_align_size() != 0) { + total_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), total_tile_size); + } + + assert(m_tile_width > 0); + uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); + uint64_t tile_start_offset = total_tile_size * tileIdx; + + + // --- read required file range + + std::vector src_data; + //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); + Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); + if (err) { + return err; + } + + UncompressedBitReader srcBits(src_data); + + + // --- decode tile + + for (ChannelListEntry& entry : channelList) { + for (uint32_t y = 0; y < entry.tile_height; y++) { + srcBits.markRowStart(); + if (entry.use_channel) { + uint64_t dst_row_offset = (out_y0 + y) * entry.dst_plane_stride; + processComponentTileRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample); + } + else { + srcBits.skip_bytes(entry.bytes_per_tile_row_src); + } + srcBits.handleRowAlignment(m_uncC->get_row_align_size()); + } + } + + return Error::Ok; + } +}; + + +class PixelInterleaveDecoder : public AbstractDecoder +{ +public: + PixelInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): + AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) + {} + + Error decode_tile(const HeifContext* context, + heif_item_id image_id, + std::shared_ptr& img, + uint32_t out_x0, uint32_t out_y0, + uint32_t image_width, uint32_t image_height, + uint32_t tile_x, uint32_t tile_y) override + { + if (m_tile_width == 0) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: PixelInterleaveDecoder tile_width=0"}; + } + + // --- compute which file range we need to read for the tile + + uint32_t bits_per_row = 0; + for (uint32_t x = 0 ; x 0) { + // start at byte boundary + bits_per_row = (bits_per_row + 7) & ~7U; + + uint32_t bytes_per_component = (bits_per_component + 7)/8; + bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); + bits_per_component = bytes_per_component * 8; + } + + bits_per_pixel += bits_per_component; + } + + if (m_uncC->get_pixel_size() != 0) { + uint32_t bytes_per_pixel = (bits_per_pixel + 7) / 8; + bytes_per_pixel += nAlignmentSkipBytes(m_uncC->get_pixel_size(), bytes_per_pixel); + bits_per_pixel = bytes_per_pixel * 8; + } + + bits_per_row += bits_per_pixel; + } + + uint32_t bytes_per_row = (bits_per_row + 7)/8; + bytes_per_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_row); + + uint64_t total_tile_size = bytes_per_row * static_cast(m_tile_height); + if (m_uncC->get_tile_align_size() != 0) { + total_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), total_tile_size); + } + + assert(m_tile_width > 0); + uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); + uint64_t tile_start_offset = total_tile_size * tileIdx; + + + // --- read required file range + + std::vector src_data; + Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); + //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); + if (err) { + return err; + } + + UncompressedBitReader srcBits(src_data); + + processTile(srcBits, tile_y, tile_x, out_x0, out_y0); + + return Error::Ok; + } + + void processTile(UncompressedBitReader &srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { + for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) { + srcBits.markRowStart(); + for (uint32_t tile_x = 0; tile_x < m_tile_width; tile_x++) { + srcBits.markPixelStart(); + for (ChannelListEntry &entry : channelList) { + if (entry.use_channel) { + uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); + if (entry.component_alignment != 0) { + srcBits.skip_to_byte_boundary(); + int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; + srcBits.skip_bits(numPadBits); + } + processComponentSample(srcBits, entry, dst_row_offset, 0, out_x0 + tile_x); + } else { + srcBits.skip_bytes(entry.bytes_per_component_sample); + } + } + srcBits.handlePixelAlignment(m_uncC->get_pixel_size()); + } + srcBits.handleRowAlignment(m_uncC->get_row_align_size()); + } + } +}; + + +class MixedInterleaveDecoder : public AbstractDecoder +{ +public: + MixedInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): + AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) + {} + + Error decode_tile(const HeifContext* context, + heif_item_id image_id, + std::shared_ptr& img, + uint32_t out_x0, uint32_t out_y0, + uint32_t image_width, uint32_t image_height, + uint32_t tile_x, uint32_t tile_y) override + { + if (m_tile_width == 0) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: MixedInterleaveDecoder tile_width=0"}; + } + + // --- compute which file range we need to read for the tile + + uint64_t tile_size = 0; + + for (ChannelListEntry& entry : channelList) { + if (entry.channel == heif_channel_Cb || entry.channel == heif_channel_Cr) { + uint32_t bits_per_row = entry.bits_per_component_sample * entry.tile_width; + bits_per_row = (bits_per_row+7) & ~7U; // align to byte boundary + + tile_size += bits_per_row / 8 * entry.tile_height; + } + else { + uint32_t bits_per_component = entry.bits_per_component_sample; + if (entry.component_alignment > 0) { + uint32_t bytes_per_component = (bits_per_component + 7)/8; + bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); + bits_per_component = bytes_per_component * 8; + } + + uint32_t bits_per_row = bits_per_component * entry.tile_width; + bits_per_row = (bits_per_row+7) & ~7U; // align to byte boundary + + tile_size += bits_per_row / 8 * entry.tile_height; + } + } + + + if (m_uncC->get_tile_align_size() != 0) { + tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), tile_size); + } + + assert(m_tile_width > 0); + uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); + uint64_t tile_start_offset = tile_size * tileIdx; + + + // --- read required file range + + std::vector src_data; + Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, tile_size, tileIdx, nullptr); + //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, tile_size); + if (err) { + return err; + } + + UncompressedBitReader srcBits(src_data); + + processTile(srcBits, tile_y, tile_x, out_x0, out_y0); + + return Error::Ok; + } + + + void processTile(UncompressedBitReader &srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { + bool haveProcessedChromaForThisTile = false; + for (ChannelListEntry &entry : channelList) { + if (entry.use_channel) { + if ((entry.channel == heif_channel_Cb) || (entry.channel == heif_channel_Cr)) { + if (!haveProcessedChromaForThisTile) { + for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { + // TODO: row padding + uint64_t dst_row_number = tile_y + out_y0; + uint64_t dst_row_offset = dst_row_number * entry.dst_plane_stride; + for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { + uint64_t dst_column_number = out_x0 + tile_x; + uint64_t dst_column_offset = dst_column_number * entry.bytes_per_component_sample; + int val = srcBits.get_bits(entry.bytes_per_component_sample * 8); + memcpy(entry.dst_plane + dst_row_offset + dst_column_offset, &val, entry.bytes_per_component_sample); + val = srcBits.get_bits(entry.bytes_per_component_sample * 8); + memcpy(entry.other_chroma_dst_plane + dst_row_offset + dst_column_offset, &val, entry.bytes_per_component_sample); + } + haveProcessedChromaForThisTile = true; + } + } + } else { + for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { + uint64_t dst_row_offset = entry.getDestinationRowOffset(tile_row, tile_y); + processComponentRow(entry, srcBits, dst_row_offset, tile_column); + } + } + } else { + // skip over the data we are not using + srcBits.skip_bytes(entry.get_bytes_per_tile()); + continue; + } + } + } +}; + +class RowInterleaveDecoder : public AbstractDecoder +{ +public: + RowInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): + AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) + {} + + Error decode_tile(const HeifContext* context, + heif_item_id image_id, + std::shared_ptr& img, + uint32_t out_x0, uint32_t out_y0, + uint32_t image_width, uint32_t image_height, + uint32_t tile_x, uint32_t tile_y) override + { + if (m_tile_width == 0) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: RowInterleaveDecoder tile_width=0"}; + } + + // --- compute which file range we need to read for the tile + + uint32_t bits_per_row = 0; + for (ChannelListEntry& entry : channelList) { + uint32_t bits_per_component = entry.bits_per_component_sample; + if (entry.component_alignment > 0) { + // start at byte boundary + bits_per_row = (bits_per_row + 7) & ~7U; + + uint32_t bytes_per_component = (bits_per_component + 7)/8; + bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); + bits_per_component = bytes_per_component * 8; + } + + if (m_uncC->get_row_align_size() != 0) { + uint32_t bytes_this_row = (bits_per_component * m_tile_width + 7) / 8; + bytes_this_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_this_row); + bits_per_row += bytes_this_row * 8; + } + else { + bits_per_row += bits_per_component * m_tile_width; + } + + bits_per_row = (bits_per_row + 7) & ~7U; + } + + uint32_t bytes_per_row = (bits_per_row + 7) / 8; + if (m_uncC->get_row_align_size()) { + bytes_per_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_row); + } + + uint64_t total_tile_size = 0; + total_tile_size += bytes_per_row * static_cast(m_tile_height); + + if (m_uncC->get_tile_align_size() != 0) { + total_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), total_tile_size); + } + + assert(m_tile_width > 0); + uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); + uint64_t tile_start_offset = total_tile_size * tileIdx; + + + // --- read required file range + + std::vector src_data; + Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); + //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); + if (err) { + return err; + } + + UncompressedBitReader srcBits(src_data); + + processTile(srcBits, tile_y, tile_x, out_x0, out_y0); + + return Error::Ok; + } + +private: + void processTile(UncompressedBitReader &srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { + for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) { + for (ChannelListEntry &entry : channelList) { + srcBits.markRowStart(); + if (entry.use_channel) { + uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); + processComponentRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample, 0); + } else { + srcBits.skip_bytes(entry.bytes_per_tile_row_src); + } + srcBits.handleRowAlignment(m_uncC->get_row_align_size()); + } + } + } +}; + + +class TileComponentInterleaveDecoder : public AbstractDecoder +{ +public: + TileComponentInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): + AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) + {} + + Error decode_tile(const HeifContext* context, + heif_item_id image_id, + std::shared_ptr& img, + uint32_t out_x0, uint32_t out_y0, + uint32_t image_width, uint32_t image_height, + uint32_t tile_column, uint32_t tile_row) override + { + if (m_tile_width == 0) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: TileComponentInterleaveDecoder tile_width=0"}; + } + if (m_tile_height == 0) { + return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: TileComponentInterleaveDecoder tile_height=0"}; + } + + // --- compute which file range we need to read for the tile + + std::map channel_tile_size; + + //uint64_t total_tile_size = 0; + + for (ChannelListEntry& entry : channelList) { + uint32_t bits_per_pixel = entry.bits_per_component_sample; + if (entry.component_alignment > 0) { + // start at byte boundary + //bits_per_row = (bits_per_row + 7) & ~7U; + + uint32_t bytes_per_component = (bits_per_pixel + 7)/8; + bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); + bits_per_pixel = bytes_per_component * 8; + } + + uint32_t bytes_per_row; + if (m_uncC->get_pixel_size() != 0) { // TODO: does pixel_size apply here? + uint32_t bytes_per_pixel = (bits_per_pixel + 7) / 8; + bytes_per_pixel += nAlignmentSkipBytes(m_uncC->get_pixel_size(), bytes_per_pixel); + bytes_per_row = bytes_per_pixel * m_tile_width; + } + else { + bytes_per_row = (bits_per_pixel * m_tile_width + 7) / 8; + } + + bytes_per_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_row); + + uint64_t component_tile_size = bytes_per_row * static_cast(m_tile_height); + + if (m_uncC->get_tile_align_size() != 0) { + component_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), component_tile_size); + } + + channel_tile_size[entry.channel] = component_tile_size; + + //total_tile_size += component_tile_size; + } + + uint64_t component_start_offset = 0; + + assert(m_tile_width > 0); + assert(m_tile_height > 0); + + for (ChannelListEntry& entry : channelList) { + //processTile(srcBits, tile_y, tile_x, out_x0, out_y0); + + if (!entry.use_channel) { + //uint64_t bytes_per_component = entry.get_bytes_per_tile() * m_uncC->get_number_of_tile_columns() * m_uncC->get_number_of_tile_rows(); + //srcBits.skip_bytes((int)bytes_per_component); + + component_start_offset += channel_tile_size[entry.channel] * (m_width / m_tile_width) * (m_height / m_tile_height); + continue; + } + + // --- read required file range + + uint32_t tileIdx = tile_column + tile_row * (image_width / m_tile_width); + uint64_t tile_start_offset = component_start_offset + channel_tile_size[entry.channel] * tileIdx; + + std::vector src_data; + Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, channel_tile_size[entry.channel], tileIdx, nullptr); + //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, channel_tile_size[entry.channel]); + if (err) { + return err; + } + + UncompressedBitReader srcBits(src_data); + + srcBits.markTileStart(); + for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { + srcBits.markRowStart(); + uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); + processComponentRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample, 0); + srcBits.handleRowAlignment(m_uncC->get_row_align_size()); + } + srcBits.handleTileAlignment(m_uncC->get_tile_align_size()); + + + component_start_offset += channel_tile_size[entry.channel] * (m_width / m_tile_width) * (m_height / m_tile_height); + } + + return Error::Ok; + } +}; + + +static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std::shared_ptr& cmpd, const std::shared_ptr& uncC) +{ + if (uncC->get_interleave_type() == interleave_mode_component) { + return new ComponentInterleaveDecoder(width, height, cmpd, uncC); + } else if (uncC->get_interleave_type() == interleave_mode_pixel) { + return new PixelInterleaveDecoder(width, height, cmpd, uncC); + } else if (uncC->get_interleave_type() == interleave_mode_mixed) { + return new MixedInterleaveDecoder(width, height, cmpd, uncC); + } else if (uncC->get_interleave_type() == interleave_mode_row) { + return new RowInterleaveDecoder(width, height, cmpd, uncC); + } else if (uncC->get_interleave_type() == interleave_mode_tile_component) { + return new TileComponentInterleaveDecoder(width, height, cmpd, uncC); + } else { + return nullptr; + } +} + + +Result> UncompressedImageCodec::create_image(const std::shared_ptr cmpd, + const std::shared_ptr uncC, + uint32_t width, + uint32_t height) +{ + auto img = std::make_shared(); + heif_chroma chroma; + heif_colorspace colourspace; + Error error = get_heif_chroma_uncompressed(uncC, cmpd, &chroma, &colourspace); + if (error) { + return error; + } + img->create(width, height, + colourspace, + chroma); + + for (Box_uncC::Component component : uncC->get_components()) { + heif_channel channel; + if (map_uncompressed_component_to_channel(cmpd, uncC, component, &channel)) { + if (img->has_channel(channel)) { + return Error{heif_error_Unsupported_feature, + heif_suberror_Unspecified, + "Cannot generate image with several similar heif_channels."}; + } + + 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 { + img->add_plane(channel, width, height, component.component_bit_depth); + } + } + } + + return img; +} + + +Error UncompressedImageCodec::decode_uncompressed_image_tile(const HeifContext* context, + heif_item_id ID, + std::shared_ptr& img, + uint32_t tile_x0, uint32_t tile_y0) +{ + auto file = context->get_heif_file(); + std::shared_ptr ispe = file->get_property(ID); + std::shared_ptr cmpd = file->get_property(ID); + std::shared_ptr uncC = file->get_property(ID); + + Error error = check_header_validity(ispe, cmpd, uncC); + if (error) { + return error; + } + + uint32_t tile_width = ispe->get_width() / uncC->get_number_of_tile_columns(); + uint32_t tile_height = ispe->get_height() / uncC->get_number_of_tile_rows(); + + Result> createImgResult = create_image(cmpd, uncC, tile_width, tile_height); + if (createImgResult.error) { + return createImgResult.error; + } + + img = createImgResult.value; + + + AbstractDecoder *decoder = makeDecoder(ispe->get_width(), ispe->get_height(), cmpd, uncC); + if (decoder == nullptr) { + std::stringstream sstr; + sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + + decoder->buildChannelList(img); + + Error result = decoder->decode_tile(context, ID, img, 0, 0, + ispe->get_width(), ispe->get_height(), + tile_x0, tile_y0); + delete decoder; + return result; +} + + +Error UncompressedImageCodec::check_header_validity(const std::shared_ptr& ispe, + const std::shared_ptr& cmpd, + const std::shared_ptr& uncC) +{ + // if we miss a required box, show error + + if (!ispe) { + return {heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Missing required ispe box for uncompressed codec"}; + } + + if (!uncC) { + return {heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Missing required uncC box for uncompressed codec"}; + } + + if (!cmpd && (uncC->get_version() != 1)) { + return {heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Missing required cmpd or uncC version 1 box for uncompressed codec"}; + } + + if (cmpd) { + if (uncC->get_components().size() != cmpd->get_components().size()) { + return {heif_error_Invalid_input, + heif_suberror_Unspecified, + "Number of components in uncC and cmpd do not match"}; + } + + for (const auto& comp : uncC->get_components()) { + if (comp.component_index > cmpd->get_components().size()) { + return {heif_error_Invalid_input, + heif_suberror_Unspecified, + "Invalid component index in uncC box"}; + } + } + } + + if (uncC->get_number_of_tile_rows() > ispe->get_height() || + uncC->get_number_of_tile_columns() > ispe->get_width()) { + return {heif_error_Invalid_input, + heif_suberror_Unspecified, + "More tiles than pixels in uncC box"}; + } + + if (ispe->get_height() % uncC->get_number_of_tile_rows() != 0 || + ispe->get_width() % uncC->get_number_of_tile_columns() != 0) { + return {heif_error_Invalid_input, + heif_suberror_Unspecified, + "Invalid tile size (image size not a multiple of the tile size)"}; + } + + return Error::Ok; +} + + +Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* context, + heif_item_id ID, + std::shared_ptr& img) +{ + // Get the properties for this item + // We need: ispe, cmpd, uncC + std::vector> item_properties; + Error error = context->get_heif_file()->get_properties(ID, item_properties); + if (error) { + return error; + } + + std::shared_ptr ispe = context->get_heif_file()->get_property(ID); + std::shared_ptr cmpd = context->get_heif_file()->get_property(ID); + std::shared_ptr uncC = context->get_heif_file()->get_property(ID); + + error = check_header_validity(ispe, cmpd, uncC); + if (error) { + return error; + } + + // check if we support the type of image + + error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO + if (error) { + return error; + } + + assert(ispe); + uint32_t width = ispe->get_width(); + uint32_t height = ispe->get_height(); + error = context->check_resolution(width, height); + if (error) { + return error; + } + + Result> createImgResult = create_image(cmpd, uncC, width, height); + if (createImgResult.error) { + return createImgResult.error; + } + else { + img = *createImgResult; + } + + AbstractDecoder *decoder = makeDecoder(width, height, cmpd, uncC); + if (decoder == nullptr) { + std::stringstream sstr; + sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + sstr.str()); + } + + decoder->buildChannelList(img); + + uint32_t tile_width = width / uncC->get_number_of_tile_columns(); + uint32_t tile_height = height / uncC->get_number_of_tile_rows(); + + for (uint32_t tile_y0 = 0; tile_y0 < height; tile_y0 += tile_height) + for (uint32_t tile_x0 = 0; tile_x0 < width; tile_x0 += tile_width) { + error = decoder->decode_tile(context, ID, img, tile_x0, tile_y0, + width, height, + tile_x0 / tile_width, tile_y0 / tile_height); + if (error) { + delete decoder; + return error; + } + } + + //Error result = decoder->decode(source_data, img); + delete decoder; + return Error::Ok; +} + +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))) + { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Invalid colourspace / channel combination - YCbCr"); + } + Box_cmpd::Component yComponent = {component_type_Y}; + cmpd->add_component(yComponent); + Box_cmpd::Component cbComponent = {component_type_Cb}; + cmpd->add_component(cbComponent); + Box_cmpd::Component crComponent = {component_type_Cr}; + cmpd->add_component(crComponent); + 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); + 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); + 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) + { + uncC->set_sampling_type(sampling_mode_no_subsampling); + } + else if (image->get_chroma_format() == heif_chroma_422) + { + uncC->set_sampling_type(sampling_mode_422); + } + else if (image->get_chroma_format() == heif_chroma_420) + { + uncC->set_sampling_type(sampling_mode_420); + } + else + { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Unsupported YCbCr sub-sampling type"); + } + uncC->set_interleave_type(interleave_mode_component); + uncC->set_block_size(0); + uncC->set_components_little_endian(false); + uncC->set_block_pad_lsb(false); + uncC->set_block_little_endian(false); + uncC->set_block_reversed(false); + uncC->set_pad_unknown(false); + uncC->set_pixel_size(0); + uncC->set_row_align_size(0); + uncC->set_tile_align_size(0); + uncC->set_number_of_tile_columns(nTileColumns); + uncC->set_number_of_tile_rows(nTileRows); + } + else if (colourspace == heif_colorspace_RGB) + { + if (!((image->get_chroma_format() == heif_chroma_444) || + (image->get_chroma_format() == heif_chroma_interleaved_RGB) || + (image->get_chroma_format() == heif_chroma_interleaved_RGBA) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE))) { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Unsupported colourspace / chroma combination - RGB"); + } + Box_cmpd::Component rComponent = {component_type_red}; + cmpd->add_component(rComponent); + Box_cmpd::Component gComponent = {component_type_green}; + cmpd->add_component(gComponent); + Box_cmpd::Component bComponent = {component_type_blue}; + cmpd->add_component(bComponent); + if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE) || + (image->has_channel(heif_channel_Alpha))) + { + Box_cmpd::Component alphaComponent = {component_type_alpha}; + cmpd->add_component(alphaComponent); + } + if ((image->get_chroma_format() == heif_chroma_interleaved_RGB) || + (image->get_chroma_format() == heif_chroma_interleaved_RGBA) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) + { + uncC->set_interleave_type(interleave_mode_pixel); + int bpp = image->get_bits_per_pixel(heif_channel_interleaved); + uint8_t component_align = 1; + if (bpp == 8) + { + component_align = 0; + } + else if (bpp > 8) + { + component_align = 2; + } + Box_uncC::Component component0 = {0, (uint8_t)(bpp), component_format_unsigned, component_align}; + uncC->add_component(component0); + Box_uncC::Component component1 = {1, (uint8_t)(bpp), component_format_unsigned, component_align}; + uncC->add_component(component1); + Box_uncC::Component component2 = {2, (uint8_t)(bpp), component_format_unsigned, component_align}; + uncC->add_component(component2); + if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) + { + Box_uncC::Component component3 = { + 3, (uint8_t)(bpp), component_format_unsigned, component_align}; + uncC->add_component(component3); + } + } else { + uncC->set_interleave_type(interleave_mode_component); + int bpp_red = image->get_bits_per_pixel(heif_channel_R); + Box_uncC::Component component0 = {0, (uint8_t)(bpp_red), component_format_unsigned, 0}; + uncC->add_component(component0); + int bpp_green = image->get_bits_per_pixel(heif_channel_G); + Box_uncC::Component component1 = {1, (uint8_t)(bpp_green), component_format_unsigned, 0}; + uncC->add_component(component1); + int bpp_blue = image->get_bits_per_pixel(heif_channel_B); + Box_uncC::Component component2 = {2, (uint8_t)(bpp_blue), component_format_unsigned, 0}; + uncC->add_component(component2); + if(image->has_channel(heif_channel_Alpha)) + { + int bpp_alpha = image->get_bits_per_pixel(heif_channel_Alpha); + Box_uncC::Component component3 = {3, (uint8_t)(bpp_alpha), component_format_unsigned, 0}; + uncC->add_component(component3); + } + } + uncC->set_sampling_type(sampling_mode_no_subsampling); + uncC->set_block_size(0); + if ((image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || + (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) + { + uncC->set_components_little_endian(true); + } else { + uncC->set_components_little_endian(false); + } + uncC->set_block_pad_lsb(false); + uncC->set_block_little_endian(false); + uncC->set_block_reversed(false); + uncC->set_pad_unknown(false); + uncC->set_pixel_size(0); + uncC->set_row_align_size(0); + uncC->set_tile_align_size(0); + uncC->set_number_of_tile_columns(nTileColumns); + uncC->set_number_of_tile_rows(nTileRows); + } + else if (colourspace == heif_colorspace_monochrome) + { + Box_cmpd::Component monoComponent = {component_type_monochrome}; + cmpd->add_component(monoComponent); + if (image->has_channel(heif_channel_Alpha)) + { + Box_cmpd::Component alphaComponent = {component_type_alpha}; + cmpd->add_component(alphaComponent); + } + int bpp = image->get_bits_per_pixel(heif_channel_Y); + Box_uncC::Component component0 = {0, (uint8_t)(bpp), component_format_unsigned, 0}; + uncC->add_component(component0); + if (image->has_channel(heif_channel_Alpha)) + { + bpp = image->get_bits_per_pixel(heif_channel_Alpha); + Box_uncC::Component component1 = {1, (uint8_t)(bpp), component_format_unsigned, 0}; + uncC->add_component(component1); + } + uncC->set_sampling_type(sampling_mode_no_subsampling); + uncC->set_interleave_type(interleave_mode_component); + uncC->set_block_size(0); + uncC->set_components_little_endian(false); + uncC->set_block_pad_lsb(false); + uncC->set_block_little_endian(false); + uncC->set_block_reversed(false); + uncC->set_pad_unknown(false); + uncC->set_pixel_size(0); + uncC->set_row_align_size(0); + uncC->set_tile_align_size(0); + uncC->set_number_of_tile_columns(nTileColumns); + uncC->set_number_of_tile_rows(nTileRows); + } + else + { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Unsupported colourspace"); + } + return Error::Ok; +} diff --git a/libheif/codecs/uncompressed/unc_dec.cc b/libheif/codecs/uncompressed/unc_dec.cc index 1ee0018566..b6673606ee 100644 --- a/libheif/codecs/uncompressed/unc_dec.cc +++ b/libheif/codecs/uncompressed/unc_dec.cc @@ -19,7 +19,7 @@ */ #include "codecs/uncompressed/unc_dec.h" -#include "codecs/uncompressed/unc_image.h" +#include "codecs/uncompressed/unc_codec.h" #include "error.h" #include "context.h" diff --git a/libheif/codecs/uncompressed/unc_image.cc b/libheif/codecs/uncompressed/unc_image.cc index 919d08e79e..a237177df8 100644 --- a/libheif/codecs/uncompressed/unc_image.cc +++ b/libheif/codecs/uncompressed/unc_image.cc @@ -35,1639 +35,10 @@ #include "unc_boxes.h" #include "unc_image.h" #include "unc_dec.h" +#include "unc_codec.h" #include "codecs/image_item.h" -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(const std::shared_ptr& uncC, - const 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; - if ((component_type > 7) && (component_type != component_type_padded)) { - std::stringstream sstr; - sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - if ((component.component_bit_depth > 8) && (component.component_bit_depth != 16)) { - std::stringstream sstr; - sstr << "Uncompressed image with component_bit_depth " << ((int) component.component_bit_depth) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - if (component.component_format != component_format_unsigned) { - std::stringstream sstr; - sstr << "Uncompressed image with component_format " << ((int) component.component_format) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - if (component.component_align_size > 2) { - std::stringstream sstr; - sstr << "Uncompressed image with component_align_size " << ((int) component.component_align_size) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - } - if ((uncC->get_sampling_type() != sampling_mode_no_subsampling) - && (uncC->get_sampling_type() != sampling_mode_422) - && (uncC->get_sampling_type() != sampling_mode_420) - ) { - std::stringstream sstr; - sstr << "Uncompressed sampling_type of " << ((int) uncC->get_sampling_type()) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - if ((uncC->get_interleave_type() != interleave_mode_component) - && (uncC->get_interleave_type() != interleave_mode_pixel) - && (uncC->get_interleave_type() != interleave_mode_mixed) - && (uncC->get_interleave_type() != interleave_mode_row) - && (uncC->get_interleave_type() != interleave_mode_tile_component) - ) { - std::stringstream sstr; - sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.3 - if (uncC->get_sampling_type() == sampling_mode_422) { - // We check Y Cb and Cr appear in the chroma test - // TODO: error for tile width not multiple of 2 - if ((uncC->get_interleave_type() != interleave_mode_component) - && (uncC->get_interleave_type() != interleave_mode_mixed) - && (uncC->get_interleave_type() != interleave_mode_multi_y)) - { - std::stringstream sstr; - sstr << "YCbCr 4:2:2 subsampling is only valid with component, mixed or multi-Y interleave mode (ISO/IEC 23001-17 5.2.1.5.3)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) { - if (uncC->get_row_align_size() % 2 != 0) { - std::stringstream sstr; - sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - } - if (uncC->get_tile_align_size() != 0) { - if (uncC->get_tile_align_size() % 2 != 0) { - std::stringstream sstr; - sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.3)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - } - } - // Validity checks per ISO/IEC 23001-17 Section 5.2.1.5.4 - if (uncC->get_sampling_type() == sampling_mode_422) { - // We check Y Cb and Cr appear in the chroma test - // TODO: error for tile width not multiple of 2 - if ((uncC->get_interleave_type() != interleave_mode_component) - && (uncC->get_interleave_type() != interleave_mode_mixed)) - { - std::stringstream sstr; - sstr << "YCbCr 4:2:0 subsampling is only valid with component or mixed interleave mode (ISO/IEC 23001-17 5.2.1.5.4)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - if ((uncC->get_row_align_size() != 0) && (uncC->get_interleave_type() == interleave_mode_component)) { - if (uncC->get_row_align_size() % 2 != 0) { - std::stringstream sstr; - sstr << "YCbCr 4:2:2 subsampling with component interleave requires row_align_size to be a multiple of 2 (ISO/IEC 23001-17 5.2.1.5.4)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - } - if (uncC->get_tile_align_size() != 0) { - if (uncC->get_tile_align_size() % 4 != 0) { - std::stringstream sstr; - sstr << "YCbCr 4:2:2 subsampling requires tile_align_size to be a multiple of 4 (ISO/IEC 23001-17 5.2.1.5.3)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - } - } - if ((uncC->get_interleave_type() == interleave_mode_mixed) && (uncC->get_sampling_type() == sampling_mode_no_subsampling)) - { - std::stringstream sstr; - sstr << "Interleave interleave mode is not valid with subsampling mode (ISO/IEC 23001-17 5.2.1.6.4)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - if ((uncC->get_interleave_type() == interleave_mode_multi_y) - && ((uncC->get_sampling_type() != sampling_mode_422) && (uncC->get_sampling_type() != sampling_mode_411))) - { - std::stringstream sstr; - sstr << "Multi-Y interleave mode is only valid with 4:2:2 and 4:1:1 subsampling modes (ISO/IEC 23001-17 5.2.1.6.7)."; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - // TODO: throw error if mixed and Cb and Cr are not adjacent. - - if (uncC->get_block_size() != 0) { - std::stringstream sstr; - sstr << "Uncompressed block_size of " << ((int) uncC->get_block_size()) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - if (uncC->is_components_little_endian()) { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Uncompressed components_little_endian == 1 is not implemented yet"); - } - if (uncC->is_block_pad_lsb()) { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Uncompressed block_pad_lsb == 1 is not implemented yet"); - } - if (uncC->is_block_little_endian()) { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Uncompressed block_little_endian == 1 is not implemented yet"); - } - if (uncC->is_block_reversed()) { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Uncompressed block_reversed == 1 is not implemented yet"); - } - if ((uncC->get_pixel_size() != 0) && ((uncC->get_interleave_type() != interleave_mode_pixel) && (uncC->get_interleave_type() != interleave_mode_multi_y))) { - std::stringstream sstr; - sstr << "Uncompressed pixel_size of " << ((int) uncC->get_pixel_size()) << " is only valid with interleave_type 1 or 5 (ISO/IEC 23001-17 5.2.1.7)"; - return Error(heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - sstr.str()); - } - return Error::Ok; -} - - -Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr& uncC, - const std::shared_ptr& cmpd, - heif_chroma* out_chroma, heif_colorspace* out_colourspace) -{ - *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; - - 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; - - if (component_type > component_type_max_valid) { - std::stringstream sstr; - sstr << "a component_type > " << component_type_max_valid << " is not supported"; - return { heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, sstr.str()}; - } - if (component_type == component_type_padded) { - // not relevant for determining chroma - continue; - } - componentSet |= (1 << component_type); - } - - if (componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue)) || - componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue) | (1 << component_type_alpha))) { - *out_chroma = heif_chroma_444; - *out_colourspace = heif_colorspace_RGB; - } - - if (componentSet == ((1 << component_type_Y) | (1 << component_type_Cb) | (1 << component_type_Cr))) { - switch (uncC->get_sampling_type()) { - case sampling_mode_no_subsampling: - *out_chroma = heif_chroma_444; - break; - case sampling_mode_422: - *out_chroma = heif_chroma_422; - break; - case sampling_mode_420: - *out_chroma = heif_chroma_420; - break; - } - *out_colourspace = heif_colorspace_YCbCr; - } - - if (componentSet == ((1 << component_type_monochrome)) || componentSet == ((1 << component_type_monochrome) | (1 << component_type_alpha))) { - // mono or mono + alpha input, mono output. - *out_chroma = heif_chroma_monochrome; - *out_colourspace = heif_colorspace_monochrome; - } - - // TODO: more combinations - - if (*out_chroma == heif_chroma_undefined) { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Could not determine chroma"); - } - else if (*out_colourspace == heif_colorspace_undefined) { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Could not determine colourspace"); - } - else { - return Error::Ok; - } -} - -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_R; - return true; - case 1: - *channel = heif_channel_G; - return true; - case 2: - *channel = heif_channel_B; - return true; - case 3: - *channel = heif_channel_Alpha; - 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; - return true; - case component_type_Y: - *channel = heif_channel_Y; - return true; - case component_type_Cb: - *channel = heif_channel_Cb; - return true; - case component_type_Cr: - *channel = heif_channel_Cr; - return true; - case component_type_red: - *channel = heif_channel_R; - return true; - case component_type_green: - *channel = heif_channel_G; - return true; - case component_type_blue: - *channel = heif_channel_B; - return true; - case component_type_alpha: - *channel = heif_channel_Alpha; - return true; - case component_type_padded: - return false; - default: - return false; - } -} - - -template T nAlignmentSkipBytes(uint32_t alignment, T size) { - if (alignment==0) { - return 0; - } - - T residual = size % alignment; - if (residual==0) { - return 0; - } - - return alignment - residual; -} - - -class UncompressedBitReader : public BitReader - { - public: - UncompressedBitReader(const std::vector& data) : BitReader(data.data(), (int)data.size()) - {} - - void markPixelStart() { - m_pixelStartOffset = get_current_byte_index(); - } - - void markRowStart() { - m_rowStartOffset = get_current_byte_index(); - } - - void markTileStart() { - m_tileStartOffset = get_current_byte_index(); - } - - inline void handlePixelAlignment(uint32_t pixel_size) { - if (pixel_size != 0) { - uint32_t bytes_in_pixel = get_current_byte_index() - m_pixelStartOffset; - uint32_t padding = pixel_size - bytes_in_pixel; - skip_bytes(padding); - } - } - - void handleRowAlignment(uint32_t alignment) { - skip_to_byte_boundary(); - if (alignment != 0) { - uint32_t bytes_in_row = get_current_byte_index() - m_rowStartOffset; - uint32_t residual = bytes_in_row % alignment; - if (residual != 0) { - uint32_t padding = alignment - residual; - skip_bytes(padding); - } - } - } - - void handleTileAlignment(uint32_t alignment) { - if (alignment != 0) { - uint32_t bytes_in_tile = get_current_byte_index() - m_tileStartOffset; - uint32_t residual = bytes_in_tile % alignment; - if (residual != 0) { - uint32_t tile_padding = alignment - residual; - skip_bytes(tile_padding); - } - } - } - - private: - int m_pixelStartOffset; - int m_rowStartOffset; - int m_tileStartOffset; -}; - - -class AbstractDecoder -{ -public: - virtual ~AbstractDecoder() = default; - - virtual Error decode_tile(const HeifContext* context, - heif_item_id item_id, - std::shared_ptr& img, - uint32_t out_x0, uint32_t out_y0, - uint32_t image_width, uint32_t image_height, - uint32_t tile_x, uint32_t tile_y) { assert(false); return Error{heif_error_Unsupported_feature, - heif_suberror_Unsupported_image_type, - "unci tile decoding not supported for this image type"};} - -protected: - AbstractDecoder(uint32_t width, uint32_t height, const std::shared_ptr cmpd, const std::shared_ptr uncC): - m_width(width), - m_height(height), - m_cmpd(std::move(cmpd)), - m_uncC(std::move(uncC)) - { - m_tile_height = m_height / m_uncC->get_number_of_tile_rows(); - m_tile_width = m_width / m_uncC->get_number_of_tile_columns(); - - assert(m_tile_width > 0); - assert(m_tile_height > 0); - } - - const uint32_t m_width; - const uint32_t m_height; - const std::shared_ptr m_cmpd; - const std::shared_ptr m_uncC; - // TODO: see if we can make this const - uint32_t m_tile_height; - uint32_t m_tile_width; - - class ChannelListEntry - { - public: - uint32_t get_bytes_per_tile() const - { - return bytes_per_tile_row_src * tile_height; - } - - inline uint64_t getDestinationRowOffset(uint32_t tile_row, uint32_t tile_y) const { - uint64_t dst_row_number = tile_row * tile_height + tile_y; - return dst_row_number * dst_plane_stride; - } - - heif_channel channel = heif_channel_Y; - uint8_t* dst_plane; - uint8_t* other_chroma_dst_plane; - uint32_t dst_plane_stride; - uint32_t other_chroma_dst_plane_stride; - uint32_t tile_width; - uint32_t tile_height; - uint32_t bytes_per_component_sample; - uint16_t bits_per_component_sample; - uint8_t component_alignment; - uint32_t bytes_per_tile_row_src; - bool use_channel; - }; - - std::vector channelList; - -public: - void buildChannelList(std::shared_ptr& img) { - for (Box_uncC::Component component : m_uncC->get_components()) { - ChannelListEntry entry = buildChannelListEntry(component, img); - channelList.push_back(entry); - } - } - -protected: - void processComponentSample(UncompressedBitReader &srcBits, const ChannelListEntry &entry, uint64_t dst_row_offset, uint32_t tile_column, uint32_t tile_x) { - uint64_t dst_col_number = tile_column * entry.tile_width + tile_x; - uint64_t dst_column_offset = dst_col_number * entry.bytes_per_component_sample; - int val = srcBits.get_bits(entry.bits_per_component_sample); - memcpy(entry.dst_plane + dst_row_offset + dst_column_offset, &val, entry.bytes_per_component_sample); - } - - // Handles the case where a row consists of a single component type - // Not valid for Pixel interleave - // Not valid for the Cb/Cr channels in Mixed Interleave - // Not valid for multi-Y pixel interleave - void processComponentRow(ChannelListEntry &entry, UncompressedBitReader &srcBits, uint64_t dst_row_offset, uint32_t tile_column) - { - for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { - if (entry.component_alignment != 0) { - srcBits.skip_to_byte_boundary(); - int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; - srcBits.skip_bits(numPadBits); - } - processComponentSample(srcBits, entry, dst_row_offset, tile_column, tile_x); - } - srcBits.skip_to_byte_boundary(); - } - - void processComponentTileSample(UncompressedBitReader &srcBits, const ChannelListEntry &entry, uint64_t dst_offset, uint32_t tile_x) { - uint64_t dst_sample_offset = tile_x * entry.bytes_per_component_sample; - int val = srcBits.get_bits(entry.bits_per_component_sample); - memcpy(entry.dst_plane + dst_offset + dst_sample_offset, &val, entry.bytes_per_component_sample); - } - - // Handles the case where a row consists of a single component type - // Not valid for Pixel interleave - // Not valid for the Cb/Cr channels in Mixed Interleave - // Not valid for multi-Y pixel interleave - void processComponentTileRow(ChannelListEntry &entry, UncompressedBitReader &srcBits, uint64_t dst_offset) - { - for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { - if (entry.component_alignment != 0) { - srcBits.skip_to_byte_boundary(); - int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; - srcBits.skip_bits(numPadBits); - } - processComponentTileSample(srcBits, entry, dst_offset, tile_x); - } - srcBits.skip_to_byte_boundary(); - } - -private: - ChannelListEntry buildChannelListEntry(Box_uncC::Component component, std::shared_ptr &img) { - ChannelListEntry entry; - 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; - if ((entry.channel == heif_channel_Cb) || (entry.channel == heif_channel_Cr)) { - if (m_uncC->get_sampling_type() == sampling_mode_422) { - entry.tile_width /= 2; - } else if (m_uncC->get_sampling_type() == sampling_mode_420) { - entry.tile_width /= 2; - entry.tile_height /= 2; - } - if (entry.channel == heif_channel_Cb) { - entry.other_chroma_dst_plane = img->get_plane(heif_channel_Cr, &(entry.other_chroma_dst_plane_stride)); - } else if (entry.channel == heif_channel_Cr) { - entry.other_chroma_dst_plane = img->get_plane(heif_channel_Cb, &(entry.other_chroma_dst_plane_stride)); - } - } - entry.bits_per_component_sample = component.component_bit_depth; - entry.component_alignment = component.component_align_size; - entry.bytes_per_component_sample = (component.component_bit_depth + 7) / 8; - entry.bytes_per_tile_row_src = entry.tile_width * entry.bytes_per_component_sample; - return entry; - } - -protected: - - // generic compression and uncompressed, per 23001-17 - const Error get_compressed_image_data_uncompressed(const HeifContext* context, heif_item_id ID, - std::vector *data, - uint64_t range_start_offset, uint64_t range_size, - uint32_t tile_idx, - const Box_iloc::Item *item) const - { - // --- get codec configuration - - std::shared_ptr cmpC_box = context->get_heif_file()->get_property(ID); - std::shared_ptr icef_box = context->get_heif_file()->get_property(ID); - - if (!cmpC_box) { - // assume no generic compression - return context->get_heif_file()->append_data_from_iloc(ID, *data, range_start_offset, range_size); - } - - if (icef_box && cmpC_box->get_compressed_unit_type() == heif_cmpC_compressed_unit_type_image_tile) { - const auto& units = icef_box->get_units(); - if (tile_idx >= units.size()) { - return {heif_error_Invalid_input, - heif_suberror_Unspecified, - "no icef-box entry for tile index"}; - } - - const auto unit = units[tile_idx]; - - // get all data and decode all - std::vector compressed_bytes; - Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes, unit.unit_offset, unit.unit_size); - if (err) { - return err; - } - - // decompress only the unit - err = do_decompress_data(cmpC_box, compressed_bytes, data); - if (err) { - return err; - } - } - else if (icef_box) { - // get all data and decode all - std::vector compressed_bytes; - Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size); - if (err) { - return err; - } - - for (Box_icef::CompressedUnitInfo unit_info : icef_box->get_units()) { - auto unit_start = compressed_bytes.begin() + unit_info.unit_offset; - auto unit_end = unit_start + unit_info.unit_size; - std::vector compressed_unit_data = std::vector(unit_start, unit_end); - std::vector uncompressed_unit_data; - err = do_decompress_data(cmpC_box, compressed_unit_data, &uncompressed_unit_data); - if (err) { - return err; - } - data->insert(data->end(), uncompressed_unit_data.data(), uncompressed_unit_data.data() + uncompressed_unit_data.size()); - } - - // cut out the range that we actually need - memcpy(data->data(), data->data() + range_start_offset, range_size); - data->resize(range_size); - } - else { - // get all data and decode all - std::vector compressed_bytes; - Error err = context->get_heif_file()->append_data_from_iloc(ID, compressed_bytes); // image_id, src_data, tile_start_offset, total_tile_size); - if (err) { - return err; - } - - // Decode as a single blob - err = do_decompress_data(cmpC_box, compressed_bytes, data); - if (err) { - return err; - } - - // cut out the range that we actually need - memcpy(data->data(), data->data() + range_start_offset, range_size); - data->resize(range_size); - } - - return Error::Ok; - } - - const Error 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 decompress_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")) { -#if HAVE_ZLIB - return decompress_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 HAVE_ZLIB - return decompress_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; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_generic_compression_method, - sstr.str()); - } - } -}; - - -class ComponentInterleaveDecoder : public AbstractDecoder -{ -public: - ComponentInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): - AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) - {} - - Error decode_tile(const HeifContext* context, - heif_item_id image_id, - std::shared_ptr& img, - uint32_t out_x0, uint32_t out_y0, - uint32_t image_width, uint32_t image_height, - uint32_t tile_x, uint32_t tile_y) override - { - if (m_tile_width == 0) { - return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: ComponentInterleaveDecoder tile_width=0"}; - } - - // --- compute which file range we need to read for the tile - - uint64_t total_tile_size = 0; - - for (ChannelListEntry& entry : channelList) { - uint32_t bits_per_component = entry.bits_per_component_sample; - if (entry.component_alignment > 0) { - uint32_t bytes_per_component = (bits_per_component + 7)/8; - bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); - bits_per_component = bytes_per_component * 8; - } - - uint32_t bytes_per_tile_row = (bits_per_component * entry.tile_width + 7)/8; - bytes_per_tile_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_tile_row); - uint64_t bytes_per_tile = bytes_per_tile_row * entry.tile_height; - total_tile_size += bytes_per_tile; - } - - if (m_uncC->get_tile_align_size() != 0) { - total_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), total_tile_size); - } - - assert(m_tile_width > 0); - uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); - uint64_t tile_start_offset = total_tile_size * tileIdx; - - - // --- read required file range - - std::vector src_data; - //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); - Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); - if (err) { - return err; - } - - UncompressedBitReader srcBits(src_data); - - - // --- decode tile - - for (ChannelListEntry& entry : channelList) { - for (uint32_t y = 0; y < entry.tile_height; y++) { - srcBits.markRowStart(); - if (entry.use_channel) { - uint64_t dst_row_offset = (out_y0 + y) * entry.dst_plane_stride; - processComponentTileRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample); - } - else { - srcBits.skip_bytes(entry.bytes_per_tile_row_src); - } - srcBits.handleRowAlignment(m_uncC->get_row_align_size()); - } - } - - return Error::Ok; - } -}; - - -class PixelInterleaveDecoder : public AbstractDecoder -{ -public: - PixelInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): - AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) - {} - - Error decode_tile(const HeifContext* context, - heif_item_id image_id, - std::shared_ptr& img, - uint32_t out_x0, uint32_t out_y0, - uint32_t image_width, uint32_t image_height, - uint32_t tile_x, uint32_t tile_y) override - { - if (m_tile_width == 0) { - return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: PixelInterleaveDecoder tile_width=0"}; - } - - // --- compute which file range we need to read for the tile - - uint32_t bits_per_row = 0; - for (uint32_t x = 0 ; x 0) { - // start at byte boundary - bits_per_row = (bits_per_row + 7) & ~7U; - - uint32_t bytes_per_component = (bits_per_component + 7)/8; - bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); - bits_per_component = bytes_per_component * 8; - } - - bits_per_pixel += bits_per_component; - } - - if (m_uncC->get_pixel_size() != 0) { - uint32_t bytes_per_pixel = (bits_per_pixel + 7) / 8; - bytes_per_pixel += nAlignmentSkipBytes(m_uncC->get_pixel_size(), bytes_per_pixel); - bits_per_pixel = bytes_per_pixel * 8; - } - - bits_per_row += bits_per_pixel; - } - - uint32_t bytes_per_row = (bits_per_row + 7)/8; - bytes_per_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_row); - - uint64_t total_tile_size = bytes_per_row * static_cast(m_tile_height); - if (m_uncC->get_tile_align_size() != 0) { - total_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), total_tile_size); - } - - assert(m_tile_width > 0); - uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); - uint64_t tile_start_offset = total_tile_size * tileIdx; - - - // --- read required file range - - std::vector src_data; - Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); - //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); - if (err) { - return err; - } - - UncompressedBitReader srcBits(src_data); - - processTile(srcBits, tile_y, tile_x, out_x0, out_y0); - - return Error::Ok; - } - - void processTile(UncompressedBitReader &srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { - for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) { - srcBits.markRowStart(); - for (uint32_t tile_x = 0; tile_x < m_tile_width; tile_x++) { - srcBits.markPixelStart(); - for (ChannelListEntry &entry : channelList) { - if (entry.use_channel) { - uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); - if (entry.component_alignment != 0) { - srcBits.skip_to_byte_boundary(); - int numPadBits = (entry.component_alignment * 8) - entry.bits_per_component_sample; - srcBits.skip_bits(numPadBits); - } - processComponentSample(srcBits, entry, dst_row_offset, 0, out_x0 + tile_x); - } else { - srcBits.skip_bytes(entry.bytes_per_component_sample); - } - } - srcBits.handlePixelAlignment(m_uncC->get_pixel_size()); - } - srcBits.handleRowAlignment(m_uncC->get_row_align_size()); - } - } -}; - - -class MixedInterleaveDecoder : public AbstractDecoder -{ -public: - MixedInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): - AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) - {} - - Error decode_tile(const HeifContext* context, - heif_item_id image_id, - std::shared_ptr& img, - uint32_t out_x0, uint32_t out_y0, - uint32_t image_width, uint32_t image_height, - uint32_t tile_x, uint32_t tile_y) override - { - if (m_tile_width == 0) { - return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: MixedInterleaveDecoder tile_width=0"}; - } - - // --- compute which file range we need to read for the tile - - uint64_t tile_size = 0; - - for (ChannelListEntry& entry : channelList) { - if (entry.channel == heif_channel_Cb || entry.channel == heif_channel_Cr) { - uint32_t bits_per_row = entry.bits_per_component_sample * entry.tile_width; - bits_per_row = (bits_per_row+7) & ~7U; // align to byte boundary - - tile_size += bits_per_row / 8 * entry.tile_height; - } - else { - uint32_t bits_per_component = entry.bits_per_component_sample; - if (entry.component_alignment > 0) { - uint32_t bytes_per_component = (bits_per_component + 7)/8; - bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); - bits_per_component = bytes_per_component * 8; - } - - uint32_t bits_per_row = bits_per_component * entry.tile_width; - bits_per_row = (bits_per_row+7) & ~7U; // align to byte boundary - - tile_size += bits_per_row / 8 * entry.tile_height; - } - } - - - if (m_uncC->get_tile_align_size() != 0) { - tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), tile_size); - } - - assert(m_tile_width > 0); - uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); - uint64_t tile_start_offset = tile_size * tileIdx; - - - // --- read required file range - - std::vector src_data; - Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, tile_size, tileIdx, nullptr); - //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, tile_size); - if (err) { - return err; - } - - UncompressedBitReader srcBits(src_data); - - processTile(srcBits, tile_y, tile_x, out_x0, out_y0); - - return Error::Ok; - } - - - void processTile(UncompressedBitReader &srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { - bool haveProcessedChromaForThisTile = false; - for (ChannelListEntry &entry : channelList) { - if (entry.use_channel) { - if ((entry.channel == heif_channel_Cb) || (entry.channel == heif_channel_Cr)) { - if (!haveProcessedChromaForThisTile) { - for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { - // TODO: row padding - uint64_t dst_row_number = tile_y + out_y0; - uint64_t dst_row_offset = dst_row_number * entry.dst_plane_stride; - for (uint32_t tile_x = 0; tile_x < entry.tile_width; tile_x++) { - uint64_t dst_column_number = out_x0 + tile_x; - uint64_t dst_column_offset = dst_column_number * entry.bytes_per_component_sample; - int val = srcBits.get_bits(entry.bytes_per_component_sample * 8); - memcpy(entry.dst_plane + dst_row_offset + dst_column_offset, &val, entry.bytes_per_component_sample); - val = srcBits.get_bits(entry.bytes_per_component_sample * 8); - memcpy(entry.other_chroma_dst_plane + dst_row_offset + dst_column_offset, &val, entry.bytes_per_component_sample); - } - haveProcessedChromaForThisTile = true; - } - } - } else { - for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { - uint64_t dst_row_offset = entry.getDestinationRowOffset(tile_row, tile_y); - processComponentRow(entry, srcBits, dst_row_offset, tile_column); - } - } - } else { - // skip over the data we are not using - srcBits.skip_bytes(entry.get_bytes_per_tile()); - continue; - } - } - } -}; - -class RowInterleaveDecoder : public AbstractDecoder -{ -public: - RowInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): - AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) - {} - - Error decode_tile(const HeifContext* context, - heif_item_id image_id, - std::shared_ptr& img, - uint32_t out_x0, uint32_t out_y0, - uint32_t image_width, uint32_t image_height, - uint32_t tile_x, uint32_t tile_y) override - { - if (m_tile_width == 0) { - return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: RowInterleaveDecoder tile_width=0"}; - } - - // --- compute which file range we need to read for the tile - - uint32_t bits_per_row = 0; - for (ChannelListEntry& entry : channelList) { - uint32_t bits_per_component = entry.bits_per_component_sample; - if (entry.component_alignment > 0) { - // start at byte boundary - bits_per_row = (bits_per_row + 7) & ~7U; - - uint32_t bytes_per_component = (bits_per_component + 7)/8; - bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); - bits_per_component = bytes_per_component * 8; - } - - if (m_uncC->get_row_align_size() != 0) { - uint32_t bytes_this_row = (bits_per_component * m_tile_width + 7) / 8; - bytes_this_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_this_row); - bits_per_row += bytes_this_row * 8; - } - else { - bits_per_row += bits_per_component * m_tile_width; - } - - bits_per_row = (bits_per_row + 7) & ~7U; - } - - uint32_t bytes_per_row = (bits_per_row + 7) / 8; - if (m_uncC->get_row_align_size()) { - bytes_per_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_row); - } - - uint64_t total_tile_size = 0; - total_tile_size += bytes_per_row * static_cast(m_tile_height); - - if (m_uncC->get_tile_align_size() != 0) { - total_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), total_tile_size); - } - - assert(m_tile_width > 0); - uint32_t tileIdx = tile_x + tile_y * (image_width / m_tile_width); - uint64_t tile_start_offset = total_tile_size * tileIdx; - - - // --- read required file range - - std::vector src_data; - Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, total_tile_size, tileIdx, nullptr); - //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, total_tile_size); - if (err) { - return err; - } - - UncompressedBitReader srcBits(src_data); - - processTile(srcBits, tile_y, tile_x, out_x0, out_y0); - - return Error::Ok; - } - -private: - void processTile(UncompressedBitReader &srcBits, uint32_t tile_row, uint32_t tile_column, uint32_t out_x0, uint32_t out_y0) { - for (uint32_t tile_y = 0; tile_y < m_tile_height; tile_y++) { - for (ChannelListEntry &entry : channelList) { - srcBits.markRowStart(); - if (entry.use_channel) { - uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); - processComponentRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample, 0); - } else { - srcBits.skip_bytes(entry.bytes_per_tile_row_src); - } - srcBits.handleRowAlignment(m_uncC->get_row_align_size()); - } - } - } -}; - - -class TileComponentInterleaveDecoder : public AbstractDecoder -{ -public: - TileComponentInterleaveDecoder(uint32_t width, uint32_t height, std::shared_ptr cmpd, std::shared_ptr uncC): - AbstractDecoder(width, height, std::move(cmpd), std::move(uncC)) - {} - - Error decode_tile(const HeifContext* context, - heif_item_id image_id, - std::shared_ptr& img, - uint32_t out_x0, uint32_t out_y0, - uint32_t image_width, uint32_t image_height, - uint32_t tile_column, uint32_t tile_row) override - { - if (m_tile_width == 0) { - return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: TileComponentInterleaveDecoder tile_width=0"}; - } - if (m_tile_height == 0) { - return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "Internal error: TileComponentInterleaveDecoder tile_height=0"}; - } - - // --- compute which file range we need to read for the tile - - std::map channel_tile_size; - - //uint64_t total_tile_size = 0; - - for (ChannelListEntry& entry : channelList) { - uint32_t bits_per_pixel = entry.bits_per_component_sample; - if (entry.component_alignment > 0) { - // start at byte boundary - //bits_per_row = (bits_per_row + 7) & ~7U; - - uint32_t bytes_per_component = (bits_per_pixel + 7)/8; - bytes_per_component += nAlignmentSkipBytes(entry.component_alignment, bytes_per_component); - bits_per_pixel = bytes_per_component * 8; - } - - uint32_t bytes_per_row; - if (m_uncC->get_pixel_size() != 0) { // TODO: does pixel_size apply here? - uint32_t bytes_per_pixel = (bits_per_pixel + 7) / 8; - bytes_per_pixel += nAlignmentSkipBytes(m_uncC->get_pixel_size(), bytes_per_pixel); - bytes_per_row = bytes_per_pixel * m_tile_width; - } - else { - bytes_per_row = (bits_per_pixel * m_tile_width + 7) / 8; - } - - bytes_per_row += nAlignmentSkipBytes(m_uncC->get_row_align_size(), bytes_per_row); - - uint64_t component_tile_size = bytes_per_row * static_cast(m_tile_height); - - if (m_uncC->get_tile_align_size() != 0) { - component_tile_size += nAlignmentSkipBytes(m_uncC->get_tile_align_size(), component_tile_size); - } - - channel_tile_size[entry.channel] = component_tile_size; - - //total_tile_size += component_tile_size; - } - - uint64_t component_start_offset = 0; - - assert(m_tile_width > 0); - assert(m_tile_height > 0); - - for (ChannelListEntry& entry : channelList) { - //processTile(srcBits, tile_y, tile_x, out_x0, out_y0); - - if (!entry.use_channel) { - //uint64_t bytes_per_component = entry.get_bytes_per_tile() * m_uncC->get_number_of_tile_columns() * m_uncC->get_number_of_tile_rows(); - //srcBits.skip_bytes((int)bytes_per_component); - - component_start_offset += channel_tile_size[entry.channel] * (m_width / m_tile_width) * (m_height / m_tile_height); - continue; - } - - // --- read required file range - - uint32_t tileIdx = tile_column + tile_row * (image_width / m_tile_width); - uint64_t tile_start_offset = component_start_offset + channel_tile_size[entry.channel] * tileIdx; - - std::vector src_data; - Error err = get_compressed_image_data_uncompressed(context, image_id, &src_data, tile_start_offset, channel_tile_size[entry.channel], tileIdx, nullptr); - //Error err = context->get_heif_file()->append_data_from_iloc(image_id, src_data, tile_start_offset, channel_tile_size[entry.channel]); - if (err) { - return err; - } - - UncompressedBitReader srcBits(src_data); - - srcBits.markTileStart(); - for (uint32_t tile_y = 0; tile_y < entry.tile_height; tile_y++) { - srcBits.markRowStart(); - uint64_t dst_row_offset = entry.getDestinationRowOffset(0, tile_y + out_y0); - processComponentRow(entry, srcBits, dst_row_offset + out_x0 * entry.bytes_per_component_sample, 0); - srcBits.handleRowAlignment(m_uncC->get_row_align_size()); - } - srcBits.handleTileAlignment(m_uncC->get_tile_align_size()); - - - component_start_offset += channel_tile_size[entry.channel] * (m_width / m_tile_width) * (m_height / m_tile_height); - } - - return Error::Ok; - } -}; - - -static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std::shared_ptr& cmpd, const std::shared_ptr& uncC) -{ - if (uncC->get_interleave_type() == interleave_mode_component) { - return new ComponentInterleaveDecoder(width, height, cmpd, uncC); - } else if (uncC->get_interleave_type() == interleave_mode_pixel) { - return new PixelInterleaveDecoder(width, height, cmpd, uncC); - } else if (uncC->get_interleave_type() == interleave_mode_mixed) { - return new MixedInterleaveDecoder(width, height, cmpd, uncC); - } else if (uncC->get_interleave_type() == interleave_mode_row) { - return new RowInterleaveDecoder(width, height, cmpd, uncC); - } else if (uncC->get_interleave_type() == interleave_mode_tile_component) { - return new TileComponentInterleaveDecoder(width, height, cmpd, uncC); - } else { - return nullptr; - } -} - - -Result> UncompressedImageCodec::create_image(const std::shared_ptr cmpd, - const std::shared_ptr uncC, - uint32_t width, - uint32_t height) -{ - auto img = std::make_shared(); - heif_chroma chroma; - heif_colorspace colourspace; - Error error = get_heif_chroma_uncompressed(uncC, cmpd, &chroma, &colourspace); - if (error) { - return error; - } - img->create(width, height, - colourspace, - chroma); - - for (Box_uncC::Component component : uncC->get_components()) { - heif_channel channel; - if (map_uncompressed_component_to_channel(cmpd, uncC, component, &channel)) { - if (img->has_channel(channel)) { - return Error{heif_error_Unsupported_feature, - heif_suberror_Unspecified, - "Cannot generate image with several similar heif_channels."}; - } - - 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 { - img->add_plane(channel, width, height, component.component_bit_depth); - } - } - } - - return img; -} - - -Error UncompressedImageCodec::decode_uncompressed_image_tile(const HeifContext* context, - heif_item_id ID, - std::shared_ptr& img, - uint32_t tile_x0, uint32_t tile_y0) -{ - auto file = context->get_heif_file(); - std::shared_ptr ispe = file->get_property(ID); - std::shared_ptr cmpd = file->get_property(ID); - std::shared_ptr uncC = file->get_property(ID); - - Error error = check_header_validity(ispe, cmpd, uncC); - if (error) { - return error; - } - - uint32_t tile_width = ispe->get_width() / uncC->get_number_of_tile_columns(); - uint32_t tile_height = ispe->get_height() / uncC->get_number_of_tile_rows(); - - Result> createImgResult = create_image(cmpd, uncC, tile_width, tile_height); - if (createImgResult.error) { - return createImgResult.error; - } - - img = createImgResult.value; - - - AbstractDecoder *decoder = makeDecoder(ispe->get_width(), ispe->get_height(), cmpd, uncC); - if (decoder == nullptr) { - std::stringstream sstr; - sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - - decoder->buildChannelList(img); - - Error result = decoder->decode_tile(context, ID, img, 0, 0, - ispe->get_width(), ispe->get_height(), - tile_x0, tile_y0); - delete decoder; - return result; -} - - -Error UncompressedImageCodec::check_header_validity(const std::shared_ptr& ispe, - const std::shared_ptr& cmpd, - const std::shared_ptr& uncC) -{ - // if we miss a required box, show error - - if (!ispe) { - return {heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Missing required ispe box for uncompressed codec"}; - } - - if (!uncC) { - return {heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Missing required uncC box for uncompressed codec"}; - } - - if (!cmpd && (uncC->get_version() != 1)) { - return {heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Missing required cmpd or uncC version 1 box for uncompressed codec"}; - } - - if (cmpd) { - if (uncC->get_components().size() != cmpd->get_components().size()) { - return {heif_error_Invalid_input, - heif_suberror_Unspecified, - "Number of components in uncC and cmpd do not match"}; - } - - for (const auto& comp : uncC->get_components()) { - if (comp.component_index > cmpd->get_components().size()) { - return {heif_error_Invalid_input, - heif_suberror_Unspecified, - "Invalid component index in uncC box"}; - } - } - } - - if (uncC->get_number_of_tile_rows() > ispe->get_height() || - uncC->get_number_of_tile_columns() > ispe->get_width()) { - return {heif_error_Invalid_input, - heif_suberror_Unspecified, - "More tiles than pixels in uncC box"}; - } - - if (ispe->get_height() % uncC->get_number_of_tile_rows() != 0 || - ispe->get_width() % uncC->get_number_of_tile_columns() != 0) { - return {heif_error_Invalid_input, - heif_suberror_Unspecified, - "Invalid tile size (image size not a multiple of the tile size)"}; - } - - return Error::Ok; -} - - -Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* context, - heif_item_id ID, - std::shared_ptr& img) -{ - // Get the properties for this item - // We need: ispe, cmpd, uncC - std::vector> item_properties; - Error error = context->get_heif_file()->get_properties(ID, item_properties); - if (error) { - return error; - } - - std::shared_ptr ispe = context->get_heif_file()->get_property(ID); - std::shared_ptr cmpd = context->get_heif_file()->get_property(ID); - std::shared_ptr uncC = context->get_heif_file()->get_property(ID); - - error = check_header_validity(ispe, cmpd, uncC); - if (error) { - return error; - } - - // check if we support the type of image - - error = uncompressed_image_type_is_supported(uncC, cmpd); // TODO TODO TODO - if (error) { - return error; - } - - assert(ispe); - uint32_t width = ispe->get_width(); - uint32_t height = ispe->get_height(); - error = context->check_resolution(width, height); - if (error) { - return error; - } - - Result> createImgResult = create_image(cmpd, uncC, width, height); - if (createImgResult.error) { - return createImgResult.error; - } - else { - img = *createImgResult; - } - - AbstractDecoder *decoder = makeDecoder(width, height, cmpd, uncC); - if (decoder == nullptr) { - std::stringstream sstr; - sstr << "Uncompressed interleave_type of " << ((int) uncC->get_interleave_type()) << " is not implemented yet"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - sstr.str()); - } - - decoder->buildChannelList(img); - - uint32_t tile_width = width / uncC->get_number_of_tile_columns(); - uint32_t tile_height = height / uncC->get_number_of_tile_rows(); - - for (uint32_t tile_y0 = 0; tile_y0 < height; tile_y0 += tile_height) - for (uint32_t tile_x0 = 0; tile_x0 < width; tile_x0 += tile_width) { - error = decoder->decode_tile(context, ID, img, tile_x0, tile_y0, - width, height, - tile_x0 / tile_width, tile_y0 / tile_height); - if (error) { - delete decoder; - return error; - } - } - - //Error result = decoder->decode(source_data, img); - delete decoder; - return Error::Ok; -} - -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))) - { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Invalid colourspace / channel combination - YCbCr"); - } - Box_cmpd::Component yComponent = {component_type_Y}; - cmpd->add_component(yComponent); - Box_cmpd::Component cbComponent = {component_type_Cb}; - cmpd->add_component(cbComponent); - Box_cmpd::Component crComponent = {component_type_Cr}; - cmpd->add_component(crComponent); - 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); - 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); - 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) - { - uncC->set_sampling_type(sampling_mode_no_subsampling); - } - else if (image->get_chroma_format() == heif_chroma_422) - { - uncC->set_sampling_type(sampling_mode_422); - } - else if (image->get_chroma_format() == heif_chroma_420) - { - uncC->set_sampling_type(sampling_mode_420); - } - else - { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Unsupported YCbCr sub-sampling type"); - } - uncC->set_interleave_type(interleave_mode_component); - uncC->set_block_size(0); - uncC->set_components_little_endian(false); - uncC->set_block_pad_lsb(false); - uncC->set_block_little_endian(false); - uncC->set_block_reversed(false); - uncC->set_pad_unknown(false); - uncC->set_pixel_size(0); - uncC->set_row_align_size(0); - uncC->set_tile_align_size(0); - uncC->set_number_of_tile_columns(nTileColumns); - uncC->set_number_of_tile_rows(nTileRows); - } - else if (colourspace == heif_colorspace_RGB) - { - if (!((image->get_chroma_format() == heif_chroma_444) || - (image->get_chroma_format() == heif_chroma_interleaved_RGB) || - (image->get_chroma_format() == heif_chroma_interleaved_RGBA) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE))) { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Unsupported colourspace / chroma combination - RGB"); - } - Box_cmpd::Component rComponent = {component_type_red}; - cmpd->add_component(rComponent); - Box_cmpd::Component gComponent = {component_type_green}; - cmpd->add_component(gComponent); - Box_cmpd::Component bComponent = {component_type_blue}; - cmpd->add_component(bComponent); - if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE) || - (image->has_channel(heif_channel_Alpha))) - { - Box_cmpd::Component alphaComponent = {component_type_alpha}; - cmpd->add_component(alphaComponent); - } - if ((image->get_chroma_format() == heif_chroma_interleaved_RGB) || - (image->get_chroma_format() == heif_chroma_interleaved_RGBA) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) - { - uncC->set_interleave_type(interleave_mode_pixel); - int bpp = image->get_bits_per_pixel(heif_channel_interleaved); - uint8_t component_align = 1; - if (bpp == 8) - { - component_align = 0; - } - else if (bpp > 8) - { - component_align = 2; - } - Box_uncC::Component component0 = {0, (uint8_t)(bpp), component_format_unsigned, component_align}; - uncC->add_component(component0); - Box_uncC::Component component1 = {1, (uint8_t)(bpp), component_format_unsigned, component_align}; - uncC->add_component(component1); - Box_uncC::Component component2 = {2, (uint8_t)(bpp), component_format_unsigned, component_align}; - uncC->add_component(component2); - if ((image->get_chroma_format() == heif_chroma_interleaved_RGBA) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) - { - Box_uncC::Component component3 = { - 3, (uint8_t)(bpp), component_format_unsigned, component_align}; - uncC->add_component(component3); - } - } else { - uncC->set_interleave_type(interleave_mode_component); - int bpp_red = image->get_bits_per_pixel(heif_channel_R); - Box_uncC::Component component0 = {0, (uint8_t)(bpp_red), component_format_unsigned, 0}; - uncC->add_component(component0); - int bpp_green = image->get_bits_per_pixel(heif_channel_G); - Box_uncC::Component component1 = {1, (uint8_t)(bpp_green), component_format_unsigned, 0}; - uncC->add_component(component1); - int bpp_blue = image->get_bits_per_pixel(heif_channel_B); - Box_uncC::Component component2 = {2, (uint8_t)(bpp_blue), component_format_unsigned, 0}; - uncC->add_component(component2); - if(image->has_channel(heif_channel_Alpha)) - { - int bpp_alpha = image->get_bits_per_pixel(heif_channel_Alpha); - Box_uncC::Component component3 = {3, (uint8_t)(bpp_alpha), component_format_unsigned, 0}; - uncC->add_component(component3); - } - } - uncC->set_sampling_type(sampling_mode_no_subsampling); - uncC->set_block_size(0); - if ((image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || - (image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) - { - uncC->set_components_little_endian(true); - } else { - uncC->set_components_little_endian(false); - } - uncC->set_block_pad_lsb(false); - uncC->set_block_little_endian(false); - uncC->set_block_reversed(false); - uncC->set_pad_unknown(false); - uncC->set_pixel_size(0); - uncC->set_row_align_size(0); - uncC->set_tile_align_size(0); - uncC->set_number_of_tile_columns(nTileColumns); - uncC->set_number_of_tile_rows(nTileRows); - } - else if (colourspace == heif_colorspace_monochrome) - { - Box_cmpd::Component monoComponent = {component_type_monochrome}; - cmpd->add_component(monoComponent); - if (image->has_channel(heif_channel_Alpha)) - { - Box_cmpd::Component alphaComponent = {component_type_alpha}; - cmpd->add_component(alphaComponent); - } - int bpp = image->get_bits_per_pixel(heif_channel_Y); - Box_uncC::Component component0 = {0, (uint8_t)(bpp), component_format_unsigned, 0}; - uncC->add_component(component0); - if (image->has_channel(heif_channel_Alpha)) - { - bpp = image->get_bits_per_pixel(heif_channel_Alpha); - Box_uncC::Component component1 = {1, (uint8_t)(bpp), component_format_unsigned, 0}; - uncC->add_component(component1); - } - uncC->set_sampling_type(sampling_mode_no_subsampling); - uncC->set_interleave_type(interleave_mode_component); - uncC->set_block_size(0); - uncC->set_components_little_endian(false); - uncC->set_block_pad_lsb(false); - uncC->set_block_little_endian(false); - uncC->set_block_reversed(false); - uncC->set_pad_unknown(false); - uncC->set_pixel_size(0); - uncC->set_row_align_size(0); - uncC->set_tile_align_size(0); - uncC->set_number_of_tile_columns(nTileColumns); - uncC->set_number_of_tile_rows(nTileRows); - } - else - { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Unsupported colourspace"); - } - return Error::Ok; -} - static void maybe_make_minimised_uncC(std::shared_ptr& uncC, const std::shared_ptr& image) { diff --git a/libheif/codecs/uncompressed/unc_image.h b/libheif/codecs/uncompressed/unc_image.h index 5b8a42647a..d2ebd66bbd 100644 --- a/libheif/codecs/uncompressed/unc_image.h +++ b/libheif/codecs/uncompressed/unc_image.h @@ -34,40 +34,6 @@ class HeifContext; -bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr& uncC); - - -class UncompressedImageCodec -{ -public: - static int get_luma_bits_per_pixel_from_configuration_unci(const HeifFile& heif_file, heif_item_id imageID); - static int get_chroma_bits_per_pixel_from_configuration_unci(const HeifFile& heif_file, heif_item_id imageID); - - static Error decode_uncompressed_image(const HeifContext* context, - heif_item_id ID, - std::shared_ptr& img); - - static Error decode_uncompressed_image_tile(const HeifContext* context, - heif_item_id ID, - std::shared_ptr& img, - uint32_t tile_x0, uint32_t tile_y0); - - static Error get_heif_chroma_uncompressed(const std::shared_ptr& uncC, - const std::shared_ptr& cmpd, - heif_chroma* out_chroma, - heif_colorspace* out_colourspace); - - static Result> create_image(std::shared_ptr, - std::shared_ptr, - uint32_t width, - uint32_t height); - - static Error check_header_validity(const std::shared_ptr&, - const std::shared_ptr&, - const std::shared_ptr&); -}; - - class ImageItem_uncompressed : public ImageItem { public: