Error HeifContext::decode_and_paste_tile_image()

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;
}