Skip to content

Commit dce9a85

Browse files
committed
Add support for handling 'tmap' items
Gain map technology offers a way to create an image (henceforth referred as derived image) from one base input image and a secondary input image called as a gainmap input image. Reconstruction is done by applying the gain map to the base image according to iso 21496-1 section 6. This change provides API to read, write base input image, gain map input image, gain map metadata. Third party applications can make use of this API to render only base image or reconstruct the derived image and render the same. By default this API is disabled and can be enabled at configure time using option -DWITH_EXPERIMENTAL_GAIN_MAP=1
1 parent 4a3f74b commit dce9a85

File tree

14 files changed

+391
-1
lines changed

14 files changed

+391
-1
lines changed

CMakePresets.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"WITH_VVDEC_PLUGIN" : "OFF",
5555
"WITH_VVENC" : "ON",
5656
"WITH_VVENC_PLUGIN" : "OFF",
57+
"WITH_EXPERIMENTAL_GAIN_MAP" : "OFF",
5758

5859
"WITH_REDUCED_VISIBILITY" : "OFF",
5960
"WITH_HEADER_COMPRESSION" : "ON",
@@ -110,6 +111,7 @@
110111
"WITH_VVDEC_PLUGIN" : "ON",
111112
"WITH_VVENC" : "ON",
112113
"WITH_VVENC_PLUGIN" : "ON",
114+
"WITH_EXPERIMENTAL_GAIN_MAP" : "OFF",
113115

114116
"WITH_REDUCED_VISIBILITY" : "ON",
115117
"WITH_HEADER_COMPRESSION" : "ON",
@@ -148,6 +150,7 @@
148150
"WITH_UVG266" : "OFF",
149151
"WITH_VVDEC" : "OFF",
150152
"WITH_VVENC" : "OFF",
153+
"WITH_EXPERIMENTAL_GAIN_MAP" : "OFF",
151154

152155
"WITH_REDUCED_VISIBILITY" : "ON",
153156
"WITH_HEADER_COMPRESSION" : "OFF",

libheif/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ if (ENABLE_EXPERIMENTAL_MINI_FORMAT)
249249
mini.cc)
250250
endif ()
251251

252+
if (WITH_EXPERIMENTAL_GAIN_MAP)
253+
target_compile_definitions(heif PUBLIC WITH_EXPERIMENTAL_GAIN_MAP=1)
254+
endif ()
255+
252256
write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion)
253257

254258
install(TARGETS heif EXPORT ${PROJECT_NAME}-config

libheif/api/libheif/heif.cc

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,80 @@ struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, he
657657
}
658658

659659

660+
#if WITH_EXPERIMENTAL_GAIN_MAP
661+
heif_error heif_context_get_gain_map_image_handle(heif_context* ctx, heif_image_handle** img) {
662+
if (!img) {
663+
Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument);
664+
return err.error_struct(ctx->context.get());
665+
}
666+
667+
std::shared_ptr<ImageItem> gain_map_image = ctx->context->get_gain_map_image();
668+
if (!gain_map_image) {
669+
Error err(heif_error_Invalid_input, heif_suberror_No_item_data);
670+
return err.error_struct(ctx->context.get());
671+
}
672+
673+
*img = new heif_image_handle();
674+
(*img)->image = std::move(gain_map_image);
675+
(*img)->context = ctx->context;
676+
677+
return Error::Ok.error_struct(ctx->context.get());
678+
}
679+
680+
struct heif_error heif_context_get_tmap_nclx_color_profile(
681+
struct heif_context* ctx, struct heif_color_profile_nclx** out_data) {
682+
if (!out_data) {
683+
Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument);
684+
return err.error_struct(ctx->context.get());
685+
}
686+
*out_data = nullptr;
687+
Error err = ctx->context->get_tmap_color_profile(out_data);
688+
return err.error_struct(ctx->context.get());
689+
}
690+
691+
size_t heif_context_get_gain_map_metadata_size(struct heif_context* ctx) {
692+
std::shared_ptr<ImageMetadata> metadata = ctx->context->get_gain_map_metadata();
693+
if (metadata) {
694+
return metadata->m_data.size() - 1;
695+
} else {
696+
return 0;
697+
}
698+
}
699+
700+
struct heif_error heif_context_get_gain_map_metadata(struct heif_context* ctx, void* out_data) {
701+
if (!out_data) {
702+
Error err(heif_error_Usage_error, heif_suberror_Null_pointer_argument);
703+
return err.error_struct(ctx->context.get());
704+
}
705+
706+
std::shared_ptr<ImageMetadata> metadata = ctx->context->get_gain_map_metadata();
707+
if (!metadata) {
708+
Error err(heif_error_Invalid_input, heif_suberror_No_item_data);
709+
return err.error_struct(ctx->context.get());
710+
}
711+
712+
uint8_t version = 0xff;
713+
size_t pos = 0;
714+
std::vector<uint8_t>& buffer = metadata->m_data;
715+
716+
if (pos >= buffer.size()) {
717+
Error err(heif_error_Invalid_input, heif_suberror_End_of_data);
718+
return err.error_struct(ctx->context.get());
719+
}
720+
version = buffer[pos++];
721+
if (version != 0) {
722+
Error err(heif_error_Invalid_input, heif_suberror_Unsupported_data_version,
723+
"Box[tmap] has unsupported version");
724+
return err.error_struct(ctx->context.get());
725+
}
726+
727+
memcpy(out_data, buffer.data() + pos, buffer.size() - pos);
728+
729+
return heif_error_success;
730+
}
731+
#endif
732+
733+
660734
int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id)
661735
{
662736
const std::vector<std::shared_ptr<ImageItem>> images = ctx->context->get_top_level_images(true);
@@ -3442,6 +3516,115 @@ struct heif_error heif_context_encode_image(struct heif_context* ctx,
34423516
}
34433517

34443518

3519+
#if WITH_EXPERIMENTAL_GAIN_MAP
3520+
struct heif_error heif_context_encode_gain_map_image(
3521+
struct heif_context* ctx, const struct heif_image* alternate_image,
3522+
const struct heif_image_handle* base_image_handle, struct heif_encoder* encoder,
3523+
const struct heif_encoding_options* input_options, const uint8_t* gain_map_data,
3524+
int gain_map_data_len, const struct heif_color_profile_nclx* targetNclx,
3525+
struct heif_image_handle** out_image_handle) {
3526+
if (!encoder) {
3527+
return Error(heif_error_Usage_error, heif_suberror_Null_pointer_argument)
3528+
.error_struct(ctx->context.get());
3529+
}
3530+
3531+
if (out_image_handle) {
3532+
*out_image_handle = nullptr;
3533+
}
3534+
3535+
if (gain_map_data_len <= 0) {
3536+
return Error(heif_error_Invalid_input, heif_suberror_Invalid_parameter_value)
3537+
.error_struct(ctx->context.get());
3538+
}
3539+
3540+
// --- write tmap item
3541+
std::vector<uint8_t> metadata;
3542+
metadata.push_back(0); // version = 0
3543+
for (int i = 0; i < gain_map_data_len; i++) {
3544+
metadata.push_back(gain_map_data[i]);
3545+
}
3546+
heif_item_id tmap_item_id = -1;
3547+
ctx->context->add_tmap_item(metadata, tmap_item_id);
3548+
3549+
std::vector<std::shared_ptr<Box>> properties;
3550+
3551+
// --- write ISPE property for tmap item
3552+
std::shared_ptr<Box_ispe> ispe = std::make_shared<Box_ispe>();
3553+
ispe->set_size(base_image_handle->image->get_ispe_width(),
3554+
base_image_handle->image->get_ispe_height());
3555+
3556+
properties.push_back(ispe);
3557+
3558+
// --- write PIXI property for tmap item
3559+
std::shared_ptr<Box_pixi> pixi = std::make_shared<Box_pixi>();
3560+
pixi->add_channel_bits(10);
3561+
pixi->add_channel_bits(10);
3562+
pixi->add_channel_bits(10);
3563+
3564+
properties.push_back(pixi);
3565+
3566+
// --- write COLR property for tmap item
3567+
if (targetNclx != nullptr) {
3568+
std::shared_ptr<Box_colr> colr = std::make_shared<Box_colr>();
3569+
auto target_nclx_profile = std::make_shared<color_profile_nclx>();
3570+
target_nclx_profile->set_colour_primaries(targetNclx->color_primaries);
3571+
target_nclx_profile->set_transfer_characteristics(targetNclx->transfer_characteristics);
3572+
target_nclx_profile->set_matrix_coefficients(targetNclx->matrix_coefficients);
3573+
target_nclx_profile->set_full_range_flag(targetNclx->full_range_flag);
3574+
colr->set_color_profile(target_nclx_profile);
3575+
3576+
properties.push_back(colr);
3577+
}
3578+
// set item properties
3579+
for (auto& propertyBox : properties) {
3580+
int index =
3581+
ctx->context->get_heif_file()->get_ipco_box()->find_or_append_child_box(propertyBox);
3582+
ctx->context->get_heif_file()->get_ipma_box()->add_property_for_item_ID(
3583+
tmap_item_id,
3584+
Box_ipma::PropertyAssociation{propertyBox->is_essential(), uint16_t(index + 1)});
3585+
}
3586+
3587+
heif_encoding_options options;
3588+
set_default_encoding_options(options);
3589+
if (input_options != nullptr) {
3590+
copy_options(options, *input_options);
3591+
}
3592+
3593+
auto gainmap_encoding_result = ctx->context->encode_image(
3594+
alternate_image->image, encoder, options, heif_image_input_class_gain_map);
3595+
if (gainmap_encoding_result.error) {
3596+
return gainmap_encoding_result.error.error_struct(ctx->context.get());
3597+
}
3598+
3599+
std::shared_ptr<ImageItem> gain_map_image = *gainmap_encoding_result;
3600+
Error error = ctx->context->link_gain_map(base_image_handle->image, gain_map_image, tmap_item_id);
3601+
if (error != Error::Ok) {
3602+
return error.error_struct(ctx->context.get());
3603+
}
3604+
3605+
if (out_image_handle) {
3606+
*out_image_handle = new heif_image_handle;
3607+
(*out_image_handle)->image = std::move(gain_map_image);
3608+
(*out_image_handle)->context = ctx->context;
3609+
}
3610+
3611+
// --- generate altr box
3612+
auto altr_box = std::make_shared<Box_EntityToGroup>();
3613+
altr_box->set_short_type(fourcc("altr"));
3614+
altr_box->set_group_id(ctx->context->get_heif_file()->get_unused_item_id());
3615+
3616+
std::vector<heif_item_id> ids;
3617+
ids.push_back(tmap_item_id);
3618+
ids.push_back(base_image_handle->image->get_id());
3619+
3620+
altr_box->set_item_ids(ids);
3621+
ctx->context->get_heif_file()->add_entity_group_box(altr_box);
3622+
3623+
return heif_error_success;
3624+
}
3625+
#endif
3626+
3627+
34453628
struct heif_error heif_context_encode_grid(struct heif_context* ctx,
34463629
struct heif_image** tiles,
34473630
uint16_t columns,

libheif/api/libheif/heif.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,13 @@ typedef uint32_t heif_brand2;
884884
*/
885885
#define heif_brand2_1pic heif_fourcc('1','p','i','c')
886886

887+
/**
888+
* HEIF tone map brand (`tmap`).
889+
*
890+
* This is a compatible brand indicating the file contains a gainmap image.
891+
*/
892+
#define heif_brand2_tmap heif_fourcc('t', 'm', 'a', 'p')
893+
887894
// input data should be at least 12 bytes
888895
LIBHEIF_API
889896
heif_brand2 heif_read_main_brand(const uint8_t* data, int len);
@@ -2621,6 +2628,41 @@ LIBHEIF_API
26212628
int heif_encoder_descriptor_supportes_lossless_compression(const struct heif_encoder_descriptor*);
26222629

26232630

2631+
2632+
// ====================================================================================================
2633+
// Gain Map API
2634+
2635+
#if WITH_EXPERIMENTAL_GAIN_MAP
2636+
2637+
// Get a handle to the gain map image of the HEIF file. If no gain map image is available, this
2638+
// method will return heif_suberror_No_item_data.
2639+
LIBHEIF_API
2640+
struct heif_error heif_context_get_gain_map_image_handle(struct heif_context* ctx,
2641+
struct heif_image_handle** img);
2642+
2643+
LIBHEIF_API
2644+
size_t heif_context_get_gain_map_metadata_size(struct heif_context* ctx);
2645+
2646+
LIBHEIF_API
2647+
struct heif_error heif_context_get_gain_map_metadata(struct heif_context* ctx, void* out_data);
2648+
2649+
LIBHEIF_API
2650+
struct heif_error heif_context_get_tmap_nclx_color_profile(
2651+
struct heif_context* ctx, struct heif_color_profile_nclx** out_data);
2652+
2653+
// Compress the gain map image and write metadata.
2654+
// Returns a handle to the coded image in 'out_image_handle' unless out_image_handle = NULL.
2655+
LIBHEIF_API
2656+
struct heif_error heif_context_encode_gain_map_image(
2657+
struct heif_context* ctx, const struct heif_image* alternate_image,
2658+
const struct heif_image_handle* base_image_handle, struct heif_encoder* encoder,
2659+
const struct heif_encoding_options* input_options, const uint8_t* gain_map_data,
2660+
int gain_map_data_len, const struct heif_color_profile_nclx* targetNclx,
2661+
struct heif_image_handle** out_image_handle);
2662+
2663+
#endif
2664+
2665+
26242666
#ifdef __cplusplus
26252667
}
26262668
#endif

libheif/api/libheif/heif_plugin.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ enum heif_image_input_class
129129
heif_image_input_class_alpha = 2,
130130
heif_image_input_class_depth = 3,
131131
heif_image_input_class_thumbnail = 4
132+
#if WITH_EXPERIMENTAL_GAIN_MAP
133+
, heif_image_input_class_gain_map = 5
134+
#endif
132135
};
133136

134137

0 commit comments

Comments
 (0)