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