in libheif/context.cc [2407:2539]
Error HeifContext::decode_and_paste_tile_image(heif_item_id tileID,
const std::shared_ptr<HeifPixelImage>& img,
heif_chroma out_chroma,
uint32_t x0, uint32_t y0,
const heif_decoding_options& options) const
{
std::shared_ptr<HeifPixelImage> tile_img;
Error err = decode_image_planar(tileID, tile_img, heif_colorspace_undefined, heif_chroma_undefined, options, false);
if (err != Error::Ok) {
return err;
}
const uint32_t w = img->get_width();
const uint32_t h = img->get_height();
// --- copy tile into output image
uint32_t src_width = tile_img->get_width();
uint32_t src_height = tile_img->get_height();
heif_chroma chroma = img->get_chroma_format();
if (chroma != tile_img->get_chroma_format()) {
return Error(heif_error_Invalid_input,
heif_suberror_Wrong_tile_image_chroma_format,
"Image tile has different chroma format than combined image");
}
// --- add alpha plane if we discovered a tile with alpha
if (tile_img->has_alpha() && !img->has_alpha()) {
#if ENABLE_PARALLEL_TILE_DECODING
// The mutex should probably be a member of heif_context, but since this is so infrequently locked, it probably doesn't matter.
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
if (!img->has_channel(heif_channel_Alpha)) // check again, after locking
#endif
{
int alpha_bpp = tile_img->get_bits_per_pixel(heif_channel_Alpha);
assert(alpha_bpp <= 16);
uint16_t alpha_default_value = static_cast<uint16_t>((1UL << alpha_bpp) - 1UL);
img->fill_new_plane(heif_channel_Alpha, alpha_default_value, w, h, alpha_bpp);
}
}
std::set<enum heif_channel> channels = tile_img->get_channel_set();
for (heif_channel channel : channels) {
int tile_stride;
uint8_t* tile_data = tile_img->get_plane(channel, &tile_stride);
int out_stride;
uint8_t* out_data = img->get_plane(channel, &out_stride);
int channel_w, channel_h, channel_x0, channel_y0 ;
if(channel == heif_channel_Cb || channel == heif_channel_Cr) {
switch(chroma) {
case heif_chroma_420 :
channel_w = (w+1)/2; channel_h = (h+1)/2; channel_x0 = (x0+1)/2; channel_y0 = (y0+1)/2 ;
break;
case heif_chroma_422 :
channel_w = (w+1)/2; channel_h = h; channel_x0 = (x0+1)/2; channel_y0 = y0;
break;
default:
channel_w = w; channel_h = h; channel_x0 = x0; channel_y0 = y0 ;
break;
}
}
else {
channel_w = w; channel_h = h; channel_x0 = x0; channel_y0 = y0 ;
}
if (channel_w <= channel_x0 || channel_h <= channel_y0) {
return Error(heif_error_Invalid_input,
heif_suberror_Invalid_grid_data);
}
if (img->get_bits_per_pixel(channel) != tile_img->get_bits_per_pixel(channel)) {
return Error(heif_error_Invalid_input,
heif_suberror_Wrong_tile_image_pixel_depth);
}
int plane_width = tile_img->get_width(channel);
int plane_height = tile_img->get_height(channel);
int copy_width = std::min(plane_width, channel_w - channel_x0);
int copy_height = std::min(plane_height, channel_h - channel_y0);
copy_width *= tile_img->get_storage_bits_per_pixel(channel) / 8;
int xs = channel_x0, ys = channel_y0;
xs *= tile_img->get_storage_bits_per_pixel(channel) / 8;
// deal with tile_img m_full_range_range
int bpp = tile_img->get_bits_per_pixel(channel);
int maxval = (1 << bpp) - 1;
float limited_range_offset = static_cast<float>(16 << (bpp - 8));
auto tile_profile_nclx = tile_img->get_color_profile_nclx();
bool full_range_flag = (tile_profile_nclx) ? tile_profile_nclx->get_full_range_flag() : true ;
int matrix_coeffs = (tile_profile_nclx ) ? tile_profile_nclx->get_matrix_coefficients() : 1;
if(tile_profile_nclx && (full_range_flag != true) && (matrix_coeffs != 0)) {
float convert_ratio = 0.0;
if(channel == heif_channel_Cb || channel == heif_channel_Cr) {
convert_ratio = 1.1429f;
}
else {
convert_ratio = 1.1689f;
}
for(int py = 0; py < copy_height; py++) {
for(int px = 0; px < copy_width; px++) {
float limit_value = static_cast<float>(*(tile_data + py * tile_stride + px));
float full_value = (limit_value - limited_range_offset) * convert_ratio ;
*(out_data + xs + (ys + py) * out_stride + px) = clip_f_u8(full_value);
}
}
}
else {
for (int py = 0; py < copy_height; py++) {
memcpy(out_data + xs + (ys + py) * out_stride,
tile_data + py * tile_stride,
copy_width);
}
}
}
return Error::Ok;
}