From afa4eb17dd3990c20428f46b49977791344b8a87 Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Fri, 2 Feb 2024 00:22:11 +0000 Subject: [PATCH 1/4] Add experimental gain map support This feature is hidden behind the WITH_EXPERIMENTAL_GAIN_MAP compile flag. See https://helpx.adobe.com/camera-raw/using/gain-map.html for an introduction to gain maps. Note that in this change, gain maps are not stored as auxiliary items as proposed by Adobe, but as a hidden input to a new 'tmap' (tone mapped) derived item. An 'altr' (alternatives group) box is also added to tell the decoder that it may show the tone mapped image (preferrably) or the base image as a fall back. The primary item id still points to the base image for backward compatibility. --- libheif/CMakeLists.txt | 7 ++ libheif/box.cc | 24 ++++++ libheif/box.h | 31 ++++++++ libheif/file.cc | 39 ++++++++++ libheif/file.h | 13 ++++ libheif/heif.cc | 118 +++++++++++++++++++++++++++++ libheif/heif.h | 37 +++++++++ libheif/heif_plugin.h | 5 +- libheif/plugins/encoder_aom.cc | 5 ++ libheif/plugins/encoder_kvazaar.cc | 5 ++ libheif/plugins/encoder_rav1e.cc | 5 ++ libheif/plugins/encoder_x265.cc | 5 ++ 12 files changed, 293 insertions(+), 1 deletion(-) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 93c65be39f..4b1f4a1d62 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -150,6 +150,13 @@ if (WITH_UNCOMPRESSED_CODEC) uncompressed_image.cc) endif () +if (WITH_EXPERIMENTAL_GAIN_MAP) + target_compile_definitions(heif PUBLIC WITH_EXPERIMENTAL_GAIN_MAP=1) + target_sources(heif PRIVATE + gain_map_metadata.h + gain_map_metadata.cc) +endif () + write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion) install(TARGETS heif EXPORT ${PROJECT_NAME}-config diff --git a/libheif/box.cc b/libheif/box.cc index 49c555eb34..1ea02e0aca 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -2018,6 +2018,30 @@ Error Box_mdcv::write(StreamWriter& writer) const return Error::Ok; } +#if WITH_EXPERIMENTAL_GAIN_MAP +void Box_altr::add_item_id(heif_item_id id) { + m_item_IDs.push_back(id); +} + +Error Box_altr::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + writer.write32(1); // unsigned int(32) group_id; + writer.write32((uint32_t) m_item_IDs.size()); // unsigned int(32) num_entities_in_group; + for (uint32_t i = 0; i < m_item_IDs.size(); i++) { + writer.write32((uint32_t) m_item_IDs[i]); // unsigned int(32) entity_id; + } + prepend_header(writer, box_start); + + return Error::Ok; +} + +Error Box_altr::parse(BitstreamRange& range) +{ + return range.get_error(); +} +#endif Error Box_ipco::get_properties_for_item_ID(uint32_t itemID, const std::shared_ptr& ipma, diff --git a/libheif/box.h b/libheif/box.h index 3a651f6ea9..a81978c5de 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -455,6 +455,10 @@ class Box_infe : public FullBox const std::string& get_item_type() const { return m_item_type; } +#if WITH_EXPERIMENTAL_GAIN_MAP + const std::string& get_item_name() const { return m_item_name; } +#endif + void set_item_type(const std::string& type) { m_item_type = type; } void set_item_name(const std::string& name) { m_item_name = name; } @@ -812,6 +816,13 @@ class Box_idat : public Box class Box_grpl : public Box { public: +#if WITH_EXPERIMENTAL_GAIN_MAP + Box_grpl() + { + set_short_type(fourcc("grpl")); + } +#endif + std::string dump(Indent&) const override; protected: @@ -965,6 +976,26 @@ class Box_mdcv : public Box Error parse(BitstreamRange& range) override; }; +#if WITH_EXPERIMENTAL_GAIN_MAP +class Box_altr : public Box +{ +public: + Box_altr() + { + set_short_type(fourcc("altr")); + } + + void add_item_id(heif_item_id id); + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + +private: + std::vector m_item_IDs; +}; +#endif /** * User Description property. diff --git a/libheif/file.cc b/libheif/file.cc index cea8f9fcbc..f611028107 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -457,6 +457,17 @@ std::string HeifFile::get_item_type(heif_item_id ID) const return infe_box->get_item_type(); } +#if WITH_EXPERIMENTAL_GAIN_MAP +std::string HeifFile::get_item_name(heif_item_id ID) const +{ + auto infe_box = get_infe(ID); + if (!infe_box) { + return ""; + } + + return infe_box->get_item_name(); +} +#endif std::string HeifFile::get_content_type(heif_item_id ID) const { @@ -1179,6 +1190,17 @@ void HeifFile::append_iloc_data(heif_item_id id, const std::vector& nal m_iloc_box->append_data(id, nal_packets, construction_method); } +#if WITH_EXPERIMENTAL_GAIN_MAP +void HeifFile::append_iloc_data(heif_item_id id, const uint8_t* data, size_t size) +{ + std::vector nal; + nal.resize(size); + + memcpy(nal.data(), data, size); + + append_iloc_data(id, nal); +} +#endif void HeifFile::append_iloc_data_with_4byte_size(heif_item_id id, const uint8_t* data, size_t size) { @@ -1239,6 +1261,23 @@ void HeifFile::set_hdlr_library_info(const std::string& encoder_plugin_version) m_hdlr_box->set_name(sstr.str()); } +#if WITH_EXPERIMENTAL_GAIN_MAP +void HeifFile::add_altr_property(heif_item_id id) { + if (!m_iref_box) { + m_iref_box = std::make_shared(); + } + + std::shared_ptr grpl_box = std::make_shared(); + std::shared_ptr altr_box = std::make_shared(); + + m_meta_box->append_child_box(m_iref_box); + m_meta_box->append_child_box(grpl_box); + grpl_box->append_child_box(altr_box); + + altr_box->add_item_id(id); +} +#endif + #if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER) std::wstring HeifFile::convert_utf8_path_to_utf16(std::string str) { diff --git a/libheif/file.h b/libheif/file.h index 3715bab98c..1651217cac 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -75,6 +75,10 @@ class HeifFile std::string get_item_type(heif_item_id ID) const; +#if WITH_EXPERIMENTAL_GAIN_MAP + std::string get_item_name(heif_item_id ID) const; +#endif + std::string get_content_type(heif_item_id ID) const; std::string get_item_uri_type(heif_item_id ID) const; @@ -171,6 +175,10 @@ class HeifFile void append_iloc_data(heif_item_id id, const std::vector& nal_packets, uint8_t construction_method = 0); +#if WITH_EXPERIMENTAL_GAIN_MAP + void append_iloc_data(heif_item_id id, const uint8_t* data, size_t size); +#endif + void append_iloc_data_with_4byte_size(heif_item_id id, const uint8_t* data, size_t size); void set_primary_item_id(heif_item_id id); @@ -185,6 +193,11 @@ class HeifFile // TODO: the hdlr box is probably not the right place for this. Into which box should we write comments? void set_hdlr_library_info(const std::string& encoder_plugin_version); +#if WITH_EXPERIMENTAL_GAIN_MAP + // Gain map support + void add_altr_property(heif_item_id id); +#endif + #if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER) static std::wstring convert_utf8_path_to_utf16(std::string pathutf8); #endif diff --git a/libheif/heif.cc b/libheif/heif.cc index 373fd9971a..f3ef320a10 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -539,6 +539,52 @@ struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, he return Error::Ok.error_struct(ctx->context.get()); } +#if WITH_EXPERIMENTAL_GAIN_MAP +heif_error heif_context_get_gain_map_image_handle(heif_context* ctx, heif_image_handle** img) +{ + if (!img) { + Error err(heif_error_Usage_error, + heif_suberror_Null_pointer_argument); + return err.error_struct(ctx->context.get()); + } + + std::shared_ptr gain_map_image = ctx->context->get_gain_map_image(); + + // It is a requirement of an HEIF file there is always a primary image. + // If there is none, an error is generated when loading the file. + if (!gain_map_image) { + Error err(heif_error_Invalid_input, + heif_suberror_No_item_data); + return err.error_struct(ctx->context.get()); + } + + *img = new heif_image_handle(); + (*img)->image = std::move(gain_map_image); + (*img)->context = ctx->context; + + return Error::Ok.error_struct(ctx->context.get()); +} + +struct heif_error heif_context_get_gain_map_image_ID(struct heif_context* ctx, heif_item_id* id) +{ + if (!id) { + return Error(heif_error_Usage_error, + heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); + } + + std::shared_ptr gain_map_image = ctx->context->get_gain_map_image(); + if (!gain_map_image) { + Error err(heif_error_Invalid_input, + heif_suberror_No_item_data); + return err.error_struct(ctx->context.get()); + } + + *id = gain_map_image->get_id(); + + return Error::Ok.error_struct(ctx->context.get()); +} +#endif + int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id) { @@ -1360,6 +1406,22 @@ uint8_t* heif_image_get_plane(struct heif_image* image, return image->image->get_plane(channel, out_stride); } +#if WITH_EXPERIMENTAL_GAIN_MAP +struct heif_error heif_image_get_gain_map_metadata(heif_context* ctx, + GainMapMetadata* out_gm_metadata) { + if (!out_gm_metadata) { + Error err(heif_error_Usage_error, + heif_suberror_Null_pointer_argument); + return err.error_struct(ctx->context.get()); + } + + std::shared_ptr metadata = ctx->context->get_gain_map_metadata(); + + GainMapMetadata::parse_gain_map_metadata(metadata->m_data, out_gm_metadata); + + return Error::Ok.error_struct(ctx->context.get()); +} +#endif void heif_image_set_premultiplied_alpha(struct heif_image* image, int is_premultiplied_alpha) @@ -2708,6 +2770,62 @@ struct heif_error heif_context_encode_image(struct heif_context* ctx, return heif_error_success; } +#if WITH_EXPERIMENTAL_GAIN_MAP +struct heif_error heif_context_encode_gain_map_image(struct heif_context* ctx, + const struct heif_image* input_image, + const struct heif_image_handle* primary_image_handle, + struct heif_encoder* encoder, + const struct heif_encoding_options* input_options, + const struct GainMapMetadata* gain_map_metadata, + struct heif_image_handle** out_image_handle) +{ + if (!encoder) { + return Error(heif_error_Usage_error, + heif_suberror_Null_pointer_argument).error_struct(ctx->context.get()); + } + + Error error; + + heif_encoding_options options; + set_default_options(options); + ctx->context->add_altr_property(primary_image_handle->image->get_id()); + + std::vector metadata; + error = GainMapMetadata::prepare_gain_map_metadata(gain_map_metadata, metadata); + if (error != Error::Ok) { + return error.error_struct(ctx->context.get()); + } + + heif_item_id tmap_item_id = -1; + ctx->context->add_tmap_box(metadata, tmap_item_id); + + if (input_options != nullptr) { + copy_options(options, *input_options); + } + + std::shared_ptr gain_map_image; + printf("[dichenzhang] heif_context_encode_gain_map_image() %d\n", heif_image_input_class_gain_map); + error = ctx->context->encode_image(input_image->image, + encoder, + options, + heif_image_input_class_gain_map, + gain_map_image); + if (error != Error::Ok) { + return error.error_struct(ctx->context.get()); + } + + error = ctx->context->link_gain_map(primary_image_handle->image, gain_map_image, tmap_item_id); + + if (out_image_handle) { + *out_image_handle = new heif_image_handle; + (*out_image_handle)->image = gain_map_image; + (*out_image_handle)->context = ctx->context; + } + + return heif_error_success; +} +#endif + struct heif_error heif_context_assign_thumbnail(struct heif_context* ctx, const struct heif_image_handle* master_image, diff --git a/libheif/heif.h b/libheif/heif.h index 2c65a01366..fa1d8005ac 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -95,6 +95,9 @@ LIBHEIF_API int heif_get_version_number_maintenance(void); struct heif_context; struct heif_image_handle; struct heif_image; +#if WITH_EXPERIMENTAL_GAIN_MAP +struct GainMapMetadata; +#endif enum heif_error_code @@ -966,6 +969,19 @@ struct heif_error heif_context_get_image_handle(struct heif_context* ctx, heif_item_id id, struct heif_image_handle**); +#if WITH_EXPERIMENTAL_GAIN_MAP +// Get id of the gain map image of the HEIF file. If no gain map image is available, this +// method will return heif_suberror_No_item_data. +LIBHEIF_API +struct heif_error heif_context_get_gain_map_image_ID(struct heif_context* ctx, heif_item_id* id); + +// Get a handle to the gain map image of the HEIF file. If no gain map image is available, this +// method will return heif_suberror_No_item_data. +LIBHEIF_API +struct heif_error heif_context_get_gain_map_image_handle(struct heif_context* ctx, + struct heif_image_handle**); +#endif + // Print information about the boxes of a HEIF file to file descriptor. // This is for debugging and informational purposes only. You should not rely on // the output having a specific format. At best, you should not use this at all. @@ -1225,6 +1241,13 @@ LIBHEIF_API const char* heif_image_handle_get_metadata_item_uri_type(const struct heif_image_handle* handle, heif_item_id metadata_id); +#if WITH_EXPERIMENTAL_GAIN_MAP +// Get a pointer to the gain map metadata. +LIBHEIF_API +struct heif_error heif_image_get_gain_map_metadata(heif_context* ctx, + GainMapMetadata* out_gm_metadata); +#endif + // ------------------------- color profiles ------------------------- enum heif_color_profile_type @@ -2037,6 +2060,20 @@ struct heif_error heif_context_encode_image(struct heif_context*, const struct heif_encoding_options* options, struct heif_image_handle** out_image_handle); +#if WITH_EXPERIMENTAL_GAIN_MAP +// Compress the gain map image and write metadata. +// Returns a handle to the coded image in 'out_image_handle' unless out_image_handle = NULL. +// 'options' should be NULL for now. +LIBHEIF_API +struct heif_error heif_context_encode_gain_map_image(struct heif_context*, + const struct heif_image* image, + const struct heif_image_handle* primary_image_handle, + struct heif_encoder* encoder, + const struct heif_encoding_options* options, + const struct GainMapMetadata* gain_map_metadata, + struct heif_image_handle** out_image_handle); +#endif + LIBHEIF_API struct heif_error heif_context_set_primary_image(struct heif_context*, struct heif_image_handle* image_handle); diff --git a/libheif/heif_plugin.h b/libheif/heif_plugin.h index 3a438bfc94..f768966c5b 100644 --- a/libheif/heif_plugin.h +++ b/libheif/heif_plugin.h @@ -128,7 +128,10 @@ enum heif_image_input_class heif_image_input_class_normal = 1, heif_image_input_class_alpha = 2, heif_image_input_class_depth = 3, - heif_image_input_class_thumbnail = 4 + heif_image_input_class_thumbnail = 4, +#if WITH_EXPERIMENTAL_GAIN_MAP + heif_image_input_class_gain_map = 5 +#endif }; diff --git a/libheif/plugins/encoder_aom.cc b/libheif/plugins/encoder_aom.cc index a36e0c540b..00ceebbdc7 100644 --- a/libheif/plugins/encoder_aom.cc +++ b/libheif/plugins/encoder_aom.cc @@ -991,7 +991,12 @@ struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* i if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif aom_codec_control(&codec, AV1E_SET_COLOR_PRIMARIES, nclx->color_primaries); aom_codec_control(&codec, AV1E_SET_MATRIX_COEFFICIENTS, nclx->matrix_coefficients); aom_codec_control(&codec, AV1E_SET_TRANSFER_CHARACTERISTICS, nclx->transfer_characteristics); diff --git a/libheif/plugins/encoder_kvazaar.cc b/libheif/plugins/encoder_kvazaar.cc index eada77f9fe..a3bacea65e 100644 --- a/libheif/plugins/encoder_kvazaar.cc +++ b/libheif/plugins/encoder_kvazaar.cc @@ -533,7 +533,12 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif config->vui.colorprim = nclx->color_primaries; config->vui.transfer = nclx->transfer_characteristics; config->vui.colormatrix = nclx->matrix_coefficients; diff --git a/libheif/plugins/encoder_rav1e.cc b/libheif/plugins/encoder_rav1e.cc index 0047a87913..22bf696b7c 100644 --- a/libheif/plugins/encoder_rav1e.cc +++ b/libheif/plugins/encoder_rav1e.cc @@ -550,7 +550,12 @@ struct heif_error rav1e_encode_image(void* encoder_raw, const struct heif_image* if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif if (rav1e_config_set_color_description(rav1eConfig.get(), (RaMatrixCoefficients) nclx->matrix_coefficients, (RaColorPrimaries) nclx->color_primaries, diff --git a/libheif/plugins/encoder_x265.cc b/libheif/plugins/encoder_x265.cc index 313b95f835..184e6a6e14 100644 --- a/libheif/plugins/encoder_x265.cc +++ b/libheif/plugins/encoder_x265.cc @@ -805,7 +805,12 @@ static struct heif_error x265_encode_image(void* encoder_raw, const struct heif_ if (nclx && (input_class == heif_image_input_class_normal || +#if WITH_EXPERIMENTAL_GAIN_MAP + input_class == heif_image_input_class_thumbnail || + input_class == heif_image_input_class_gain_map)) { +#else input_class == heif_image_input_class_thumbnail)) { +#endif { std::stringstream sstr; From cf37ae7187321bf0326c222ec82792e2c500904d Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Fri, 2 Feb 2024 00:22:11 +0000 Subject: [PATCH 2/4] Add experimental gain map support This feature is hidden behind the WITH_EXPERIMENTAL_GAIN_MAP compile flag. See https://helpx.adobe.com/camera-raw/using/gain-map.html for an introduction to gain maps. Note that in this change, gain maps are not stored as auxiliary items as proposed by Adobe, but as a hidden input to a new 'tmap' (tone mapped) derived item. An 'altr' (alternatives group) box is also added to tell the decoder that it may show the tone mapped image (preferrably) or the base image as a fall back. The primary item id still points to the base image for backward compatibility. --- CMakePresets.json | 3 + libheif/context.cc | 125 ++++++++++++++++++-- libheif/context.h | 26 +++++ libheif/gain_map_metadata.cc | 219 +++++++++++++++++++++++++++++++++++ libheif/gain_map_metadata.h | 76 ++++++++++++ 5 files changed, 439 insertions(+), 10 deletions(-) create mode 100644 libheif/gain_map_metadata.cc create mode 100644 libheif/gain_map_metadata.h diff --git a/CMakePresets.json b/CMakePresets.json index b62e2dd2bd..d72248b761 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -43,6 +43,7 @@ "WITH_OpenJPEG_ENCODER_PLUGIN" : "OFF", "WITH_FFMPEG_DECODER" : "ON", "WITH_FFMPEG_DECODER_PLUGIN" : "OFF", + "WITH_EXPERIMENTAL_GAIN_MAP" : "OFF", "WITH_REDUCED_VISIBILITY" : "OFF", "WITH_DEFLATE_HEADER_COMPRESSION" : "ON", @@ -88,6 +89,7 @@ "WITH_OpenJPEG_ENCODER_PLUGIN" : "ON", "WITH_FFMPEG_DECODER" : "ON", "WITH_FFMPEG_DECODER_PLUGIN" : "ON", + "WITH_EXPERIMENTAL_GAIN_MAP" : "OFF", "WITH_REDUCED_VISIBILITY" : "ON", "WITH_DEFLATE_HEADER_COMPRESSION" : "ON", @@ -120,6 +122,7 @@ "WITH_OpenJPEG_DECODER" : "OFF", "WITH_OpenJPEG_ENCODER" : "OFF", "WITH_FFMPEG_DECODER" : "OFF", + "WITH_EXPERIMENTAL_GAIN_MAP" : "OFF", "WITH_REDUCED_VISIBILITY" : "ON", "WITH_DEFLATE_HEADER_COMPRESSION" : "OFF", diff --git a/libheif/context.cc b/libheif/context.cc index ec9ae708bc..d12d1e7598 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -517,6 +517,18 @@ static bool item_type_is_image(const std::string& item_type, const std::string& item_type == "mski"); } +#if WITH_EXPERIMENTAL_GAIN_MAP +static bool item_type_is_gain_map_image(const std::string& item_type, const std::string& item_name) { + return (item_name == "GMap" && + (item_type == "hvc1" || + item_type == "av01")); +} + +static bool item_type_is_gain_map_metadata(const std::string& item_type, const std::string& item_name) { + return (item_name == "GMap" && item_type == "tmap"); +} +#endif + void HeifContext::remove_top_level_image(const std::shared_ptr& image) { @@ -537,6 +549,10 @@ Error HeifContext::interpret_heif_file() m_all_images.clear(); m_top_level_images.clear(); m_primary_image.reset(); +#if WITH_EXPERIMENTAL_GAIN_MAP + m_gain_map_image.reset(); + m_gain_map_metadata.reset(); +#endif // --- reference all non-hidden images @@ -563,6 +579,13 @@ Error HeifContext::interpret_heif_file() m_top_level_images.push_back(image); } } + +#if WITH_EXPERIMENTAL_GAIN_MAP + if (item_type_is_gain_map_image(infe_box->get_item_type(), infe_box->get_item_name())) { + auto image = std::make_shared(this, id); + m_gain_map_image = std::move(image); + } +#endif } if (!m_primary_image) { @@ -882,8 +905,11 @@ Error HeifContext::interpret_heif_file() if (item_type == "rgan") { continue; } - std::string content_type = m_heif_file->get_content_type(id); +#if WITH_EXPERIMENTAL_GAIN_MAP + std::string item_name = m_heif_file->get_item_name(id); +#endif + std::string content_type = m_heif_file->get_content_type(id); std::string item_uri_type = m_heif_file->get_item_uri_type(id); // we now assign all kinds of metadata to the image, not only 'Exif' and 'XMP' @@ -891,6 +917,9 @@ Error HeifContext::interpret_heif_file() std::shared_ptr metadata = std::make_shared(); metadata->item_id = id; metadata->item_type = item_type; +#if WITH_EXPERIMENTAL_GAIN_MAP + metadata->item_name = item_name; +#endif metadata->content_type = content_type; metadata->item_uri_type = item_uri_type; @@ -924,8 +953,7 @@ Error HeifContext::interpret_heif_file() } img_iter->second->add_metadata(metadata); - } - else if (ref.header.get_short_type() == fourcc("prem")) { + } else if (ref.header.get_short_type() == fourcc("prem")) { uint32_t color_image_id = ref.from_item_ID; auto img_iter = m_all_images.find(color_image_id); if (img_iter == m_all_images.end()) { @@ -934,10 +962,33 @@ Error HeifContext::interpret_heif_file() "`prem` link assigned to non-existing image"); } - img_iter->second->set_is_premultiplied_alpha(true);; + img_iter->second->set_is_premultiplied_alpha(true); +#if WITH_EXPERIMENTAL_GAIN_MAP + } else if (ref.header.get_short_type() == fourcc("dimg")) { + if (item_type_is_gain_map_metadata(item_type, item_name)) { + for (auto to_id : ref.to_item_ID) { + auto img_iter = m_all_images.find(to_id); + if (img_iter == m_all_images.end()) { + return Error(heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced, + "Metadata assigned to non-existing image"); + } + img_iter->second->add_metadata(metadata); + } + } } +#else + } +#endif } } + +#if WITH_EXPERIMENTAL_GAIN_MAP + // --- asign gain map metadata + if (item_type_is_gain_map_metadata(item_type, item_name)) { + m_gain_map_metadata = metadata; + } +#endif } // --- read region item and assign to image(s) @@ -2255,7 +2306,7 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima error = encode_image_as_hevc(pixel_image, encoder, options, - heif_image_input_class_normal, + input_class, out_image); } break; @@ -2264,7 +2315,7 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima error = encode_image_as_av1(pixel_image, encoder, options, - heif_image_input_class_normal, + input_class, out_image); } break; @@ -2272,7 +2323,7 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima error = encode_image_as_jpeg2000(pixel_image, encoder, options, - heif_image_input_class_normal, + input_class, out_image); } break; @@ -2281,7 +2332,7 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima error = encode_image_as_jpeg(pixel_image, encoder, options, - heif_image_input_class_normal, + input_class, out_image); } break; @@ -2290,7 +2341,7 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima error = encode_image_as_uncompressed(pixel_image, encoder, options, - heif_image_input_class_normal, + input_class, out_image); } break; @@ -2299,7 +2350,7 @@ Error HeifContext::encode_image(const std::shared_ptr& pixel_ima error = encode_image_as_mask(pixel_image, encoder, options, - heif_image_input_class_normal, + input_class, out_image); } break; @@ -2451,6 +2502,23 @@ static std::shared_ptr compute_target_nclx_profile(const std return target_nclx_profile; } +#if WITH_EXPERIMENTAL_GAIN_MAP +Error HeifContext::add_tmap_box(const std::vector& data, + heif_item_id &item_id) { + auto tmap_infe = m_heif_file->add_new_infe_box("tmap"); // gain map metadata + tmap_infe->set_item_name("GMap"); + item_id = tmap_infe->get_item_ID(); + + m_heif_file->append_iloc_data(item_id, &data[0], data.size()); + + return Error::Ok; +} + +void HeifContext::add_altr_property(heif_item_id item_id) { + m_heif_file->add_altr_property(item_id); +} +#endif + Error HeifContext::encode_image_as_hevc(const std::shared_ptr& image, struct heif_encoder* encoder, @@ -2458,7 +2526,20 @@ Error HeifContext::encode_image_as_hevc(const std::shared_ptr& i enum heif_image_input_class input_class, std::shared_ptr& out_image) { +#if WITH_EXPERIMENTAL_GAIN_MAP + heif_item_id image_id; + if (input_class == heif_image_input_class_gain_map) { + auto gain_map = m_heif_file->add_new_infe_box("hvc1"); + gain_map->set_item_name("GMap"); + gain_map->set_hidden_item(true); + image_id = gain_map->get_item_ID(); + } else { + image_id = m_heif_file->add_new_image("hvc1"); + } +#else heif_item_id image_id = m_heif_file->add_new_image("hvc1"); +#endif + out_image = std::make_shared(this, image_id); @@ -2678,7 +2759,19 @@ Error HeifContext::encode_image_as_av1(const std::shared_ptr& im enum heif_image_input_class input_class, std::shared_ptr& out_image) { +#if WITH_EXPERIMENTAL_GAIN_MAP + heif_item_id image_id; + if (input_class == heif_image_input_class_gain_map) { + auto gain_map = m_heif_file->add_new_infe_box("av01"); + gain_map->set_item_name("GMap"); + gain_map->set_hidden_item(true); + image_id = gain_map->get_item_ID(); + } else { + image_id = m_heif_file->add_new_image("av01"); + } +#else heif_item_id image_id = m_heif_file->add_new_image("av01"); +#endif out_image = std::make_shared(this, image_id); m_top_level_images.push_back(out_image); @@ -3268,6 +3361,18 @@ Error HeifContext::assign_thumbnail(const std::shared_ptr& master_image, return Error::Ok; } +#if WITH_EXPERIMENTAL_GAIN_MAP +Error HeifContext::link_gain_map(const std::shared_ptr& primary_image, + const std::shared_ptr& gain_map_image, + const heif_item_id tmap_id) +{ + m_heif_file->add_iref_reference(tmap_id, fourcc("dimg"), + {primary_image->get_id(), gain_map_image->get_id()}); + + return Error::Ok; +} +#endif + Error HeifContext::encode_thumbnail(const std::shared_ptr& image, struct heif_encoder* encoder, diff --git a/libheif/context.h b/libheif/context.h index 6e88d85022..f0a2dd4947 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -37,6 +37,9 @@ #include "box.h" // only for color_profile, TODO: maybe move the color_profiles to its own header #include "region.h" +#if WITH_EXPERIMENTAL_GAIN_MAP +#include "gain_map_metadata.h" +#endif class HeifContext; @@ -52,6 +55,9 @@ class ImageMetadata public: heif_item_id item_id; std::string item_type; // e.g. "Exif" +#if WITH_EXPERIMENTAL_GAIN_MAP + std::string item_name; +#endif std::string content_type; std::string item_uri_type; std::vector m_data; @@ -359,6 +365,10 @@ class HeifContext : public ErrorBuffer } std::shared_ptr get_primary_image() { return m_primary_image; } +#if WITH_EXPERIMENTAL_GAIN_MAP + std::shared_ptr get_gain_map_image() { return m_gain_map_image; } + std::shared_ptr get_gain_map_metadata() { return m_gain_map_metadata; } +#endif bool is_image(heif_item_id ID) const; @@ -454,6 +464,17 @@ class HeifContext : public ErrorBuffer heif_property_id add_property(heif_item_id targetItem, std::shared_ptr property, bool essential); +#if WITH_EXPERIMENTAL_GAIN_MAP + void add_altr_property(heif_item_id id); + + Error add_tmap_box(const std::vector& metadata, + heif_item_id &item_id); + + Error link_gain_map(const std::shared_ptr& primary_image, + const std::shared_ptr& gain_map_image, + const heif_item_id tmap_id); +#endif + // --- region items void add_region_item(std::shared_ptr region_item) @@ -484,6 +505,11 @@ class HeifContext : public ErrorBuffer // TODO: stable indices are obsolet now... std::vector> m_top_level_images; +#if WITH_EXPERIMENTAL_GAIN_MAP + std::shared_ptr m_gain_map_image; + std::shared_ptr m_gain_map_metadata; +#endif + std::shared_ptr m_primary_image; // shortcut to primary image std::shared_ptr m_heif_file; diff --git a/libheif/gain_map_metadata.cc b/libheif/gain_map_metadata.cc new file mode 100644 index 0000000000..85f84887da --- /dev/null +++ b/libheif/gain_map_metadata.cc @@ -0,0 +1,219 @@ +/* + * 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 "gain_map_metadata.h" + +Error streamWriteU8(std::vector &data, uint8_t value) { + data.push_back(value); + return Error::Ok; +} + +Error streamWriteU32(std::vector &data, uint32_t value) { + data.push_back((value >> 24) & 0xff); + data.push_back((value >> 16) & 0xff); + data.push_back((value >> 8) & 0xff); + data.push_back(value & 0xff); + return Error::Ok; +} + +Error streamReadU8(const std::vector &data, uint8_t &value, size_t &pos) { + if (pos >= data.size()) { + return Error(heif_error_Invalid_input, heif_suberror_End_of_data); + } + value = data[pos++]; + return Error::Ok; +} + +Error streamReadU32(const std::vector &data, uint32_t &value, size_t &pos) { + if (pos >= data.size() - 3) { + return Error(heif_error_Invalid_input, heif_suberror_End_of_data); + } + value = (data[pos] << 24 | + data[pos + 1] << 16 | + data[pos + 2] << 8 | + data[pos + 3]); + pos += 4; + return Error::Ok; +} + +Error GainMapMetadata::prepare_gain_map_metadata(const GainMapMetadata* metadata, + std::vector &data) { + if (metadata == nullptr) { + return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument); + } + const uint8_t version = 0; + streamWriteU8(data, version); + + uint8_t flags = 0u; + // Always write three channels for now for simplicity. + // TODO(maryla): the draft says that this specifies the count of channels of the + // gain map. But tone mapping is done in RGB space so there are always three + // channels, even if the gain map is grayscale. Should this be revised? + const bool allChannelsIdentical = + metadata->gainMapMinN[0] == metadata->gainMapMinN[1] && metadata->gainMapMinN[0] == metadata->gainMapMinN[2] && + metadata->gainMapMinD[0] == metadata->gainMapMinD[1] && metadata->gainMapMinD[0] == metadata->gainMapMinD[2] && + metadata->gainMapMaxN[0] == metadata->gainMapMaxN[1] && metadata->gainMapMaxN[0] == metadata->gainMapMaxN[2] && + metadata->gainMapMaxD[0] == metadata->gainMapMaxD[1] && metadata->gainMapMaxD[0] == metadata->gainMapMaxD[2] && + metadata->gainMapGammaN[0] == metadata->gainMapGammaN[1] && metadata->gainMapGammaN[0] == metadata->gainMapGammaN[2] && + metadata->gainMapGammaD[0] == metadata->gainMapGammaD[1] && metadata->gainMapGammaD[0] == metadata->gainMapGammaD[2] && + metadata->baseOffsetN[0] == metadata->baseOffsetN[1] && metadata->baseOffsetN[0] == metadata->baseOffsetN[2] && + metadata->baseOffsetD[0] == metadata->baseOffsetD[1] && metadata->baseOffsetD[0] == metadata->baseOffsetD[2] && + metadata->alternateOffsetN[0] == metadata->alternateOffsetN[1] && + metadata->alternateOffsetN[0] == metadata->alternateOffsetN[2] && + metadata->alternateOffsetD[0] == metadata->alternateOffsetD[1] && + metadata->alternateOffsetD[0] == metadata->alternateOffsetD[2]; + const uint8_t channelCount = allChannelsIdentical ? 1u : 3u; + + if (channelCount == 3) { + flags |= 1; + } + if (metadata->useBaseColorSpace) { + flags |= 2; + } + if (metadata->backwardDirection) { + flags |= 4; + } + const uint32_t denom = metadata->baseHdrHeadroomD; + bool useCommonDenominator = metadata->baseHdrHeadroomD == denom && metadata->alternateHdrHeadroomD == denom; + for (int c = 0; c < channelCount; ++c) { + useCommonDenominator = useCommonDenominator && metadata->gainMapMinD[c] == denom && metadata->gainMapMaxD[c] == denom && + metadata->gainMapGammaD[c] == denom && metadata->baseOffsetD[c] == denom && + metadata->alternateOffsetD[c] == denom; + } + if (useCommonDenominator) { + flags |= 8; + } + streamWriteU8(data, flags); + + if (useCommonDenominator) { + streamWriteU32(data, denom); + streamWriteU32(data, metadata->baseHdrHeadroomN); + streamWriteU32(data, metadata->alternateHdrHeadroomN); + for (int c = 0; c < channelCount; ++c) { + streamWriteU32(data, (uint32_t)metadata->gainMapMinN[c]); + streamWriteU32(data, (uint32_t)metadata->gainMapMaxN[c]); + streamWriteU32(data, metadata->gainMapGammaN[c]); + streamWriteU32(data, (uint32_t)metadata->baseOffsetN[c]); + streamWriteU32(data, (uint32_t)metadata->alternateOffsetN[c]); + } + } else { + streamWriteU32(data, metadata->baseHdrHeadroomN); + streamWriteU32(data, metadata->baseHdrHeadroomD); + streamWriteU32(data, metadata->alternateHdrHeadroomN); + streamWriteU32(data, metadata->alternateHdrHeadroomD); + for (int c = 0; c < channelCount; ++c) { + streamWriteU32(data, (uint32_t)metadata->gainMapMinN[c]); + streamWriteU32(data, metadata->gainMapMinD[c]); + streamWriteU32(data, (uint32_t)metadata->gainMapMaxN[c]); + streamWriteU32(data, metadata->gainMapMaxD[c]); + streamWriteU32(data, metadata->gainMapGammaN[c]); + streamWriteU32(data, metadata->gainMapGammaD[c]); + streamWriteU32(data, (uint32_t)metadata->baseOffsetN[c]); + streamWriteU32(data, metadata->baseOffsetD[c]); + streamWriteU32(data, (uint32_t)metadata->alternateOffsetN[c]); + streamWriteU32(data, metadata->alternateOffsetD[c]); + } + } + + return Error::Ok; +} + +Error GainMapMetadata::parse_gain_map_metadata(const std::vector &data, + GainMapMetadata* metadata) { + if (metadata == nullptr) { + return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument); + } + size_t pos = 0; + + uint8_t version = 0xff; + streamReadU8(data, version, pos); + if (version != 0) { + return Error(heif_error_Invalid_input, + heif_suberror_Unsupported_data_version, + "Box[tmap] has unsupported version"); + } + + uint8_t flags = 0xff; + streamReadU8(data, flags, pos); + uint8_t channelCount = (flags & 1) * 2 + 1; + if (!(channelCount == 1 || channelCount == 3)) { + return Error(heif_error_Invalid_input, + heif_suberror_Unsupported_data_version, + "Gain map image must have either 1 or 3 channels"); + } + metadata->useBaseColorSpace = (flags & 2) != 0; + metadata->backwardDirection = (flags & 4) != 0; + const bool useCommonDenominator = (flags & 8) != 0; + + if (useCommonDenominator) { + uint32_t commonDenominator; + streamReadU32(data, commonDenominator, pos); + + streamReadU32(data, metadata->baseHdrHeadroomN, pos); + metadata->baseHdrHeadroomD = commonDenominator; + streamReadU32(data, metadata->alternateHdrHeadroomN, pos); + metadata->alternateHdrHeadroomD = commonDenominator; + + for (int c = 0; c < channelCount; ++c) { + streamReadU32(data, metadata->gainMapMinN[c], pos); + metadata->gainMapMinD[c] = commonDenominator; + streamReadU32(data, metadata->gainMapMaxN[c], pos); + metadata->gainMapMaxD[c] = commonDenominator; + streamReadU32(data, metadata->gainMapGammaN[c], pos); + metadata->gainMapGammaD[c] = commonDenominator; + streamReadU32(data, metadata->baseOffsetN[c], pos); + metadata->baseOffsetD[c] = commonDenominator; + streamReadU32(data, metadata->alternateOffsetN[c], pos); + metadata->alternateOffsetD[c] = commonDenominator; + } + } else { + streamReadU32(data, metadata->baseHdrHeadroomN, pos); + streamReadU32(data, metadata->baseHdrHeadroomD, pos); + streamReadU32(data, metadata->alternateHdrHeadroomN, pos); + streamReadU32(data, metadata->alternateHdrHeadroomD, pos); + for (int c = 0; c < channelCount; ++c) { + streamReadU32(data, metadata->gainMapMinN[c], pos); + streamReadU32(data, metadata->gainMapMinD[c], pos); + streamReadU32(data, metadata->gainMapMaxN[c], pos); + streamReadU32(data, metadata->gainMapMaxD[c], pos); + streamReadU32(data, metadata->gainMapGammaN[c], pos); + streamReadU32(data, metadata->gainMapGammaD[c], pos); + streamReadU32(data, metadata->baseOffsetN[c], pos); + streamReadU32(data, metadata->baseOffsetD[c], pos); + streamReadU32(data, metadata->alternateOffsetN[c], pos); + streamReadU32(data, metadata->alternateOffsetD[c], pos); + } + } + // Fill the remaining values by copying those from the first channel. + for (int c = channelCount; c < 3; ++c) { + metadata->gainMapMinN[c] = metadata->gainMapMinN[0]; + metadata->gainMapMinD[c] = metadata->gainMapMinD[0]; + metadata->gainMapMaxN[c] = metadata->gainMapMaxN[0]; + metadata->gainMapMaxD[c] = metadata->gainMapMaxD[0]; + metadata->gainMapGammaN[c] = metadata->gainMapGammaN[0]; + metadata->gainMapGammaD[c] = metadata->gainMapGammaD[0]; + metadata->baseOffsetN[c] = metadata->baseOffsetN[0]; + metadata->baseOffsetD[c] = metadata->baseOffsetD[0]; + metadata->alternateOffsetN[c] = metadata->alternateOffsetN[0]; + metadata->alternateOffsetD[c] = metadata->alternateOffsetD[0]; + } + + return Error::Ok; +} \ No newline at end of file diff --git a/libheif/gain_map_metadata.h b/libheif/gain_map_metadata.h new file mode 100644 index 0000000000..c470336a7e --- /dev/null +++ b/libheif/gain_map_metadata.h @@ -0,0 +1,76 @@ +/* + * HEIF codec. + * Copyright (c) 2017 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_GAIN_MAP_METADATA_H +#define LIBHEIF_GAIN_MAP_METADATA_H + +#include "error.h" + +// Gain map metadata, for tone mapping between SDR and HDR. +class GainMapMetadata { +public: + uint32_t gainMapMinN[3]; + uint32_t gainMapMinD[3]; + uint32_t gainMapMaxN[3]; + uint32_t gainMapMaxD[3]; + uint32_t gainMapGammaN[3]; + uint32_t gainMapGammaD[3]; + + uint32_t baseOffsetN[3]; + uint32_t baseOffsetD[3]; + uint32_t alternateOffsetN[3]; + uint32_t alternateOffsetD[3]; + + uint32_t baseHdrHeadroomN; + uint32_t baseHdrHeadroomD; + uint32_t alternateHdrHeadroomN; + uint32_t alternateHdrHeadroomD; + + bool backwardDirection; + bool useBaseColorSpace; + + static Error prepare_gain_map_metadata(const GainMapMetadata* gain_map_metadata, + std::vector &data); + + static Error parse_gain_map_metadata(const std::vector &data, + GainMapMetadata* gain_map_metadata); + + void dump() const { + printf("GAIN MAP METADATA: \n"); + printf("min numerator: %d, %d, %d\n", gainMapMinN[0], gainMapMinN[1], gainMapMinN[2]); + printf("min denominator: %d, %d, %d\n", gainMapMinD[0], gainMapMinD[1], gainMapMinD[2]); + printf("max numerator: %d, %d, %d\n", gainMapMaxN[0], gainMapMaxN[1], gainMapMaxN[2]); + printf("max denominator: %d, %d, %d\n", gainMapMaxD[0], gainMapMaxD[1], gainMapMaxD[2]); + printf("gamma numerator: %d, %d, %d\n", gainMapGammaN[0], gainMapGammaN[1], gainMapGammaN[2]); + printf("gamma denominator: %d, %d, %d\n", gainMapGammaD[0], gainMapGammaD[1], gainMapGammaD[2]); + printf("SDR offset numerator: %d, %d, %d\n", baseOffsetN[0], baseOffsetN[1], baseOffsetN[2]); + printf("SDR offset denominator: %d, %d, %d\n", baseOffsetD[0], baseOffsetD[1], baseOffsetD[2]); + printf("HDR offset numerator: %d, %d, %d\n", alternateOffsetN[0], alternateOffsetN[1], alternateOffsetN[2]); + printf("HDR offset denominator: %d, %d, %d\n", alternateOffsetD[0], alternateOffsetD[1], alternateOffsetD[2]); + printf("base HDR head room numerator: %d\n", baseHdrHeadroomN); + printf("base HDR head room denominator: %d\n", baseHdrHeadroomD); + printf("alternate HDR head room numerator: %d\n", alternateHdrHeadroomN); + printf("alternate HDR head room denominator: %d\n", alternateHdrHeadroomD); + printf("backwardDirection: %s\n", backwardDirection ? "true" : "false"); + printf("use base color space: %s\n", useBaseColorSpace ? "true" : "false"); + } +}; + +#endif From 3fe697ae23c349a6b6e1043862933abca8a69dfe Mon Sep 17 00:00:00 2001 From: DichenZhang1 <140119224+DichenZhang1@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:08:18 -0800 Subject: [PATCH 3/4] Update heif.cc --- libheif/heif.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libheif/heif.cc b/libheif/heif.cc index f3ef320a10..ad33ded7ae 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -2804,7 +2804,7 @@ struct heif_error heif_context_encode_gain_map_image(struct heif_context* ctx, } std::shared_ptr gain_map_image; - printf("[dichenzhang] heif_context_encode_gain_map_image() %d\n", heif_image_input_class_gain_map); + error = ctx->context->encode_image(input_image->image, encoder, options, From 739566745af9bee86c633ce75a8b6808127a037c Mon Sep 17 00:00:00 2001 From: Dichen Zhang Date: Thu, 18 Apr 2024 22:16:35 +0000 Subject: [PATCH 4/4] Change name from `GainMapMetadata` to `heif_gain_map_metadata` Add verification code in heif_enc and heif_info --- examples/heif_enc.cc | 29 ++++++++++++++++++- examples/heif_info.cc | 20 +++++++++++++ libheif/CMakeLists.txt | 4 +-- libheif/context.h | 2 +- libheif/heif.cc | 8 ++--- libheif/heif.h | 6 ++-- ...{gain_map_metadata.cc => heif_gain_map.cc} | 10 +++---- .../{gain_map_metadata.h => heif_gain_map.h} | 11 ++++--- 8 files changed, 68 insertions(+), 22 deletions(-) rename libheif/{gain_map_metadata.cc => heif_gain_map.cc} (96%) rename libheif/{gain_map_metadata.h => heif_gain_map.h} (92%) diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc index 754f566379..5798295432 100644 --- a/examples/heif_enc.cc +++ b/examples/heif_enc.cc @@ -53,6 +53,9 @@ #include "benchmark.h" #include "libheif/exif.h" #include "common.h" +#if WITH_EXPERIMENTAL_GAIN_MAP +#include "libheif/heif_gain_map.h" +#endif int master_alpha = 1; int thumb_alpha = 1; @@ -724,10 +727,18 @@ int main(int argc, char** argv) struct heif_error error; std::shared_ptr primary_image; + struct heif_image_handle* handle; for (; optind < argc; optind++) { std::string input_filename = argv[optind]; +#if WITH_EXPERIMENTAL_GAIN_MAP + bool is_gain_map = false; + if (input_filename.find("gain_map") != std::string::npos) { + is_gain_map = true; + } +#endif + if (output_filename.empty()) { std::string filename_without_suffix; std::string::size_type dot_position = input_filename.find_last_of('.'); @@ -865,12 +876,28 @@ int main(int argc, char** argv) } - struct heif_image_handle* handle; error = heif_context_encode_image(context.get(), image.get(), encoder, options, &handle); + +#if WITH_EXPERIMENTAL_GAIN_MAP + if (is_gain_map) { + struct heif_image_handle* gain_map_image_handle; + heif_gain_map_metadata gmm; + + error = heif_context_encode_gain_map_image(context.get(), + image.get(), + handle, + encoder, + nullptr, + &gmm, + &gain_map_image_handle); + heif_image_handle_release(gain_map_image_handle); + } +#endif + if (error.code != 0) { heif_encoding_options_free(options); std::cerr << "Could not encode HEIF/AVIF file: " << error.message << "\n"; diff --git a/examples/heif_info.cc b/examples/heif_info.cc index 2314430c4f..fb0df82faf 100644 --- a/examples/heif_info.cc +++ b/examples/heif_info.cc @@ -48,6 +48,9 @@ #include #include #include "common.h" +#if WITH_EXPERIMENTAL_GAIN_MAP +#include "libheif/heif_gain_map.h" +#endif /* @@ -687,6 +690,23 @@ int main(int argc, char** argv) } } +#if WITH_EXPERIMENTAL_GAIN_MAP + // gain map pixels + heif_image_handle* gain_map_image_handle; + heif_image* gain_map_image; + err = heif_context_get_gain_map_image_handle(ctx.get(), &gain_map_image_handle); + if (err.code != heif_error_Ok) { + goto __exit; + } + heif_decode_image(gain_map_image_handle, &gain_map_image, heif_colorspace_undefined, heif_chroma_undefined, nullptr); + + // gain map metadata + heif_gain_map_metadata gmm; + heif_image_get_gain_map_metadata(ctx.get(), &gmm); + gmm.dump(); +__exit: +#endif + heif_image_release(image); heif_image_handle_release(handle); diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 4b1f4a1d62..80ab3ef9ac 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -153,8 +153,8 @@ endif () if (WITH_EXPERIMENTAL_GAIN_MAP) target_compile_definitions(heif PUBLIC WITH_EXPERIMENTAL_GAIN_MAP=1) target_sources(heif PRIVATE - gain_map_metadata.h - gain_map_metadata.cc) + heif_gain_map.h + heif_gain_map.cc) endif () write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion) diff --git a/libheif/context.h b/libheif/context.h index f0a2dd4947..6c7cec4a71 100644 --- a/libheif/context.h +++ b/libheif/context.h @@ -38,7 +38,7 @@ #include "region.h" #if WITH_EXPERIMENTAL_GAIN_MAP -#include "gain_map_metadata.h" +#include "heif_gain_map.h" #endif class HeifContext; diff --git a/libheif/heif.cc b/libheif/heif.cc index e46c442bb7..f56c4e70a2 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -1406,7 +1406,7 @@ uint8_t* heif_image_get_plane(struct heif_image* image, #if WITH_EXPERIMENTAL_GAIN_MAP struct heif_error heif_image_get_gain_map_metadata(heif_context* ctx, - GainMapMetadata* out_gm_metadata) { + heif_gain_map_metadata* out_gm_metadata) { if (!out_gm_metadata) { Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument); @@ -1415,7 +1415,7 @@ struct heif_error heif_image_get_gain_map_metadata(heif_context* ctx, std::shared_ptr metadata = ctx->context->get_gain_map_metadata(); - GainMapMetadata::parse_gain_map_metadata(metadata->m_data, out_gm_metadata); + heif_gain_map_metadata::parse_gain_map_metadata(metadata->m_data, out_gm_metadata); return Error::Ok.error_struct(ctx->context.get()); } @@ -2774,7 +2774,7 @@ struct heif_error heif_context_encode_gain_map_image(struct heif_context* ctx, const struct heif_image_handle* primary_image_handle, struct heif_encoder* encoder, const struct heif_encoding_options* input_options, - const struct GainMapMetadata* gain_map_metadata, + const struct heif_gain_map_metadata* gain_map_metadata, struct heif_image_handle** out_image_handle) { if (!encoder) { @@ -2789,7 +2789,7 @@ struct heif_error heif_context_encode_gain_map_image(struct heif_context* ctx, ctx->context->add_altr_property(primary_image_handle->image->get_id()); std::vector metadata; - error = GainMapMetadata::prepare_gain_map_metadata(gain_map_metadata, metadata); + error = heif_gain_map_metadata::prepare_gain_map_metadata(gain_map_metadata, metadata); if (error != Error::Ok) { return error.error_struct(ctx->context.get()); } diff --git a/libheif/heif.h b/libheif/heif.h index fa1d8005ac..332878f4eb 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -96,7 +96,7 @@ struct heif_context; struct heif_image_handle; struct heif_image; #if WITH_EXPERIMENTAL_GAIN_MAP -struct GainMapMetadata; +struct heif_gain_map_metadata; #endif @@ -1245,7 +1245,7 @@ const char* heif_image_handle_get_metadata_item_uri_type(const struct heif_image // Get a pointer to the gain map metadata. LIBHEIF_API struct heif_error heif_image_get_gain_map_metadata(heif_context* ctx, - GainMapMetadata* out_gm_metadata); + heif_gain_map_metadata* out_gm_metadata); #endif // ------------------------- color profiles ------------------------- @@ -2070,7 +2070,7 @@ struct heif_error heif_context_encode_gain_map_image(struct heif_context*, const struct heif_image_handle* primary_image_handle, struct heif_encoder* encoder, const struct heif_encoding_options* options, - const struct GainMapMetadata* gain_map_metadata, + const struct heif_gain_map_metadata* gain_map_metadata, struct heif_image_handle** out_image_handle); #endif diff --git a/libheif/gain_map_metadata.cc b/libheif/heif_gain_map.cc similarity index 96% rename from libheif/gain_map_metadata.cc rename to libheif/heif_gain_map.cc index 85f84887da..8d1c80124c 100644 --- a/libheif/gain_map_metadata.cc +++ b/libheif/heif_gain_map.cc @@ -18,7 +18,7 @@ * along with libheif. If not, see . */ -#include "gain_map_metadata.h" +#include "heif_gain_map.h" Error streamWriteU8(std::vector &data, uint8_t value) { data.push_back(value); @@ -53,8 +53,8 @@ Error streamReadU32(const std::vector &data, uint32_t &value, size_t &p return Error::Ok; } -Error GainMapMetadata::prepare_gain_map_metadata(const GainMapMetadata* metadata, - std::vector &data) { +Error heif_gain_map_metadata::prepare_gain_map_metadata(const heif_gain_map_metadata* metadata, + std::vector &data) { if (metadata == nullptr) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument); } @@ -135,8 +135,8 @@ Error GainMapMetadata::prepare_gain_map_metadata(const GainMapMetadata* metadata return Error::Ok; } -Error GainMapMetadata::parse_gain_map_metadata(const std::vector &data, - GainMapMetadata* metadata) { +Error heif_gain_map_metadata::parse_gain_map_metadata(const std::vector &data, + heif_gain_map_metadata* metadata) { if (metadata == nullptr) { return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument); } diff --git a/libheif/gain_map_metadata.h b/libheif/heif_gain_map.h similarity index 92% rename from libheif/gain_map_metadata.h rename to libheif/heif_gain_map.h index c3a131e8b2..98703824a2 100644 --- a/libheif/gain_map_metadata.h +++ b/libheif/heif_gain_map.h @@ -18,15 +18,14 @@ * along with libheif. If not, see . */ -#ifndef LIBHEIF_GAIN_MAP_METADATA_H -#define LIBHEIF_GAIN_MAP_METADATA_H +#ifndef LIBHEIF_HEIF_GAIN_MAP_H +#define LIBHEIF_HEIF_GAIN_MAP_H #include "error.h" #include // Gain map metadata, for tone mapping between SDR and HDR. -class GainMapMetadata { -public: +struct heif_gain_map_metadata { uint32_t gainMapMinN[3]; uint32_t gainMapMinD[3]; uint32_t gainMapMaxN[3]; @@ -47,11 +46,11 @@ class GainMapMetadata { bool backwardDirection; bool useBaseColorSpace; - static Error prepare_gain_map_metadata(const GainMapMetadata* gain_map_metadata, + static Error prepare_gain_map_metadata(const heif_gain_map_metadata* gain_map_metadata, std::vector &data); static Error parse_gain_map_metadata(const std::vector &data, - GainMapMetadata* gain_map_metadata); + heif_gain_map_metadata* gain_map_metadata); void dump() const { printf("GAIN MAP METADATA: \n");