diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 88dc899600..ab34b6b564 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -50,30 +50,56 @@ set(libheif_sources api/libheif/heif_items.cc api/libheif/heif_experimental.h api/libheif/heif_experimental.cc - codecs/hevc.cc - codecs/hevc.h - codecs/avif.cc - codecs/avif.h - codecs/jpeg.h - codecs/jpeg.cc - codecs/jpeg2000.h - codecs/jpeg2000.cc - codecs/vvc.h - codecs/vvc.cc - codecs/avc.h - codecs/avc.cc - codecs/mask_image.h - codecs/mask_image.cc - codecs/image_item.h - codecs/image_item.cc - codecs/grid.h - codecs/grid.cc - codecs/overlay.h - codecs/overlay.cc - codecs/iden.h - codecs/iden.cc - codecs/tild.h - codecs/tild.cc + codecs/decoder.h + codecs/decoder.cc + image-items/hevc.cc + image-items/hevc.h + codecs/hevc_boxes.cc + codecs/hevc_boxes.h + codecs/hevc_dec.cc + codecs/hevc_dec.h + image-items/avif.cc + image-items/avif.h + codecs/avif_dec.cc + codecs/avif_dec.h + codecs/avif_boxes.cc + codecs/avif_boxes.h + image-items/jpeg.h + image-items/jpeg.cc + codecs/jpeg_boxes.h + codecs/jpeg_boxes.cc + codecs/jpeg_dec.h + codecs/jpeg_dec.cc + image-items/jpeg2000.h + image-items/jpeg2000.cc + codecs/jpeg2000_dec.h + codecs/jpeg2000_dec.cc + codecs/jpeg2000_boxes.h + codecs/jpeg2000_boxes.cc + image-items/vvc.h + image-items/vvc.cc + codecs/vvc_dec.h + codecs/vvc_dec.cc + codecs/vvc_boxes.h + codecs/vvc_boxes.cc + image-items/avc.h + image-items/avc.cc + codecs/avc_boxes.h + codecs/avc_boxes.cc + codecs/avc_dec.h + codecs/avc_dec.cc + image-items/mask_image.h + image-items/mask_image.cc + image-items/image_item.h + image-items/image_item.cc + image-items/grid.h + image-items/grid.cc + image-items/overlay.h + image-items/overlay.cc + image-items/iden.h + image-items/iden.cc + image-items/tild.h + image-items/tild.cc color-conversion/colorconversion.cc color-conversion/colorconversion.h color-conversion/rgb2yuv.cc @@ -172,8 +198,24 @@ if (WITH_UNCOMPRESSED_CODEC) target_sources(heif PRIVATE codecs/uncompressed/unc_boxes.h codecs/uncompressed/unc_boxes.cc - codecs/uncompressed/unc_image.h - codecs/uncompressed/unc_image.cc) + image-items/unc_image.h + image-items/unc_image.cc + codecs/uncompressed/unc_codec.h + codecs/uncompressed/unc_codec.cc + codecs/uncompressed/unc_dec.h + codecs/uncompressed/unc_dec.cc + codecs/uncompressed/decoder_abstract.h + codecs/uncompressed/decoder_abstract.cc + codecs/uncompressed/decoder_component_interleave.h + codecs/uncompressed/decoder_component_interleave.cc + codecs/uncompressed/decoder_pixel_interleave.h + codecs/uncompressed/decoder_pixel_interleave.cc + codecs/uncompressed/decoder_mixed_interleave.h + codecs/uncompressed/decoder_mixed_interleave.cc + codecs/uncompressed/decoder_row_interleave.h + codecs/uncompressed/decoder_row_interleave.cc + codecs/uncompressed/decoder_tile_component_interleave.h + codecs/uncompressed/decoder_tile_component_interleave.cc) endif () write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion) diff --git a/libheif/api/libheif/api_structs.h b/libheif/api/libheif/api_structs.h index a997aeff8f..77840ca8ee 100644 --- a/libheif/api/libheif/api_structs.h +++ b/libheif/api/libheif/api_structs.h @@ -25,7 +25,7 @@ #include "context.h" #include -#include +#include "image-items/image_item.h" struct heif_image_handle { diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index 9b79ac25e7..e4f4873b02 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -31,14 +31,14 @@ #include "error.h" #include "bitstream.h" #include "init.h" -#include "codecs/grid.h" -#include "codecs/overlay.h" -#include "codecs/tild.h" +#include "image-items/grid.h" +#include "image-items/overlay.h" +#include "image-items/tild.h" #include #include #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed/unc_image.h" +#include "image-items/unc_image.h" #endif #if defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_STANDALONE_WASM__) @@ -292,13 +292,13 @@ heif_brand2 heif_read_main_brand(const uint8_t* data, int len) } -heif_brand2 heif_fourcc_to_brand(const char* fourcc) +heif_brand2 heif_fourcc_to_brand(const char* fourcc_string) { - if (fourcc == nullptr || !fourcc[0] || !fourcc[1] || !fourcc[2] || !fourcc[3]) { + if (fourcc_string == nullptr || !fourcc_string[0] || !fourcc_string[1] || !fourcc_string[2] || !fourcc_string[3]) { return 0; } - return fourcc_to_uint32(fourcc); + return fourcc(fourcc_string); } @@ -337,7 +337,7 @@ int heif_has_compatible_brand(const uint8_t* data, int len, const char* brand_fo return -2; } - return ftyp->has_compatible_brand(fourcc_to_uint32(brand_fourcc)) ? 1 : 0; + return ftyp->has_compatible_brand(fourcc(brand_fourcc)) ? 1 : 0; } @@ -3478,7 +3478,7 @@ struct heif_error heif_context_add_image_tile(struct heif_context* ctx, const struct heif_image* image, struct heif_encoder* encoder) { - if (tiled_image->image->get_infe_type() == std::string{"tili"}) { + if (tiled_image->image->get_infe_type() == fourcc("tili")) { Error err = ctx->context->add_tild_image_tile(tiled_image->image->get_id(), tile_x, tile_y, image->image, encoder); return err.error_struct(ctx->context.get()); } @@ -3642,8 +3642,14 @@ struct heif_error heif_context_add_generic_metadata(struct heif_context* ctx, const void* data, int size, const char* item_type, const char* content_type) { + if (item_type == nullptr || strlen(item_type) != 4) { + return {heif_error_Usage_error, + heif_suberror_Invalid_parameter_value, + "called heif_context_add_generic_metadata() with invalid 'item_type'."}; + } + Error error = ctx->context->add_generic_metadata(image_handle->image, data, size, - item_type, content_type, nullptr, heif_metadata_compression_off, nullptr); + fourcc(item_type), content_type, nullptr, heif_metadata_compression_off, nullptr); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } @@ -3660,7 +3666,7 @@ struct heif_error heif_context_add_generic_uri_metadata(struct heif_context* ctx heif_item_id* out_item_id) { Error error = ctx->context->add_generic_metadata(image_handle->image, data, size, - "uri ", nullptr, item_uri_type, heif_metadata_compression_off, out_item_id); + fourcc("uri "), nullptr, item_uri_type, heif_metadata_compression_off, out_item_id); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index 5d7920e922..972abe3714 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -1141,16 +1141,18 @@ LIBHEIF_API int heif_image_handle_is_premultiplied_alpha(const struct heif_image_handle*); // Returns -1 on error, e.g. if this information is not present in the image. +// Only defined for images coded in the YCbCr colorspace. LIBHEIF_API int heif_image_handle_get_luma_bits_per_pixel(const struct heif_image_handle*); // Returns -1 on error, e.g. if this information is not present in the image. +// Only defined for images coded in the YCbCr colorspace. LIBHEIF_API int heif_image_handle_get_chroma_bits_per_pixel(const struct heif_image_handle*); // Return the colorspace that libheif proposes to use for decoding. // Usually, these will be either YCbCr or Monochrome, but it may also propose RGB for images -// encoded with matrix_coefficients=0. +// encoded with matrix_coefficients=0 or for images coded natively in RGB. // It may also return *_undefined if the file misses relevant information to determine this without decoding. LIBHEIF_API struct heif_error heif_image_handle_get_preferred_decoding_colorspace(const struct heif_image_handle* image_handle, diff --git a/libheif/api/libheif/heif_items.cc b/libheif/api/libheif/heif_items.cc index dd96dd4ae4..044bec5938 100644 --- a/libheif/api/libheif/heif_items.cc +++ b/libheif/api/libheif/heif_items.cc @@ -61,13 +61,7 @@ int heif_context_get_list_of_item_IDs(const struct heif_context* ctx, uint32_t heif_context_get_item_type(const struct heif_context* ctx, heif_item_id item_id) { - auto type = ctx->context->get_heif_file()->get_item_type(item_id); - if (type.empty()) { - return 0; - } - else { - return fourcc(type.c_str()); - } + return ctx->context->get_heif_file()->get_item_type_4cc(item_id); } @@ -88,7 +82,7 @@ const char* heif_context_get_mime_item_content_type(const struct heif_context* c auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (!infe) { return nullptr; } - if (infe->get_item_type() != "mime") { + if (infe->get_item_type_4cc() != fourcc("mime")) { return nullptr; } @@ -100,7 +94,7 @@ const char* heif_context_get_mime_item_content_encoding(const struct heif_contex auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (!infe) { return nullptr; } - if (infe->get_item_type() != "mime") { + if (infe->get_item_type_4cc() != fourcc("mime")) { return nullptr; } @@ -113,7 +107,7 @@ const char* heif_context_get_uri_item_uri_type(const struct heif_context* ctx, h auto infe = ctx->context->get_heif_file()->get_infe_box(item_id); if (!infe) { return nullptr; } - if (infe->get_item_type() != "uri ") { + if (infe->get_item_type_4cc() != fourcc("uri ")) { return nullptr; } @@ -178,7 +172,7 @@ struct heif_error heif_context_get_item_data(struct heif_context* ctx, void* out_data) { std::vector data; - Error err = ctx->context->get_heif_file()->get_compressed_image_data(item_id, &data); + Error err = ctx->context->get_heif_file()->get_uncompressed_item_data(item_id, &data); if (err) { return err.error_struct(ctx->context.get()); @@ -244,7 +238,13 @@ struct heif_error heif_context_add_item(struct heif_context* ctx, const void* data, int size, heif_item_id* out_item_id) { - Result result = ctx->context->get_heif_file()->add_infe(item_type, (const uint8_t*) data, size); + if (item_type == nullptr || strlen(item_type) != 4) { + return {heif_error_Usage_error, + heif_suberror_Invalid_parameter_value, + "called heif_context_add_item() with invalid 'item_type'."}; + } + + Result result = ctx->context->get_heif_file()->add_infe(fourcc(item_type), (const uint8_t*) data, size); if (result && out_item_id) { *out_item_id = result.value; diff --git a/libheif/box.cc b/libheif/box.cc index f0b622cd1e..a8b9630465 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -23,13 +23,14 @@ #include "box.h" #include "security_limits.h" #include "nclx.h" -#include "codecs/jpeg.h" -#include "codecs/jpeg2000.h" -#include "codecs/hevc.h" -#include "codecs/mask_image.h" -#include "codecs/vvc.h" -#include "codecs/avc.h" -#include "codecs/tild.h" +#include "codecs/jpeg_boxes.h" +#include "codecs/jpeg2000_boxes.h" +#include "codecs/hevc_boxes.h" +#include "image-items/mask_image.h" +#include "codecs/vvc_boxes.h" +#include "codecs/avc_boxes.h" +#include "codecs/avif_boxes.h" +#include "image-items/tild.h" #include #include @@ -161,25 +162,6 @@ bool Fraction::is_valid() const return denominator != 0; } -static uint32_t from_fourcc(const char* string) -{ - return ((string[0] << 24) | - (string[1] << 16) | - (string[2] << 8) | - (string[3])); -} - -std::string to_fourcc(uint32_t code) -{ - std::string str(" "); - str[0] = static_cast((code >> 24) & 0xFF); - str[1] = static_cast((code >> 16) & 0xFF); - str[2] = static_cast((code >> 8) & 0xFF); - str[3] = static_cast((code >> 0) & 0xFF); - - return str; -} - BoxHeader::BoxHeader() = default; @@ -221,7 +203,7 @@ std::string BoxHeader::get_type_string() const return sstr.str(); } else { - return to_fourcc(m_type); + return fourcc_to_string(m_type); } } @@ -1024,7 +1006,7 @@ std::string Box_other::dump(Indent& indent) const std::string Box_Error::dump(Indent& indent) const { std::ostringstream sstr; - sstr << indent << '\'' << to_fourcc(m_box_type_with_parse_error) << "' parse error: " << m_error.message << "\n"; + sstr << indent << '\'' << fourcc_to_string(m_box_type_with_parse_error) << "' parse error: " << m_error.message << "\n"; sstr << indent << "fatality: "; switch (m_fatality) { case parse_error_fatality::fatal: sstr << "fatal\n"; @@ -1079,7 +1061,7 @@ std::string Box_ftyp::dump(Indent& indent) const sstr << BoxHeader::dump(indent); - sstr << indent << "major brand: " << to_fourcc(m_major_brand) << "\n" + sstr << indent << "major brand: " << fourcc_to_string(m_major_brand) << "\n" << indent << "minor version: " << m_minor_version << "\n" << indent << "compatible brands: "; @@ -1088,7 +1070,7 @@ std::string Box_ftyp::dump(Indent& indent) const if (first) { first = false; } else { sstr << ','; } - sstr << to_fourcc(brand); + sstr << fourcc_to_string(brand); } sstr << "\n"; @@ -1190,7 +1172,7 @@ std::string Box_hdlr::dump(Indent& indent) const std::ostringstream sstr; sstr << Box::dump(indent); sstr << indent << "pre_defined: " << m_pre_defined << "\n" - << indent << "handler_type: " << to_fourcc(m_handler_type) << "\n" + << indent << "handler_type: " << fourcc_to_string(m_handler_type) << "\n" << indent << "name: " << m_name << "\n"; return sstr.str(); @@ -2075,6 +2057,8 @@ Error Box_infe::parse(BitstreamRange& range) m_content_encoding = range.read_string(); } + m_item_type_4cc = 0; + if (get_version() >= 2) { m_hidden_item = (get_flags() & 1); @@ -2086,17 +2070,14 @@ Error Box_infe::parse(BitstreamRange& range) } m_item_protection_index = range.read16(); - uint32_t item_type = range.read32(); - if (item_type != 0) { - m_item_type = to_fourcc(item_type); - } + m_item_type_4cc = range.read32(); m_item_name = range.read_string(); - if (item_type == fourcc("mime")) { + if (m_item_type_4cc == fourcc("mime")) { m_content_type = range.read_string(); m_content_encoding = range.read_string(); } - else if (item_type == fourcc("uri ")) { + else if (m_item_type_4cc == fourcc("uri ")) { m_item_uri_type = range.read_string(); } } @@ -2118,7 +2099,7 @@ void Box_infe::derive_box_version() } - if (m_item_type != "") { + if (m_item_type_4cc != 0) { min_version = std::max(min_version, 2); } @@ -2161,19 +2142,14 @@ Error Box_infe::write(StreamWriter& writer) const writer.write16(m_item_protection_index); - if (m_item_type.empty()) { - writer.write32(0); - } - else { - writer.write32(from_fourcc(m_item_type.c_str())); - } + writer.write32(m_item_type_4cc); writer.write(m_item_name); - if (m_item_type == "mime") { + if (m_item_type_4cc == fourcc("mime")) { writer.write(m_content_type); writer.write(m_content_encoding); } - else if (m_item_type == "uri ") { + else if (m_item_type_4cc == fourcc("uri ")) { writer.write(m_item_uri_type); } } @@ -2191,15 +2167,15 @@ std::string Box_infe::dump(Indent& indent) const sstr << indent << "item_ID: " << m_item_ID << "\n" << indent << "item_protection_index: " << m_item_protection_index << "\n" - << indent << "item_type: " << m_item_type << "\n" + << indent << "item_type: " << fourcc_to_string(m_item_type_4cc) << "\n" << indent << "item_name: " << m_item_name << "\n"; - if (m_item_type == "mime") { + if (m_item_type_4cc == fourcc("mime")) { sstr << indent << "content_type: " << m_content_type << "\n" << indent << "content_encoding: " << m_content_encoding << "\n"; } - if (m_item_type == "uri ") { + if (m_item_type_4cc == fourcc("uri ")) { sstr << indent << "item uri type: " << m_item_uri_type << "\n"; } diff --git a/libheif/box.h b/libheif/box.h index 73e7091c9c..b843c474e5 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -45,11 +45,6 @@ #define HAS_BOOL_ARRAY 1 #endif -// abbreviation -constexpr inline uint32_t fourcc(const char* id) { return fourcc_to_uint32(id); } - -std::string to_fourcc(uint32_t code); - /* constexpr uint32_t fourcc(const char* string) { @@ -594,9 +589,9 @@ class Box_infe : public FullBox void set_item_ID(heif_item_id id) { m_item_ID = id; } - const std::string& get_item_type() const { return m_item_type; } + uint32_t get_item_type_4cc() const { return m_item_type_4cc; } - void set_item_type(const std::string& type) { m_item_type = type; } + void set_item_type_4cc(uint32_t type) { m_item_type_4cc = type; } void set_item_name(const std::string& name) { m_item_name = name; } @@ -625,7 +620,7 @@ class Box_infe : public FullBox heif_item_id m_item_ID = 0; uint16_t m_item_protection_index = 0; - std::string m_item_type; + uint32_t m_item_type_4cc = 0; std::string m_item_name; std::string m_content_type; std::string m_content_encoding; diff --git a/libheif/codecs/avc.cc b/libheif/codecs/avc_boxes.cc similarity index 71% rename from libheif/codecs/avc.cc rename to libheif/codecs/avc_boxes.cc index c735c05bcb..5b205bd1ec 100644 --- a/libheif/codecs/avc.cc +++ b/libheif/codecs/avc_boxes.cc @@ -18,13 +18,14 @@ * along with libheif. If not, see . */ -#include "avc.h" +#include "avc_boxes.h" #include #include #include #include #include "file.h" #include "context.h" +#include "avc_dec.h" Error Box_avcC::parse(BitstreamRange& range) @@ -264,130 +265,3 @@ void Box_avcC::get_header_nals(std::vector& data) const data.insert(data.end(), pps.begin(), pps.end()); } } - - -Result ImageItem_AVC::encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) -{ -#if 0 - CodedImageData codedImage; - - auto hvcC = std::make_shared(); - - heif_image c_api_image; - c_api_image.image = image; - - struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); - if (err.code) { - return Error(err.code, - err.subcode, - err.message); - } - - int encoded_width = 0; - int encoded_height = 0; - - for (;;) { - uint8_t* data; - int size; - - encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); - - if (data == nullptr) { - break; - } - - - const uint8_t NAL_SPS = 33; - - if ((data[0] >> 1) == NAL_SPS) { - Box_hvcC::configuration config; - - parse_sps_for_hvcC_configuration(data, size, &config, &encoded_width, &encoded_height); - - hvcC->set_configuration(config); - - codedImage.encoded_image_width = encoded_width; - codedImage.encoded_image_height = encoded_height; - } - - switch (data[0] >> 1) { - case 0x20: - case 0x21: - case 0x22: - hvcC->append_nal_data(data, size); - break; - - default: - codedImage.append_with_4bytes_size(data, size); - // m_heif_file->append_iloc_data_with_4byte_size(image_id, data, size); - } - } - - if (!encoded_width || !encoded_height) { - return Error(heif_error_Encoder_plugin_error, - heif_suberror_Invalid_image_size); - } - - codedImage.properties.push_back(hvcC); - - - // Make sure that the encoder plugin works correctly and the encoded image has the correct size. - - if (encoder->plugin->plugin_api_version >= 3 && - encoder->plugin->query_encoded_size != nullptr) { - uint32_t check_encoded_width = image->get_width(), check_encoded_height = image->get_height(); - - encoder->plugin->query_encoded_size(encoder->encoder, - image->get_width(), image->get_height(), - &check_encoded_width, - &check_encoded_height); - - assert((int)check_encoded_width == encoded_width); - assert((int)check_encoded_height == encoded_height); - } - - return codedImage; -#endif - assert(false); // TODO - return {}; -} - -Result> ImageItem_AVC::read_bitstream_configuration_data(heif_item_id itemId) const -{ - std::vector data; - - auto avcC_box = get_file()->get_property(itemId); - if (!avcC_box) { - return Error{heif_error_Invalid_input, - heif_suberror_No_avcC_box}; - } - - avcC_box->get_header_nals(data); - - return data; -} - - -int ImageItem_AVC::get_luma_bits_per_pixel() const -{ - auto avcC_box = get_file()->get_property(get_id()); - if (avcC_box) { - return avcC_box->get_configuration().bit_depth_luma; - } - - return -1; -} - - -int ImageItem_AVC::get_chroma_bits_per_pixel() const -{ - auto avcC_box = get_file()->get_property(get_id()); - if (avcC_box) { - return avcC_box->get_configuration().bit_depth_chroma; - } - - return -1; -} diff --git a/libheif/codecs/avc.h b/libheif/codecs/avc_boxes.h similarity index 67% rename from libheif/codecs/avc.h rename to libheif/codecs/avc_boxes.h index 1bbbaddd4e..47cf782898 100644 --- a/libheif/codecs/avc.h +++ b/libheif/codecs/avc_boxes.h @@ -18,8 +18,8 @@ * along with libheif. If not, see . */ -#ifndef HEIF_AVC_H -#define HEIF_AVC_H +#ifndef HEIF_AVC_BOXES_H +#define HEIF_AVC_BOXES_H #include "box.h" #include "error.h" @@ -27,7 +27,8 @@ #include #include #include -#include "codecs/image_item.h" +#include "image-items/image_item.h" + class Box_avcC : public Box { public: @@ -89,35 +90,4 @@ class Box_avcC : public Box { std::vector< std::vector > m_sps_ext; }; - -class ImageItem_AVC : public ImageItem -{ -public: - ImageItem_AVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} - - ImageItem_AVC(HeifContext* ctx) : ImageItem(ctx) {} - - const char* get_infe_type() const override { return "avc1"; } - - const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; } - - const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } - - heif_compression_format get_compression_format() const override { return heif_compression_AVC; } - - int get_luma_bits_per_pixel() const override; - - int get_chroma_bits_per_pixel() const override; - -protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; - -public: - - Result encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) override; -}; - #endif diff --git a/libheif/codecs/avc_dec.cc b/libheif/codecs/avc_dec.cc new file mode 100644 index 0000000000..8af79a4c6a --- /dev/null +++ b/libheif/codecs/avc_dec.cc @@ -0,0 +1,62 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "avc_dec.h" +#include "avc_boxes.h" +#include "error.h" +#include "context.h" + +#include + + +Result> Decoder_AVC::read_bitstream_configuration_data() const +{ + std::vector data; + m_avcC->get_header_nals(data); + + return data; +} + + +int Decoder_AVC::get_luma_bits_per_pixel() const +{ + return m_avcC->get_configuration().bit_depth_luma; +} + + +int Decoder_AVC::get_chroma_bits_per_pixel() const +{ + return m_avcC->get_configuration().bit_depth_chroma; +} + + +Error Decoder_AVC::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + *out_chroma = m_avcC->get_configuration().chroma_format; + + if (*out_chroma == heif_chroma_monochrome) { + *out_colorspace = heif_colorspace_monochrome; + } + else { + *out_colorspace = heif_colorspace_YCbCr; + } + + return Error::Ok; +} diff --git a/libheif/codecs/avc_dec.h b/libheif/codecs/avc_dec.h new file mode 100644 index 0000000000..3d08653b6b --- /dev/null +++ b/libheif/codecs/avc_dec.h @@ -0,0 +1,53 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_AVC_DEC_H +#define HEIF_AVC_DEC_H + +#include "libheif/heif.h" +#include "error.h" + +#include +#include +#include + +class Box_avcC; + + +class Decoder_AVC : public Decoder +{ +public: + explicit Decoder_AVC(const std::shared_ptr& avcC) : m_avcC(avcC) {} + + heif_compression_format get_compression_format() const override { return heif_compression_AVC; } + + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; + + Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; + + Result> read_bitstream_configuration_data() const override; + +private: + const std::shared_ptr m_avcC; +}; + +#endif diff --git a/libheif/codecs/avif.cc b/libheif/codecs/avif_boxes.cc similarity index 86% rename from libheif/codecs/avif.cc rename to libheif/codecs/avif_boxes.cc index 5c44d08e29..4eead97dcc 100644 --- a/libheif/codecs/avif.cc +++ b/libheif/codecs/avif_boxes.cc @@ -19,7 +19,8 @@ */ #include "pixelimage.h" -#include "avif.h" +#include "avif_boxes.h" +#include "avif_dec.h" #include "bitstream.h" #include "common_utils.h" #include "libheif/api_structs.h" @@ -559,98 +560,3 @@ bool fill_av1C_configuration_from_stream(Box_av1C::configuration* out_config, co return true; } - - -Result ImageItem_AVIF::encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) -{ - CodedImageData codedImage; - - Box_av1C::configuration config; - - // Fill preliminary av1C in case we cannot parse the sequence_header() correctly in the code below. - // TODO: maybe we can remove this later. - fill_av1C_configuration(&config, image); - - heif_image c_api_image; - c_api_image.image = image; - - struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); - if (err.code) { - return Error(err.code, - err.subcode, - err.message); - } - - for (;;) { - uint8_t* data; - int size; - - encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); - - bool found_config = fill_av1C_configuration_from_stream(&config, data, size); - (void) found_config; - - if (data == nullptr) { - break; - } - - codedImage.append(data, size); - } - - auto av1C = std::make_shared(); - av1C->set_configuration(config); - codedImage.properties.push_back(av1C); - - return codedImage; -} - - -Result> ImageItem_AVIF::read_bitstream_configuration_data(heif_item_id itemId) const -{ - // --- get codec configuration - - std::shared_ptr av1C_box = get_file()->get_property(itemId); - if (!av1C_box) - { - return Error(heif_error_Invalid_input, - heif_suberror_No_av1C_box); - } - - std::vector data; - if (!av1C_box->get_headers(&data)) - { - return Error(heif_error_Invalid_input, - heif_suberror_No_item_data); - } - - return data; -} - - -int ImageItem_AVIF::get_luma_bits_per_pixel() const -{ - auto av1C_box = get_file()->get_property(get_id()); - if (av1C_box) { - Box_av1C::configuration config = av1C_box->get_configuration(); - if (!config.high_bitdepth) { - return 8; - } - else if (config.twelve_bit) { - return 12; - } - else { - return 10; - } - } - - return -1; -} - - -int ImageItem_AVIF::get_chroma_bits_per_pixel() const -{ - return get_luma_bits_per_pixel(); -} diff --git a/libheif/codecs/avif.h b/libheif/codecs/avif_boxes.h similarity index 70% rename from libheif/codecs/avif.h rename to libheif/codecs/avif_boxes.h index 44bed0f924..8ce380c28e 100644 --- a/libheif/codecs/avif.h +++ b/libheif/codecs/avif_boxes.h @@ -18,8 +18,8 @@ * along with libheif. If not, see . */ -#ifndef HEIF_AVIF_H -#define HEIF_AVIF_H +#ifndef HEIF_AVIF_BOXES_H +#define HEIF_AVIF_BOXES_H #include #include @@ -30,7 +30,7 @@ #include "libheif/heif.h" #include "box.h" #include "error.h" -#include +#include "image-items/image_item.h" class Box_av1C : public Box @@ -53,8 +53,8 @@ class Box_av1C : public Box uint8_t high_bitdepth = 0; uint8_t twelve_bit = 0; uint8_t monochrome = 0; - uint8_t chroma_subsampling_x = 0; - uint8_t chroma_subsampling_y = 0; + uint8_t chroma_subsampling_x = 0; // (minus 1) that is: either 0 or 1 + uint8_t chroma_subsampling_y = 0; // (minus 1) uint8_t chroma_sample_position = 0; //uint8_t reserved = 0; @@ -64,13 +64,13 @@ class Box_av1C : public Box //unsigned int (8)[] configOBUs; heif_chroma get_heif_chroma() const { - if (chroma_subsampling_x==2 && chroma_subsampling_y==2) { + if (chroma_subsampling_x==1 && chroma_subsampling_y==1) { return heif_chroma_420; } - else if (chroma_subsampling_x==2 && chroma_subsampling_y==1) { + else if (chroma_subsampling_x==1 && chroma_subsampling_y==0) { return heif_chroma_422; } - else if (chroma_subsampling_x==1 && chroma_subsampling_y==1) { + else if (chroma_subsampling_x==0 && chroma_subsampling_y==0) { return heif_chroma_444; } else { @@ -151,34 +151,4 @@ Error fill_av1C_configuration(Box_av1C::configuration* inout_config, const std:: bool fill_av1C_configuration_from_stream(Box_av1C::configuration* out_config, const uint8_t* data, int dataSize); - -class ImageItem_AVIF : public ImageItem -{ -public: - ImageItem_AVIF(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} - - ImageItem_AVIF(HeifContext* ctx) : ImageItem(ctx) {} - - const char* get_infe_type() const override { return "av01"; } - - const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; } - - const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } - - heif_compression_format get_compression_format() const override { return heif_compression_AV1; } - - int get_luma_bits_per_pixel() const override; - - int get_chroma_bits_per_pixel() const override; - - - Result encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) override; - -protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; -}; - #endif diff --git a/libheif/codecs/avif_dec.cc b/libheif/codecs/avif_dec.cc new file mode 100644 index 0000000000..e0446a2de6 --- /dev/null +++ b/libheif/codecs/avif_dec.cc @@ -0,0 +1,74 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "avif_dec.h" +#include "avif_boxes.h" +#include "error.h" +#include "context.h" + +#include + + +Result> Decoder_AVIF::read_bitstream_configuration_data() const +{ + std::vector data; + if (!m_av1C->get_headers(&data)) { + return Error{heif_error_Invalid_input, + heif_suberror_No_item_data}; + } + + return data; +} + + +int Decoder_AVIF::get_luma_bits_per_pixel() const +{ + Box_av1C::configuration config = m_av1C->get_configuration(); + if (!config.high_bitdepth) { + return 8; + } + else if (config.twelve_bit) { + return 12; + } + else { + return 10; + } +} + + +int Decoder_AVIF::get_chroma_bits_per_pixel() const +{ + return get_luma_bits_per_pixel(); +} + + +Error Decoder_AVIF::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + *out_chroma = (heif_chroma) (m_av1C->get_configuration().get_heif_chroma()); + + if (*out_chroma == heif_chroma_monochrome) { + *out_colorspace = heif_colorspace_monochrome; + } + else { + *out_colorspace = heif_colorspace_YCbCr; + } + + return Error::Ok; +} diff --git a/libheif/codecs/avif_dec.h b/libheif/codecs/avif_dec.h new file mode 100644 index 0000000000..441f1864ac --- /dev/null +++ b/libheif/codecs/avif_dec.h @@ -0,0 +1,53 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_AVIF_DEC_H +#define HEIF_AVIF_DEC_H + +#include "libheif/heif.h" +#include "error.h" + +#include +#include +#include + +class Box_av1C; + + +class Decoder_AVIF : public Decoder +{ +public: + explicit Decoder_AVIF(const std::shared_ptr& av1C) : m_av1C(av1C) {} + + heif_compression_format get_compression_format() const override { return heif_compression_AV1; } + + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; + + Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; + + Result> read_bitstream_configuration_data() const override; + +private: + const std::shared_ptr m_av1C; +}; + +#endif diff --git a/libheif/codecs/decoder.cc b/libheif/codecs/decoder.cc new file mode 100644 index 0000000000..8c765aa6a8 --- /dev/null +++ b/libheif/codecs/decoder.cc @@ -0,0 +1,223 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "codecs/decoder.h" +#include "error.h" +#include "context.h" +#include "plugin_registry.h" +#include "libheif/api_structs.h" + +#include "codecs/hevc_dec.h" +#include "codecs/avif_dec.h" +#include "codecs/avc_dec.h" +#include "codecs/vvc_dec.h" +#include "codecs/jpeg_dec.h" +#include "codecs/jpeg2000_dec.h" +#include "avc_boxes.h" +#include "avif_boxes.h" +#include "hevc_boxes.h" +#include "vvc_boxes.h" +#include "jpeg_boxes.h" +#include "jpeg2000_boxes.h" + + + +void DataExtent::set_from_image_item(std::shared_ptr file, heif_item_id item) +{ + m_file = file; + m_item_id = item; + m_source = Source::Image; +} + + +Result*> DataExtent::read_data() const +{ + if (!m_raw.empty()) { + return &m_raw; + } + else if (m_source == Source::Image) { + assert(m_file); + + // image + Error err = m_file->append_data_from_iloc(m_item_id, m_raw); + if (err) { + return err; + } + } + else { + // sequence + assert(false); // TODO + } + + return &m_raw; +} + + +Result> DataExtent::read_data(uint64_t offset, uint64_t size) const +{ + std::vector data; + + if (!m_raw.empty()) { + data.insert(data.begin(), m_raw.begin() + offset, m_raw.begin() + offset + size); + return data; + } + else if (m_source == Source::Image) { + // TODO: cache data + + // image + Error err = m_file->append_data_from_iloc(m_item_id, m_raw, 0, size); + if (err) { + return err; + } + return data; + } + else { + // sequence + assert(false); // TODO + return Error::Ok; + } +} + + +std::shared_ptr Decoder::alloc_for_infe_type(const HeifContext* ctx, heif_item_id id, uint32_t format_4cc) +{ + switch (format_4cc) { + case fourcc("hvc1"): { + auto hvcC = ctx->get_heif_file()->get_property(id); + return std::make_shared(hvcC); + } + case fourcc("av01"): { + auto av1C = ctx->get_heif_file()->get_property(id); + return std::make_shared(av1C); + } + case fourcc("avc1"): { + auto avcC = ctx->get_heif_file()->get_property(id); + return std::make_shared(avcC); + } + case fourcc("j2k1"): { + auto j2kH = ctx->get_heif_file()->get_property(id); + return std::make_shared(j2kH); + } + case fourcc("vvc1"): { + auto vvcC = ctx->get_heif_file()->get_property(id); + return std::make_shared(vvcC); + } + case fourcc("jpeg"): { + auto jpgC = ctx->get_heif_file()->get_property(id); + return std::make_shared(jpgC); + } +#if WITH_UNCOMPRESSED_CODEC + case fourcc("unci"): { + auto jpgC = ctx->get_heif_file()->get_property(id); + return std::make_shared(jpgC); + } +#endif + case fourcc("mski"): { + return nullptr; // do we need a decoder for this? + } + default: + return nullptr; + } +} + + +Result> Decoder::get_compressed_data() const +{ + // --- get the compressed image data + + // data from configuration blocks + + Result> confData = read_bitstream_configuration_data(); + if (confData.error) { + return confData.error; + } + + std::vector data = confData.value; + + // append image data + + Result dataResult = m_data_extent.read_data(); + if (dataResult.error) { + return dataResult.error; + } + + data.insert(data.end(), dataResult.value->begin(), dataResult.value->end()); + + return data; +} + + +Result> +Decoder::decode_single_frame_from_compressed_data(const struct heif_decoding_options& options) +{ + const struct heif_decoder_plugin* decoder_plugin = get_decoder(get_compression_format(), options.decoder_id); + if (!decoder_plugin) { + return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed); + } + + + // --- decode image with the plugin + + void* decoder; + struct heif_error err = decoder_plugin->new_decoder(&decoder); + if (err.code != heif_error_Ok) { + return Error(err.code, err.subcode, err.message); + } + + if (decoder_plugin->plugin_api_version >= 2) { + if (decoder_plugin->set_strict_decoding) { + decoder_plugin->set_strict_decoding(decoder, options.strict_decoding); + } + } + + auto dataResult = get_compressed_data(); + if (dataResult.error) { + return dataResult.error; + } + + err = decoder_plugin->push_data(decoder, dataResult.value.data(), dataResult.value.size()); + if (err.code != heif_error_Ok) { + decoder_plugin->free_decoder(decoder); + return Error(err.code, err.subcode, err.message); + } + + heif_image* decoded_img = nullptr; + + err = decoder_plugin->decode_image(decoder, &decoded_img); + if (err.code != heif_error_Ok) { + decoder_plugin->free_decoder(decoder); + return Error(err.code, err.subcode, err.message); + } + + if (!decoded_img) { + // TODO(farindk): The plugin should return an error in this case. + decoder_plugin->free_decoder(decoder); + return Error(heif_error_Decoder_plugin_error, heif_suberror_Unspecified); + } + + // -- cleanup + + std::shared_ptr img = std::move(decoded_img->image); + heif_image_release(decoded_img); + + decoder_plugin->free_decoder(decoder); + + return img; +} diff --git a/libheif/codecs/decoder.h b/libheif/codecs/decoder.h new file mode 100644 index 0000000000..f6d82b4a4b --- /dev/null +++ b/libheif/codecs/decoder.h @@ -0,0 +1,96 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_DECODER_H +#define HEIF_DECODER_H + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" +#include "file.h" + +#include +#include +#include +#include +#include "image-items/hevc.h" + + +// Specifies the input data for decoding. +// For images, this points to the iloc extents. +// For sequences, this points to the track data. +struct DataExtent +{ + std::shared_ptr m_file; + enum class Source : uint8_t { Raw, Image, Sequence } m_source = Source::Raw; + + // --- raw data + mutable std::vector m_raw; // also for cached data + + // --- image + heif_item_id m_item_id = 0; + + // --- sequence + // TODO + + void set_from_image_item(std::shared_ptr file, heif_item_id item); + + Result*> read_data() const; + + Result> read_data(uint64_t offset, uint64_t size) const; +}; + + +class Decoder +{ +public: + static std::shared_ptr alloc_for_infe_type(const HeifContext* ctx, heif_item_id, uint32_t format_4cc); + + + virtual ~Decoder() = default; + + virtual heif_compression_format get_compression_format() const = 0; + + void set_data_extent(DataExtent extent) { m_data_extent = std::move(extent); } + + // --- information about the image format + + [[nodiscard]] virtual int get_luma_bits_per_pixel() const = 0; + + [[nodiscard]] virtual int get_chroma_bits_per_pixel() const = 0; + + [[nodiscard]] virtual Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const = 0; + + // --- raw data access + + [[nodiscard]] virtual Result> read_bitstream_configuration_data() const = 0; + + Result> get_compressed_data() const; + + // --- decoding + + virtual Result> + decode_single_frame_from_compressed_data(const struct heif_decoding_options& options); + +private: + DataExtent m_data_extent; +}; + +#endif diff --git a/libheif/codecs/hevc.cc b/libheif/codecs/hevc_boxes.cc similarity index 83% rename from libheif/codecs/hevc.cc rename to libheif/codecs/hevc_boxes.cc index cb7069bdc8..47fe7a8438 100644 --- a/libheif/codecs/hevc.cc +++ b/libheif/codecs/hevc_boxes.cc @@ -18,10 +18,11 @@ * along with libheif. If not, see . */ -#include "hevc.h" +#include "hevc_boxes.h" #include "bitstream.h" #include "error.h" #include "file.h" +#include "hevc_dec.h" #include #include @@ -636,130 +637,3 @@ Error parse_sps_for_hvcC_configuration(const uint8_t* sps, size_t size, return Error::Ok; } - - -Result ImageItem_HEVC::encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) -{ - CodedImageData codedImage; - - auto hvcC = std::make_shared(); - - heif_image c_api_image; - c_api_image.image = image; - - struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); - if (err.code) { - return Error(err.code, - err.subcode, - err.message); - } - - int encoded_width = 0; - int encoded_height = 0; - - for (;;) { - uint8_t* data; - int size; - - encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); - - if (data == nullptr) { - break; - } - - - const uint8_t NAL_SPS = 33; - - if ((data[0] >> 1) == NAL_SPS) { - Box_hvcC::configuration config; - - parse_sps_for_hvcC_configuration(data, size, &config, &encoded_width, &encoded_height); - - hvcC->set_configuration(config); - - codedImage.encoded_image_width = encoded_width; - codedImage.encoded_image_height = encoded_height; - } - - switch (data[0] >> 1) { - case 0x20: - case 0x21: - case 0x22: - hvcC->append_nal_data(data, size); - break; - - default: - codedImage.append_with_4bytes_size(data, size); - // m_heif_file->append_iloc_data_with_4byte_size(image_id, data, size); - } - } - - if (!encoded_width || !encoded_height) { - return Error(heif_error_Encoder_plugin_error, - heif_suberror_Invalid_image_size); - } - - codedImage.properties.push_back(hvcC); - - - // Make sure that the encoder plugin works correctly and the encoded image has the correct size. - - if (encoder->plugin->plugin_api_version >= 3 && - encoder->plugin->query_encoded_size != nullptr) { - uint32_t check_encoded_width = image->get_width(), check_encoded_height = image->get_height(); - - encoder->plugin->query_encoded_size(encoder->encoder, - image->get_width(), image->get_height(), - &check_encoded_width, - &check_encoded_height); - - assert((int)check_encoded_width == encoded_width); - assert((int)check_encoded_height == encoded_height); - } - - return codedImage; -} - -Result> ImageItem_HEVC::read_bitstream_configuration_data(heif_item_id itemId) const -{ - // --- get codec configuration - - std::shared_ptr hvcC_box = get_file()->get_property(itemId); - if (!hvcC_box) { - return Error{heif_error_Invalid_input, - heif_suberror_No_hvcC_box}; - } - - std::vector data; - if (!hvcC_box->get_headers(&data)) { - return Error{heif_error_Invalid_input, - heif_suberror_No_item_data}; - } - - return data; -} - - -int ImageItem_HEVC::get_luma_bits_per_pixel() const -{ - auto hvcC_box = get_file()->get_property(get_id()); - if (hvcC_box) { - return hvcC_box->get_configuration().bit_depth_luma; - } - - return -1; -} - - -int ImageItem_HEVC::get_chroma_bits_per_pixel() const -{ - auto hvcC_box = get_file()->get_property(get_id()); - if (hvcC_box) { - return hvcC_box->get_configuration().bit_depth_chroma; - } - - return -1; -} diff --git a/libheif/codecs/hevc.h b/libheif/codecs/hevc_boxes.h similarity index 71% rename from libheif/codecs/hevc.h rename to libheif/codecs/hevc_boxes.h index 5cdd13bb02..1224b6a8ad 100644 --- a/libheif/codecs/hevc.h +++ b/libheif/codecs/hevc_boxes.h @@ -18,8 +18,8 @@ * along with libheif. If not, see . */ -#ifndef HEIF_HEVC_H -#define HEIF_HEVC_H +#ifndef HEIF_HEVC_BOXES_H +#define HEIF_HEVC_BOXES_H #include "libheif/heif.h" #include "box.h" @@ -28,7 +28,7 @@ #include #include #include -#include +#include "image-items/image_item.h" class Box_hvcC : public Box @@ -121,35 +121,4 @@ Error parse_sps_for_hvcC_configuration(const uint8_t* sps, size_t size, Box_hvcC::configuration* inout_config, int* width, int* height); -class ImageItem_HEVC : public ImageItem -{ -public: - ImageItem_HEVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} - - ImageItem_HEVC(HeifContext* ctx) : ImageItem(ctx) {} - - const char* get_infe_type() const override { return "hvc1"; } - - // TODO: MIAF says that the *:hevc:* urn is deprecated and we should use "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha" - const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:hevc:2015:auxid:1"; } - - const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } - - heif_compression_format get_compression_format() const override { return heif_compression_HEVC; } - - int get_luma_bits_per_pixel() const override; - - int get_chroma_bits_per_pixel() const override; - -protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; - -public: - - Result encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) override; -}; - #endif diff --git a/libheif/codecs/hevc_dec.cc b/libheif/codecs/hevc_dec.cc new file mode 100644 index 0000000000..3b08b49e90 --- /dev/null +++ b/libheif/codecs/hevc_dec.cc @@ -0,0 +1,65 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "hevc_dec.h" +#include "hevc_boxes.h" +#include "error.h" +#include "context.h" + +#include + + +Result> Decoder_HEVC::read_bitstream_configuration_data() const +{ + std::vector data; + if (!m_hvcC->get_headers(&data)) { + return Error{heif_error_Invalid_input, + heif_suberror_No_item_data}; + } + + return data; +} + + +int Decoder_HEVC::get_luma_bits_per_pixel() const +{ + return m_hvcC->get_configuration().bit_depth_luma; +} + + +int Decoder_HEVC::get_chroma_bits_per_pixel() const +{ + return m_hvcC->get_configuration().bit_depth_chroma; +} + + +Error Decoder_HEVC::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + *out_chroma = (heif_chroma) (m_hvcC->get_configuration().chroma_format); + + if (*out_chroma == heif_chroma_monochrome) { + *out_colorspace = heif_colorspace_monochrome; + } + else { + *out_colorspace = heif_colorspace_YCbCr; + } + + return Error::Ok; +} diff --git a/libheif/codecs/hevc_dec.h b/libheif/codecs/hevc_dec.h new file mode 100644 index 0000000000..ac4e67e0f0 --- /dev/null +++ b/libheif/codecs/hevc_dec.h @@ -0,0 +1,54 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_HEVC_DEC_H +#define HEIF_HEVC_DEC_H + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" + +#include +#include +#include + +class Box_hvcC; + + +class Decoder_HEVC : public Decoder +{ +public: + explicit Decoder_HEVC(const std::shared_ptr& hvcC) : m_hvcC(hvcC) {} + + heif_compression_format get_compression_format() const override { return heif_compression_HEVC; } + + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; + + Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; + + Result> read_bitstream_configuration_data() const override; + +private: + const std::shared_ptr m_hvcC; +}; + +#endif diff --git a/libheif/codecs/jpeg2000.cc b/libheif/codecs/jpeg2000_boxes.cc similarity index 84% rename from libheif/codecs/jpeg2000.cc rename to libheif/codecs/jpeg2000_boxes.cc index 7045b65ebe..15ed6e1af1 100644 --- a/libheif/codecs/jpeg2000.cc +++ b/libheif/codecs/jpeg2000_boxes.cc @@ -18,11 +18,11 @@ * along with libheif. If not, see . */ -#include "jpeg2000.h" +#include "jpeg2000_boxes.h" #include "libheif/api_structs.h" #include #include -#include +#include static const uint16_t JPEG2000_CAP_MARKER = 0xFF50; static const uint16_t JPEG2000_SIZ_MARKER = 0xFF51; @@ -481,95 +481,3 @@ heif_chroma JPEG2000MainHeader::get_chroma_format() const return heif_chroma_undefined; } - - -Result ImageItem_JPEG2000::encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) -{ - CodedImageData codedImageData; - - heif_image c_api_image; - c_api_image.image = image; - - encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); - - // get compressed data - for (;;) { - uint8_t* data; - int size; - - encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); - - if (data == nullptr) { - break; - } - - codedImageData.append(data, size); - } - - // add 'j2kH' property - auto j2kH = std::make_shared(); - - // add 'cdef' to 'j2kH' - auto cdef = std::make_shared(); - cdef->set_channels(image->get_colorspace()); - j2kH->append_child_box(cdef); - - codedImageData.properties.push_back(j2kH); - - return codedImageData; -} - - -Result> ImageItem_JPEG2000::read_bitstream_configuration_data(heif_item_id itemId) const -{ - // --- get codec configuration - - std::shared_ptr j2kH_box = get_file()->get_property(itemId); - if (!j2kH_box) - { - // TODO - Correctly Find the j2kH box - // return Error(heif_error_Invalid_input, - // heif_suberror_Unspecified); - } - // else if (!j2kH_box->get_headers(data)) { - // return Error(heif_error_Invalid_input, - // heif_suberror_No_item_data); - // } - - return std::vector{}; -} - - -int ImageItem_JPEG2000::get_luma_bits_per_pixel() const -{ - Result> imageDataResult = get_compressed_image_data(); - if (imageDataResult.error) { - return -1; - } - - JPEG2000MainHeader header; - Error err = header.parseHeader(*imageDataResult); - if (err) { - return -1; - } - return header.get_precision(0); -} - - -int ImageItem_JPEG2000::get_chroma_bits_per_pixel() const -{ - Result> imageDataResult = get_compressed_image_data(); - if (imageDataResult.error) { - return -1; - } - - JPEG2000MainHeader header; - Error err = header.parseHeader(*imageDataResult); - if (err) { - return -1; - } - return header.get_precision(1); -} diff --git a/libheif/codecs/jpeg2000.h b/libheif/codecs/jpeg2000_boxes.h similarity index 93% rename from libheif/codecs/jpeg2000.h rename to libheif/codecs/jpeg2000_boxes.h index 19cbf9d728..230bfc0b3f 100644 --- a/libheif/codecs/jpeg2000.h +++ b/libheif/codecs/jpeg2000_boxes.h @@ -18,8 +18,8 @@ * along with libheif. If not, see . */ -#ifndef LIBHEIF_JPEG2000_H -#define LIBHEIF_JPEG2000_H +#ifndef LIBHEIF_JPEG2000_BOXES_H +#define LIBHEIF_JPEG2000_BOXES_H #include "box.h" #include "file.h" @@ -515,29 +515,4 @@ struct JPEG2000MainHeader size_t cursor; }; - -class ImageItem_JPEG2000 : public ImageItem -{ -public: - ImageItem_JPEG2000(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} - - ImageItem_JPEG2000(HeifContext* ctx) : ImageItem(ctx) {} - - const char* get_infe_type() const override { return "j2k1"; } - - heif_compression_format get_compression_format() const override { return heif_compression_JPEG2000; } - - int get_luma_bits_per_pixel() const override; - - int get_chroma_bits_per_pixel() const override; - - Result encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) override; - -protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; -}; - -#endif // LIBHEIF_JPEG2000_H +#endif // LIBHEIF_JPEG2000_BOXES_H diff --git a/libheif/codecs/jpeg2000_dec.cc b/libheif/codecs/jpeg2000_dec.cc new file mode 100644 index 0000000000..a14a661dfe --- /dev/null +++ b/libheif/codecs/jpeg2000_dec.cc @@ -0,0 +1,84 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "jpeg2000_dec.h" +#include "jpeg2000_boxes.h" +#include "error.h" +#include "context.h" + +#include + + +Result> Decoder_JPEG2000::read_bitstream_configuration_data() const +{ + return std::vector{}; +} + + +int Decoder_JPEG2000::get_luma_bits_per_pixel() const +{ + Result> imageDataResult = get_compressed_data(); + if (imageDataResult.error) { + return -1; + } + + JPEG2000MainHeader header; + Error err = header.parseHeader(*imageDataResult); + if (err) { + return -1; + } + return header.get_precision(0); +} + + +int Decoder_JPEG2000::get_chroma_bits_per_pixel() const +{ + Result> imageDataResult = get_compressed_data(); + if (imageDataResult.error) { + return -1; + } + + JPEG2000MainHeader header; + Error err = header.parseHeader(*imageDataResult); + if (err) { + return -1; + } + return header.get_precision(1); +} + + +Error Decoder_JPEG2000::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ +#if 0 + *out_chroma = (heif_chroma) (m_hvcC->get_configuration().chroma_format); + + if (*out_chroma == heif_chroma_monochrome) { + *out_colorspace = heif_colorspace_monochrome; + } + else { + *out_colorspace = heif_colorspace_YCbCr; + } +#endif + + *out_colorspace = heif_colorspace_YCbCr; + *out_chroma = heif_chroma_444; + + return Error::Ok; +} diff --git a/libheif/codecs/jpeg2000_dec.h b/libheif/codecs/jpeg2000_dec.h new file mode 100644 index 0000000000..083c78f955 --- /dev/null +++ b/libheif/codecs/jpeg2000_dec.h @@ -0,0 +1,59 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_JPEG2000_DEC_H +#define HEIF_JPEG2000_DEC_H + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" +#include "file.h" + +#include +#include +#include +#include +#include "image-items/jpeg2000.h" +#include + + +class Decoder_JPEG2000 : public Decoder +{ +public: + Decoder_JPEG2000(const std::shared_ptr& j2kH) : m_j2kH(j2kH) {} + + heif_compression_format get_compression_format() const override { return heif_compression_JPEG2000; } + + void set_data_extent(DataExtent extent) { m_data_extent = std::move(extent); } + + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; + + Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; + + Result> read_bitstream_configuration_data() const override; + +private: + const std::shared_ptr m_j2kH; + DataExtent m_data_extent; +}; + +#endif diff --git a/libheif/codecs/jpeg_boxes.cc b/libheif/codecs/jpeg_boxes.cc new file mode 100644 index 0000000000..b8cde160bf --- /dev/null +++ b/libheif/codecs/jpeg_boxes.cc @@ -0,0 +1,78 @@ +/* + * HEIF JPEG 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 "jpeg_boxes.h" +#include +#include "security_limits.h" +#include + + +// returns 0 if the marker_type was not found +size_t find_jpeg_marker_start(const std::vector& data, uint8_t marker_type) +{ + for (size_t i = 0; i < data.size() - 1; i++) { + if (data[i] == 0xFF && data[i + 1] == marker_type) { + return i; + } + } + + return 0; +} + + +std::string Box_jpgC::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + sstr << indent << "num bytes: " << m_data.size() << "\n"; + + return sstr.str(); +} + + +Error Box_jpgC::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + writer.write(m_data); + + prepend_header(writer, box_start); + + return Error::Ok; +} + + +Error Box_jpgC::parse(BitstreamRange& range) +{ + if (!has_fixed_box_size()) { + return Error{heif_error_Unsupported_feature, heif_suberror_Unspecified, "jpgC with unspecified size are not supported"}; + } + + size_t nBytes = range.get_remaining_bytes(); + if (nBytes > MAX_MEMORY_BLOCK_SIZE) { + return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "jpgC block exceeds maximum size"}; + } + + m_data.resize(nBytes); + range.read(m_data.data(), nBytes); + return range.get_error(); +} + diff --git a/libheif/codecs/jpeg_boxes.h b/libheif/codecs/jpeg_boxes.h new file mode 100644 index 0000000000..5df25bcd30 --- /dev/null +++ b/libheif/codecs/jpeg_boxes.h @@ -0,0 +1,54 @@ +/* + * HEIF JPEG codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_JPEG_BOXES_H +#define LIBHEIF_JPEG_BOXES_H + +#include "box.h" +#include +#include +#include "image-items/image_item.h" +#include + + +class Box_jpgC : public Box +{ +public: + Box_jpgC() + { + set_short_type(fourcc("jpgC")); + } + + const std::vector& get_data() const { return m_data; } + + void set_data(const std::vector& data) { m_data = data; } + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + +private: + std::vector m_data; +}; + +#endif // LIBHEIF_JPEG_BOXES_H diff --git a/libheif/codecs/jpeg_dec.cc b/libheif/codecs/jpeg_dec.cc new file mode 100644 index 0000000000..fdc6e0472e --- /dev/null +++ b/libheif/codecs/jpeg_dec.cc @@ -0,0 +1,160 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "jpeg_dec.h" +#include "jpeg_boxes.h" +#include "error.h" +#include "context.h" + +#include +#include + + +Result> Decoder_JPEG::read_bitstream_configuration_data() const +{ + if (m_jpgC) { + return m_jpgC->get_data(); + } + else { + return std::vector{}; + } +} + + +// This checks whether a start code FFCx with nibble 'x' is a SOF marker. +// E.g. FFC0-FFC3 are, while FFC4 is not. +static bool isSOF[16] = {true, true, true, true, false, true, true, true, + false, true, true, true, false, true, true, true}; + +Error Decoder_JPEG::parse_SOF() +{ + if (m_config) { + return Error::Ok; + } + + // image data, usually from 'mdat' + + auto dataResult = get_compressed_data(); + if (dataResult.error) { + return dataResult.error; + } + + const std::vector& data = dataResult.value; + + const Error error_invalidSOF{heif_error_Invalid_input, + heif_suberror_Unspecified, + "Invalid JPEG SOF header"}; + + for (size_t i = 0; i + 1 < data.size(); i++) { + if (data[i] == 0xFF && (data[i + 1] & 0xF0) == 0xC0 && isSOF[data[i + 1] & 0x0F]) { + + if (i + 9 >= data.size()) { + return error_invalidSOF; + } + + ConfigInfo info; + info.sample_precision = data[i + 4]; + info.nComponents = data[i + 9]; + + if (i + 11 + 3 * info.nComponents >= data.size()) { + return error_invalidSOF; + } + + for (int c = 0; c < std::min(info.nComponents, uint8_t(3)); c++) { + int ss = data[i + 11 + 3 * c]; + info.h_sampling[c] = (ss >> 4) & 0xF; + info.v_sampling[c] = ss & 0xF; + } + + if (info.nComponents == 1) { + info.chroma = heif_chroma_monochrome; + } + else if (info.nComponents != 3) { + return error_invalidSOF; + } + else { + if (info.h_sampling[1] != info.h_sampling[2] || + info.v_sampling[1] != info.v_sampling[2]) { + return error_invalidSOF; + } + + if (info.h_sampling[0] == 2 && info.v_sampling[0] == 2 && + info.h_sampling[1] == 1 && info.v_sampling[1] == 1) { + info.chroma = heif_chroma_420; + } + else if (info.h_sampling[0] == 2 && info.v_sampling[0] == 1 && + info.h_sampling[1] == 1 && info.v_sampling[1] == 1) { + info.chroma = heif_chroma_422; + } + else if (info.h_sampling[0] == 1 && info.v_sampling[0] == 1 && + info.h_sampling[1] == 1 && info.v_sampling[1] == 1) { + info.chroma = heif_chroma_444; + } + else { + return error_invalidSOF; + } + } + + m_config = info; + + return Error::Ok; + } + } + + return error_invalidSOF; +} + + +int Decoder_JPEG::get_luma_bits_per_pixel() const +{ + Error err = const_cast(this)->parse_SOF(); + if (err) { + return -1; + } + else { + return m_config->sample_precision; + } +} + + +int Decoder_JPEG::get_chroma_bits_per_pixel() const +{ + return get_luma_bits_per_pixel(); +} + + +Error Decoder_JPEG::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + Error err = const_cast(this)->parse_SOF(); + if (err) { + return err; + } + + *out_chroma = m_config->chroma; + + if (*out_chroma == heif_chroma_monochrome) { + *out_colorspace = heif_colorspace_monochrome; + } + else { + *out_colorspace = heif_colorspace_YCbCr; + } + + return Error::Ok; +} diff --git a/libheif/codecs/jpeg_dec.h b/libheif/codecs/jpeg_dec.h new file mode 100644 index 0000000000..4de18eb58e --- /dev/null +++ b/libheif/codecs/jpeg_dec.h @@ -0,0 +1,68 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_JPEG_DEC_H +#define HEIF_JPEG_DEC_H + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" +#include "codecs/decoder.h" + +#include +#include +#include + +class Box_jpgC; + + +class Decoder_JPEG : public Decoder +{ +public: + explicit Decoder_JPEG(const std::shared_ptr& jpgC) : m_jpgC(jpgC) {} + + heif_compression_format get_compression_format() const override { return heif_compression_JPEG; } + + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; + + Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; + + Result> read_bitstream_configuration_data() const override; + +private: + const std::shared_ptr m_jpgC; // Optional jpgC box. May be NULL. + + struct ConfigInfo { + uint8_t sample_precision = 0; + heif_chroma chroma = heif_chroma_undefined; + + uint8_t nComponents = 0; + uint8_t h_sampling[3]{}; + uint8_t v_sampling[3]{}; + }; + + std::optional m_config; + + Error parse_SOF(); +}; + +#endif diff --git a/libheif/codecs/uncompressed/decoder_abstract.cc b/libheif/codecs/uncompressed/decoder_abstract.cc new file mode 100644 index 0000000000..7b66edbcd4 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_abstract.cc @@ -0,0 +1,270 @@ +/* + * 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 "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 "decoder_abstract.h" + + +AbstractDecoder::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); +} + +void AbstractDecoder::buildChannelList(std::shared_ptr& img) +{ + for (Box_uncC::Component component : m_uncC->get_components()) { + ChannelListEntry entry = buildChannelListEntry(component, img); + channelList.push_back(entry); + } +} + +void AbstractDecoder::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 AbstractDecoder::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 AbstractDecoder::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 AbstractDecoder::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(); +} + + +AbstractDecoder::ChannelListEntry AbstractDecoder::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; +} + + +// generic compression and uncompressed, per 23001-17 +const Error AbstractDecoder::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 AbstractDecoder::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()); + } +} diff --git a/libheif/codecs/uncompressed/decoder_abstract.h b/libheif/codecs/uncompressed/decoder_abstract.h new file mode 100644 index 0000000000..60d5acb2d9 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_abstract.h @@ -0,0 +1,208 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef UNCI_DECODER_ABSTRACT_H +#define UNCI_DECODER_ABSTRACT_H + +#include +#include +#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" + + +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; +}; + + +template void skip_to_alignment(T& position, uint32_t alignment) +{ + if (alignment == 0) { + return; + } + + T residual = position % alignment; + if (residual == 0) { + return; + } + + position += alignment - residual; +} + + +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) = 0; + + void buildChannelList(std::shared_ptr& img); + +protected: + AbstractDecoder(uint32_t width, uint32_t height, + const std::shared_ptr cmpd, + const std::shared_ptr uncC); + + 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; + + void processComponentSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_row_offset, uint32_t tile_column, uint32_t tile_x); + + // 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); + + void processComponentTileSample(UncompressedBitReader& srcBits, const ChannelListEntry& entry, uint64_t dst_offset, uint32_t tile_x); + + // 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); + + // 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; + + const Error do_decompress_data(std::shared_ptr& cmpC_box, + std::vector compressed_data, + std::vector* data) const; + +private: + ChannelListEntry buildChannelListEntry(Box_uncC::Component component, std::shared_ptr& img); +}; + +#endif diff --git a/libheif/codecs/uncompressed/decoder_component_interleave.cc b/libheif/codecs/uncompressed/decoder_component_interleave.cc new file mode 100644 index 0000000000..5c20e284c9 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_component_interleave.cc @@ -0,0 +1,96 @@ +/* + * 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 "decoder_component_interleave.h" +#include "context.h" +#include "error.h" + +#include +#include + + +Error ComponentInterleaveDecoder::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) +{ + 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; + skip_to_alignment(bytes_per_component, entry.component_alignment); + bits_per_component = bytes_per_component * 8; + } + + uint32_t bytes_per_tile_row = (bits_per_component * entry.tile_width + 7) / 8; + skip_to_alignment(bytes_per_tile_row, m_uncC->get_row_align_size()); + 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) { + skip_to_alignment(total_tile_size, m_uncC->get_tile_align_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; +} diff --git a/libheif/codecs/uncompressed/decoder_component_interleave.h b/libheif/codecs/uncompressed/decoder_component_interleave.h new file mode 100644 index 0000000000..f25f40491f --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_component_interleave.h @@ -0,0 +1,45 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef UNCI_DECODER_COMPONENT_INTERLEAVE_H +#define UNCI_DECODER_COMPONENT_INTERLEAVE_H + +#include "decoder_abstract.h" +#include +#include + + +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; +}; + +#endif // UNCI_DECODER_COMPONENT_INTERLEAVE_H diff --git a/libheif/codecs/uncompressed/decoder_mixed_interleave.cc b/libheif/codecs/uncompressed/decoder_mixed_interleave.cc new file mode 100644 index 0000000000..4187315b67 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_mixed_interleave.cc @@ -0,0 +1,130 @@ +/* + * 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 "decoder_mixed_interleave.h" +#include "context.h" +#include "error.h" + +#include +#include +#include + + +Error MixedInterleaveDecoder::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) +{ + 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; + skip_to_alignment(bytes_per_component, entry.component_alignment); + 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) { + skip_to_alignment(tile_size, m_uncC->get_tile_align_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 MixedInterleaveDecoder::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; + } + } +} diff --git a/libheif/codecs/uncompressed/decoder_mixed_interleave.h b/libheif/codecs/uncompressed/decoder_mixed_interleave.h new file mode 100644 index 0000000000..c0f26abc64 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_mixed_interleave.h @@ -0,0 +1,46 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef UNCI_DECODER_MIXED_INTERLEAVE_H +#define UNCI_DECODER_MIXED_INTERLEAVE_H + +#include "decoder_abstract.h" +#include +#include + + +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; + + void processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, + uint32_t out_x0, uint32_t out_y0); +}; + +#endif // UNCI_DECODER_MIXED_INTERLEAVE_H diff --git a/libheif/codecs/uncompressed/decoder_pixel_interleave.cc b/libheif/codecs/uncompressed/decoder_pixel_interleave.cc new file mode 100644 index 0000000000..978b9affcd --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_pixel_interleave.cc @@ -0,0 +1,122 @@ +/* + * 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 "decoder_pixel_interleave.h" +#include "context.h" +#include "error.h" + +#include +#include + + +Error PixelInterleaveDecoder::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) +{ + 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 < m_tile_width; x++) { + uint32_t bits_per_pixel = 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; + skip_to_alignment(bytes_per_component, entry.component_alignment); + 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; + skip_to_alignment(bytes_per_pixel, m_uncC->get_pixel_size()); + bits_per_pixel = bytes_per_pixel * 8; + } + + bits_per_row += bits_per_pixel; + } + + uint32_t bytes_per_row = (bits_per_row + 7) / 8; + skip_to_alignment(bytes_per_row, m_uncC->get_row_align_size()); + + uint64_t total_tile_size = bytes_per_row * static_cast(m_tile_height); + if (m_uncC->get_tile_align_size() != 0) { + skip_to_alignment(total_tile_size, m_uncC->get_tile_align_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 PixelInterleaveDecoder::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()); + } +} diff --git a/libheif/codecs/uncompressed/decoder_pixel_interleave.h b/libheif/codecs/uncompressed/decoder_pixel_interleave.h new file mode 100644 index 0000000000..b2fc053b16 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_pixel_interleave.h @@ -0,0 +1,65 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef UNCI_DECODER_PIXEL_INTERLEAVE_H +#define UNCI_DECODER_PIXEL_INTERLEAVE_H + +#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" + +#include "decoder_abstract.h" +#include "decoder_component_interleave.h" +#include +#include + + +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; + + void processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, + uint32_t out_x0, uint32_t out_y0); +}; + +#endif // UNCI_DECODER_PIXEL_INTERLEAVE_H diff --git a/libheif/codecs/uncompressed/decoder_row_interleave.cc b/libheif/codecs/uncompressed/decoder_row_interleave.cc new file mode 100644 index 0000000000..b5efb6bfd3 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_row_interleave.cc @@ -0,0 +1,115 @@ +/* + * 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 "decoder_row_interleave.h" +#include "context.h" +#include "error.h" + +#include +#include + + +Error RowInterleaveDecoder::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) +{ + 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; + skip_to_alignment(bytes_per_component, entry.component_alignment); + 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; + skip_to_alignment(bytes_this_row, m_uncC->get_row_align_size()); + 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()) { + skip_to_alignment(bytes_per_row, m_uncC->get_row_align_size()); + } + + 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) { + skip_to_alignment(total_tile_size, m_uncC->get_tile_align_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 RowInterleaveDecoder::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()); + } + } +} + diff --git a/libheif/codecs/uncompressed/decoder_row_interleave.h b/libheif/codecs/uncompressed/decoder_row_interleave.h new file mode 100644 index 0000000000..d6bff32008 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_row_interleave.h @@ -0,0 +1,48 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef UNCI_DECODER_ROW_INTERLEAVE_H +#define UNCI_DECODER_ROW_INTERLEAVE_H + +#include "decoder_abstract.h" +#include +#include + + +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; + +private: + void processTile(UncompressedBitReader& srcBits, uint32_t tile_row, uint32_t tile_column, + uint32_t out_x0, uint32_t out_y0); +}; + +#endif // UNCI_DECODER_ROW_INTERLEAVE_H diff --git a/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc b/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc new file mode 100644 index 0000000000..4190b97ed0 --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_tile_component_interleave.cc @@ -0,0 +1,131 @@ +/* + * 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 "decoder_tile_component_interleave.h" +#include "context.h" +#include "error.h" + +#include +#include +#include + + +Error TileComponentInterleaveDecoder::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) +{ + 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; + skip_to_alignment(bytes_per_component, entry.component_alignment); + 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; + skip_to_alignment(bytes_per_pixel, m_uncC->get_pixel_size()); + bytes_per_row = bytes_per_pixel * m_tile_width; + } + else { + bytes_per_row = (bits_per_pixel * m_tile_width + 7) / 8; + } + + skip_to_alignment(bytes_per_row, m_uncC->get_row_align_size()); + + uint64_t component_tile_size = bytes_per_row * static_cast(m_tile_height); + + if (m_uncC->get_tile_align_size() != 0) { + skip_to_alignment(component_tile_size, m_uncC->get_tile_align_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; +} + + diff --git a/libheif/codecs/uncompressed/decoder_tile_component_interleave.h b/libheif/codecs/uncompressed/decoder_tile_component_interleave.h new file mode 100644 index 0000000000..590c18dfcb --- /dev/null +++ b/libheif/codecs/uncompressed/decoder_tile_component_interleave.h @@ -0,0 +1,44 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef UNCI_DECODER_TILE_COMPONENT_INTERLEAVE_H +#define UNCI_DECODER_TILE_COMPONENT_INTERLEAVE_H + +#include "decoder_abstract.h" +#include +#include + + +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; +}; + +#endif // UNCI_DECODER_TILE_COMPONENT_INTERLEAVE_H diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc index ab304884c1..28afcfda89 100644 --- a/libheif/codecs/uncompressed/unc_boxes.cc +++ b/libheif/codecs/uncompressed/unc_boxes.cc @@ -201,14 +201,14 @@ Error Box_uncC::parse(BitstreamRange& range) parse_full_box_header(range); m_profile = range.read32(); if (get_version() == 1) { - if (m_profile == fourcc_to_uint32("rgb3")) { + if (m_profile == fourcc("rgb3")) { Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; add_component(component0); Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; add_component(component1); Box_uncC::Component component2 = {2, 8, component_format_unsigned, 0}; add_component(component2); - } else if ((m_profile == fourcc_to_uint32("rgba")) || (m_profile == fourcc_to_uint32("abgr"))) { + } else if ((m_profile == fourcc("rgba")) || (m_profile == fourcc("abgr"))) { Box_uncC::Component component0 = {0, 8, component_format_unsigned, 0}; add_component(component0); Box_uncC::Component component1 = {1, 8, component_format_unsigned, 0}; @@ -288,7 +288,7 @@ std::string Box_uncC::dump(Indent& indent) const sstr << indent << "profile: " << m_profile; if (m_profile != 0) { - sstr << " (" << to_fourcc(m_profile) << ")"; + sstr << " (" << fourcc_to_string(m_profile) << ")"; } sstr << "\n"; if (get_version() == 0) { @@ -420,7 +420,7 @@ std::string Box_cmpC::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); - sstr << indent << "compression_type: " << to_fourcc(m_compression_type) << "\n"; + sstr << indent << "compression_type: " << fourcc_to_string(m_compression_type) << "\n"; sstr << indent << "compressed_entity_type: " << (int)m_compressed_unit_type << "\n"; return sstr.str(); } diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc new file mode 100644 index 0000000000..af270b3699 --- /dev/null +++ b/libheif/codecs/uncompressed/unc_codec.cc @@ -0,0 +1,814 @@ +/* + * 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 "unc_codec.h" + +#include "common_utils.h" +#include "context.h" +#include "error.h" +#include "libheif/heif.h" +#include "unc_types.h" +#include "unc_boxes.h" + +#include "decoder_abstract.h" +#include "decoder_component_interleave.h" +#include "decoder_pixel_interleave.h" +#include "decoder_mixed_interleave.h" +#include "decoder_row_interleave.h" +#include "decoder_tile_component_interleave.h" + +#include +#include +#include +#include + + +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; + } +} + +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; + } +} + + + +static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std::shared_ptr& cmpd, const std::shared_ptr& uncC) +{ + switch (uncC->get_interleave_type()) { + case interleave_mode_component: + return new ComponentInterleaveDecoder(width, height, cmpd, uncC); + case interleave_mode_pixel: + return new PixelInterleaveDecoder(width, height, cmpd, uncC); + case interleave_mode_mixed: + return new MixedInterleaveDecoder(width, height, cmpd, uncC); + case interleave_mode_row: + return new RowInterleaveDecoder(width, height, cmpd, uncC); + case interleave_mode_tile_component: + return new TileComponentInterleaveDecoder(width, height, cmpd, uncC); + default: + 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_codec.h b/libheif/codecs/uncompressed/unc_codec.h new file mode 100644 index 0000000000..3084330841 --- /dev/null +++ b/libheif/codecs/uncompressed/unc_codec.h @@ -0,0 +1,77 @@ +/* + * HEIF codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + + +#ifndef LIBHEIF_UNC_CODEC_H +#define LIBHEIF_UNC_CODEC_H + +#include "pixelimage.h" +#include "file.h" +#include "context.h" + +#include +#include +#include +#include + +class HeifContext; + + +bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr& uncC); + +Error fill_cmpd_and_uncC(std::shared_ptr& cmpd, + std::shared_ptr& uncC, + const std::shared_ptr& image, + const heif_unci_image_parameters* parameters); + +bool map_uncompressed_component_to_channel(const std::shared_ptr &cmpd, + const std::shared_ptr &uncC, + Box_uncC::Component component, + heif_channel *channel); + + +class UncompressedImageCodec +{ +public: + 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&); +}; + +#endif //LIBHEIF_UNC_CODEC_H diff --git a/libheif/codecs/uncompressed/unc_dec.cc b/libheif/codecs/uncompressed/unc_dec.cc new file mode 100644 index 0000000000..b85a755f12 --- /dev/null +++ b/libheif/codecs/uncompressed/unc_dec.cc @@ -0,0 +1,149 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "codecs/uncompressed/unc_dec.h" +#include "codecs/uncompressed/unc_codec.h" +#include "error.h" +#include "context.h" + +#include +#include + + +Result> Decoder_uncompressed::read_bitstream_configuration_data() const +{ + return std::vector{}; +} + + +int Decoder_uncompressed::get_luma_bits_per_pixel() const +{ + assert(m_uncC); + + if (!m_cmpd) { + if (isKnownUncompressedFrameConfigurationBoxProfile(m_uncC)) { + return 8; + } + else { + return -1; + } + } + + int luma_bits = 0; + int alternate_channel_bits = 0; + for (Box_uncC::Component component : m_uncC->get_components()) { + uint16_t component_index = component.component_index; + if (component_index >= m_cmpd->get_components().size()) { + return -1; + } + auto component_type = m_cmpd->get_components()[component_index].component_type; + switch (component_type) { + case component_type_monochrome: + case component_type_red: + case component_type_green: + case component_type_blue: + alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth); + break; + case component_type_Y: + luma_bits = std::max(luma_bits, (int) component.component_bit_depth); + break; + // TODO: there are other things we'll need to handle eventually, like palette. + } + } + if (luma_bits > 0) { + return luma_bits; + } + else if (alternate_channel_bits > 0) { + return alternate_channel_bits; + } + else { + return 8; + } +} + +int Decoder_uncompressed::get_chroma_bits_per_pixel() const +{ + if (m_uncC && m_uncC->get_version() == 1) { + // All of the version 1 cases are 8 bit + return 8; + } + + if (!m_uncC || !m_cmpd) { + return -1; + } + + int chroma_bits = 0; + int alternate_channel_bits = 0; + for (Box_uncC::Component component : m_uncC->get_components()) { + uint16_t component_index = component.component_index; + if (component_index >= m_cmpd->get_components().size()) { + return -1; + } + auto component_type = m_cmpd->get_components()[component_index].component_type; + switch (component_type) { + case component_type_monochrome: + case component_type_red: + case component_type_green: + case component_type_blue: + alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth); + break; + case component_type_Cb: + case component_type_Cr: + chroma_bits = std::max(chroma_bits, (int) component.component_bit_depth); + break; + // TODO: there are other things we'll need to handle eventually, like palette. + } + } + if (chroma_bits > 0) { + return chroma_bits; + } + else if (alternate_channel_bits > 0) { + return alternate_channel_bits; + } + else { + return 8; + } +} + + +Error Decoder_uncompressed::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + if (m_uncC->get_version() == 1) { + // This is the shortform case, no cmpd box, and always some kind of RGB + *out_colorspace = heif_colorspace_RGB; + if (m_uncC->get_profile() == fourcc("rgb3")) { + *out_chroma = heif_chroma_interleaved_RGB; + } + else if ((m_uncC->get_profile() == fourcc("rgba")) || (m_uncC->get_profile() == fourcc("abgr"))) { + *out_chroma = heif_chroma_interleaved_RGBA; + } + + return Error::Ok; + } + else if (m_cmpd) { + UncompressedImageCodec::get_heif_chroma_uncompressed(m_uncC, m_cmpd, out_chroma, out_colorspace); + return Error::Ok; + } + else { + return {heif_error_Invalid_input, + heif_suberror_Unspecified, + "Missing 'cmpd' box."}; + } +} diff --git a/libheif/codecs/uncompressed/unc_dec.h b/libheif/codecs/uncompressed/unc_dec.h new file mode 100644 index 0000000000..2521df1493 --- /dev/null +++ b/libheif/codecs/uncompressed/unc_dec.h @@ -0,0 +1,57 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_UNC_DEC_H +#define HEIF_UNC_DEC_H + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" + +#include +#include +#include "codecs/decoder.h" + +class Box_uncC; +class Box_cmpd; + + +class Decoder_uncompressed : public Decoder +{ +public: + explicit Decoder_uncompressed(const std::shared_ptr& uncC, + const std::shared_ptr& cmpd) : m_uncC(uncC), m_cmpd(cmpd) {} + + heif_compression_format get_compression_format() const override { return heif_compression_uncompressed; } + + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; + + Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; + + Result> read_bitstream_configuration_data() const override; + +private: + const std::shared_ptr m_uncC; + const std::shared_ptr m_cmpd; +}; + +#endif diff --git a/libheif/codecs/uncompressed/unc_image.cc b/libheif/codecs/uncompressed/unc_image.cc deleted file mode 100644 index b1fe15c996..0000000000 --- a/libheif/codecs/uncompressed/unc_image.cc +++ /dev/null @@ -1,2228 +0,0 @@ -/* - * 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_image.h" -#include "codecs/image_item.h" - -static bool isKnownUncompressedFrameConfigurationBoxProfile(const std::shared_ptr& uncC) -{ - return ((uncC != nullptr) && (uncC->get_version() == 1) && ((uncC->get_profile() == fourcc("rgb3")) || (uncC->get_profile() == fourcc("rgba")) || (uncC->get_profile() == fourcc("abgr")))); -} - - -static Error uncompressed_image_type_is_supported(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; - } -} - - -int UncompressedImageCodec::get_luma_bits_per_pixel_from_configuration_unci(const HeifFile& heif_file, heif_item_id imageID) -{ - std::shared_ptr uncC_box = heif_file.get_property(imageID); - std::shared_ptr cmpd_box = heif_file.get_property(imageID); - - if (!uncC_box) { - return -1; - } - - if (!cmpd_box) { - if (isKnownUncompressedFrameConfigurationBoxProfile(uncC_box)) { - return 8; - } else { - return -1; - } - } - - int luma_bits = 0; - int alternate_channel_bits = 0; - for (Box_uncC::Component component : uncC_box->get_components()) { - uint16_t component_index = component.component_index; - if (component_index >= cmpd_box->get_components().size()) { - return -1; - } - auto component_type = cmpd_box->get_components()[component_index].component_type; - switch (component_type) { - case component_type_monochrome: - case component_type_red: - case component_type_green: - case component_type_blue: - alternate_channel_bits = std::max(alternate_channel_bits, (int)component.component_bit_depth); - break; - case component_type_Y: - luma_bits = std::max(luma_bits, (int)component.component_bit_depth); - break; - // TODO: there are other things we'll need to handle eventually, like palette. - } - } - if (luma_bits > 0) { - return luma_bits; - } - else if (alternate_channel_bits > 0) { - return alternate_channel_bits; - } - else { - return 8; - } -} - -int UncompressedImageCodec::get_chroma_bits_per_pixel_from_configuration_unci(const HeifFile& heif_file, heif_item_id imageID) -{ - std::shared_ptr uncC_box = heif_file.get_property(imageID); - std::shared_ptr cmpd_box = heif_file.get_property(imageID); - - if (uncC_box && uncC_box->get_version() == 1) { - // All of the version 1 cases are 8 bit - return 8; - } - - if (!uncC_box || !cmpd_box) { - return -1; - } - - int chroma_bits = 0; - int alternate_channel_bits = 0; - for (Box_uncC::Component component : uncC_box->get_components()) { - uint16_t component_index = component.component_index; - if (component_index >= cmpd_box->get_components().size()) { - return -1; - } - auto component_type = cmpd_box->get_components()[component_index].component_type; - switch (component_type) { - case component_type_monochrome: - case component_type_red: - case component_type_green: - case component_type_blue: - alternate_channel_bits = std::max(alternate_channel_bits, (int)component.component_bit_depth); - break; - case component_type_Cb: - case component_type_Cr: - chroma_bits = std::max(chroma_bits, (int)component.component_bit_depth); - break; - // TODO: there are other things we'll need to handle eventually, like palette. - } - } - if (chroma_bits > 0) { - return chroma_bits; - } - else if (alternate_channel_bits > 0) { - return alternate_channel_bits; - } - else { - return 8; - } -} - -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) -{ - uncC->set_version(0); - if (image->get_colorspace() != heif_colorspace_RGB) { - return; - } - if (!((image->get_chroma_format() == heif_chroma_interleaved_RGB) || (image->get_chroma_format() == heif_chroma_interleaved_RGBA))) { - return; - } - if (image->get_bits_per_pixel(heif_channel_interleaved) != 8) { - return; - } - if (image->get_chroma_format() == heif_chroma_interleaved_RGBA) { - uncC->set_profile(fourcc_to_uint32("rgba")); - } else { - uncC->set_profile(fourcc_to_uint32("rgb3")); - } - uncC->set_version(1); -} - - -Result> ImageItem_uncompressed::decode_compressed_image(const struct heif_decoding_options& options, - bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const -{ - std::shared_ptr img; - - std::vector data; - - Error err; - - if (decode_tile_only) { - err = UncompressedImageCodec::decode_uncompressed_image_tile(get_context(), - get_id(), - img, - tile_x0, tile_y0); - } - else { - err = UncompressedImageCodec::decode_uncompressed_image(get_context(), - get_id(), - img); - } - - if (err) { - return err; - } - else { - return img; - } -} - - -struct unciHeaders -{ - std::shared_ptr uncC; - std::shared_ptr cmpd; -}; - - -static Result generate_headers(const std::shared_ptr& src_image, - const heif_unci_image_parameters* parameters, - const struct heif_encoding_options* options) -{ - unciHeaders headers; - - std::shared_ptr uncC = std::make_shared(); - if (options && options->prefer_uncC_short_form) { - maybe_make_minimised_uncC(uncC, src_image); - } - - if (uncC->get_version() == 1) { - headers.uncC = uncC; - } else { - std::shared_ptr cmpd = std::make_shared(); - - Error error = fill_cmpd_and_uncC(cmpd, uncC, src_image, parameters); - if (error) { - return error; - } - - headers.cmpd = cmpd; - headers.uncC = uncC; - } - - return headers; -} - - -Result> encode_image_tile(const std::shared_ptr& src_image) -{ - std::vector data; - - if (src_image->get_colorspace() == heif_colorspace_YCbCr) - { - uint64_t offset = 0; - for (heif_channel channel : {heif_channel_Y, heif_channel_Cb, heif_channel_Cr}) - { - uint32_t src_stride; - uint32_t src_width = src_image->get_width(channel); - uint32_t src_height = src_image->get_height(channel); - const uint8_t* src_data = src_image->get_plane(channel, &src_stride); - uint64_t out_size = src_width * src_height; - data.resize(data.size() + out_size); - for (uint32_t y = 0; y < src_height; y++) { - memcpy(data.data() + offset + y * src_width, src_data + src_stride * y, src_width); - } - offset += out_size; - } - - return data; - } - else if (src_image->get_colorspace() == heif_colorspace_RGB) - { - if (src_image->get_chroma_format() == heif_chroma_444) - { - uint64_t offset = 0; - std::vector channels = {heif_channel_R, heif_channel_G, heif_channel_B}; - if (src_image->has_channel(heif_channel_Alpha)) - { - channels.push_back(heif_channel_Alpha); - } - for (heif_channel channel : channels) - { - uint32_t src_stride; - const uint8_t* src_data = src_image->get_plane(channel, &src_stride); - uint64_t out_size = src_image->get_height() * src_image->get_width(); - - data.resize(data.size() + out_size); - for (uint32_t y = 0; y < src_image->get_height(); y++) { - memcpy(data.data() + offset + y * src_image->get_width(), src_data + y * src_stride, src_image->get_width()); - } - - offset += out_size; - } - - return data; - } - else if ((src_image->get_chroma_format() == heif_chroma_interleaved_RGB) || - (src_image->get_chroma_format() == heif_chroma_interleaved_RGBA) || - (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || - (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || - (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || - (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) - { - int bytes_per_pixel = 0; - switch (src_image->get_chroma_format()) { - case heif_chroma_interleaved_RGB: - bytes_per_pixel=3; - break; - case heif_chroma_interleaved_RGBA: - bytes_per_pixel=4; - break; - case heif_chroma_interleaved_RRGGBB_BE: - case heif_chroma_interleaved_RRGGBB_LE: - bytes_per_pixel=6; - break; - case heif_chroma_interleaved_RRGGBBAA_BE: - case heif_chroma_interleaved_RRGGBBAA_LE: - bytes_per_pixel=8; - break; - default: - assert(false); - } - - uint32_t src_stride; - const uint8_t* src_data = src_image->get_plane(heif_channel_interleaved, &src_stride); - uint64_t out_size = src_image->get_height() * src_image->get_width() * bytes_per_pixel; - data.resize(out_size); - for (uint32_t y = 0; y < src_image->get_height(); y++) { - memcpy(data.data() + y * src_image->get_width() * bytes_per_pixel, src_data + src_stride * y, src_image->get_width() * bytes_per_pixel); - } - - return data; - } - else - { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Unsupported RGB chroma"); - } - } - else if (src_image->get_colorspace() == heif_colorspace_monochrome) - { - uint64_t offset = 0; - std::vector channels; - if (src_image->has_channel(heif_channel_Alpha)) - { - channels = {heif_channel_Y, heif_channel_Alpha}; - } - else - { - channels = {heif_channel_Y}; - } - for (heif_channel channel : channels) - { - uint32_t src_stride; - const uint8_t* src_data = src_image->get_plane(channel, &src_stride); - uint64_t out_size = src_image->get_height() * src_stride; - data.resize(data.size() + out_size); - memcpy(data.data() + offset, src_data, out_size); - offset += out_size; - } - - return data; - } - else - { - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_data_version, - "Unsupported colourspace"); - } - -} - - -Result ImageItem_uncompressed::encode(const std::shared_ptr& src_image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) -{ - heif_unci_image_parameters parameters{}; - parameters.image_width = src_image->get_width(); - parameters.image_height = src_image->get_height(); - parameters.tile_width = parameters.image_width; - parameters.tile_height = parameters.image_height; - - - // --- generate configuration property boxes - - Result genHeadersResult = generate_headers(src_image, ¶meters, &options); - if (genHeadersResult.error) { - return genHeadersResult.error; - } - - const unciHeaders& headers = *genHeadersResult; - - CodedImageData codedImageData; - if (headers.uncC) { - codedImageData.properties.push_back(headers.uncC); - } - if (headers.cmpd) { - codedImageData.properties.push_back(headers.cmpd); - } - - - // --- encode image - - Result> codedBitstreamResult = encode_image_tile(src_image); - if (codedBitstreamResult.error) { - return codedBitstreamResult.error; - } - - codedImageData.bitstream = *codedBitstreamResult; - - return codedImageData; -} - - -Result> ImageItem_uncompressed::add_unci_item(HeifContext* ctx, - const heif_unci_image_parameters* parameters, - const struct heif_encoding_options* encoding_options, - const std::shared_ptr& prototype) -{ - // Check input parameters - - if (parameters->image_width % parameters->tile_width != 0 || - parameters->image_height % parameters->tile_height != 0) { - return Error{heif_error_Invalid_input, - heif_suberror_Invalid_parameter_value, - "ISO 23001-17 image size must be an integer multiple of the tile size."}; - } - - // Create 'unci' Item - - auto file = ctx->get_heif_file(); - - heif_item_id unci_id = ctx->get_heif_file()->add_new_image("unci"); - auto unci_image = std::make_shared(ctx, unci_id); - ctx->insert_new_image(unci_id, unci_image); - - - // Generate headers - - Result genHeadersResult = generate_headers(prototype, parameters, encoding_options); - if (genHeadersResult.error) { - return genHeadersResult.error; - } - - const unciHeaders& headers = *genHeadersResult; - - if (headers.uncC) { - file->add_property(unci_id, headers.uncC, true); - } - - if (headers.cmpd) { - file->add_property(unci_id, headers.cmpd, true); - } - - // Add `ispe` property - - file->add_ispe_property(unci_id, - static_cast(parameters->image_width), - static_cast(parameters->image_height), - true); - - if (parameters->compression != heif_metadata_compression_off) { - auto icef = std::make_shared(); - auto cmpC = std::make_shared(); - cmpC->set_compressed_unit_type(heif_cmpC_compressed_unit_type_image_tile); - - if (parameters->compression == heif_metadata_compression_deflate) { - cmpC->set_compression_type(fourcc("defl")); - } - else if (parameters->compression == heif_metadata_compression_zlib) { - cmpC->set_compression_type(fourcc("zlib")); - } -#if HAVE_BROTLI - else if (parameters->compression == heif_metadata_compression_brotli) { - cmpC->set_compression_type(fourcc("brot")); - } -#endif - else { - assert(false); - } - - file->add_property(unci_id, cmpC, true); - file->add_property_without_deduplication(unci_id, icef, true); // icef is empty. A normal add_property() would lead to a wrong deduplication. - } - - // Create empty image. If we use compression, we append the data piece by piece. - - if (parameters->compression == heif_metadata_compression_off) { - uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width / headers.uncC->get_number_of_tile_columns(), - parameters->image_height / headers.uncC->get_number_of_tile_rows()); - - std::vector dummydata; - dummydata.resize(tile_size); - - for (uint64_t i = 0; i < tile_size; i++) { - const int construction_method = 0; // 0=mdat 1=idat - file->append_iloc_data(unci_id, dummydata, construction_method); - } - } - - // Set Brands - ctx->get_heif_file()->set_brand(heif_compression_uncompressed, unci_image->is_miaf_compatible()); - - return {unci_image}; -} - - -Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image) -{ - std::shared_ptr uncC = get_file()->get_property(get_id()); - assert(uncC); - - uint32_t tile_width = image->get_width(); - uint32_t tile_height = image->get_height(); - - uint64_t tile_data_size = uncC->compute_tile_data_size_bytes(tile_width, tile_height); - - uint32_t tile_idx = tile_y * uncC->get_number_of_tile_columns() + tile_x; - - Result> codedBitstreamResult = encode_image_tile(image); - if (codedBitstreamResult.error) { - return codedBitstreamResult.error; - } - - std::shared_ptr cmpC = get_file()->get_property(get_id()); - std::shared_ptr icef = get_file()->get_property(get_id()); - - if (!icef || !cmpC) { - assert(!icef); - assert(!cmpC); - - // uncompressed - - get_file()->replace_iloc_data(get_id(), tile_idx * tile_data_size, *codedBitstreamResult, 0); - } - else { - std::vector compressed_data; - const std::vector& raw_data = codedBitstreamResult.value; - - uint32_t compr = cmpC->get_compression_type(); - switch (compr) { - case fourcc("defl"): - compressed_data = compress_deflate(raw_data.data(), raw_data.size()); - break; - case fourcc("zlib"): - compressed_data = compress_zlib(raw_data.data(), raw_data.size()); - break; -#if HAVE_BROTLI - case fourcc("brot"): - compressed_data = compress_brotli(raw_data.data(), raw_data.size()); - break; -#endif - default: - assert(false); - break; - } - - get_file()->append_iloc_data(get_id(), compressed_data, 0); - - Box_icef::CompressedUnitInfo unit_info; - unit_info.unit_offset = m_next_tile_write_pos; - unit_info.unit_size = compressed_data.size(); - icef->set_component(tile_idx, unit_info); - - m_next_tile_write_pos += compressed_data.size(); - } - - return Error::Ok; -} - - -int ImageItem_uncompressed::get_luma_bits_per_pixel() const -{ - int bpp = UncompressedImageCodec::get_luma_bits_per_pixel_from_configuration_unci(*get_file(), get_id()); - return bpp; -} - - -int ImageItem_uncompressed::get_chroma_bits_per_pixel() const -{ - int bpp = UncompressedImageCodec::get_chroma_bits_per_pixel_from_configuration_unci(*get_file(), get_id()); - return bpp; -} - - -void ImageItem_uncompressed::get_tile_size(uint32_t& w, uint32_t& h) const -{ - auto ispe = get_file()->get_property(get_id()); - auto uncC = get_file()->get_property(get_id()); - - if (!ispe || !uncC) { - w=h=0; - } - - w = ispe->get_width() / uncC->get_number_of_tile_columns(); - h = ispe->get_height() / uncC->get_number_of_tile_rows(); -} - - -heif_image_tiling ImageItem_uncompressed::get_heif_image_tiling() const -{ - heif_image_tiling tiling{}; - - auto ispe = get_file()->get_property(get_id()); - auto uncC = get_file()->get_property(get_id()); - assert(ispe && uncC); - - tiling.num_columns = uncC->get_number_of_tile_columns(); - tiling.num_rows = uncC->get_number_of_tile_rows(); - - tiling.tile_width = ispe->get_width() / tiling.num_columns; - tiling.tile_height = ispe->get_height() / tiling.num_rows; - - tiling.image_width = ispe->get_width(); - tiling.image_height = ispe->get_height(); - tiling.number_of_extra_dimensions = 0; - - return tiling; -} diff --git a/libheif/codecs/vvc.cc b/libheif/codecs/vvc_boxes.cc similarity index 85% rename from libheif/codecs/vvc.cc rename to libheif/codecs/vvc_boxes.cc index c54f4f363c..f8bb73cefc 100644 --- a/libheif/codecs/vvc.cc +++ b/libheif/codecs/vvc_boxes.cc @@ -18,7 +18,7 @@ * along with libheif. If not, see . */ -#include "vvc.h" +#include "vvc_boxes.h" #include "file.h" #include #include @@ -551,114 +551,3 @@ Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, return Error::Ok; } - -Result ImageItem_VVC::encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) -{ - CodedImageData codedImage; - - auto vvcC = std::make_shared(); - codedImage.properties.push_back(vvcC); - - - heif_image c_api_image; - c_api_image.image = image; - - struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); - if (err.code) { - return Error(err.code, - err.subcode, - err.message); - } - - int encoded_width = 0; - int encoded_height = 0; - - for (;;) { - uint8_t* data; - int size; - - encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, NULL); - - if (data == NULL) { - break; - } - - - const uint8_t NAL_SPS = 15; - - uint8_t nal_type = 0; - if (size>=2) { - nal_type = (data[1] >> 3) & 0x1F; - } - - if (nal_type == NAL_SPS) { - Box_vvcC::configuration config; - - parse_sps_for_vvcC_configuration(data, size, &config, &encoded_width, &encoded_height); - - vvcC->set_configuration(config); - } - - switch (nal_type) { - case 14: // VPS - case 15: // SPS - case 16: // PPS - vvcC->append_nal_data(data, size); - break; - - default: - codedImage.append_with_4bytes_size(data, size); - } - } - - return codedImage; -} - - -Result> ImageItem_VVC::read_bitstream_configuration_data(heif_item_id itemId) const -{ - // --- get codec configuration - - std::shared_ptr vvcC_box = get_file()->get_property(itemId); - if (!vvcC_box) - { - assert(false); - return Error(heif_error_Invalid_input, - heif_suberror_No_vvcC_box); - } - - std::vector data; - if (!vvcC_box->get_headers(&data)) - { - return Error(heif_error_Invalid_input, - heif_suberror_No_item_data); - } - - return data; -} - - -int ImageItem_VVC::get_luma_bits_per_pixel() const -{ - auto vvcC_box = get_file()->get_property(get_id()); - if (vvcC_box) { - const Box_vvcC::configuration& config = vvcC_box->get_configuration(); - if (config.ptl_present_flag) { - return config.bit_depth_minus8 + 8; - } - else { - return 8; // TODO: what shall we do if the bit-depth is unknown? Use PIXI? - } - } - - return -1; -} - - -int ImageItem_VVC::get_chroma_bits_per_pixel() const -{ - return get_luma_bits_per_pixel(); -} diff --git a/libheif/codecs/vvc.h b/libheif/codecs/vvc_boxes.h similarity index 72% rename from libheif/codecs/vvc.h rename to libheif/codecs/vvc_boxes.h index abb85524b9..38fdc3e627 100644 --- a/libheif/codecs/vvc.h +++ b/libheif/codecs/vvc_boxes.h @@ -18,13 +18,13 @@ * along with libheif. If not, see . */ -#ifndef LIBHEIF_VVC_H -#define LIBHEIF_VVC_H +#ifndef LIBHEIF_VVC_BOXES_H +#define LIBHEIF_VVC_BOXES_H #include "box.h" #include #include -#include +#include "image-items/image_item.h" #include @@ -105,32 +105,4 @@ Error parse_sps_for_vvcC_configuration(const uint8_t* sps, size_t size, Box_vvcC::configuration* inout_config, int* width, int* height); -class ImageItem_VVC : public ImageItem -{ -public: - ImageItem_VVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} - - ImageItem_VVC(HeifContext* ctx) : ImageItem(ctx) {} - - const char* get_infe_type() const override { return "vvc1"; } - - const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; } - - const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } - - heif_compression_format get_compression_format() const override { return heif_compression_VVC; } - - int get_luma_bits_per_pixel() const override; - - int get_chroma_bits_per_pixel() const override; - - Result encode(const std::shared_ptr& image, - struct heif_encoder* encoder, - const struct heif_encoding_options& options, - enum heif_image_input_class input_class) override; - -protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; -}; - -#endif // LIBHEIF_VVC_H +#endif // LIBHEIF_VVC_BOXES_H diff --git a/libheif/codecs/vvc_dec.cc b/libheif/codecs/vvc_dec.cc new file mode 100644 index 0000000000..3c929713a9 --- /dev/null +++ b/libheif/codecs/vvc_dec.cc @@ -0,0 +1,71 @@ +/* + * HEIF codec. + * Copyright (c) 2024 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 "vvc_dec.h" +#include "vvc_boxes.h" +#include "error.h" +#include "context.h" + +#include + + +Result> Decoder_VVC::read_bitstream_configuration_data() const +{ + std::vector data; + if (!m_vvcC->get_headers(&data)) { + return Error{heif_error_Invalid_input, + heif_suberror_No_item_data}; + } + + return data; +} + + +int Decoder_VVC::get_luma_bits_per_pixel() const +{ + const Box_vvcC::configuration& config = m_vvcC->get_configuration(); + if (config.ptl_present_flag) { + return config.bit_depth_minus8 + 8; + } + else { + return 8; // TODO: what shall we do if the bit-depth is unknown? Use PIXI? + } +} + + +int Decoder_VVC::get_chroma_bits_per_pixel() const +{ + return get_luma_bits_per_pixel(); +} + + +Error Decoder_VVC::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + *out_chroma = (heif_chroma) (m_vvcC->get_configuration().chroma_format_idc); + + if (*out_chroma == heif_chroma_monochrome) { + *out_colorspace = heif_colorspace_monochrome; + } + else { + *out_colorspace = heif_colorspace_YCbCr; + } + + return Error::Ok; +} diff --git a/libheif/codecs/vvc_dec.h b/libheif/codecs/vvc_dec.h new file mode 100644 index 0000000000..0d55b1c39a --- /dev/null +++ b/libheif/codecs/vvc_dec.h @@ -0,0 +1,54 @@ +/* + * HEIF codec. + * Copyright (c) 2024 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_VVC_DEC_H +#define HEIF_VVC_DEC_H + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" + +#include +#include +#include + +class Box_vvcC; + + +class Decoder_VVC : public Decoder +{ +public: + explicit Decoder_VVC(const std::shared_ptr& vvcC) : m_vvcC(vvcC) {} + + heif_compression_format get_compression_format() const override { return heif_compression_VVC; } + + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; + + Error get_coded_image_colorspace(heif_colorspace*, heif_chroma*) const override; + + Result> read_bitstream_configuration_data() const override; + +private: + const std::shared_ptr m_vvcC; +}; + +#endif diff --git a/libheif/common_utils.cc b/libheif/common_utils.cc index 060cef4623..2a204e49ac 100644 --- a/libheif/common_utils.cc +++ b/libheif/common_utils.cc @@ -100,3 +100,15 @@ uint8_t compute_avif_profile(int bits_per_pixel, heif_chroma chroma) return 2; } } + + +std::string fourcc_to_string(uint32_t code) +{ + std::string str(" "); + str[0] = static_cast((code >> 24) & 0xFF); + str[1] = static_cast((code >> 16) & 0xFF); + str[2] = static_cast((code >> 8) & 0xFF); + str[3] = static_cast((code >> 0) & 0xFF); + + return str; +} diff --git a/libheif/common_utils.h b/libheif/common_utils.h index 07820070a1..180880ae29 100644 --- a/libheif/common_utils.h +++ b/libheif/common_utils.h @@ -23,6 +23,7 @@ #include #include "libheif/heif.h" +#include #ifdef _MSC_VER #define MAYBE_UNUSED @@ -31,7 +32,7 @@ #endif -constexpr inline uint32_t fourcc_to_uint32(const char* id) +constexpr inline uint32_t fourcc(const char* id) { return (((((uint32_t) id[0])&0xFF) << 24) | ((((uint32_t) id[1])&0xFF) << 16) | @@ -39,6 +40,8 @@ constexpr inline uint32_t fourcc_to_uint32(const char* id) ((((uint32_t) id[3])&0xFF) << 0)); } +std::string fourcc_to_string(uint32_t code); + // Functions for common use in libheif and the plugins. diff --git a/libheif/context.cc b/libheif/context.cc index 0553f8cbfc..cdcece5d8f 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -30,7 +30,8 @@ #include #include #include -#include +#include "image-items/image_item.h" +#include #if ENABLE_PARALLEL_TILE_DECODING #include @@ -44,18 +45,18 @@ #include "compression.h" #include "color-conversion/colorconversion.h" #include "plugin_registry.h" -#include "codecs/hevc.h" -#include "codecs/vvc.h" -#include "codecs/avif.h" -#include "codecs/jpeg.h" -#include "codecs/mask_image.h" -#include "codecs/jpeg2000.h" -#include "codecs/grid.h" -#include "codecs/overlay.h" -#include "codecs/tild.h" +#include "image-items/hevc.h" +#include "image-items/vvc.h" +#include "image-items/avif.h" +#include "image-items/jpeg.h" +#include "image-items/mask_image.h" +#include "image-items/jpeg2000.h" +#include "image-items/grid.h" +#include "image-items/overlay.h" +#include "image-items/tild.h" #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed/unc_image.h" +#include "image-items/unc_image.h" #endif @@ -180,7 +181,7 @@ Error HeifContext::check_resolution(uint32_t width, uint32_t height) const { std::shared_ptr HeifContext::add_region_item(uint32_t reference_width, uint32_t reference_height) { - std::shared_ptr box = m_heif_file->add_new_infe_box("rgan"); + std::shared_ptr box = m_heif_file->add_new_infe_box(fourcc("rgan")); box->set_hidden_item(true); auto regionItem = std::make_shared(box->get_item_ID(), reference_width, reference_height); @@ -230,21 +231,21 @@ std::string HeifContext::debug_dump_boxes() const } -static bool item_type_is_image(const std::string& item_type, const std::string& content_type) +static bool item_type_is_image(uint32_t item_type, const std::string& content_type) { - return (item_type == "hvc1" || - item_type == "av01" || - item_type == "grid" || - item_type == "tild" || - item_type == "iden" || - item_type == "iovl" || - item_type == "avc1" || - item_type == "unci" || - item_type == "vvc1" || - item_type == "jpeg" || - (item_type == "mime" && content_type == "image/jpeg") || - item_type == "j2k1" || - item_type == "mski"); + return (item_type == fourcc("hvc1") || + item_type == fourcc("av01") || + item_type == fourcc("grid") || + item_type == fourcc("tild") || + item_type == fourcc("iden") || + item_type == fourcc("iovl") || + item_type == fourcc("avc1") || + item_type == fourcc("unci") || + item_type == fourcc("vvc1") || + item_type == fourcc("jpeg") || + (item_type == fourcc("mime") && content_type == "image/jpeg") || + item_type == fourcc("j2k1") || + item_type == fourcc("mski")); } @@ -629,7 +630,7 @@ Error HeifContext::interpret_heif_file() auto& image = pair.second; std::shared_ptr infe = m_heif_file->get_infe_box(image->get_id()); - if (infe->get_item_type() == "hvc1") { + if (infe->get_item_type_4cc() == fourcc("hvc1")) { auto ipma = m_heif_file->get_ipma_box(); auto ipco = m_heif_file->get_ipco_box(); @@ -640,7 +641,7 @@ Error HeifContext::interpret_heif_file() "No hvcC property in hvc1 type image"); } } - if (infe->get_item_type() == "vvc1") { + if (infe->get_item_type_4cc() == fourcc("vvc1")) { auto ipma = m_heif_file->get_ipma_box(); auto ipco = m_heif_file->get_ipco_box(); @@ -669,7 +670,7 @@ Error HeifContext::interpret_heif_file() break; } - if (infe_box->get_item_type() == "grid") { + if (infe_box->get_item_type_4cc() == fourcc("grid")) { std::vector image_references = iref_box->get_references(id, fourcc("dimg")); if (image_references.empty()) { @@ -698,12 +699,12 @@ Error HeifContext::interpret_heif_file() // --- read metadata and assign to image for (heif_item_id id : image_IDs) { - std::string item_type = m_heif_file->get_item_type(id); + uint32_t item_type = m_heif_file->get_item_type_4cc(id); std::string content_type = m_heif_file->get_content_type(id); // 'rgan': skip region annotations, handled next // 'iden': iden images are no metadata - if (item_type_is_image(item_type, content_type) || item_type == "rgan") { + if (item_type_is_image(item_type, content_type) || item_type == fourcc("rgan")) { continue; } @@ -713,13 +714,13 @@ Error HeifContext::interpret_heif_file() std::shared_ptr metadata = std::make_shared(); metadata->item_id = id; - metadata->item_type = item_type; + metadata->item_type = fourcc_to_string(item_type); metadata->content_type = content_type; metadata->item_uri_type = item_uri_type; - Error err = m_heif_file->get_compressed_image_data(id, &(metadata->m_data)); + Error err = m_heif_file->get_uncompressed_item_data(id, &(metadata->m_data)); if (err) { - if (item_type == "Exif" || item_type == "mime") { + if (item_type == fourcc("Exif") || item_type == fourcc("mime")) { // these item types should have data return err; } @@ -771,15 +772,15 @@ Error HeifContext::interpret_heif_file() // --- read region item and assign to image(s) for (heif_item_id id : image_IDs) { - std::string item_type = m_heif_file->get_item_type(id); - if (item_type != "rgan") { + uint32_t item_type = m_heif_file->get_item_type_4cc(id); + if (item_type != fourcc("rgan")) { continue; } std::shared_ptr region_item = std::make_shared(); region_item->item_id = id; std::vector region_data; - Error err = m_heif_file->get_compressed_image_data(id, &(region_data)); + Error err = m_heif_file->get_uncompressed_item_data(id, &(region_data)); if (err) { return err; } @@ -875,10 +876,10 @@ bool HeifContext::has_alpha(heif_item_id ID) const // TODO: move this into ImageItem - std::string image_type = m_heif_file->get_item_type(ID); - if (image_type == "grid") { + uint32_t image_type = m_heif_file->get_item_type_4cc(ID); + if (image_type == fourcc("grid")) { std::vector grid_data; - Error error = m_heif_file->get_compressed_image_data(ID, &grid_data); + Error error = m_heif_file->get_uncompressed_item_data(ID, &grid_data); if (error) { return false; } @@ -937,10 +938,10 @@ bool HeifContext::has_alpha(heif_item_id ID) const Error HeifContext::get_id_of_non_virtual_child_image(heif_item_id id, heif_item_id& out) const { - std::string image_type = m_heif_file->get_item_type(id); - if (image_type == "grid" || - image_type == "iden" || - image_type == "iovl") { + uint32_t image_type = m_heif_file->get_item_type_4cc(id); + if (image_type == fourcc("grid") || + image_type == fourcc("iden") || + image_type == fourcc("iovl")) { auto iref_box = m_heif_file->get_iref_box(); if (!iref_box) { return Error(heif_error_Invalid_input, @@ -974,8 +975,6 @@ Result> HeifContext::decode_image(heif_item_id I const struct heif_decoding_options& options, bool decode_only_tile, uint32_t tx, uint32_t ty) const { - std::string image_type = m_heif_file->get_item_type(ID); - std::shared_ptr imginfo; if (m_all_images.find(ID) != m_all_images.end()) { imginfo = m_all_images.find(ID)->second; @@ -1185,7 +1184,7 @@ Error HeifContext::encode_grid(const std::vector } // Create Grid Item - heif_item_id grid_id = m_heif_file->add_new_image("grid"); + heif_item_id grid_id = m_heif_file->add_new_image(fourcc("grid")); out_grid_image = std::make_shared(this, grid_id); m_all_images.insert(std::make_pair(grid_id, out_grid_image)); const int construction_method = 1; // 0=mdat 1=idat @@ -1240,7 +1239,7 @@ Error HeifContext::add_grid_item(const std::vector& tile_ids, // Create Grid Item - heif_item_id grid_id = m_heif_file->add_new_image("grid"); + heif_item_id grid_id = m_heif_file->add_new_image(fourcc("grid")); out_grid_image = std::make_shared(this, grid_id); m_all_images.insert(std::make_pair(grid_id, out_grid_image)); const int construction_method = 1; // 0=mdat 1=idat @@ -1286,7 +1285,7 @@ Result> HeifContext::add_iovl_item(const Imag // Create IOVL Item - heif_item_id iovl_id = m_heif_file->add_new_image("iovl"); + heif_item_id iovl_id = m_heif_file->add_new_image(fourcc("iovl")); std::shared_ptr iovl_image = std::make_shared(this, iovl_id); m_all_images.insert(std::make_pair(iovl_id, iovl_image)); const int construction_method = 1; // 0=mdat 1=idat @@ -1518,19 +1517,19 @@ Error HeifContext::add_exif_metadata(const std::shared_ptr& master_im return add_generic_metadata(master_image, data_array.data(), (int) data_array.size(), - "Exif", nullptr, nullptr, heif_metadata_compression_off, nullptr); + fourcc("Exif"), nullptr, nullptr, heif_metadata_compression_off, nullptr); } Error HeifContext::add_XMP_metadata(const std::shared_ptr& master_image, const void* data, int size, heif_metadata_compression compression) { - return add_generic_metadata(master_image, data, size, "mime", "application/rdf+xml", nullptr, compression, nullptr); + return add_generic_metadata(master_image, data, size, fourcc("mime"), "application/rdf+xml", nullptr, compression, nullptr); } Error HeifContext::add_generic_metadata(const std::shared_ptr& master_image, const void* data, int size, - const char* item_type, const char* content_type, const char* item_uri_type, heif_metadata_compression compression, + uint32_t item_type, const char* content_type, const char* item_uri_type, heif_metadata_compression compression, heif_item_id* out_item_id) { // create an infe box describing what kind of data we are storing (this also creates a new ID) @@ -1561,7 +1560,7 @@ Error HeifContext::add_generic_metadata(const std::shared_ptr& master // only set metadata compression for MIME type data which has 'content_encoding' field if (compression != heif_metadata_compression_off && - strcmp(item_type, "mime") != 0) { + item_type != fourcc("mime")) { // TODO: error, compression not supported } diff --git a/libheif/context.h b/libheif/context.h index bd5f3dcf72..976763a7fd 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -184,7 +184,7 @@ class HeifContext : public ErrorBuffer Error add_XMP_metadata(const std::shared_ptr& master_image, const void* data, int size, heif_metadata_compression compression); Error add_generic_metadata(const std::shared_ptr& master_image, const void* data, int size, - const char* item_type, const char* content_type, const char* item_uri_type, + uint32_t item_type, const char* content_type, const char* item_uri_type, heif_metadata_compression compression, heif_item_id* out_item_id); heif_property_id add_property(heif_item_id targetItem, std::shared_ptr property, bool essential); diff --git a/libheif/file.cc b/libheif/file.cc index 9a524b32c8..ae28f84126 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -23,9 +23,9 @@ #include "libheif/heif.h" #include "libheif/heif_properties.h" #include "compression.h" -#include "codecs/jpeg2000.h" -#include "codecs/jpeg.h" -#include "codecs/vvc.h" +#include "image-items/jpeg2000.h" +#include "image-items/jpeg.h" +#include "image-items/vvc.h" #include "codecs/uncompressed/unc_boxes.h" #include @@ -48,7 +48,7 @@ #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed/unc_image.h" +#include "image-items/unc_image.h" #endif // TODO: make this a decoder option @@ -478,14 +478,14 @@ bool HeifFile::has_item_with_id(heif_item_id ID) const } -std::string HeifFile::get_item_type(heif_item_id ID) const +uint32_t HeifFile::get_item_type_4cc(heif_item_id ID) const { auto infe_box = get_infe_box(ID); if (!infe_box) { - return ""; + return 0; } - return infe_box->get_item_type(); + return infe_box->get_item_type_4cc(); } @@ -526,56 +526,10 @@ Error HeifFile::get_properties(heif_item_id imageID, } -heif_chroma HeifFile::get_image_chroma_from_configuration(heif_item_id imageID) const -{ - // HEVC - - std::shared_ptr hvcC_box = get_property(imageID); - if (hvcC_box) { - return (heif_chroma) (hvcC_box->get_configuration().chroma_format); - } - - - // VVC - - std::shared_ptr vvcC_box = get_property(imageID); - if (vvcC_box) { - return (heif_chroma) (vvcC_box->get_configuration().chroma_format_idc); - } - - - // AV1 - - std::shared_ptr av1C_box = get_property(imageID); - if (av1C_box) { - Box_av1C::configuration config = av1C_box->get_configuration(); - if (config.chroma_subsampling_x == 1 && - config.chroma_subsampling_y == 1) { - return heif_chroma_420; - } - else if (config.chroma_subsampling_x == 1 && - config.chroma_subsampling_y == 0) { - return heif_chroma_422; - } - else if (config.chroma_subsampling_x == 0 && - config.chroma_subsampling_y == 0) { - return heif_chroma_444; - } - else { - return heif_chroma_undefined; - } - } - - - assert(false); - return heif_chroma_undefined; -} - - -Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* data) const +Error HeifFile::get_uncompressed_item_data(heif_item_id ID, std::vector* data) const { #if ENABLE_PARALLEL_TILE_DECODING - std::lock_guard guard(m_read_mutex); + // std::lock_guard guard(m_read_mutex); // TODO: I think that this is not needed anymore because this function is not used for image data anymore. #endif if (!image_exists(ID)) { @@ -590,104 +544,75 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* } - std::string item_type = infe_box->get_item_type(); + uint32_t item_type = infe_box->get_item_type_4cc(); std::string content_type = infe_box->get_content_type(); - // --- get coded image data pointers + // --- decompress data - if (item_type == "hvc1") { - // --- --- --- HEVC - assert(false); - } - else if (item_type == "vvc1") { - // --- --- --- VVC - assert(false); - } - else if (item_type == "av01") { - assert(false); - } - else if (item_type == "jpeg" || - (item_type == "mime" && get_content_type(ID) == "image/jpeg")) { - assert(false); - } - else if (item_type == "j2k1") { - assert(false); - } -#if WITH_UNCOMPRESSED_CODEC - else if (item_type == "unci") { - assert(false); - // return get_compressed_image_data_uncompressed(ID, data, item); - } -#endif - else if (true || // fallback case for all kinds of generic metadata (e.g. 'iptc') - item_type == "grid" || - item_type == "iovl" || - item_type == "Exif" || - (item_type == "mime" && content_type == "application/rdf+xml")) { - Error error; - bool read_uncompressed = true; - if (item_type == "mime") { - std::string encoding = infe_box->get_content_encoding(); - if (encoding == "compress_zlib") { + Error error; + bool read_uncompressed = true; + if (item_type == fourcc("mime")) { + std::string encoding = infe_box->get_content_encoding(); + if (encoding == "compress_zlib") { #if HAVE_ZLIB - read_uncompressed = false; - std::vector compressed_data; - error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data); - if (error) { - return error; - } - error = decompress_zlib(compressed_data, data); - if (error) { - return error; - } + read_uncompressed = false; + std::vector compressed_data; + error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data); + if (error) { + return error; + } + error = decompress_zlib(compressed_data, data); + if (error) { + return error; + } #else - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_header_compression_method, - encoding); + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method, + encoding); #endif - } - else if (encoding == "deflate") { + } + else if (encoding == "deflate") { #if HAVE_ZLIB - read_uncompressed = false; - std::vector compressed_data; - error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data); - if (error) { - return error; - } - error = decompress_deflate(compressed_data, data); - if (error) { - return error; - } + read_uncompressed = false; + std::vector compressed_data; + error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data); + if (error) { + return error; + } + error = decompress_deflate(compressed_data, data); + if (error) { + return error; + } #else - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_header_compression_method, - encoding); + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method, + encoding); #endif - } - else if (encoding == "br") { + } + else if (encoding == "br") { #if HAVE_BROTLI - read_uncompressed = false; - std::vector compressed_data; - error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data); - if (error) { - return error; - } - error = decompress_brotli(compressed_data, data); - if (error) { - return error; - } + read_uncompressed = false; + std::vector compressed_data; + error = m_iloc_box->read_data(ID, m_input_stream, m_idat_box, &compressed_data); + if (error) { + return error; + } + error = decompress_brotli(compressed_data, data); + if (error) { + return error; + } #else - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_header_compression_method, - encoding); + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_header_compression_method, + encoding); #endif - } } + } - if (read_uncompressed) { - return m_iloc_box->read_data(ID, m_input_stream, m_idat_box, data); - } + if (read_uncompressed) { + return m_iloc_box->read_data(ID, m_input_stream, m_idat_box, data); } + return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_codec); } @@ -725,12 +650,12 @@ Error HeifFile::get_item_data(heif_item_id ID, std::vector* out_data, h heif_suberror_Nonexisting_item_referenced}; } - std::string item_type = infe_box->get_item_type(); + uint32_t item_type = infe_box->get_item_type_4cc(); std::string content_type = infe_box->get_content_type(); // --- non 'mime' data (uncompressed) - if (item_type != "mime") { + if (item_type != fourcc("mime")) { if (out_compression) { *out_compression = heif_metadata_compression_off; } @@ -820,21 +745,21 @@ heif_item_id HeifFile::get_unused_item_id() const } -heif_item_id HeifFile::add_new_image(const char* item_type) +heif_item_id HeifFile::add_new_image(uint32_t item_type) { auto box = add_new_infe_box(item_type); return box->get_item_ID(); } -std::shared_ptr HeifFile::add_new_infe_box(const char* item_type) +std::shared_ptr HeifFile::add_new_infe_box(uint32_t item_type) { heif_item_id id = get_unused_item_id(); auto infe = std::make_shared(); infe->set_item_ID(id); infe->set_hidden_item(false); - infe->set_item_type(item_type); + infe->set_item_type_4cc(item_type); m_infe_boxes[id] = infe; m_iinf_box->append_child_box(infe); @@ -937,22 +862,7 @@ void HeifFile::add_orientation_properties(heif_item_id id, heif_orientation orie } -void HeifFile::add_pixi_property(heif_item_id id, uint8_t c1, uint8_t c2, uint8_t c3) -{ - auto pixi = std::make_shared(); - pixi->add_channel_bits(c1); - if (c2 || c3) { - pixi->add_channel_bits(c2); - pixi->add_channel_bits(c3); - } - - int index = m_ipco_box->find_or_append_child_box(pixi); - - m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{false, uint16_t(index + 1)}); -} - - -Result HeifFile::add_infe(const char* item_type, const uint8_t* data, size_t size) +Result HeifFile::add_infe(uint32_t item_type, const uint8_t* data, size_t size) { Result result; @@ -976,7 +886,7 @@ Result HeifFile::add_infe_mime(const char* content_type, heif_meta // create an infe box describing what kind of data we are storing (this also creates a new ID) - auto infe_box = add_new_infe_box("mime"); + auto infe_box = add_new_infe_box(fourcc("mime")); infe_box->set_hidden_item(true); infe_box->set_content_type(content_type); @@ -995,7 +905,7 @@ Result HeifFile::add_precompressed_infe_mime(const char* content_t // create an infe box describing what kind of data we are storing (this also creates a new ID) - auto infe_box = add_new_infe_box("mime"); + auto infe_box = add_new_infe_box(fourcc("mime")); infe_box->set_hidden_item(true); infe_box->set_content_type(content_type); @@ -1014,7 +924,7 @@ Result HeifFile::add_infe_uri(const char* item_uri_type, const uin // create an infe box describing what kind of data we are storing (this also creates a new ID) - auto infe_box = add_new_infe_box("uri "); + auto infe_box = add_new_infe_box(fourcc("uri ")); infe_box->set_hidden_item(true); infe_box->set_item_uri_type(item_uri_type); @@ -1037,7 +947,7 @@ Error HeifFile::set_item_data(const std::shared_ptr& item, const uint8 // only set metadata compression for MIME type data which has 'content_encoding' field if (compression != heif_metadata_compression_off && - item->get_item_type() != "mime") { + item->get_item_type_4cc() != fourcc("mime")) { // TODO: error, compression not supported } @@ -1081,7 +991,7 @@ Error HeifFile::set_precompressed_item_data(const std::shared_ptr& ite { // only set metadata compression for MIME type data which has 'content_encoding' field if (!content_encoding.empty() && - item->get_item_type() != "mime") { + item->get_item_type_4cc() != fourcc("mime")) { // TODO: error, compression not supported } diff --git a/libheif/file.h b/libheif/file.h index 258d8dcadb..3b1c42bfac 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -23,9 +23,9 @@ #include "box.h" #include "nclx.h" -#include "codecs/avif.h" -#include "codecs/hevc.h" -#include "codecs/vvc.h" +#include "image-items/avif.h" +#include "image-items/hevc.h" +#include "image-items/vvc.h" #include "codecs/uncompressed/unc_boxes.h" #include "file_layout.h" @@ -82,13 +82,13 @@ class HeifFile bool has_item_with_id(heif_item_id ID) const; - std::string get_item_type(heif_item_id ID) const; + uint32_t get_item_type_4cc(heif_item_id ID) const; std::string get_content_type(heif_item_id ID) const; std::string get_item_uri_type(heif_item_id ID) const; - Error get_compressed_image_data(heif_item_id ID, std::vector* out_data) const; + Error get_uncompressed_item_data(heif_item_id ID, std::vector* data) const; Error append_data_from_iloc(heif_item_id ID, std::vector& out_data, uint64_t offset, uint64_t size) const; @@ -141,8 +141,6 @@ class HeifFile return nullptr; } - heif_chroma get_image_chroma_from_configuration(heif_item_id imageID) const; // TODO: move to ImageItem - std::string debug_dump_boxes() const; @@ -150,23 +148,21 @@ class HeifFile heif_item_id get_unused_item_id() const; - heif_item_id add_new_image(const char* item_type); + heif_item_id add_new_image(uint32_t item_type); - std::shared_ptr add_new_infe_box(const char* item_type); + std::shared_ptr add_new_infe_box(uint32_t item_type); void add_ispe_property(heif_item_id id, uint32_t width, uint32_t height, bool essential); // set irot/imir according to heif_orientation void add_orientation_properties(heif_item_id id, heif_orientation); - void add_pixi_property(heif_item_id id, uint8_t c1, uint8_t c2 = 0, uint8_t c3 = 0); - // TODO: can we remove the 'essential' parameter and take this from the box? Or is that depending on the context? heif_property_id add_property(heif_item_id id, const std::shared_ptr& property, bool essential); heif_property_id add_property_without_deduplication(heif_item_id id, const std::shared_ptr& property, bool essential); - Result add_infe(const char* item_type, const uint8_t* data, size_t size); + Result add_infe(uint32_t item_type, const uint8_t* data, size_t size); Result add_infe_mime(const char* content_type, heif_metadata_compression content_encoding, const uint8_t* data, size_t size); diff --git a/libheif/image-items/avc.cc b/libheif/image-items/avc.cc new file mode 100644 index 0000000000..4efdb3b2e4 --- /dev/null +++ b/libheif/image-items/avc.cc @@ -0,0 +1,144 @@ +/* + * HEIF AVC codec. + * Copyright (c) 2023 Brad Hards + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "avc.h" +#include +#include +#include +#include +#include "file.h" +#include "context.h" +#include "codecs/avc_dec.h" +#include "codecs/avc_boxes.h" + + +Result ImageItem_AVC::encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ +#if 0 + CodedImageData codedImage; + + auto hvcC = std::make_shared(); + + heif_image c_api_image; + c_api_image.image = image; + + struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); + if (err.code) { + return Error(err.code, + err.subcode, + err.message); + } + + int encoded_width = 0; + int encoded_height = 0; + + for (;;) { + uint8_t* data; + int size; + + encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); + + if (data == nullptr) { + break; + } + + + const uint8_t NAL_SPS = 33; + + if ((data[0] >> 1) == NAL_SPS) { + Box_hvcC::configuration config; + + parse_sps_for_hvcC_configuration(data, size, &config, &encoded_width, &encoded_height); + + hvcC->set_configuration(config); + + codedImage.encoded_image_width = encoded_width; + codedImage.encoded_image_height = encoded_height; + } + + switch (data[0] >> 1) { + case 0x20: + case 0x21: + case 0x22: + hvcC->append_nal_data(data, size); + break; + + default: + codedImage.append_with_4bytes_size(data, size); + // m_heif_file->append_iloc_data_with_4byte_size(image_id, data, size); + } + } + + if (!encoded_width || !encoded_height) { + return Error(heif_error_Encoder_plugin_error, + heif_suberror_Invalid_image_size); + } + + codedImage.properties.push_back(hvcC); + + + // Make sure that the encoder plugin works correctly and the encoded image has the correct size. + + if (encoder->plugin->plugin_api_version >= 3 && + encoder->plugin->query_encoded_size != nullptr) { + uint32_t check_encoded_width = image->get_width(), check_encoded_height = image->get_height(); + + encoder->plugin->query_encoded_size(encoder->encoder, + image->get_width(), image->get_height(), + &check_encoded_width, + &check_encoded_height); + + assert((int)check_encoded_width == encoded_width); + assert((int)check_encoded_height == encoded_height); + } + + return codedImage; +#endif + assert(false); // TODO + return {}; +} + + +std::shared_ptr ImageItem_AVC::get_decoder() const +{ + return m_decoder; +} + + +Error ImageItem_AVC::on_load_file() +{ + auto avcC_box = get_file()->get_property(get_id()); + if (!avcC_box) { + return Error{heif_error_Invalid_input, + heif_suberror_No_av1C_box}; + } + + m_decoder = std::make_shared(avcC_box); + + DataExtent extent; + extent.set_from_image_item(get_context()->get_heif_file(), get_id()); + + m_decoder->set_data_extent(extent); + + return Error::Ok; +} diff --git a/libheif/image-items/avc.h b/libheif/image-items/avc.h new file mode 100644 index 0000000000..d0033b615e --- /dev/null +++ b/libheif/image-items/avc.h @@ -0,0 +1,62 @@ +/* + * HEIF AVC codec. + * Copyright (c) 2023 Brad Hards + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_AVC_H +#define HEIF_AVC_H + +#include "box.h" +#include "error.h" +#include +#include +#include +#include +#include "image_item.h" + + +class ImageItem_AVC : public ImageItem +{ +public: + ImageItem_AVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} + + ImageItem_AVC(HeifContext* ctx) : ImageItem(ctx) {} + + uint32_t get_infe_type() const override { return fourcc("avc1"); } + + const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; } + + const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } + + heif_compression_format get_compression_format() const override { return heif_compression_AVC; } + + Error on_load_file() override; + +protected: + std::shared_ptr get_decoder() const override; + +public: + Result encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) override; + + std::shared_ptr m_decoder; +}; + +#endif diff --git a/libheif/image-items/avif.cc b/libheif/image-items/avif.cc new file mode 100644 index 0000000000..c2cadb0059 --- /dev/null +++ b/libheif/image-items/avif.cc @@ -0,0 +1,113 @@ +/* + * HEIF codec. + * Copyright (c) 2017 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "pixelimage.h" +#include "avif.h" +#include "codecs/avif_dec.h" +#include "codecs/avif_boxes.h" +#include "bitstream.h" +#include "common_utils.h" +#include "libheif/api_structs.h" +#include "file.h" +#include +#include +#include +#include + +// https://aomediacodec.github.io/av1-spec/av1-spec.pdf + + + +Error ImageItem_AVIF::on_load_file() +{ + auto av1C_box = get_file()->get_property(get_id()); + if (!av1C_box) { + return Error{heif_error_Invalid_input, + heif_suberror_No_av1C_box}; + } + + m_decoder = std::make_shared(av1C_box); + + DataExtent extent; + extent.set_from_image_item(get_context()->get_heif_file(), get_id()); + + m_decoder->set_data_extent(extent); + + return Error::Ok; +} + + +Result ImageItem_AVIF::encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ + CodedImageData codedImage; + + Box_av1C::configuration config; + + // Fill preliminary av1C in case we cannot parse the sequence_header() correctly in the code below. + // TODO: maybe we can remove this later. + fill_av1C_configuration(&config, image); + + heif_image c_api_image; + c_api_image.image = image; + + struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); + if (err.code) { + return Error(err.code, + err.subcode, + err.message); + } + + for (;;) { + uint8_t* data; + int size; + + encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); + + bool found_config = fill_av1C_configuration_from_stream(&config, data, size); + (void) found_config; + + if (data == nullptr) { + break; + } + + codedImage.append(data, size); + } + + auto av1C = std::make_shared(); + av1C->set_configuration(config); + codedImage.properties.push_back(av1C); + + return codedImage; +} + + +Result> ImageItem_AVIF::read_bitstream_configuration_data(heif_item_id itemId) const +{ + return m_decoder->read_bitstream_configuration_data(); +} + + +std::shared_ptr ImageItem_AVIF::get_decoder() const +{ + return m_decoder; +} diff --git a/libheif/image-items/avif.h b/libheif/image-items/avif.h new file mode 100644 index 0000000000..65b98581b1 --- /dev/null +++ b/libheif/image-items/avif.h @@ -0,0 +1,68 @@ +/* + * HEIF codec. + * Copyright (c) 2017 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_AVIF_H +#define HEIF_AVIF_H + +#include +#include +#include +#include +#include + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" +#include "image_item.h" + + +class ImageItem_AVIF : public ImageItem +{ +public: + ImageItem_AVIF(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} + + ImageItem_AVIF(HeifContext* ctx) : ImageItem(ctx) {} + + uint32_t get_infe_type() const override { return fourcc("av01"); } + + const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; } + + const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } + + heif_compression_format get_compression_format() const override { return heif_compression_AV1; } + + Error on_load_file() override; + +public: + Result encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) override; + +protected: + Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + + std::shared_ptr get_decoder() const override; + +private: + std::shared_ptr m_decoder; +}; + +#endif diff --git a/libheif/codecs/grid.cc b/libheif/image-items/grid.cc similarity index 99% rename from libheif/codecs/grid.cc rename to libheif/image-items/grid.cc index b75fa78c49..cf3bcfc62d 100644 --- a/libheif/codecs/grid.cc +++ b/libheif/image-items/grid.cc @@ -19,8 +19,8 @@ */ #include "grid.h" -#include -#include +#include "context.h" +#include "file.h" #include #include #include @@ -169,7 +169,7 @@ Error ImageItem_Grid::read_grid_spec() auto heif_file = get_context()->get_heif_file(); std::vector grid_data; - Error err = heif_file->get_compressed_image_data(get_id(), &grid_data); + Error err = heif_file->get_uncompressed_item_data(get_id(), &grid_data); if (err) { return err; } diff --git a/libheif/codecs/grid.h b/libheif/image-items/grid.h similarity index 96% rename from libheif/codecs/grid.h rename to libheif/image-items/grid.h index dab6404a0f..fa95108c91 100644 --- a/libheif/codecs/grid.h +++ b/libheif/image-items/grid.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_IMAGEITEM_GRID_H #define LIBHEIF_IMAGEITEM_GRID_H -#include +#include "image_item.h" #include #include #include @@ -80,8 +80,9 @@ class ImageItem_Grid : public ImageItem ImageItem_Grid(HeifContext* ctx); - const char* get_infe_type() const override { return "grid"; } + uint32_t get_infe_type() const override { return fourcc("grid"); } + // TODO: nclx depends on contained format // const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } // heif_compression_format get_compression_format() const override { return heif_compression_HEVC; } diff --git a/libheif/image-items/hevc.cc b/libheif/image-items/hevc.cc new file mode 100644 index 0000000000..9e8e15d3d6 --- /dev/null +++ b/libheif/image-items/hevc.cc @@ -0,0 +1,247 @@ +/* + * HEIF codec. + * Copyright (c) 2017 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "hevc.h" +#include "codecs/hevc_boxes.h" +#include "bitstream.h" +#include "error.h" +#include "file.h" +#include "codecs/hevc_dec.h" + +#include +#include +#include +#include +#include +#include +#include "api/libheif/api_structs.h" + + +Error ImageItem_HEVC::on_load_file() +{ + auto hvcC_box = get_file()->get_property(get_id()); + if (!hvcC_box) { + return Error{heif_error_Invalid_input, + heif_suberror_No_hvcC_box}; + } + + m_decoder = std::make_shared(hvcC_box); + + DataExtent extent; + extent.set_from_image_item(get_context()->get_heif_file(), get_id()); + + m_decoder->set_data_extent(extent); + + return Error::Ok; +} + + +Result ImageItem_HEVC::encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ + CodedImageData codedImage; + + auto hvcC = std::make_shared(); + + heif_image c_api_image; + c_api_image.image = image; + + struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); + if (err.code) { + return Error(err.code, + err.subcode, + err.message); + } + + int encoded_width = 0; + int encoded_height = 0; + + for (;;) { + uint8_t* data; + int size; + + encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); + + if (data == nullptr) { + break; + } + + + const uint8_t NAL_SPS = 33; + + if ((data[0] >> 1) == NAL_SPS) { + Box_hvcC::configuration config; + + parse_sps_for_hvcC_configuration(data, size, &config, &encoded_width, &encoded_height); + + hvcC->set_configuration(config); + + codedImage.encoded_image_width = encoded_width; + codedImage.encoded_image_height = encoded_height; + } + + switch (data[0] >> 1) { + case 0x20: + case 0x21: + case 0x22: + hvcC->append_nal_data(data, size); + break; + + default: + codedImage.append_with_4bytes_size(data, size); + // m_heif_file->append_iloc_data_with_4byte_size(image_id, data, size); + } + } + + if (!encoded_width || !encoded_height) { + return Error(heif_error_Encoder_plugin_error, + heif_suberror_Invalid_image_size); + } + + codedImage.properties.push_back(hvcC); + + + // Make sure that the encoder plugin works correctly and the encoded image has the correct size. + + if (encoder->plugin->plugin_api_version >= 3 && + encoder->plugin->query_encoded_size != nullptr) { + uint32_t check_encoded_width = image->get_width(), check_encoded_height = image->get_height(); + + encoder->plugin->query_encoded_size(encoder->encoder, + image->get_width(), image->get_height(), + &check_encoded_width, + &check_encoded_height); + + assert((int)check_encoded_width == encoded_width); + assert((int)check_encoded_height == encoded_height); + } + + return codedImage; +} + + +Result> ImageItem_HEVC::read_bitstream_configuration_data(heif_item_id itemId) const +{ + return m_decoder->read_bitstream_configuration_data(); +} + + +std::shared_ptr ImageItem_HEVC::get_decoder() const +{ + return m_decoder; +} + + +void ImageItem_HEVC::set_preencoded_hevc_image(const std::vector& data) +{ + auto hvcC = std::make_shared(); + + + // --- parse the h265 stream and set hvcC headers and compressed image data + + int state = 0; + + bool first = true; + bool eof = false; + + int prev_start_code_start = -1; // init to an invalid value, will always be overwritten before use + int start_code_start; + int ptr = 0; + + for (;;) { + bool dump_nal = false; + + uint8_t c = data[ptr++]; + + if (state == 3) { + state = 0; + } + + if (c == 0 && state <= 1) { + state++; + } + else if (c == 0) { + // NOP + } + else if (c == 1 && state == 2) { + start_code_start = ptr - 3; + dump_nal = true; + state = 3; + } + else { + state = 0; + } + + if (ptr == (int) data.size()) { + start_code_start = (int) data.size(); + dump_nal = true; + eof = true; + } + + if (dump_nal) { + if (first) { + first = false; + } + else { + std::vector nal_data; + size_t length = start_code_start - (prev_start_code_start + 3); + + nal_data.resize(length); + + assert(prev_start_code_start >= 0); + memcpy(nal_data.data(), data.data() + prev_start_code_start + 3, length); + + int nal_type = (nal_data[0] >> 1); + + switch (nal_type) { + case 0x20: + case 0x21: + case 0x22: + hvcC->append_nal_data(nal_data); + break; + + default: { + std::vector nal_data_with_size; + nal_data_with_size.resize(nal_data.size() + 4); + + memcpy(nal_data_with_size.data() + 4, nal_data.data(), nal_data.size()); + nal_data_with_size[0] = ((nal_data.size() >> 24) & 0xFF); + nal_data_with_size[1] = ((nal_data.size() >> 16) & 0xFF); + nal_data_with_size[2] = ((nal_data.size() >> 8) & 0xFF); + nal_data_with_size[3] = ((nal_data.size() >> 0) & 0xFF); + + get_file()->append_iloc_data(get_id(), nal_data_with_size, 0); + } + break; + } + } + + prev_start_code_start = start_code_start; + } + + if (eof) { + break; + } + } + + get_file()->add_property(get_id(), hvcC, true); +} diff --git a/libheif/image-items/hevc.h b/libheif/image-items/hevc.h new file mode 100644 index 0000000000..50bedfeea8 --- /dev/null +++ b/libheif/image-items/hevc.h @@ -0,0 +1,69 @@ +/* + * HEIF codec. + * Copyright (c) 2017 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef HEIF_HEVC_H +#define HEIF_HEVC_H + +#include "libheif/heif.h" +#include "box.h" +#include "error.h" + +#include +#include +#include +#include "image_item.h" + + +class ImageItem_HEVC : public ImageItem +{ +public: + ImageItem_HEVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} + + ImageItem_HEVC(HeifContext* ctx) : ImageItem(ctx) {} + + uint32_t get_infe_type() const override { return fourcc("hvc1"); } + + // TODO: MIAF says that the *:hevc:* urn is deprecated and we should use "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha" + const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:hevc:2015:auxid:1"; } + + const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } + + heif_compression_format get_compression_format() const override { return heif_compression_HEVC; } + + Error on_load_file() override; + + Result encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) override; + + // currently not used + void set_preencoded_hevc_image(const std::vector& data); + +protected: + Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + + std::shared_ptr get_decoder() const override; + +private: + std::shared_ptr m_decoder; +}; + +#endif diff --git a/libheif/codecs/iden.cc b/libheif/image-items/iden.cc similarity index 100% rename from libheif/codecs/iden.cc rename to libheif/image-items/iden.cc diff --git a/libheif/codecs/iden.h b/libheif/image-items/iden.h similarity index 93% rename from libheif/codecs/iden.h rename to libheif/image-items/iden.h index c2a129ccc2..25403cd2a6 100644 --- a/libheif/codecs/iden.h +++ b/libheif/image-items/iden.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_IDEN_H #define LIBHEIF_IDEN_H -#include +#include "image_item.h" #include #include #include @@ -34,8 +34,9 @@ class ImageItem_iden : public ImageItem ImageItem_iden(HeifContext* ctx); - const char* get_infe_type() const override { return "iden"; } + uint32_t get_infe_type() const override { return fourcc("iden"); } + // TODO: nclx depends on contained format // const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } // heif_compression_format get_compression_format() const override { return heif_compression_HEVC; } @@ -52,7 +53,7 @@ class ImageItem_iden : public ImageItem enum heif_image_input_class input_class) override { return Error{heif_error_Unsupported_feature, - heif_suberror_Unspecified, "Cannot encode image to 'iovl'"}; + heif_suberror_Unspecified, "Cannot encode image to 'iden'"}; } Result> decode_compressed_image(const struct heif_decoding_options& options, diff --git a/libheif/codecs/image_item.cc b/libheif/image-items/image_item.cc similarity index 76% rename from libheif/codecs/image_item.cc rename to libheif/image-items/image_item.cc index 14b8d1bd94..e0e6e37352 100644 --- a/libheif/codecs/image_item.cc +++ b/libheif/image-items/image_item.cc @@ -20,26 +20,27 @@ #include "image_item.h" #include "mask_image.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "context.h" +#include "file.h" +#include "jpeg.h" +#include "jpeg2000.h" +#include "avif.h" +#include "avc.h" +#include "hevc.h" +#include "grid.h" +#include "overlay.h" +#include "iden.h" +#include "tild.h" +#include "codecs/decoder.h" +#include "color-conversion/colorconversion.h" +#include "api/libheif/api_structs.h" +#include "plugin_registry.h" #include #include #include #if WITH_UNCOMPRESSED_CODEC -#include "codecs/uncompressed/unc_image.h" +#include "image-items/unc_image.h" #endif @@ -63,13 +64,7 @@ bool HeifContext::is_image(heif_item_id ID) const } -std::shared_ptr ImageItem::get_file() -{ - return m_heif_context->get_heif_file(); -} - - -std::shared_ptr ImageItem::get_file() const +std::shared_ptr ImageItem::get_file() const { return m_heif_context->get_heif_file(); } @@ -106,46 +101,46 @@ heif_compression_format ImageItem::compression_format_from_fourcc_infe_type(uint std::shared_ptr ImageItem::alloc_for_infe_box(HeifContext* ctx, const std::shared_ptr& infe) { - std::string item_type = infe->get_item_type(); + uint32_t item_type = infe->get_item_type_4cc(); heif_item_id id = infe->get_item_ID(); - if (item_type == "jpeg" || - (item_type == "mime" && infe->get_content_type() == "image/jpeg")) { + if (item_type == fourcc("jpeg") || + (item_type == fourcc("mime") && infe->get_content_type() == "image/jpeg")) { return std::make_shared(ctx, id); } - else if (item_type == "hvc1") { + else if (item_type == fourcc("hvc1")) { return std::make_shared(ctx, id); } - else if (item_type == "av01") { + else if (item_type == fourcc("av01")) { return std::make_shared(ctx, id); } - else if (item_type == "vvc1") { + else if (item_type == fourcc("vvc1")) { return std::make_shared(ctx, id); } - else if (item_type == "avc1") { + else if (item_type == fourcc("avc1")) { return std::make_shared(ctx, id); } #if WITH_UNCOMPRESSED_CODEC - else if (item_type == "unci") { + else if (item_type == fourcc("unci")) { return std::make_shared(ctx, id); } #endif - else if (item_type == "j2k1") { + else if (item_type == fourcc("j2k1")) { return std::make_shared(ctx, id); } - else if (item_type == "mski") { + else if (item_type == fourcc("mski")) { return std::make_shared(ctx, id); } - else if (item_type == "grid") { + else if (item_type == fourcc("grid")) { return std::make_shared(ctx, id); } - else if (item_type == "iovl") { + else if (item_type == fourcc("iovl")) { return std::make_shared(ctx, id); } - else if (item_type == "iden") { + else if (item_type == fourcc("iden")) { return std::make_shared(ctx, id); } - else if (item_type == "tild") { + else if (item_type == fourcc("tild")) { return std::make_shared(ctx, id); } else { @@ -410,6 +405,29 @@ void ImageItem::get_tile_size(uint32_t& w, uint32_t& h) const } +Error ImageItem::postprocess_coded_image_colorspace(heif_colorspace* inout_colorspace, heif_chroma* inout_chroma) const +{ +#if 0 + auto pixi = m_heif_context->get_heif_file()->get_property(id); + if (pixi && pixi->get_num_channels() == 1) { + *out_colorspace = heif_colorspace_monochrome; + *out_chroma = heif_chroma_monochrome; + } +#endif + + if (*inout_colorspace == heif_colorspace_YCbCr) { + auto nclx = get_color_profile_nclx(); + if (nclx && nclx->get_matrix_coefficients() == 0) { + *inout_colorspace = heif_colorspace_RGB; + *inout_chroma = heif_chroma_444; // TODO: this or keep the original chroma? + } + } + + return Error::Ok; +} + + +// TODO: when all decoders are implemented, this function should just forward to the Decoder object. Error ImageItem::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { heif_item_id id; @@ -418,6 +436,7 @@ Error ImageItem::get_coded_image_colorspace(heif_colorspace* out_colorspace, hei return err; } + // TODO: should we use this or maybe use this just as an additional check that pixi and coded colorspace match? auto pixi = m_heif_context->get_heif_file()->get_property(id); if (pixi && pixi->get_num_channels() == 1) { *out_colorspace = heif_colorspace_monochrome; @@ -432,175 +451,27 @@ Error ImageItem::get_coded_image_colorspace(heif_colorspace* out_colorspace, hei return err; } - // TODO: this should be codec specific. JPEG 2000, for example, can use RGB internally. - - *out_colorspace = heif_colorspace_YCbCr; - *out_chroma = heif_chroma_undefined; - - if (auto hvcC = m_heif_context->get_heif_file()->get_property(id)) { - *out_chroma = (heif_chroma) (hvcC->get_configuration().chroma_format); - } - else if (auto vvcC = m_heif_context->get_heif_file()->get_property(id)) { - *out_chroma = (heif_chroma) (vvcC->get_configuration().chroma_format_idc); - } - else if (auto av1C = m_heif_context->get_heif_file()->get_property(id)) { - *out_chroma = (heif_chroma) (av1C->get_configuration().get_heif_chroma()); - } - else if (auto j2kH = m_heif_context->get_heif_file()->get_property(id)) { - Result> dataResult = get_compressed_image_data(); - if (dataResult.error) { - return dataResult.error; - } - - JPEG2000MainHeader jpeg2000Header; - err = jpeg2000Header.parseHeader(*dataResult); - if (err) { - return err; - } - *out_chroma = jpeg2000Header.get_chroma_format(); - } -#if WITH_UNCOMPRESSED_CODEC - else if (auto uncC = m_heif_context->get_heif_file()->get_property(id)) { - if (uncC->get_version() == 1) { - // This is the shortform case, no cmpd box, and always some kind of RGB - *out_colorspace = heif_colorspace_RGB; - if (uncC->get_profile() == fourcc("rgb3")) { - *out_chroma = heif_chroma_interleaved_RGB; - } - else if ((uncC->get_profile() == fourcc("rgba")) || (uncC->get_profile() == fourcc("abgr"))) { - *out_chroma = heif_chroma_interleaved_RGBA; - } - } - if (auto cmpd = m_heif_context->get_heif_file()->get_property(id)) { - UncompressedImageCodec::get_heif_chroma_uncompressed(uncC, cmpd, out_chroma, out_colorspace); - } - } -#endif - - return err; + auto decoder = get_decoder(); + assert(decoder); + return decoder->get_coded_image_colorspace(out_colorspace, out_chroma); } -#if 0 + int ImageItem::get_luma_bits_per_pixel() const { - heif_item_id id; - Error err = m_heif_context->get_id_of_non_virtual_child_image(m_id, id); - if (err) { - return -1; - } + auto decoder = get_decoder(); + assert(decoder); - // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) - return m_heif_context->get_heif_file()->get_luma_bits_per_pixel_from_configuration(id); + return decoder->get_luma_bits_per_pixel(); } int ImageItem::get_chroma_bits_per_pixel() const { - heif_item_id id; - Error err = m_heif_context->get_id_of_non_virtual_child_image(m_id, id); - if (err) { - return -1; - } + auto decoder = get_decoder(); + assert(decoder); - // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) - return m_heif_context->get_heif_file()->get_chroma_bits_per_pixel_from_configuration(id); -} -#endif - - -void ImageItem::set_preencoded_hevc_image(const std::vector& data) -{ - auto hvcC = std::make_shared(); - - - // --- parse the h265 stream and set hvcC headers and compressed image data - - int state = 0; - - bool first = true; - bool eof = false; - - int prev_start_code_start = -1; // init to an invalid value, will always be overwritten before use - int start_code_start; - int ptr = 0; - - for (;;) { - bool dump_nal = false; - - uint8_t c = data[ptr++]; - - if (state == 3) { - state = 0; - } - - if (c == 0 && state <= 1) { - state++; - } - else if (c == 0) { - // NOP - } - else if (c == 1 && state == 2) { - start_code_start = ptr - 3; - dump_nal = true; - state = 3; - } - else { - state = 0; - } - - if (ptr == (int) data.size()) { - start_code_start = (int) data.size(); - dump_nal = true; - eof = true; - } - - if (dump_nal) { - if (first) { - first = false; - } - else { - std::vector nal_data; - size_t length = start_code_start - (prev_start_code_start + 3); - - nal_data.resize(length); - - assert(prev_start_code_start >= 0); - memcpy(nal_data.data(), data.data() + prev_start_code_start + 3, length); - - int nal_type = (nal_data[0] >> 1); - - switch (nal_type) { - case 0x20: - case 0x21: - case 0x22: - hvcC->append_nal_data(nal_data); - break; - - default: { - std::vector nal_data_with_size; - nal_data_with_size.resize(nal_data.size() + 4); - - memcpy(nal_data_with_size.data() + 4, nal_data.data(), nal_data.size()); - nal_data_with_size[0] = ((nal_data.size() >> 24) & 0xFF); - nal_data_with_size[1] = ((nal_data.size() >> 16) & 0xFF); - nal_data_with_size[2] = ((nal_data.size() >> 8) & 0xFF); - nal_data_with_size[3] = ((nal_data.size() >> 0) & 0xFF); - - m_heif_context->get_heif_file()->append_iloc_data(m_id, nal_data_with_size, 0); - } - break; - } - } - - prev_start_code_start = start_code_start; - } - - if (eof) { - break; - } - } - - m_heif_context->get_heif_file()->add_property(m_id, hvcC, true); + return decoder->get_chroma_bits_per_pixel(); } @@ -1020,63 +891,10 @@ Result> ImageItem::read_bitstream_configuration_data_overri } -Result> ImageItem::decode_from_compressed_data(heif_compression_format compression_format, - const struct heif_decoding_options& options, - const std::vector& data) -{ - const struct heif_decoder_plugin* decoder_plugin = get_decoder(compression_format, options.decoder_id); - if (!decoder_plugin) { - return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed); - } - - - // --- decode image with the plugin - - void* decoder; - struct heif_error err = decoder_plugin->new_decoder(&decoder); - if (err.code != heif_error_Ok) { - return Error(err.code, err.subcode, err.message); - } - - if (decoder_plugin->plugin_api_version >= 2) { - if (decoder_plugin->set_strict_decoding) { - decoder_plugin->set_strict_decoding(decoder, options.strict_decoding); - } - } - - err = decoder_plugin->push_data(decoder, data.data(), data.size()); - if (err.code != heif_error_Ok) { - decoder_plugin->free_decoder(decoder); - return Error(err.code, err.subcode, err.message); - } - - heif_image* decoded_img = nullptr; - - err = decoder_plugin->decode_image(decoder, &decoded_img); - if (err.code != heif_error_Ok) { - decoder_plugin->free_decoder(decoder); - return Error(err.code, err.subcode, err.message); - } - - if (!decoded_img) { - // TODO(farindk): The plugin should return an error in this case. - decoder_plugin->free_decoder(decoder); - return Error(heif_error_Decoder_plugin_error, heif_suberror_Unspecified); - } - - // -- cleanup - - std::shared_ptr img = std::move(decoded_img->image); - heif_image_release(decoded_img); - - decoder_plugin->free_decoder(decoder); - - return img; -} - - Result> ImageItem::get_compressed_image_data() const { + // TODO: Remove this later when decoding is done through Decoder. + // --- get the compressed image data // data from configuration blocks @@ -1102,17 +920,13 @@ Result> ImageItem::get_compressed_image_data() const Result> ImageItem::decode_compressed_image(const struct heif_decoding_options& options, bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const { - // --- find the decoder plugin with the correct compression format + DataExtent extent; + extent.set_from_image_item(get_file(), get_id()); - heif_compression_format compression_format = get_compression_format(); - if (compression_format == heif_compression_undefined) { - return Error{heif_error_Decoder_plugin_error, heif_suberror_Unsupported_codec, "Decoding not supported"}; - } + auto decoder = get_decoder(); + assert(decoder); - Result> dataResult = get_compressed_image_data(); - if (dataResult.error) { - return dataResult.error; - } + decoder->set_data_extent(std::move(extent)); - return decode_from_compressed_data(compression_format, options, *dataResult); + return decoder->decode_single_frame_from_compressed_data(options); } diff --git a/libheif/codecs/image_item.h b/libheif/image-items/image_item.h similarity index 93% rename from libheif/codecs/image_item.h rename to libheif/image-items/image_item.h index 0f8518e91f..14f22370a7 100644 --- a/libheif/codecs/image_item.h +++ b/libheif/image-items/image_item.h @@ -21,14 +21,14 @@ #ifndef LIBHEIF_IMAGEITEM_H #define LIBHEIF_IMAGEITEM_H -#include -#include -#include +#include "api/libheif/heif.h" +#include "error.h" +#include "nclx.h" #include #include #include #include -#include +#include "api/libheif/heif_plugin.h" class HeifContext; @@ -66,7 +66,7 @@ class ImageItem : public ErrorBuffer struct heif_encoder* encoder, const struct heif_encoding_options& options); - virtual const char* get_infe_type() const { return "????"; } // TODO = 0; + virtual uint32_t get_infe_type() const { return 0; } virtual const char* get_auxC_alpha_channel_type() const { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; } @@ -77,6 +77,8 @@ class ImageItem : public ErrorBuffer virtual heif_compression_format get_compression_format() const { return heif_compression_undefined; } + virtual Result> read_bitstream_configuration_data(heif_item_id itemId) const { return std::vector{}; } + void clear() { m_thumbnails.clear(); @@ -89,9 +91,7 @@ class ImageItem : public ErrorBuffer const HeifContext* get_context() const { return m_heif_context; } - std::shared_ptr get_file(); - - std::shared_ptr get_file() const; + std::shared_ptr get_file() const; Error check_resolution(uint32_t w, uint32_t h) const; @@ -118,9 +118,11 @@ class ImageItem : public ErrorBuffer uint32_t get_ispe_height() const; - virtual int get_luma_bits_per_pixel() const { return -1; } + // Default behavior: forward call to Decoder + [[nodiscard]] virtual int get_luma_bits_per_pixel() const; - virtual int get_chroma_bits_per_pixel() const { return -1; } + // Default behavior: forward call to Decoder + [[nodiscard]] virtual int get_chroma_bits_per_pixel() const; void set_size(uint32_t w, uint32_t h) { @@ -130,7 +132,9 @@ class ImageItem : public ErrorBuffer virtual void get_tile_size(uint32_t& w, uint32_t& h) const; - Error get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const; + virtual Error get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const; + + Error postprocess_coded_image_colorspace(heif_colorspace* inout_colorspace, heif_chroma* inout_chroma) const; virtual void process_before_write() { } @@ -294,8 +298,6 @@ class ImageItem : public ErrorBuffer const struct heif_encoding_options& options, enum heif_image_input_class input_class); - void set_preencoded_hevc_image(const std::vector& data); - const std::shared_ptr& get_color_profile_nclx() const { return m_color_profile_nclx; } const std::shared_ptr& get_color_profile_icc() const { return m_color_profile_icc; } @@ -389,14 +391,8 @@ class ImageItem : public ErrorBuffer std::vector m_decoding_warnings; protected: - static Result> decode_from_compressed_data(heif_compression_format compression_format, - const struct heif_decoding_options& options, - const std::vector& data); - Result> read_bitstream_configuration_data_override(heif_item_id itemId, heif_compression_format format) const; - virtual Result> read_bitstream_configuration_data(heif_item_id itemId) const { return std::vector{}; } - virtual Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, @@ -409,6 +405,8 @@ class ImageItem : public ErrorBuffer enum heif_image_input_class input_class, const heif_color_profile_nclx* target_heif_nclx, ImageItem::CodedImageData& inout_codedImage); + + virtual std::shared_ptr get_decoder() const { return nullptr; } }; diff --git a/libheif/codecs/jpeg.cc b/libheif/image-items/jpeg.cc similarity index 59% rename from libheif/codecs/jpeg.cc rename to libheif/image-items/jpeg.cc index 52a3eda7a6..ac22ba7472 100644 --- a/libheif/codecs/jpeg.cc +++ b/libheif/image-items/jpeg.cc @@ -19,68 +19,16 @@ */ #include "jpeg.h" -#include +#include "codecs/jpeg_dec.h" +#include "codecs/jpeg_boxes.h" #include "security_limits.h" -#include -#include +#include "pixelimage.h" +#include "api/libheif/api_structs.h" #include -#include "file.h" static uint8_t JPEG_SOS = 0xDA; -// returns 0 if the marker_type was not found -size_t find_jpeg_marker_start(const std::vector& data, uint8_t marker_type) -{ - for (size_t i = 0; i < data.size() - 1; i++) { - if (data[i] == 0xFF && data[i + 1] == marker_type) { - return i; - } - } - - return 0; -} - - -std::string Box_jpgC::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - sstr << indent << "num bytes: " << m_data.size() << "\n"; - - return sstr.str(); -} - - -Error Box_jpgC::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - - writer.write(m_data); - - prepend_header(writer, box_start); - - return Error::Ok; -} - - -Error Box_jpgC::parse(BitstreamRange& range) -{ - if (!has_fixed_box_size()) { - return Error{heif_error_Unsupported_feature, heif_suberror_Unspecified, "jpgC with unspecified size are not supported"}; - } - - size_t nBytes = range.get_remaining_bytes(); - if (nBytes > MAX_MEMORY_BLOCK_SIZE) { - return Error{heif_error_Invalid_input, heif_suberror_Unspecified, "jpgC block exceeds maximum size"}; - } - - m_data.resize(nBytes); - range.read(m_data.data(), nBytes); - return range.get_error(); -} - const heif_color_profile_nclx* ImageItem_JPEG::get_forced_output_nclx() const { @@ -165,50 +113,26 @@ Result ImageItem_JPEG::encode(const std::shared_ptr> ImageItem_JPEG::read_bitstream_configuration_data(heif_item_id itemId) const { - // --- get codec configuration - - std::shared_ptr jpgC_box = get_file()->get_property(itemId); - if (jpgC_box) { - return jpgC_box->get_data(); - } - - return std::vector{}; + return m_decoder->read_bitstream_configuration_data(); } -// This checks whether a start code FFCx with nibble 'x' is a SOF marker. -// E.g. FFC0-FFC3 are, while FFC4 is not. -static bool isSOF[16] = {true, true, true, true, false, true, true, true, - false, true, true, true, false, true, true, true}; - -int ImageItem_JPEG::get_luma_bits_per_pixel() const +std::shared_ptr ImageItem_JPEG::get_decoder() const { - std::vector data; - - // image data, usually from 'mdat' + return m_decoder; +} - Error error = get_file()->append_data_from_iloc(get_id(), data); - if (error) { - return error; - } +Error ImageItem_JPEG::on_load_file() +{ + // Note: jpgC box is optional. NULL is a valid value. + auto jpgC_box = get_file()->get_property(get_id()); - for (size_t i = 0; i + 1 < data.size(); i++) { - if (data[i] == 0xFF && (data[i + 1] & 0xF0) == 0xC0 && isSOF[data[i + 1] & 0x0F]) { - i += 4; - if (i < data.size()) { - return data[i]; - } - else { - return -1; - } - } - } + m_decoder = std::make_shared(jpgC_box); - return -1; -} + DataExtent extent; + extent.set_from_image_item(get_context()->get_heif_file(), get_id()); + m_decoder->set_data_extent(extent); -int ImageItem_JPEG::get_chroma_bits_per_pixel() const -{ - return get_luma_bits_per_pixel(); + return Error::Ok; } diff --git a/libheif/codecs/jpeg.h b/libheif/image-items/jpeg.h similarity index 74% rename from libheif/codecs/jpeg.h rename to libheif/image-items/jpeg.h index ee1f04f86e..ecc9bd7533 100644 --- a/libheif/codecs/jpeg.h +++ b/libheif/image-items/jpeg.h @@ -24,34 +24,10 @@ #include "box.h" #include #include -#include +#include "image_item.h" #include -class Box_jpgC : public Box -{ -public: - Box_jpgC() - { - set_short_type(fourcc("jpgC")); - } - - const std::vector& get_data() { return m_data; } - - void set_data(const std::vector& data) { m_data = data; } - - std::string dump(Indent&) const override; - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; - -private: - std::vector m_data; -}; - - class ImageItem_JPEG : public ImageItem { public: @@ -59,15 +35,16 @@ class ImageItem_JPEG : public ImageItem ImageItem_JPEG(HeifContext* ctx) : ImageItem(ctx) { } - const char* get_infe_type() const override { return "jpeg"; } + uint32_t get_infe_type() const override { return fourcc("jpeg"); } const heif_color_profile_nclx* get_forced_output_nclx() const override; heif_compression_format get_compression_format() const override { return heif_compression_JPEG; } - int get_luma_bits_per_pixel() const override; - int get_chroma_bits_per_pixel() const override; + Error on_load_file() override; + +public: Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, @@ -75,7 +52,12 @@ class ImageItem_JPEG : public ImageItem enum heif_image_input_class input_class) override; protected: + std::shared_ptr get_decoder() const override; + Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + +private: + std::shared_ptr m_decoder; }; #endif // LIBHEIF_JPEG_H diff --git a/libheif/image-items/jpeg2000.cc b/libheif/image-items/jpeg2000.cc new file mode 100644 index 0000000000..984e5aa097 --- /dev/null +++ b/libheif/image-items/jpeg2000.cc @@ -0,0 +1,112 @@ +/* + * HEIF JPEG 2000 codec. + * Copyright (c) 2023 Brad Hards + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "jpeg2000.h" +#include "libheif/api_structs.h" +#include "codecs/jpeg2000_dec.h" +#include "codecs/jpeg2000_boxes.h" +#include +#include +#include + + + +Result ImageItem_JPEG2000::encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ + CodedImageData codedImageData; + + heif_image c_api_image; + c_api_image.image = image; + + encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); + + // get compressed data + for (;;) { + uint8_t* data; + int size; + + encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, nullptr); + + if (data == nullptr) { + break; + } + + codedImageData.append(data, size); + } + + // add 'j2kH' property + auto j2kH = std::make_shared(); + + // add 'cdef' to 'j2kH' + auto cdef = std::make_shared(); + cdef->set_channels(image->get_colorspace()); + j2kH->append_child_box(cdef); + + codedImageData.properties.push_back(j2kH); + + return codedImageData; +} + + +Result> ImageItem_JPEG2000::read_bitstream_configuration_data(heif_item_id itemId) const +{ + // --- get codec configuration + + std::shared_ptr j2kH_box = get_file()->get_property(itemId); + if (!j2kH_box) + { + // TODO - Correctly Find the j2kH box + // return Error(heif_error_Invalid_input, + // heif_suberror_Unspecified); + } + // else if (!j2kH_box->get_headers(data)) { + // return Error(heif_error_Invalid_input, + // heif_suberror_No_item_data); + // } + + return std::vector{}; +} + +std::shared_ptr ImageItem_JPEG2000::get_decoder() const +{ + return m_decoder; +} + +Error ImageItem_JPEG2000::on_load_file() +{ + auto j2kH = get_file()->get_property(get_id()); + if (!j2kH) { + return Error{heif_error_Invalid_input, + heif_suberror_Unspecified, + "No j2kH box found."}; + } + + m_decoder = std::make_shared(j2kH); + + DataExtent extent; + extent.set_from_image_item(get_context()->get_heif_file(), get_id()); + + m_decoder->set_data_extent(extent); + + return Error::Ok; +} diff --git a/libheif/image-items/jpeg2000.h b/libheif/image-items/jpeg2000.h new file mode 100644 index 0000000000..be6471dfc1 --- /dev/null +++ b/libheif/image-items/jpeg2000.h @@ -0,0 +1,61 @@ +/* + * HEIF JPEG 2000 codec. + * Copyright (c) 2023 Brad Hards + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_JPEG2000_H +#define LIBHEIF_JPEG2000_H + +#include "box.h" +#include "file.h" +#include "context.h" +#include +#include +#include +#include + + +class ImageItem_JPEG2000 : public ImageItem +{ +public: + ImageItem_JPEG2000(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} + + ImageItem_JPEG2000(HeifContext* ctx) : ImageItem(ctx) {} + + uint32_t get_infe_type() const override { return fourcc("j2k1"); } + + heif_compression_format get_compression_format() const override { return heif_compression_JPEG2000; } + + Result encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) override; + +protected: + Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + + std::shared_ptr get_decoder() const override; + +public: + Error on_load_file() override; + +private: + std::shared_ptr m_decoder; +}; + +#endif // LIBHEIF_JPEG2000_H diff --git a/libheif/codecs/mask_image.cc b/libheif/image-items/mask_image.cc similarity index 99% rename from libheif/codecs/mask_image.cc rename to libheif/image-items/mask_image.cc index 4bcc2155dc..a7dd59d5f2 100644 --- a/libheif/codecs/mask_image.cc +++ b/libheif/image-items/mask_image.cc @@ -30,7 +30,7 @@ #include "libheif/heif.h" #include "logging.h" #include "mask_image.h" -#include +#include "image_item.h" Error Box_mskC::parse(BitstreamRange& range) { diff --git a/libheif/codecs/mask_image.h b/libheif/image-items/mask_image.h similarity index 97% rename from libheif/codecs/mask_image.h rename to libheif/image-items/mask_image.h index fc34f903d2..4b0fed6980 100644 --- a/libheif/codecs/mask_image.h +++ b/libheif/image-items/mask_image.h @@ -87,7 +87,7 @@ class ImageItem_mask : public ImageItem ImageItem_mask(HeifContext* ctx) : ImageItem(ctx) {} - const char* get_infe_type() const override { return "mski"; } + uint32_t get_infe_type() const override { return fourcc("mski"); } const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } diff --git a/libheif/codecs/overlay.cc b/libheif/image-items/overlay.cc similarity index 99% rename from libheif/codecs/overlay.cc rename to libheif/image-items/overlay.cc index e5e64c4554..8250941ed9 100644 --- a/libheif/codecs/overlay.cc +++ b/libheif/image-items/overlay.cc @@ -253,7 +253,7 @@ Error ImageItem_Overlay::read_overlay_spec() std::vector overlay_data; - Error err = heif_file->get_compressed_image_data(get_id(), &overlay_data); + Error err = heif_file->get_uncompressed_item_data(get_id(), &overlay_data); if (err) { return err; } diff --git a/libheif/codecs/overlay.h b/libheif/image-items/overlay.h similarity index 96% rename from libheif/codecs/overlay.h rename to libheif/image-items/overlay.h index a799e3551a..e41bc04ae4 100644 --- a/libheif/codecs/overlay.h +++ b/libheif/image-items/overlay.h @@ -21,7 +21,7 @@ #ifndef LIBHEIF_OVERLAY_H #define LIBHEIF_OVERLAY_H -#include +#include "image_item.h" #include #include #include @@ -90,8 +90,9 @@ class ImageItem_Overlay : public ImageItem ImageItem_Overlay(HeifContext* ctx); - const char* get_infe_type() const override { return "iovl"; } + uint32_t get_infe_type() const override { return fourcc("iovl"); } + // TODO: nclx depends on contained format // const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } // heif_compression_format get_compression_format() const override { return heif_compression_HEVC; } diff --git a/libheif/codecs/tild.cc b/libheif/image-items/tild.cc similarity index 90% rename from libheif/codecs/tild.cc rename to libheif/image-items/tild.cc index 2cc517d774..33d48e5924 100644 --- a/libheif/codecs/tild.cc +++ b/libheif/image-items/tild.cc @@ -22,7 +22,8 @@ #include "context.h" #include "file.h" #include -#include +#include "security_limits.h" +#include "codecs/hevc_dec.h" static uint64_t readvec(const std::vector& data, size_t& ptr, int len) @@ -149,7 +150,7 @@ std::string Box_tilC::dump(Indent& indent) const sstr << indent << "version: " << ((int) get_version()) << "\n" //<< indent << "image size: " << m_parameters.image_width << "x" << m_parameters.image_height << "\n" << indent << "tile size: " << m_parameters.tile_width << "x" << m_parameters.tile_height << "\n" - << indent << "compression: " << to_fourcc(m_parameters.compression_type_fourcc) << "\n" + << indent << "compression: " << fourcc_to_string(m_parameters.compression_type_fourcc) << "\n" << indent << "tiles are sequential: " << (m_parameters.tiles_are_sequential ? "yes" : "no") << "\n" << indent << "offset field length: " << ((int) m_parameters.offset_field_length) << " bits\n" << indent << "size field length: " << ((int) m_parameters.size_field_length) << " bits\n" @@ -467,6 +468,13 @@ Error ImageItem_Tild::on_load_file() m_tild_header.set_parameters(parameters); + m_tile_decoder = Decoder::alloc_for_infe_type(get_context(), get_id(), parameters.compression_type_fourcc); + if (!m_tile_decoder) { + return {heif_error_Unsupported_feature, + heif_suberror_Unsupported_codec, + "'tild' image with unsupported compression format."}; + } + if (m_preload_offset_table) { err = m_tild_header.read_full_offset_table(heif_file, get_id()); if (err) { @@ -485,7 +493,7 @@ ImageItem_Tild::add_new_tild_item(HeifContext* ctx, const heif_tild_image_parame auto file = ctx->get_heif_file(); - heif_item_id tild_id = ctx->get_heif_file()->add_new_image("tild"); + heif_item_id tild_id = ctx->get_heif_file()->add_new_image(fourcc("tild")); auto tild_image = std::make_shared(ctx, tild_id); ctx->insert_new_image(tild_id, tild_image); @@ -561,6 +569,29 @@ ImageItem_Tild::decode_compressed_image(const struct heif_decoding_options& opti } +Error ImageItem_Tild::append_compressed_tile_data(std::vector& data, uint32_t tx, uint32_t ty) const +{ + uint32_t idx = (uint32_t) (ty * nTiles_h(m_tild_header.get_parameters()) + tx); + + if (!m_tild_header.is_tile_offset_known(idx)) { + Error err = const_cast(this)->load_tile_offset_entry(idx); + if (err) { + return err; + } + } + + uint64_t offset = m_tild_header.get_tile_offset(idx); + uint64_t size = m_tild_header.get_tile_size(idx); + + Error err = get_file()->append_data_from_iloc(get_id(), data, offset, size); + if (err.error_code) { + return err; + } + + return Error::Ok; +} + + Result> ImageItem_Tild::decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const { @@ -575,27 +606,19 @@ ImageItem_Tild::decode_grid_tile(const heif_decoding_options& options, uint32_t } std::vector& data = dataResult.value; + Error err = append_compressed_tile_data(data, tx, ty); + if (err) { + return err; + } // --- decode - uint32_t idx = (uint32_t) (ty * nTiles_h(m_tild_header.get_parameters()) + tx); + DataExtent extent; + extent.m_raw = data; - if (!m_tild_header.is_tile_offset_known(idx)) { - Error err = const_cast(this)->load_tile_offset_entry(idx); - if (err) { - return err; - } - } + m_tile_decoder->set_data_extent(std::move(extent)); - uint64_t offset = m_tild_header.get_tile_offset(idx); - uint64_t size = m_tild_header.get_tile_size(idx); - - Error err = get_file()->append_data_from_iloc(get_id(), data, offset, size); - if (err.error_code) { - return err; - } - - return decode_from_compressed_data(get_compression_format(), options, data); + return m_tile_decoder->decode_single_frame_from_compressed_data(options); } @@ -634,3 +657,35 @@ void ImageItem_Tild::get_tile_size(uint32_t& w, uint32_t& h) const w = m_tild_header.get_parameters().tile_width; h = m_tild_header.get_parameters().tile_height; } + + +Error ImageItem_Tild::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + Error err = m_tile_decoder->get_coded_image_colorspace(out_colorspace, out_chroma); + if (err) { + return err; + } + + postprocess_coded_image_colorspace(out_colorspace, out_chroma); + + return Error::Ok; +} + + +int ImageItem_Tild::get_luma_bits_per_pixel() const +{ + DataExtent any_tile_extent; + append_compressed_tile_data(any_tile_extent.m_raw, 0,0); // TODO: use tile that is already loaded + m_tile_decoder->set_data_extent(any_tile_extent); + + return m_tile_decoder->get_luma_bits_per_pixel(); +} + +int ImageItem_Tild::get_chroma_bits_per_pixel() const +{ + DataExtent any_tile_extent; + append_compressed_tile_data(any_tile_extent.m_raw, 0,0); // TODO: use tile that is already loaded + m_tile_decoder->set_data_extent(any_tile_extent); + + return m_tile_decoder->get_chroma_bits_per_pixel(); +} \ No newline at end of file diff --git a/libheif/codecs/tild.h b/libheif/image-items/tild.h similarity index 92% rename from libheif/codecs/tild.h rename to libheif/image-items/tild.h index 3ad2c44d92..ebbf847afc 100644 --- a/libheif/codecs/tild.h +++ b/libheif/image-items/tild.h @@ -22,7 +22,7 @@ #define LIBHEIF_TILD_H -#include +#include "image_item.h" #include "box.h" #include #include @@ -132,8 +132,9 @@ class ImageItem_Tild : public ImageItem ImageItem_Tild(HeifContext* ctx); - const char* get_infe_type() const override { return "tili"; } + uint32_t get_infe_type() const override { return fourcc("tili"); } + // TODO: nclx depends on contained format // const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } heif_compression_format get_compression_format() const override; @@ -144,9 +145,11 @@ class ImageItem_Tild : public ImageItem void process_before_write() override; - int get_luma_bits_per_pixel() const override { return -1; } // TODO (create dummy ImageItem, then call this function) + Error get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const override; - int get_chroma_bits_per_pixel() const override { return -1; } // TODO + int get_luma_bits_per_pixel() const override; + + int get_chroma_bits_per_pixel() const override; Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, @@ -180,9 +183,13 @@ class ImageItem_Tild : public ImageItem uint32_t mReadChunkSize_bytes = 64*1024; // 64 kiB bool m_preload_offset_table = false; + std::shared_ptr m_tile_decoder; + Result> decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const; Error load_tile_offset_entry(uint32_t idx); + + Error append_compressed_tile_data(std::vector& data, uint32_t tx, uint32_t ty) const; }; diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc new file mode 100644 index 0000000000..356ea0992a --- /dev/null +++ b/libheif/image-items/unc_image.cc @@ -0,0 +1,516 @@ +/* + * 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 "codecs/uncompressed/unc_types.h" +#include "codecs/uncompressed/unc_boxes.h" +#include "unc_image.h" +#include "codecs/uncompressed/unc_dec.h" +#include "codecs/uncompressed/unc_codec.h" +#include "image_item.h" + + + +static void maybe_make_minimised_uncC(std::shared_ptr& uncC, const std::shared_ptr& image) +{ + uncC->set_version(0); + if (image->get_colorspace() != heif_colorspace_RGB) { + return; + } + if (!((image->get_chroma_format() == heif_chroma_interleaved_RGB) || (image->get_chroma_format() == heif_chroma_interleaved_RGBA))) { + return; + } + if (image->get_bits_per_pixel(heif_channel_interleaved) != 8) { + return; + } + if (image->get_chroma_format() == heif_chroma_interleaved_RGBA) { + uncC->set_profile(fourcc("rgba")); + } else { + uncC->set_profile(fourcc("rgb3")); + } + uncC->set_version(1); +} + + +Result> ImageItem_uncompressed::decode_compressed_image(const struct heif_decoding_options& options, + bool decode_tile_only, uint32_t tile_x0, uint32_t tile_y0) const +{ + std::shared_ptr img; + + std::vector data; + + Error err; + + if (decode_tile_only) { + err = UncompressedImageCodec::decode_uncompressed_image_tile(get_context(), + get_id(), + img, + tile_x0, tile_y0); + } + else { + err = UncompressedImageCodec::decode_uncompressed_image(get_context(), + get_id(), + img); + } + + if (err) { + return err; + } + else { + return img; + } +} + + +struct unciHeaders +{ + std::shared_ptr uncC; + std::shared_ptr cmpd; +}; + + +static Result generate_headers(const std::shared_ptr& src_image, + const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* options) +{ + unciHeaders headers; + + std::shared_ptr uncC = std::make_shared(); + if (options && options->prefer_uncC_short_form) { + maybe_make_minimised_uncC(uncC, src_image); + } + + if (uncC->get_version() == 1) { + headers.uncC = uncC; + } else { + std::shared_ptr cmpd = std::make_shared(); + + Error error = fill_cmpd_and_uncC(cmpd, uncC, src_image, parameters); + if (error) { + return error; + } + + headers.cmpd = cmpd; + headers.uncC = uncC; + } + + return headers; +} + + +Result> encode_image_tile(const std::shared_ptr& src_image) +{ + std::vector data; + + if (src_image->get_colorspace() == heif_colorspace_YCbCr) + { + uint64_t offset = 0; + for (heif_channel channel : {heif_channel_Y, heif_channel_Cb, heif_channel_Cr}) + { + uint32_t src_stride; + uint32_t src_width = src_image->get_width(channel); + uint32_t src_height = src_image->get_height(channel); + const uint8_t* src_data = src_image->get_plane(channel, &src_stride); + uint64_t out_size = src_width * src_height; + data.resize(data.size() + out_size); + for (uint32_t y = 0; y < src_height; y++) { + memcpy(data.data() + offset + y * src_width, src_data + src_stride * y, src_width); + } + offset += out_size; + } + + return data; + } + else if (src_image->get_colorspace() == heif_colorspace_RGB) + { + if (src_image->get_chroma_format() == heif_chroma_444) + { + uint64_t offset = 0; + std::vector channels = {heif_channel_R, heif_channel_G, heif_channel_B}; + if (src_image->has_channel(heif_channel_Alpha)) + { + channels.push_back(heif_channel_Alpha); + } + for (heif_channel channel : channels) + { + uint32_t src_stride; + const uint8_t* src_data = src_image->get_plane(channel, &src_stride); + uint64_t out_size = src_image->get_height() * src_image->get_width(); + + data.resize(data.size() + out_size); + for (uint32_t y = 0; y < src_image->get_height(); y++) { + memcpy(data.data() + offset + y * src_image->get_width(), src_data + y * src_stride, src_image->get_width()); + } + + offset += out_size; + } + + return data; + } + else if ((src_image->get_chroma_format() == heif_chroma_interleaved_RGB) || + (src_image->get_chroma_format() == heif_chroma_interleaved_RGBA) || + (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_BE) || + (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE) || + (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE) || + (src_image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE)) + { + int bytes_per_pixel = 0; + switch (src_image->get_chroma_format()) { + case heif_chroma_interleaved_RGB: + bytes_per_pixel=3; + break; + case heif_chroma_interleaved_RGBA: + bytes_per_pixel=4; + break; + case heif_chroma_interleaved_RRGGBB_BE: + case heif_chroma_interleaved_RRGGBB_LE: + bytes_per_pixel=6; + break; + case heif_chroma_interleaved_RRGGBBAA_BE: + case heif_chroma_interleaved_RRGGBBAA_LE: + bytes_per_pixel=8; + break; + default: + assert(false); + } + + uint32_t src_stride; + const uint8_t* src_data = src_image->get_plane(heif_channel_interleaved, &src_stride); + uint64_t out_size = src_image->get_height() * src_image->get_width() * bytes_per_pixel; + data.resize(out_size); + for (uint32_t y = 0; y < src_image->get_height(); y++) { + memcpy(data.data() + y * src_image->get_width() * bytes_per_pixel, src_data + src_stride * y, src_image->get_width() * bytes_per_pixel); + } + + return data; + } + else + { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Unsupported RGB chroma"); + } + } + else if (src_image->get_colorspace() == heif_colorspace_monochrome) + { + uint64_t offset = 0; + std::vector channels; + if (src_image->has_channel(heif_channel_Alpha)) + { + channels = {heif_channel_Y, heif_channel_Alpha}; + } + else + { + channels = {heif_channel_Y}; + } + for (heif_channel channel : channels) + { + uint32_t src_stride; + const uint8_t* src_data = src_image->get_plane(channel, &src_stride); + uint64_t out_size = src_image->get_height() * src_stride; + data.resize(data.size() + out_size); + memcpy(data.data() + offset, src_data, out_size); + offset += out_size; + } + + return data; + } + else + { + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_data_version, + "Unsupported colourspace"); + } + +} + + +Result ImageItem_uncompressed::encode(const std::shared_ptr& src_image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ + heif_unci_image_parameters parameters{}; + parameters.image_width = src_image->get_width(); + parameters.image_height = src_image->get_height(); + parameters.tile_width = parameters.image_width; + parameters.tile_height = parameters.image_height; + + + // --- generate configuration property boxes + + Result genHeadersResult = generate_headers(src_image, ¶meters, &options); + if (genHeadersResult.error) { + return genHeadersResult.error; + } + + const unciHeaders& headers = *genHeadersResult; + + CodedImageData codedImageData; + if (headers.uncC) { + codedImageData.properties.push_back(headers.uncC); + } + if (headers.cmpd) { + codedImageData.properties.push_back(headers.cmpd); + } + + + // --- encode image + + Result> codedBitstreamResult = encode_image_tile(src_image); + if (codedBitstreamResult.error) { + return codedBitstreamResult.error; + } + + codedImageData.bitstream = *codedBitstreamResult; + + return codedImageData; +} + + +Result> ImageItem_uncompressed::add_unci_item(HeifContext* ctx, + const heif_unci_image_parameters* parameters, + const struct heif_encoding_options* encoding_options, + const std::shared_ptr& prototype) +{ + // Check input parameters + + if (parameters->image_width % parameters->tile_width != 0 || + parameters->image_height % parameters->tile_height != 0) { + return Error{heif_error_Invalid_input, + heif_suberror_Invalid_parameter_value, + "ISO 23001-17 image size must be an integer multiple of the tile size."}; + } + + // Create 'unci' Item + + auto file = ctx->get_heif_file(); + + heif_item_id unci_id = ctx->get_heif_file()->add_new_image(fourcc("unci")); + auto unci_image = std::make_shared(ctx, unci_id); + ctx->insert_new_image(unci_id, unci_image); + + + // Generate headers + + Result genHeadersResult = generate_headers(prototype, parameters, encoding_options); + if (genHeadersResult.error) { + return genHeadersResult.error; + } + + const unciHeaders& headers = *genHeadersResult; + + if (headers.uncC) { + file->add_property(unci_id, headers.uncC, true); + } + + if (headers.cmpd) { + file->add_property(unci_id, headers.cmpd, true); + } + + // Add `ispe` property + + file->add_ispe_property(unci_id, + static_cast(parameters->image_width), + static_cast(parameters->image_height), + true); + + if (parameters->compression != heif_metadata_compression_off) { + auto icef = std::make_shared(); + auto cmpC = std::make_shared(); + cmpC->set_compressed_unit_type(heif_cmpC_compressed_unit_type_image_tile); + + if (parameters->compression == heif_metadata_compression_deflate) { + cmpC->set_compression_type(fourcc("defl")); + } + else if (parameters->compression == heif_metadata_compression_zlib) { + cmpC->set_compression_type(fourcc("zlib")); + } +#if HAVE_BROTLI + else if (parameters->compression == heif_metadata_compression_brotli) { + cmpC->set_compression_type(fourcc("brot")); + } +#endif + else { + assert(false); + } + + file->add_property(unci_id, cmpC, true); + file->add_property_without_deduplication(unci_id, icef, true); // icef is empty. A normal add_property() would lead to a wrong deduplication. + } + + // Create empty image. If we use compression, we append the data piece by piece. + + if (parameters->compression == heif_metadata_compression_off) { + uint64_t tile_size = headers.uncC->compute_tile_data_size_bytes(parameters->image_width / headers.uncC->get_number_of_tile_columns(), + parameters->image_height / headers.uncC->get_number_of_tile_rows()); + + std::vector dummydata; + dummydata.resize(tile_size); + + for (uint64_t i = 0; i < tile_size; i++) { + const int construction_method = 0; // 0=mdat 1=idat + file->append_iloc_data(unci_id, dummydata, construction_method); + } + } + + // Set Brands + ctx->get_heif_file()->set_brand(heif_compression_uncompressed, unci_image->is_miaf_compatible()); + + return {unci_image}; +} + + +Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image) +{ + std::shared_ptr uncC = get_file()->get_property(get_id()); + assert(uncC); + + uint32_t tile_width = image->get_width(); + uint32_t tile_height = image->get_height(); + + uint64_t tile_data_size = uncC->compute_tile_data_size_bytes(tile_width, tile_height); + + uint32_t tile_idx = tile_y * uncC->get_number_of_tile_columns() + tile_x; + + Result> codedBitstreamResult = encode_image_tile(image); + if (codedBitstreamResult.error) { + return codedBitstreamResult.error; + } + + std::shared_ptr cmpC = get_file()->get_property(get_id()); + std::shared_ptr icef = get_file()->get_property(get_id()); + + if (!icef || !cmpC) { + assert(!icef); + assert(!cmpC); + + // uncompressed + + get_file()->replace_iloc_data(get_id(), tile_idx * tile_data_size, *codedBitstreamResult, 0); + } + else { + std::vector compressed_data; + const std::vector& raw_data = codedBitstreamResult.value; + + uint32_t compr = cmpC->get_compression_type(); + switch (compr) { + case fourcc("defl"): + compressed_data = compress_deflate(raw_data.data(), raw_data.size()); + break; + case fourcc("zlib"): + compressed_data = compress_zlib(raw_data.data(), raw_data.size()); + break; +#if HAVE_BROTLI + case fourcc("brot"): + compressed_data = compress_brotli(raw_data.data(), raw_data.size()); + break; +#endif + default: + assert(false); + break; + } + + get_file()->append_iloc_data(get_id(), compressed_data, 0); + + Box_icef::CompressedUnitInfo unit_info; + unit_info.unit_offset = m_next_tile_write_pos; + unit_info.unit_size = compressed_data.size(); + icef->set_component(tile_idx, unit_info); + + m_next_tile_write_pos += compressed_data.size(); + } + + return Error::Ok; +} + + +void ImageItem_uncompressed::get_tile_size(uint32_t& w, uint32_t& h) const +{ + auto ispe = get_file()->get_property(get_id()); + auto uncC = get_file()->get_property(get_id()); + + if (!ispe || !uncC) { + w=h=0; + } + + w = ispe->get_width() / uncC->get_number_of_tile_columns(); + h = ispe->get_height() / uncC->get_number_of_tile_rows(); +} + + +heif_image_tiling ImageItem_uncompressed::get_heif_image_tiling() const +{ + heif_image_tiling tiling{}; + + auto ispe = get_file()->get_property(get_id()); + auto uncC = get_file()->get_property(get_id()); + assert(ispe && uncC); + + tiling.num_columns = uncC->get_number_of_tile_columns(); + tiling.num_rows = uncC->get_number_of_tile_rows(); + + tiling.tile_width = ispe->get_width() / tiling.num_columns; + tiling.tile_height = ispe->get_height() / tiling.num_rows; + + tiling.image_width = ispe->get_width(); + tiling.image_height = ispe->get_height(); + tiling.number_of_extra_dimensions = 0; + + return tiling; +} + +std::shared_ptr ImageItem_uncompressed::get_decoder() const +{ + return m_decoder; +} + +Error ImageItem_uncompressed::on_load_file() +{ + std::shared_ptr cmpd = get_file()->get_property(get_id()); + std::shared_ptr uncC = get_file()->get_property(get_id()); + + if (!uncC) { + return Error{heif_error_Invalid_input, + heif_suberror_Unspecified, + "No 'uncC' box found."}; + } + + m_decoder = std::make_shared(uncC, cmpd); + + DataExtent extent; + extent.set_from_image_item(get_context()->get_heif_file(), get_id()); + + m_decoder->set_data_extent(extent); + + return Error::Ok; +} diff --git a/libheif/codecs/uncompressed/unc_image.h b/libheif/image-items/unc_image.h similarity index 64% rename from libheif/codecs/uncompressed/unc_image.h rename to libheif/image-items/unc_image.h index 1e91033a8e..c163e51a84 100644 --- a/libheif/codecs/uncompressed/unc_image.h +++ b/libheif/image-items/unc_image.h @@ -33,36 +33,6 @@ class HeifContext; -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 { @@ -71,7 +41,7 @@ class ImageItem_uncompressed : public ImageItem ImageItem_uncompressed(HeifContext* ctx) : ImageItem(ctx) {} - const char* get_infe_type() const override { return "unci"; } + uint32_t get_infe_type() const override { return fourcc("unci"); } heif_compression_format get_compression_format() const override { return heif_compression_uncompressed; } @@ -82,10 +52,6 @@ class ImageItem_uncompressed : public ImageItem bool is_ispe_essential() const override { return true; } - int get_luma_bits_per_pixel() const override; - - int get_chroma_bits_per_pixel() const override; - void get_tile_size(uint32_t& w, uint32_t& h) const override; // Code from encode_uncompressed_image() has been moved to here. @@ -95,6 +61,10 @@ class ImageItem_uncompressed : public ImageItem heif_image_tiling get_heif_image_tiling() const; + Error on_load_file() override; + +public: + // --- encoding Result encode(const std::shared_ptr& image, @@ -109,7 +79,11 @@ class ImageItem_uncompressed : public ImageItem Error add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image); +protected: + std::shared_ptr get_decoder() const override; + private: + std::shared_ptr m_decoder; /* Result generate_headers(const std::shared_ptr& src_image, const heif_unci_image_parameters* parameters, diff --git a/libheif/image-items/vvc.cc b/libheif/image-items/vvc.cc new file mode 100644 index 0000000000..2e3bb9236f --- /dev/null +++ b/libheif/image-items/vvc.cc @@ -0,0 +1,140 @@ +/* + * HEIF VVC 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 "vvc.h" +#include "codecs/vvc_dec.h" +#include "codecs/vvc_boxes.h" +#include +#include +#include +#include "api/libheif/api_structs.h" + + +Result ImageItem_VVC::encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) +{ + CodedImageData codedImage; + + auto vvcC = std::make_shared(); + codedImage.properties.push_back(vvcC); + + + heif_image c_api_image; + c_api_image.image = image; + + struct heif_error err = encoder->plugin->encode_image(encoder->encoder, &c_api_image, input_class); + if (err.code) { + return Error(err.code, + err.subcode, + err.message); + } + + int encoded_width = 0; + int encoded_height = 0; + + for (;;) { + uint8_t* data; + int size; + + encoder->plugin->get_compressed_data(encoder->encoder, &data, &size, NULL); + + if (data == NULL) { + break; + } + + + const uint8_t NAL_SPS = 15; + + uint8_t nal_type = 0; + if (size>=2) { + nal_type = (data[1] >> 3) & 0x1F; + } + + if (nal_type == NAL_SPS) { + Box_vvcC::configuration config; + + parse_sps_for_vvcC_configuration(data, size, &config, &encoded_width, &encoded_height); + + vvcC->set_configuration(config); + } + + switch (nal_type) { + case 14: // VPS + case 15: // SPS + case 16: // PPS + vvcC->append_nal_data(data, size); + break; + + default: + codedImage.append_with_4bytes_size(data, size); + } + } + + return codedImage; +} + + +Result> ImageItem_VVC::read_bitstream_configuration_data(heif_item_id itemId) const +{ + // --- get codec configuration + + std::shared_ptr vvcC_box = get_file()->get_property(itemId); + if (!vvcC_box) + { + assert(false); + return Error(heif_error_Invalid_input, + heif_suberror_No_vvcC_box); + } + + std::vector data; + if (!vvcC_box->get_headers(&data)) + { + return Error(heif_error_Invalid_input, + heif_suberror_No_item_data); + } + + return data; +} + + +std::shared_ptr ImageItem_VVC::get_decoder() const +{ + return m_decoder; +} + +Error ImageItem_VVC::on_load_file() +{ + auto vvcC_box = get_file()->get_property(get_id()); + if (!vvcC_box) { + return Error{heif_error_Invalid_input, + heif_suberror_No_av1C_box}; + } + + m_decoder = std::make_shared(vvcC_box); + + DataExtent extent; + extent.set_from_image_item(get_context()->get_heif_file(), get_id()); + + m_decoder->set_data_extent(extent); + + return Error::Ok; +} diff --git a/libheif/image-items/vvc.h b/libheif/image-items/vvc.h new file mode 100644 index 0000000000..384e7e9507 --- /dev/null +++ b/libheif/image-items/vvc.h @@ -0,0 +1,62 @@ +/* + * HEIF VVC codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_VVC_H +#define LIBHEIF_VVC_H + +#include "box.h" +#include +#include +#include "image_item.h" +#include + + +class ImageItem_VVC : public ImageItem +{ +public: + ImageItem_VVC(HeifContext* ctx, heif_item_id id) : ImageItem(ctx, id) {} + + ImageItem_VVC(HeifContext* ctx) : ImageItem(ctx) {} + + uint32_t get_infe_type() const override { return fourcc("vvc1"); } + + const char* get_auxC_alpha_channel_type() const override { return "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"; } + + const heif_color_profile_nclx* get_forced_output_nclx() const override { return nullptr; } + + heif_compression_format get_compression_format() const override { return heif_compression_VVC; } + + Result encode(const std::shared_ptr& image, + struct heif_encoder* encoder, + const struct heif_encoding_options& options, + enum heif_image_input_class input_class) override; + + Error on_load_file() override; + +protected: + std::shared_ptr get_decoder() const override; + + Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + +private: + std::shared_ptr m_decoder; +}; + +#endif // LIBHEIF_VVC_H diff --git a/libheif/nclx.cc b/libheif/nclx.cc index 1e455aed0c..80535c4e68 100644 --- a/libheif/nclx.cc +++ b/libheif/nclx.cc @@ -414,7 +414,7 @@ std::string Box_colr::dump(Indent& indent) const sstr << Box::dump(indent); if (m_color_profile) { - sstr << indent << "colour_type: " << to_fourcc(get_color_profile_type()) << "\n"; + sstr << indent << "colour_type: " << fourcc_to_string(get_color_profile_type()) << "\n"; sstr << m_color_profile->dump(indent); } else { diff --git a/libheif/plugins/encoder_vvenc.cc b/libheif/plugins/encoder_vvenc.cc index 6111e604ce..a7eee6a490 100644 --- a/libheif/plugins/encoder_vvenc.cc +++ b/libheif/plugins/encoder_vvenc.cc @@ -82,11 +82,11 @@ static void vvenc_init_parameters() p->version = 2; p->name = heif_encoder_parameter_name_quality; p->type = heif_encoder_parameter_type_integer; - p->integer.default_value = 32; + p->integer.default_value = 50; p->has_default = true; p->integer.have_minimum_maximum = true; p->integer.minimum = 0; - p->integer.maximum = 63; + p->integer.maximum = 100; p->integer.valid_values = NULL; p->integer.num_valid_values = 0; d[i++] = p++; @@ -438,7 +438,12 @@ static struct heif_error vvenc_encode_image(void* encoder_raw, const struct heif vvenc_config params; - int ret = vvenc_init_default(¶ms, encoded_width, encoded_height, 25, 0, encoder->quality, VVENC_MEDIUM); + // invert encoder quality range and scale to 0-63 + int encoder_quality = 63 - encoder->quality*63/100; + + int ret = vvenc_init_default(¶ms, encoded_width, encoded_height, 25, 0, + encoder_quality, + VVENC_MEDIUM); if (ret != VVENC_OK) { // TODO: cleanup memory diff --git a/tests/avc_box.cc b/tests/avc_box.cc index fad1cd9814..53efb5efb6 100644 --- a/tests/avc_box.cc +++ b/tests/avc_box.cc @@ -25,7 +25,7 @@ */ #include "catch.hpp" -#include "codecs/avc.h" +#include "codecs/avc_boxes.h" #include "error.h" #include #include diff --git a/tests/jpeg2000.cc b/tests/jpeg2000.cc index f528a5ddcc..4b97fb57f1 100644 --- a/tests/jpeg2000.cc +++ b/tests/jpeg2000.cc @@ -26,7 +26,7 @@ #include "catch.hpp" #include "libheif/heif.h" -#include "codecs/jpeg2000.h" +#include "codecs/jpeg2000_boxes.h" #include #include