std::shared_ptr convert_colorspace()

in libheif/color-conversion/colorconversion.cc [487:596]


std::shared_ptr<HeifPixelImage> convert_colorspace(const std::shared_ptr<HeifPixelImage>& input,
                                                   heif_colorspace target_colorspace,
                                                   heif_chroma target_chroma,
                                                   const std::shared_ptr<const color_profile_nclx>& target_profile,
                                                   int output_bpp,
                                                   const heif_color_conversion_options& options)
{
  // --- check that input image is valid

  int width = input->get_width();
  int height = input->get_height();

  // alpha image should have full image resolution

//   if (input->has_channel(heif_channel_Alpha)) {
//     if (input->get_width(heif_channel_Alpha) != width ||
//         input->get_height(heif_channel_Alpha) != height) {
//       return nullptr;
//     }
//   }

  // check for valid target YCbCr chroma formats

  if (target_colorspace == heif_colorspace_YCbCr) {
    if (target_chroma != heif_chroma_420 &&
        target_chroma != heif_chroma_422 &&
        target_chroma != heif_chroma_444) {
      return nullptr;
    }
  }

  // --- prepare conversion

  ColorState input_state;
  input_state.colorspace = input->get_colorspace();
  input_state.chroma = input->get_chroma_format();
  input_state.has_alpha = input->has_channel(heif_channel_Alpha) || is_chroma_with_alpha(input->get_chroma_format());
  if (input->get_color_profile_nclx()) {
    input_state.nclx_profile = *input->get_color_profile_nclx();
  }

  input_state.nclx_profile.replace_undefined_values_with_sRGB_defaults();

  std::set<enum heif_channel> channels = input->get_channel_set();
  assert(!channels.empty());
  input_state.bits_per_pixel = input->get_bits_per_pixel(*(channels.begin()));

  ColorState output_state = input_state;
  output_state.colorspace = target_colorspace;
  output_state.chroma = target_chroma;
  if (target_profile) {
    output_state.nclx_profile = *target_profile;
  }

  // If some output nclx values are unspecified, set them to the same as the input.

  if (output_state.nclx_profile.get_matrix_coefficients() == heif_matrix_coefficients_unspecified) {
    output_state.nclx_profile.set_matrix_coefficients(input_state.nclx_profile.get_matrix_coefficients());
  }

  if (output_state.nclx_profile.get_colour_primaries() == heif_color_primaries_unspecified) {
    output_state.nclx_profile.set_colour_primaries(input_state.nclx_profile.get_colour_primaries());
  }

  if (output_state.nclx_profile.get_transfer_characteristics() == heif_transfer_characteristic_unspecified) {
    output_state.nclx_profile.set_transfer_characteristics(input_state.nclx_profile.get_transfer_characteristics());
  }

  // If we convert to an interleaved format, we want alpha only if present in the
  // interleaved output format.
  // For planar formats, we include an alpha plane when included in the input.

  if (num_interleaved_pixels_per_plane(target_chroma) > 1) {
    output_state.has_alpha = is_chroma_with_alpha(target_chroma);
  }
  else {
    output_state.has_alpha = input_state.has_alpha;
  }

  if (output_bpp) {
    output_state.bits_per_pixel = output_bpp;
  }


  // interleaved RGB formats always have to be 8-bit

  if (target_chroma == heif_chroma_interleaved_RGB ||
      target_chroma == heif_chroma_interleaved_RGBA) {
    output_state.bits_per_pixel = 8;
  }

  // interleaved RRGGBB formats have to be >8-bit.
  // If we don't know a target bit-depth, use 10 bit.

  if ((target_chroma == heif_chroma_interleaved_RRGGBB_LE ||
       target_chroma == heif_chroma_interleaved_RRGGBB_BE ||
       target_chroma == heif_chroma_interleaved_RRGGBBAA_LE ||
       target_chroma == heif_chroma_interleaved_RRGGBBAA_BE) &&
      output_state.bits_per_pixel <= 8) {
    output_state.bits_per_pixel = 10;
  }

  ColorConversionPipeline pipeline;
  bool success = pipeline.construct_pipeline(input_state, output_state, options);
  if (!success) {
    return nullptr;
  }

  return pipeline.convert_image(input);
}