Commit e379fd51 for libheif
commit e379fd5139cfde94913c3db5275ec49df9adbb19
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sat Sep 19 14:22:25 2026 +0200
Track one bit depth per plane in ColorState instead of a single image-wide value
ColorState carried a single bits_per_pixel for all colour planes, plus has_alpha
and alpha_bits_per_pixel. That cannot describe images whose planes have different
depths (an 'unci' item declares a depth per component), so operators that
reinterpret several planes through one sample type had no way to decline such
input in state_after_conversion() and had to check at runtime instead.
Replace the three fields with one depth per plane (R, G, B, Y, Cb, Cr, alpha and
the Bayer filter array), where 0 means the plane does not exist. has_alpha becomes
a query on the alpha depth. Interleaved RGB formats are represented by their
per-component depth in R/G/B (and alpha), so operators see the same fields as for
planar RGB.
New helpers: set_color_bits_per_pixel() fills the planes of the current
colorspace/chroma, get_color_bits_per_pixel() returns the first colour plane (the
old single value), and color_channels_have_same_bpp(), all_channels_have_same_bpp(),
all_channels_sdr() and all_channels_hdr() let operators state their per-plane
requirements. has_samples_wider_than_16bit() now checks every plane.
convert_colorspace() keeps the input's per-plane depths untouched when the target
has the same colorspace and chroma and no explicit depth, so an identity request
on an image with differing plane depths (e.g. RGB 5/6/5) stays a no-op.
Behaviour is unchanged for images whose planes share one depth. The helpers are
not used by any operator yet.
diff --git a/libheif/color-conversion/alpha.cc b/libheif/color-conversion/alpha.cc
index 0d2478e0..e9483eda 100644
--- a/libheif/color-conversion/alpha.cc
+++ b/libheif/color-conversion/alpha.cc
@@ -71,8 +71,8 @@ Op_drop_alpha_plane::state_after_conversion(const ColorState& input_state,
input_state.chroma != heif_chroma_420 &&
input_state.chroma != heif_chroma_422 &&
input_state.chroma != heif_chroma_444) ||
- input_state.has_alpha == false ||
- target_state.has_alpha == true) {
+ !input_state.has_alpha() ||
+ target_state.has_alpha()) {
return {};
}
@@ -87,7 +87,7 @@ Op_drop_alpha_plane::state_after_conversion(const ColorState& input_state,
// --- drop alpha plane
output_state = input_state;
- output_state.has_alpha = false;
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Trivial);
@@ -136,10 +136,10 @@ Op_flatten_alpha_plane<Pixel>::state_after_conversion(const ColorState& input_st
{
bool hdr = !std::is_same<Pixel, uint8_t>::value;
- // TODO: this Op only works when all channels are either HDR or all are SDR.
- // But there is currently no easy way to check that.
+ // TODO: this Op only works when all channels are either HDR or all are SDR
+ // (see ColorState::all_channels_sdr() / all_channels_hdr()).
- if ((input_state.bits_per_pixel > 8) != hdr) {
+ if ((input_state.get_color_bits_per_pixel() > 8) != hdr) {
return {};
}
@@ -147,7 +147,7 @@ Op_flatten_alpha_plane<Pixel>::state_after_conversion(const ColorState& input_st
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.get_color_bits_per_pixel()) {
return {};
}
@@ -157,8 +157,8 @@ Op_flatten_alpha_plane<Pixel>::state_after_conversion(const ColorState& input_st
input_state.chroma != heif_chroma_420 &&
input_state.chroma != heif_chroma_422 &&
input_state.chroma != heif_chroma_444) ||
- input_state.has_alpha == false ||
- target_state.has_alpha == true) {
+ !input_state.has_alpha() ||
+ target_state.has_alpha()) {
return {};
}
@@ -173,7 +173,7 @@ Op_flatten_alpha_plane<Pixel>::state_after_conversion(const ColorState& input_st
// --- drop alpha plane
output_state = input_state;
- output_state.has_alpha = false;
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Trivial);
@@ -200,7 +200,7 @@ Op_flatten_alpha_plane<Pixel>::convert_colorspace(const std::shared_ptr<const He
heif_colorspace_RGB,
heif_chroma_444,
input_state.nclx,
- input_state.bits_per_pixel,
+ input_state.get_color_bits_per_pixel(),
options, &options_ext_skip_alpha,
limits);
if (!convInput) {
@@ -223,7 +223,7 @@ Op_flatten_alpha_plane<Pixel>::convert_colorspace(const std::shared_ptr<const He
for (heif_channel channel : {heif_channel_R,
heif_channel_G,
heif_channel_B}) {
- outimg->add_channel(channel, width, height, target_state.bits_per_pixel, limits);
+ outimg->add_channel(channel, width, height, target_state.get_bits_per_pixel(channel), limits);
const Pixel* p_alpha;
size_t stride_alpha;
@@ -331,7 +331,7 @@ Op_flatten_alpha_plane<Pixel>::convert_colorspace(const std::shared_ptr<const He
input_raw->get_colorspace(),
input_raw->get_chroma_format(),
input_state.nclx,
- input_state.bits_per_pixel,
+ input_state.get_color_bits_per_pixel(),
options, &options_ext_skip_alpha,
limits);
if (!convOutput) {
@@ -357,8 +357,8 @@ Op_adjust_alpha_bit_depth::state_after_conversion(const ColorState& input_state,
const heif_color_conversion_options_ext& options_ext) const
{
// Only applicable when alpha BPP differs from color BPP
- if (!input_state.has_alpha ||
- input_state.get_alpha_bits_per_pixel() == input_state.bits_per_pixel) {
+ if (!input_state.has_alpha() ||
+ input_state.bits_per_pixel_alpha == input_state.get_color_bits_per_pixel()) {
return {};
}
@@ -379,7 +379,7 @@ Op_adjust_alpha_bit_depth::state_after_conversion(const ColorState& input_state,
std::vector<ColorStateWithCost> states;
ColorState output_state = input_state;
- output_state.alpha_bits_per_pixel = input_state.bits_per_pixel;
+ output_state.bits_per_pixel_alpha = input_state.get_color_bits_per_pixel();
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -414,7 +414,7 @@ Op_adjust_alpha_bit_depth::convert_colorspace(const std::shared_ptr<const HeifPi
}
int input_alpha_bpp = input->get_bits_per_pixel(heif_channel_Alpha);
- int target_bpp = input_state.bits_per_pixel;
+ int target_bpp = input_state.get_color_bits_per_pixel();
uint32_t alpha_width = input->get_width(heif_channel_Alpha);
uint32_t alpha_height = input->get_height(heif_channel_Alpha);
diff --git a/libheif/color-conversion/bayer_bilinear.cc b/libheif/color-conversion/bayer_bilinear.cc
index 4ce307c1..941d81f8 100644
--- a/libheif/color-conversion/bayer_bilinear.cc
+++ b/libheif/color-conversion/bayer_bilinear.cc
@@ -40,15 +40,16 @@ Op_bayer_bilinear_to_RGB24_32::state_after_conversion(const ColorState& input_st
ColorState output_state;
output_state.colorspace = heif_colorspace_RGB;
- output_state.has_alpha = false;
- if (input_state.bits_per_pixel == 8) {
+ int bpp = input_state.bits_per_pixel_filter_array;
+
+ if (bpp == 8) {
output_state.chroma = heif_chroma_interleaved_RGB;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
}
- else if (input_state.bits_per_pixel > 8 && input_state.bits_per_pixel <= 16) {
+ else if (bpp > 8 && bpp <= 16) {
output_state.chroma = heif_chroma_interleaved_RRGGBB_LE;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(bpp);
}
else {
return {};
diff --git a/libheif/color-conversion/chroma_sampling.cc b/libheif/color-conversion/chroma_sampling.cc
index 3d0a9cd1..511a05d5 100644
--- a/libheif/color-conversion/chroma_sampling.cc
+++ b/libheif/color-conversion/chroma_sampling.cc
@@ -45,7 +45,7 @@ Op_YCbCr444_to_YCbCr420_average<Pixel>::state_after_conversion(const ColorState&
bool hdr = !std::is_same<Pixel, uint8_t>::value;
- if ((input_state.bits_per_pixel > 8) != hdr) {
+ if ((input_state.bits_per_pixel_Y > 8) != hdr) {
return {};
}
@@ -69,9 +69,8 @@ Op_YCbCr444_to_YCbCr420_average<Pixel>::state_after_conversion(const ColorState&
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_420;
- output_state.has_alpha = input_state.has_alpha; // we simply keep the old alpha plane
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.alpha_bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we simply keep the old alpha plane
output_state.nclx = input_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -273,7 +272,7 @@ Op_YCbCr444_to_YCbCr422_average<Pixel>::state_after_conversion(const ColorState&
bool hdr = !std::is_same<Pixel, uint8_t>::value;
- if ((input_state.bits_per_pixel > 8) != hdr) {
+ if ((input_state.bits_per_pixel_Y > 8) != hdr) {
return {};
}
@@ -297,9 +296,8 @@ Op_YCbCr444_to_YCbCr422_average<Pixel>::state_after_conversion(const ColorState&
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_422;
- output_state.has_alpha = input_state.has_alpha; // we simply keep the old alpha plane
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.alpha_bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we simply keep the old alpha plane
output_state.nclx = input_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -479,7 +477,7 @@ Op_YCbCr420_bilinear_to_YCbCr444<Pixel>::state_after_conversion(const ColorState
bool hdr = !std::is_same<Pixel, uint8_t>::value;
- if ((input_state.bits_per_pixel > 8) != hdr) {
+ if ((input_state.bits_per_pixel_Y > 8) != hdr) {
return {};
}
@@ -499,9 +497,8 @@ Op_YCbCr420_bilinear_to_YCbCr444<Pixel>::state_after_conversion(const ColorState
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_444;
- output_state.has_alpha = input_state.has_alpha; // we simply keep the old alpha plane
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.alpha_bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we simply keep the old alpha plane
output_state.nclx = input_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -764,7 +761,7 @@ Op_YCbCr422_bilinear_to_YCbCr444<Pixel>::state_after_conversion(const ColorState
bool hdr = !std::is_same<Pixel, uint8_t>::value;
- if ((input_state.bits_per_pixel > 8) != hdr) {
+ if ((input_state.bits_per_pixel_Y > 8) != hdr) {
return {};
}
@@ -784,9 +781,8 @@ Op_YCbCr422_bilinear_to_YCbCr444<Pixel>::state_after_conversion(const ColorState
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_444;
- output_state.has_alpha = input_state.has_alpha; // we simply keep the old alpha plane
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.alpha_bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we simply keep the old alpha plane
output_state.nclx = input_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
diff --git a/libheif/color-conversion/colorconversion.cc b/libheif/color-conversion/colorconversion.cc
index eee9333b..ae1e1b36 100644
--- a/libheif/color-conversion/colorconversion.cc
+++ b/libheif/color-conversion/colorconversion.cc
@@ -147,23 +147,214 @@ static void __attribute__ ((unused)) print_spec(std::ostream& ostr, const std::s
#endif
+ColorState::ColorState(heif_colorspace cs, heif_chroma chr, bool with_alpha, int bpp)
+ : colorspace(cs), chroma(chr)
+{
+ set_color_bits_per_pixel(bpp);
+ bits_per_pixel_alpha = with_alpha ? bpp : 0;
+}
+
+
+int ColorState::get_bits_per_pixel(heif_channel channel) const
+{
+ switch (channel) {
+ case heif_channel_R:
+ return bits_per_pixel_R;
+ case heif_channel_G:
+ return bits_per_pixel_G;
+ case heif_channel_B:
+ return bits_per_pixel_B;
+ case heif_channel_Y:
+ return bits_per_pixel_Y;
+ case heif_channel_Cb:
+ return bits_per_pixel_Cb;
+ case heif_channel_Cr:
+ return bits_per_pixel_Cr;
+ case heif_channel_Alpha:
+ return bits_per_pixel_alpha;
+ case heif_channel_filter_array:
+ return bits_per_pixel_filter_array;
+ case heif_channel_interleaved:
+ return bits_per_pixel_R;
+ default:
+ return 0;
+ }
+}
+
+
+void ColorState::set_bits_per_pixel(heif_channel channel, int bpp)
+{
+ switch (channel) {
+ case heif_channel_R:
+ bits_per_pixel_R = bpp;
+ break;
+ case heif_channel_G:
+ bits_per_pixel_G = bpp;
+ break;
+ case heif_channel_B:
+ bits_per_pixel_B = bpp;
+ break;
+ case heif_channel_Y:
+ bits_per_pixel_Y = bpp;
+ break;
+ case heif_channel_Cb:
+ bits_per_pixel_Cb = bpp;
+ break;
+ case heif_channel_Cr:
+ bits_per_pixel_Cr = bpp;
+ break;
+ case heif_channel_Alpha:
+ bits_per_pixel_alpha = bpp;
+ break;
+ case heif_channel_filter_array:
+ bits_per_pixel_filter_array = bpp;
+ break;
+ case heif_channel_interleaved:
+ bits_per_pixel_R = bits_per_pixel_G = bits_per_pixel_B = bpp;
+ break;
+ default:
+ break;
+ }
+}
+
+
+void ColorState::set_color_bits_per_pixel(int bpp)
+{
+ bits_per_pixel_R = bits_per_pixel_G = bits_per_pixel_B = 0;
+ bits_per_pixel_Y = bits_per_pixel_Cb = bits_per_pixel_Cr = 0;
+ bits_per_pixel_filter_array = 0;
+
+ if (colorspace == heif_colorspace_filter_array) {
+ bits_per_pixel_filter_array = bpp;
+ return;
+ }
+
+ switch (chroma) {
+ case heif_chroma_planar: // == heif_chroma_monochrome
+ if (colorspace == heif_colorspace_RGB) {
+ bits_per_pixel_R = bits_per_pixel_G = bits_per_pixel_B = bpp;
+ }
+ else {
+ bits_per_pixel_Y = bpp;
+ }
+ break;
+
+ case heif_chroma_420:
+ case heif_chroma_422:
+ bits_per_pixel_Y = bits_per_pixel_Cb = bits_per_pixel_Cr = bpp;
+ break;
+
+ case heif_chroma_444:
+ if (colorspace == heif_colorspace_RGB) {
+ bits_per_pixel_R = bits_per_pixel_G = bits_per_pixel_B = bpp;
+ }
+ else {
+ bits_per_pixel_Y = bits_per_pixel_Cb = bits_per_pixel_Cr = bpp;
+ }
+ break;
+
+ case heif_chroma_interleaved_RGB:
+ case heif_chroma_interleaved_RGBA:
+ case heif_chroma_interleaved_RRGGBB_BE:
+ case heif_chroma_interleaved_RRGGBB_LE:
+ case heif_chroma_interleaved_RRGGBBAA_BE:
+ case heif_chroma_interleaved_RRGGBBAA_LE:
+ bits_per_pixel_R = bits_per_pixel_G = bits_per_pixel_B = bpp;
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+int ColorState::get_color_bits_per_pixel() const
+{
+ if (bits_per_pixel_Y != 0) {
+ return bits_per_pixel_Y;
+ }
+ if (bits_per_pixel_R != 0) {
+ return bits_per_pixel_R;
+ }
+ if (bits_per_pixel_filter_array != 0) {
+ return bits_per_pixel_filter_array;
+ }
+ return 0;
+}
+
+
+int ColorState::get_max_bits_per_pixel() const
+{
+ return std::max({bits_per_pixel_R, bits_per_pixel_G, bits_per_pixel_B,
+ bits_per_pixel_Y, bits_per_pixel_Cb, bits_per_pixel_Cr,
+ bits_per_pixel_alpha, bits_per_pixel_filter_array});
+}
+
+
+// Applies 'pred' to the depth of every existing plane (planes with depth 0 do not exist and
+// are skipped) and returns whether it holds for all of them.
+template<typename Pred>
+static bool all_existing_planes_satisfy(const ColorState& s, bool include_alpha, Pred pred)
+{
+ for (int bpp : {s.bits_per_pixel_R, s.bits_per_pixel_G, s.bits_per_pixel_B,
+ s.bits_per_pixel_Y, s.bits_per_pixel_Cb, s.bits_per_pixel_Cr,
+ s.bits_per_pixel_filter_array}) {
+ if (bpp != 0 && !pred(bpp)) {
+ return false;
+ }
+ }
+
+ if (include_alpha && s.bits_per_pixel_alpha != 0 && !pred(s.bits_per_pixel_alpha)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+bool ColorState::color_channels_have_same_bpp() const
+{
+ int ref = get_color_bits_per_pixel();
+ return all_existing_planes_satisfy(*this, false, [ref](int bpp) { return bpp == ref; });
+}
+
+
+bool ColorState::all_channels_have_same_bpp() const
+{
+ int ref = get_color_bits_per_pixel();
+ return all_existing_planes_satisfy(*this, true, [ref](int bpp) { return bpp == ref; });
+}
+
+
+bool ColorState::all_channels_sdr() const
+{
+ return all_existing_planes_satisfy(*this, true, [](int bpp) { return bpp <= 8; });
+}
+
+
+bool ColorState::all_channels_hdr() const
+{
+ return all_existing_planes_satisfy(*this, true, [](int bpp) { return bpp > 8; });
+}
+
+
bool ColorState::operator==(const ColorState& b) const
{
bool mainParamsMatch = (colorspace == b.colorspace &&
chroma == b.chroma &&
- has_alpha == b.has_alpha &&
- bits_per_pixel == b.bits_per_pixel);
+ bits_per_pixel_R == b.bits_per_pixel_R &&
+ bits_per_pixel_G == b.bits_per_pixel_G &&
+ bits_per_pixel_B == b.bits_per_pixel_B &&
+ bits_per_pixel_Y == b.bits_per_pixel_Y &&
+ bits_per_pixel_Cb == b.bits_per_pixel_Cb &&
+ bits_per_pixel_Cr == b.bits_per_pixel_Cr &&
+ bits_per_pixel_alpha == b.bits_per_pixel_alpha &&
+ bits_per_pixel_filter_array == b.bits_per_pixel_filter_array);
if (!mainParamsMatch) {
return false;
}
- if (has_alpha && b.has_alpha) {
- if (get_alpha_bits_per_pixel() != b.get_alpha_bits_per_pixel()) {
- return false;
- }
- }
-
if (colorspace == heif_colorspace_YCbCr) {
bool ycbcr_parameters_match = nclx.equal_except_transfer_curve(b.nclx);
@@ -202,12 +393,27 @@ struct Node
std::ostream& operator<<(std::ostream& ostr, const ColorState& state)
{
- ostr << "colorspace=" << state.colorspace << " chroma=" << state.chroma
- << " bpp(R)=" << state.bits_per_pixel
- << " alpha=" << (state.has_alpha ? "yes" : "no");
+ ostr << "colorspace=" << state.colorspace << " chroma=" << state.chroma;
- if (state.has_alpha && state.get_alpha_bits_per_pixel() != state.bits_per_pixel) {
- ostr << " alpha_bpp=" << state.get_alpha_bits_per_pixel();
+ auto print_plane = [&ostr](const char* name, int bpp) {
+ if (bpp != 0) {
+ ostr << " bpp(" << name << ")=" << bpp;
+ }
+ };
+
+ print_plane("Y", state.bits_per_pixel_Y);
+ print_plane("Cb", state.bits_per_pixel_Cb);
+ print_plane("Cr", state.bits_per_pixel_Cr);
+ print_plane("R", state.bits_per_pixel_R);
+ print_plane("G", state.bits_per_pixel_G);
+ print_plane("B", state.bits_per_pixel_B);
+ print_plane("filter_array", state.bits_per_pixel_filter_array);
+
+ if (state.has_alpha()) {
+ ostr << " alpha_bpp=" << state.bits_per_pixel_alpha;
+ }
+ else {
+ ostr << " alpha=no";
}
if (state.colorspace == heif_colorspace_YCbCr) {
@@ -531,7 +737,6 @@ Result<std::shared_ptr<HeifPixelImage>> convert_colorspace(const std::shared_ptr
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_interleaved_with_alpha(input->get_chroma_format());
if (input->has_nclx_color_profile()) {
input_state.nclx = input->get_color_profile_nclx();
}
@@ -540,10 +745,27 @@ Result<std::shared_ptr<HeifPixelImage>> convert_colorspace(const std::shared_ptr
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()));
- if (input_state.has_alpha && input->has_channel(heif_channel_Alpha)) {
- input_state.alpha_bits_per_pixel = input->get_bits_per_pixel(heif_channel_Alpha);
+ // Record the bit depth of every plane the image has. They may differ from each other
+ // (e.g. 'unci' declares a depth per component), which is why ColorState keeps one value
+ // per plane instead of a single image-wide depth.
+ for (heif_channel channel : {heif_channel_Y, heif_channel_Cb, heif_channel_Cr,
+ heif_channel_R, heif_channel_G, heif_channel_B,
+ heif_channel_Alpha, heif_channel_filter_array}) {
+ if (input->has_channel(channel)) {
+ input_state.set_bits_per_pixel(channel, input->get_bits_per_pixel(channel));
+ }
+ }
+
+ // Interleaved RGB formats keep all components in one plane. Represent them by their
+ // per-component depth so that operators see the same R/G/B (and alpha) fields as for
+ // planar RGB.
+ if (input->has_channel(heif_channel_interleaved)) {
+ int bpp = input->get_bits_per_pixel(heif_channel_interleaved);
+ input_state.set_bits_per_pixel(heif_channel_interleaved, bpp);
+ if (is_interleaved_with_alpha(input->get_chroma_format())) {
+ input_state.bits_per_pixel_alpha = bpp;
+ }
}
ColorState output_state = input_state;
@@ -569,28 +791,29 @@ Result<std::shared_ptr<HeifPixelImage>> convert_colorspace(const std::shared_ptr
// interleaved output format.
// For planar formats, we include an alpha plane when included in the input.
+ bool output_has_alpha;
+
if (num_interleaved_components_per_plane(target_chroma) > 1) {
- output_state.has_alpha = is_interleaved_with_alpha(target_chroma);
+ output_has_alpha = is_interleaved_with_alpha(target_chroma);
}
else {
if (options_ext->alpha_composition_mode != heif_alpha_composition_mode_none) {
- output_state.has_alpha = false;
+ output_has_alpha = false;
}
else {
- output_state.has_alpha = input_state.has_alpha;
+ output_has_alpha = input_state.has_alpha();
}
}
- if (output_bpp) {
- output_state.bits_per_pixel = output_bpp;
- }
+ // --- output colour bit depth (0 = keep the input depth)
+ int output_color_bpp = 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;
+ output_color_bpp = 8;
}
// interleaved RRGGBB formats have to be >8-bit.
@@ -600,12 +823,28 @@ Result<std::shared_ptr<HeifPixelImage>> convert_colorspace(const std::shared_ptr
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;
+ (output_color_bpp != 0 ? output_color_bpp : input_state.get_color_bits_per_pixel()) <= 8) {
+ output_color_bpp = 10;
+ }
+
+ bool same_plane_layout = (target_colorspace == input_state.colorspace &&
+ target_chroma == input_state.chroma);
+
+ if (output_color_bpp == 0 && same_plane_layout) {
+ // No depth change requested and the plane layout stays the same: keep the input
+ // planes exactly as they are, even when their depths differ from each other.
+ output_color_bpp = input_state.get_color_bits_per_pixel();
+ }
+ else {
+ if (output_color_bpp == 0) {
+ output_color_bpp = input_state.get_color_bits_per_pixel();
+ }
+
+ output_state.set_color_bits_per_pixel(output_color_bpp);
}
// Output alpha should always match the output color BPP
- output_state.alpha_bits_per_pixel = output_state.bits_per_pixel;
+ output_state.bits_per_pixel_alpha = output_has_alpha ? output_color_bpp : 0;
ColorConversionPipeline pipeline;
bool success = pipeline.construct_pipeline(input_state, output_state, options, *options_ext);
diff --git a/libheif/color-conversion/colorconversion.h b/libheif/color-conversion/colorconversion.h
index e1a6d8a6..9621e38d 100644
--- a/libheif/color-conversion/colorconversion.h
+++ b/libheif/color-conversion/colorconversion.h
@@ -32,9 +32,21 @@ struct ColorState
{
heif_colorspace colorspace = heif_colorspace_undefined;
heif_chroma chroma = heif_chroma_undefined;
- bool has_alpha = false;
- int bits_per_pixel = 8;
- int alpha_bits_per_pixel = 0; // 0 = not set, treated as bits_per_pixel
+
+ // Bit depth of each plane. A value of 0 means that the plane does not exist in this state.
+ // The depths may differ from each other (e.g. 'unci' declares a depth per component).
+ //
+ // The interleaved RGB chroma formats store all components in a single plane. They are
+ // represented by their per-component depth in R/G/B (and alpha, if the format has one),
+ // so that operators see the same fields as for planar RGB.
+ int bits_per_pixel_R = 0;
+ int bits_per_pixel_G = 0;
+ int bits_per_pixel_B = 0;
+ int bits_per_pixel_Y = 0;
+ int bits_per_pixel_Cb = 0;
+ int bits_per_pixel_Cr = 0;
+ int bits_per_pixel_alpha = 0;
+ int bits_per_pixel_filter_array = 0;
// ColorConversionOperations can assume that the input and target nclx has no 'unspecified' values
// if the colorspace is heif_colorspace_YCbCr. Otherwise, the values should preferably be 'unspecified'.
@@ -42,11 +54,41 @@ struct ColorState
ColorState() = default;
- ColorState(heif_colorspace colorspace, heif_chroma chroma, bool has_alpha, int bits_per_pixel)
- : colorspace(colorspace), chroma(chroma), has_alpha(has_alpha), bits_per_pixel(bits_per_pixel) {}
+ // Convenience constructor: all colour planes of the given colorspace/chroma get 'bpp' and,
+ // if 'with_alpha' is set, an alpha plane of the same depth is added.
+ ColorState(heif_colorspace cs, heif_chroma chr, bool with_alpha, int bpp);
+
+ bool has_alpha() const { return bits_per_pixel_alpha != 0; }
+
+ // Bit depth of a single plane, 0 if the plane does not exist.
+ // 'heif_channel_interleaved' maps to the R/G/B depth.
+ int get_bits_per_pixel(heif_channel channel) const;
+ void set_bits_per_pixel(heif_channel channel, int bpp);
+
+ // Sets all colour planes that exist for the current colorspace/chroma to 'bpp' and clears
+ // all other colour planes. The alpha plane is not touched.
+ // Call this after 'colorspace' and 'chroma' have been set.
+ void set_color_bits_per_pixel(int bpp);
+
+ // Bit depth of the first colour plane (Y for YCbCr/monochrome, R for RGB, or the filter array).
+ // This only characterizes the whole image when all colour planes have the same depth,
+ // see color_channels_have_same_bpp().
+ int get_color_bits_per_pixel() const;
+
+ // Maximum bit depth over all existing planes, including alpha.
+ int get_max_bits_per_pixel() const;
+
+ // True if all existing colour planes (R/G/B or Y/Cb/Cr) have the same bit depth.
+ bool color_channels_have_same_bpp() const;
+
+ // True if all existing planes, including alpha, have the same bit depth.
+ bool all_channels_have_same_bpp() const;
+
+ // True if all existing planes, including alpha, have at most 8 bits.
+ bool all_channels_sdr() const;
- // Returns effective alpha BPP (treats 0 as bits_per_pixel for backward compatibility)
- int get_alpha_bits_per_pixel() const { return alpha_bits_per_pixel ? alpha_bits_per_pixel : bits_per_pixel; }
+ // True if all existing planes, including alpha, have more than 8 bits.
+ bool all_channels_hdr() const;
bool operator==(const ColorState&) const;
};
@@ -70,8 +112,7 @@ std::ostream& operator<<(std::ostream& ostr, const ColorState& state);
// catch-all in convert_colorspace() can go away.
inline bool has_samples_wider_than_16bit(const ColorState& state)
{
- return state.bits_per_pixel > 16 ||
- (state.has_alpha && state.get_alpha_bits_per_pixel() > 16);
+ return state.get_max_bits_per_pixel() > 16;
}
// These are some integer constants for typical color conversion Op speed costs.
diff --git a/libheif/color-conversion/hdr_sdr.cc b/libheif/color-conversion/hdr_sdr.cc
index 34031724..e76264cd 100644
--- a/libheif/color-conversion/hdr_sdr.cc
+++ b/libheif/color-conversion/hdr_sdr.cc
@@ -32,7 +32,7 @@ Op_to_hdr_planes::state_after_conversion(const ColorState& input_state,
input_state.chroma != heif_chroma_420 &&
input_state.chroma != heif_chroma_422 &&
input_state.chroma != heif_chroma_444) ||
- input_state.bits_per_pixel != 8) { // TODO: support for <8 bpp
+ input_state.get_color_bits_per_pixel() != 8) { // TODO: support for <8 bpp
return {};
}
@@ -41,8 +41,8 @@ Op_to_hdr_planes::state_after_conversion(const ColorState& input_state,
// only holds for target bit depths m in (8, 16]; a larger m would both make
// the right shift exponent negative (undefined behavior) and exceed the range
// of the uint16_t output plane. Only offer the conversion within that range.
- if (target_state.bits_per_pixel <= 8 ||
- target_state.bits_per_pixel > 16) {
+ if (target_state.get_color_bits_per_pixel() <= 8 ||
+ target_state.get_color_bits_per_pixel() > 16) {
return {};
}
@@ -53,8 +53,10 @@ Op_to_hdr_planes::state_after_conversion(const ColorState& input_state,
// --- increase bit depth
output_state = input_state;
- output_state.bits_per_pixel = target_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = target_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(target_state.get_color_bits_per_pixel());
+ if (output_state.has_alpha()) {
+ output_state.bits_per_pixel_alpha = target_state.get_color_bits_per_pixel();
+ }
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -87,12 +89,12 @@ Op_to_hdr_planes::convert_colorspace(const std::shared_ptr<const HeifPixelImage>
if (input->has_channel(channel)) {
uint32_t width = input->get_width(channel);
uint32_t height = input->get_height(channel);
- if (auto err = outimg->add_channel(channel, width, height, target_state.bits_per_pixel, limits)) {
+ if (auto err = outimg->add_channel(channel, width, height, target_state.get_color_bits_per_pixel(), limits)) {
return err;
}
int input_bits = input->get_bits_per_pixel(channel);
- int output_bits = target_state.bits_per_pixel;
+ int output_bits = target_state.get_color_bits_per_pixel();
// Guard against unsupported bit-depth combinations. state_after_conversion()
// only offers this operation for 8-bit input and 8 < output <= 16, but a
@@ -140,11 +142,11 @@ Op_to_sdr_planes::state_after_conversion(const ColorState& input_state,
input_state.chroma != heif_chroma_420 &&
input_state.chroma != heif_chroma_422 &&
input_state.chroma != heif_chroma_444) ||
- input_state.bits_per_pixel == 8) {
+ input_state.get_color_bits_per_pixel() == 8) {
return {};
}
- if (target_state.bits_per_pixel != 8) {
+ if (target_state.get_color_bits_per_pixel() != 8) {
return {};
}
@@ -161,8 +163,10 @@ Op_to_sdr_planes::state_after_conversion(const ColorState& input_state,
// --- output bit depth = 8
output_state = input_state;
- output_state.bits_per_pixel = 8;
- output_state.alpha_bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ if (output_state.has_alpha()) {
+ output_state.bits_per_pixel_alpha = 8;
+ }
states.emplace_back(output_state, SpeedCosts_Unoptimized);
diff --git a/libheif/color-conversion/monochrome.cc b/libheif/color-conversion/monochrome.cc
index afdebdb0..acce06ba 100644
--- a/libheif/color-conversion/monochrome.cc
+++ b/libheif/color-conversion/monochrome.cc
@@ -45,9 +45,8 @@ Op_mono_to_YCbCr420::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_420;
- output_state.has_alpha = input_state.has_alpha;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.alpha_bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha;
states.emplace_back(output_state, SpeedCosts_OptimizedSoftware);
@@ -176,11 +175,11 @@ Op_mono_to_RGB24_32::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_monochrome ||
input_state.chroma != heif_chroma_monochrome ||
- input_state.bits_per_pixel != 8) {
+ input_state.bits_per_pixel_Y != 8) {
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_Y) {
return {};
}
@@ -190,11 +189,11 @@ Op_mono_to_RGB24_32::state_after_conversion(const ColorState& input_state,
// --- convert to RGB24
- if (input_state.has_alpha == false) {
+ if (!input_state.has_alpha()) {
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGB;
- output_state.has_alpha = false;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
}
@@ -204,8 +203,8 @@ Op_mono_to_RGB24_32::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGBA;
- output_state.has_alpha = true;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = 8;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -232,7 +231,7 @@ Op_mono_to_RGB24_32::convert_colorspace(const std::shared_ptr<const HeifPixelIma
bool has_alpha = input->has_channel(heif_channel_Alpha);
- if (target_state.has_alpha) {
+ if (target_state.has_alpha()) {
outimg->create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_32bit);
}
else {
@@ -258,7 +257,7 @@ Op_mono_to_RGB24_32::convert_colorspace(const std::shared_ptr<const HeifPixelIma
uint32_t x, y;
for (y = 0; y < height; y++) {
- if (target_state.has_alpha == false) {
+ if (!target_state.has_alpha()) {
for (x = 0; x < width; x++) {
uint8_t v = in_y[x + y * in_y_stride];
out_p[y * out_p_stride + 3 * x + 0] = v;
diff --git a/libheif/color-conversion/rgb2rgb.cc b/libheif/color-conversion/rgb2rgb.cc
index 777021d7..4bb78495 100644
--- a/libheif/color-conversion/rgb2rgb.cc
+++ b/libheif/color-conversion/rgb2rgb.cc
@@ -33,11 +33,11 @@ Op_RGB_to_RGB24_32::state_after_conversion(const ColorState& input_state,
{
if (input_state.colorspace != heif_colorspace_RGB ||
input_state.chroma != heif_chroma_444 ||
- input_state.bits_per_pixel != 8) {
+ input_state.bits_per_pixel_R != 8) {
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_R) {
return {};
}
@@ -50,8 +50,8 @@ Op_RGB_to_RGB24_32::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGBA;
- output_state.has_alpha = true;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = 8;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -59,8 +59,8 @@ Op_RGB_to_RGB24_32::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGB;
- output_state.has_alpha = false;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -77,7 +77,7 @@ Op_RGB_to_RGB24_32::convert_colorspace(const std::shared_ptr<const HeifPixelImag
const heif_security_limits* limits) const
{
bool has_alpha = input->has_channel(heif_channel_Alpha);
- bool want_alpha = target_state.has_alpha;
+ bool want_alpha = target_state.has_alpha();
if (input->get_bits_per_pixel(heif_channel_R) != 8 ||
input->get_bits_per_pixel(heif_channel_G) != 8 ||
@@ -160,7 +160,7 @@ Op_RGB_HDR_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_RGB ||
input_state.chroma != heif_chroma_444 ||
- input_state.bits_per_pixel <= 8) {
+ input_state.bits_per_pixel_R <= 8) {
return {};
}
@@ -168,7 +168,7 @@ Op_RGB_HDR_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_R) {
return {};
}
@@ -178,11 +178,11 @@ Op_RGB_HDR_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
// --- convert to RRGGBB_BE
- if (input_state.has_alpha == false) {
+ if (!input_state.has_alpha()) {
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBB_BE;
- output_state.has_alpha = false;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
}
@@ -192,8 +192,8 @@ Op_RGB_HDR_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBBAA_BE;
- output_state.has_alpha = true;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_R;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -217,7 +217,7 @@ Op_RGB_HDR_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptr<const HeifPi
}
bool input_has_alpha = input->has_channel(heif_channel_Alpha);
- bool output_has_alpha = input_has_alpha || target_state.has_alpha;
+ bool output_has_alpha = input_has_alpha || target_state.has_alpha();
if (input_has_alpha) {
if (input->get_bits_per_pixel(heif_channel_Alpha) <= 8) {
@@ -318,11 +318,11 @@ Op_RGB_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_RGB ||
input_state.chroma != heif_chroma_444 ||
- input_state.bits_per_pixel != 8) {
+ input_state.bits_per_pixel_R != 8) {
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_R) {
return {};
}
@@ -332,11 +332,11 @@ Op_RGB_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
// --- convert to RRGGBB_BE
- if (input_state.has_alpha == false) {
+ if (!input_state.has_alpha()) {
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBB_BE;
- output_state.has_alpha = false;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
}
@@ -346,8 +346,8 @@ Op_RGB_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RRGGBBAA_BE;
- output_state.has_alpha = true;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_R;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -372,7 +372,7 @@ Op_RGB_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptr<const HeifPixelI
//int bpp = input->get_bits_per_pixel(heif_channel_R);
bool input_has_alpha = input->has_channel(heif_channel_Alpha);
- bool output_has_alpha = input_has_alpha || target_state.has_alpha;
+ bool output_has_alpha = input_has_alpha || target_state.has_alpha();
if (input_has_alpha && input->get_bits_per_pixel(heif_channel_Alpha) != 8) {
return Error::InternalError;
@@ -454,7 +454,7 @@ Op_RRGGBBaa_BE_to_RGB_HDR::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_RGB ||
(input_state.chroma != heif_chroma_interleaved_RRGGBB_BE &&
input_state.chroma != heif_chroma_interleaved_RRGGBBAA_BE) ||
- input_state.bits_per_pixel <= 8) {
+ input_state.bits_per_pixel_R <= 8) {
return {};
}
@@ -470,9 +470,8 @@ Op_RRGGBBaa_BE_to_RGB_HDR::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_444;
- output_state.has_alpha = target_state.has_alpha;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = target_state.has_alpha() ? input_state.bits_per_pixel_R : 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -490,7 +489,7 @@ Op_RRGGBBaa_BE_to_RGB_HDR::convert_colorspace(const std::shared_ptr<const HeifPi
{
bool has_alpha = (input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE ||
input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE);
- bool want_alpha = target_state.has_alpha;
+ bool want_alpha = target_state.has_alpha();
auto outimg = std::make_shared<HeifPixelImage>();
@@ -575,7 +574,7 @@ Op_RGB24_32_to_RGB::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_RGB ||
(input_state.chroma != heif_chroma_interleaved_RGB &&
input_state.chroma != heif_chroma_interleaved_RGBA) ||
- input_state.bits_per_pixel != 8) {
+ input_state.bits_per_pixel_R != 8) {
return {};
}
@@ -587,9 +586,8 @@ Op_RGB24_32_to_RGB::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_444;
- output_state.has_alpha = target_state.has_alpha;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = target_state.has_alpha() ? input_state.bits_per_pixel_R : 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -606,7 +604,7 @@ Op_RGB24_32_to_RGB::convert_colorspace(const std::shared_ptr<const HeifPixelImag
const heif_security_limits* limits) const
{
bool has_alpha = input->get_chroma_format() == heif_chroma_interleaved_RGBA;
- bool want_alpha = target_state.has_alpha;
+ bool want_alpha = target_state.has_alpha();
auto outimg = std::make_shared<HeifPixelImage>();
@@ -702,8 +700,8 @@ Op_RRGGBBaa_swap_endianness::state_after_conversion(const ColorState& input_stat
output_state.chroma = heif_chroma_interleaved_RRGGBB_LE;
}
- output_state.has_alpha = false;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
}
@@ -722,8 +720,8 @@ Op_RRGGBBaa_swap_endianness::state_after_conversion(const ColorState& input_stat
output_state.chroma = heif_chroma_interleaved_RRGGBBAA_LE;
}
- output_state.has_alpha = true;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
}
diff --git a/libheif/color-conversion/rgb2yuv.cc b/libheif/color-conversion/rgb2yuv.cc
index 12678128..355775fd 100644
--- a/libheif/color-conversion/rgb2yuv.cc
+++ b/libheif/color-conversion/rgb2yuv.cc
@@ -36,25 +36,25 @@ Op_RGB_to_YCbCr<Pixel>::state_after_conversion(const ColorState& input_state,
{
bool hdr = !std::is_same<Pixel, uint8_t>::value;
- if ((input_state.bits_per_pixel > 8) != hdr) {
+ if (input_state.colorspace != heif_colorspace_RGB ||
+ input_state.chroma != heif_chroma_444) {
return {};
}
- if (has_samples_wider_than_16bit(input_state)) {
+ if ((input_state.bits_per_pixel_R > 8) != hdr) {
return {};
}
- // TODO: add support for <8 bpp
- if (input_state.bits_per_pixel < 8) {
+ if (has_samples_wider_than_16bit(input_state)) {
return {};
}
- if (input_state.colorspace != heif_colorspace_RGB ||
- input_state.chroma != heif_chroma_444) {
+ // TODO: add support for <8 bpp
+ if (input_state.bits_per_pixel_R < 8) {
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_R) {
return {};
}
@@ -80,8 +80,8 @@ Op_RGB_to_YCbCr<Pixel>::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = target_state.chroma;
- output_state.has_alpha = input_state.has_alpha; // we simply keep the old alpha plane
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we simply keep the old alpha plane
output_state.nclx = target_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -91,8 +91,8 @@ Op_RGB_to_YCbCr<Pixel>::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_444;
- output_state.has_alpha = input_state.has_alpha; // we simply keep the old alpha plane
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we simply keep the old alpha plane
output_state.nclx = target_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -340,7 +340,7 @@ Op_RRGGBBxx_HDR_to_YCbCr420::state_after_conversion(const ColorState& input_stat
input_state.chroma == heif_chroma_interleaved_RRGGBB_LE ||
input_state.chroma == heif_chroma_interleaved_RRGGBBAA_BE ||
input_state.chroma == heif_chroma_interleaved_RRGGBBAA_LE) ||
- input_state.bits_per_pixel <= 8) {
+ input_state.bits_per_pixel_R <= 8) {
return {};
}
@@ -369,8 +369,8 @@ Op_RRGGBBxx_HDR_to_YCbCr420::state_after_conversion(const ColorState& input_stat
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_420;
- output_state.has_alpha = input_state.has_alpha; // we generate an alpha plane if the source contains data
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_R);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we generate an alpha plane if the source contains data
output_state.nclx = target_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -544,7 +544,7 @@ Op_RGB24_32_to_YCbCr::state_after_conversion(const ColorState& input_state,
// The interleaved input is indexed as bytes and the output is hard-coded to 8 bits,
// so this operator handles 8-bit input only.
- if (input_state.bits_per_pixel != 8) {
+ if (input_state.bits_per_pixel_R != 8) {
return {};
}
@@ -565,8 +565,8 @@ Op_RGB24_32_to_YCbCr::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = target_state.chroma;
- output_state.has_alpha = target_state.has_alpha;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = target_state.has_alpha() ? 8 : 0;
output_state.nclx = target_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -617,7 +617,7 @@ Op_RGB24_32_to_YCbCr::convert_colorspace(const std::shared_ptr<const HeifPixelIm
int chroma_height = (height + chromaSubV - 1) / chromaSubV;
const bool has_alpha = (input->get_chroma_format() == heif_chroma_interleaved_32bit);
- const bool want_alpha = target_state.has_alpha;
+ const bool want_alpha = target_state.has_alpha();
if (auto err = outimg->add_channel(heif_channel_Y, width, height, 8, limits) ||
outimg->add_channel(heif_channel_Cb, chroma_width, chroma_height, 8, limits) ||
@@ -847,7 +847,7 @@ Op_RGB24_32_to_YCbCr444_GBR::state_after_conversion(const ColorState& input_stat
// The interleaved input is indexed as bytes and the output is hard-coded to 8 bits,
// so this operator handles 8-bit input only.
- if (input_state.bits_per_pixel != 8) {
+ if (input_state.bits_per_pixel_R != 8) {
return {};
}
@@ -865,8 +865,8 @@ Op_RGB24_32_to_YCbCr444_GBR::state_after_conversion(const ColorState& input_stat
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_444;
- output_state.has_alpha = target_state.has_alpha;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = target_state.has_alpha() ? 8 : 0;
output_state.nclx = target_state.nclx;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -891,7 +891,7 @@ Op_RGB24_32_to_YCbCr444_GBR::convert_colorspace(const std::shared_ptr<const Heif
outimg->create(width, height, heif_colorspace_YCbCr, heif_chroma_444);
const bool has_alpha = (input->get_chroma_format() == heif_chroma_interleaved_32bit);
- const bool want_alpha = target_state.has_alpha;
+ const bool want_alpha = target_state.has_alpha();
if (auto err = outimg->add_channel(heif_channel_Y, width, height, 8, limits) ||
outimg->add_channel(heif_channel_Cb, width, height, 8, limits) ||
diff --git a/libheif/color-conversion/rgb2yuv_sharp.cc b/libheif/color-conversion/rgb2yuv_sharp.cc
index 8b9c3f4a..35bc4c11 100644
--- a/libheif/color-conversion/rgb2yuv_sharp.cc
+++ b/libheif/color-conversion/rgb2yuv_sharp.cc
@@ -86,13 +86,17 @@ Op_Any_RGB_to_YCbCr_420_Sharp::state_after_conversion(
return {};
}
- if (input_state.bits_per_pixel != 8 && input_state.bits_per_pixel != 10 &&
- input_state.bits_per_pixel != 12 && input_state.bits_per_pixel != 16) {
+ if (input_state.bits_per_pixel_R != 8 && input_state.bits_per_pixel_R != 10 &&
+ input_state.bits_per_pixel_R != 12 && input_state.bits_per_pixel_R != 16) {
return {};
}
- if (target_state.bits_per_pixel != 8 && target_state.bits_per_pixel != 10 &&
- target_state.bits_per_pixel != 12) {
+ if (target_state.chroma != heif_chroma_420) {
+ return {};
+ }
+
+ if (target_state.bits_per_pixel_Y != 8 && target_state.bits_per_pixel_Y != 10 &&
+ target_state.bits_per_pixel_Y != 12) {
return {};
}
@@ -105,11 +109,7 @@ Op_Any_RGB_to_YCbCr_420_Sharp::state_after_conversion(
// color channels, so 10-bit R/G/B next to an 8-bit alpha is normal here. Decline it,
// exactly as every other RGB operator does; the pipeline then inserts
// Op_adjust_alpha_bit_depth first and hands us a matched-depth image.
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
- return {};
- }
-
- if (target_state.chroma != heif_chroma_420) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_R) {
return {};
}
@@ -124,8 +124,8 @@ Op_Any_RGB_to_YCbCr_420_Sharp::state_after_conversion(
output_state.colorspace = heif_colorspace_YCbCr;
output_state.chroma = heif_chroma_420;
- output_state.has_alpha = target_state.has_alpha;
- output_state.bits_per_pixel = target_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(target_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = target_state.has_alpha() ? target_state.bits_per_pixel_Y : 0;
output_state.nclx = target_state.nclx;
states.emplace_back(output_state, SpeedCosts_Slow);
@@ -167,9 +167,9 @@ Op_Any_RGB_to_YCbCr_420_Sharp::convert_colorspace(
input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE ||
(input->get_chroma_format() == heif_chroma_444 &&
input->has_channel(heif_channel_Alpha));
- bool want_alpha = target_state.has_alpha;
+ bool want_alpha = target_state.has_alpha();
- int output_bits = target_state.bits_per_pixel;
+ int output_bits = target_state.bits_per_pixel_Y;
if (auto err = outimg->add_channel(heif_channel_Y, width, height, output_bits, limits) ||
outimg->add_channel(heif_channel_Cb, chroma_width, chroma_height, output_bits, limits) ||
outimg->add_channel(heif_channel_Cr, chroma_width, chroma_height, output_bits, limits)) {
diff --git a/libheif/color-conversion/yuv2rgb.cc b/libheif/color-conversion/yuv2rgb.cc
index 625e01e0..d5e1cf34 100644
--- a/libheif/color-conversion/yuv2rgb.cc
+++ b/libheif/color-conversion/yuv2rgb.cc
@@ -60,18 +60,18 @@ Op_YCbCr_to_RGB<Pixel>::state_after_conversion(const ColorState& input_state,
bool hdr = !std::is_same<Pixel, uint8_t>::value;
- if ((input_state.bits_per_pixel > 8) != hdr) {
+ if ((input_state.bits_per_pixel_Y > 8) != hdr) {
return {};
}
// TODO: add support for <8 bpp
- if (input_state.bits_per_pixel < 8) {
+ if (input_state.bits_per_pixel_Y < 8) {
return {};
}
// The YCgCo-Re conversion computes with int16_t intermediates. 14 bpp input is the
// maximum for which these cannot overflow.
- if (matrix == 16 && input_state.bits_per_pixel > 14) {
+ if (matrix == 16 && input_state.bits_per_pixel_Y > 14) {
return {};
}
@@ -88,9 +88,8 @@ Op_YCbCr_to_RGB<Pixel>::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_444;
- output_state.has_alpha = input_state.has_alpha; // we simply keep the old alpha plane
- output_state.bits_per_pixel = input_state.bits_per_pixel;
- output_state.alpha_bits_per_pixel = input_state.alpha_bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha; // we simply keep the old alpha plane
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -327,8 +326,8 @@ Op_YCbCr420_to_RGB24::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_YCbCr ||
input_state.chroma != heif_chroma_420 ||
- input_state.bits_per_pixel != 8 ||
- input_state.has_alpha == true) {
+ input_state.bits_per_pixel_Y != 8 ||
+ input_state.has_alpha()) {
return {};
}
@@ -348,8 +347,8 @@ Op_YCbCr420_to_RGB24::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGB;
- output_state.has_alpha = false;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = 0;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -460,11 +459,11 @@ Op_YCbCr420_to_RGB32::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_YCbCr ||
input_state.chroma != heif_chroma_420 ||
- input_state.bits_per_pixel != 8) {
+ input_state.bits_per_pixel_Y != 8) {
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_Y) {
return {};
}
@@ -484,8 +483,8 @@ Op_YCbCr420_to_RGB32::state_after_conversion(const ColorState& input_state,
output_state.colorspace = heif_colorspace_RGB;
output_state.chroma = heif_chroma_interleaved_RGBA;
- output_state.has_alpha = true;
- output_state.bits_per_pixel = 8;
+ output_state.set_color_bits_per_pixel(8);
+ output_state.bits_per_pixel_alpha = 8;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
@@ -594,7 +593,7 @@ Op_YCbCr420_to_RRGGBBaa::state_after_conversion(const ColorState& input_state,
if (input_state.colorspace != heif_colorspace_YCbCr ||
input_state.chroma != heif_chroma_420 ||
- input_state.bits_per_pixel <= 8) {
+ input_state.bits_per_pixel_Y <= 8) {
return {};
}
@@ -602,7 +601,7 @@ Op_YCbCr420_to_RRGGBBaa::state_after_conversion(const ColorState& input_state,
return {};
}
- if (input_state.has_alpha && input_state.get_alpha_bits_per_pixel() != input_state.bits_per_pixel) {
+ if (input_state.has_alpha() && input_state.bits_per_pixel_alpha != input_state.bits_per_pixel_Y) {
return {};
}
@@ -618,19 +617,19 @@ Op_YCbCr420_to_RRGGBBaa::state_after_conversion(const ColorState& input_state,
// --- convert to YCbCr
output_state.colorspace = heif_colorspace_RGB;
- output_state.chroma = (input_state.has_alpha ?
+ output_state.chroma = (input_state.has_alpha() ?
heif_chroma_interleaved_RRGGBBAA_LE : heif_chroma_interleaved_RRGGBB_LE);
- output_state.has_alpha = input_state.has_alpha;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
output_state.colorspace = heif_colorspace_RGB;
- output_state.chroma = (input_state.has_alpha ?
+ output_state.chroma = (input_state.has_alpha() ?
heif_chroma_interleaved_RRGGBBAA_BE : heif_chroma_interleaved_RRGGBB_BE);
- output_state.has_alpha = input_state.has_alpha;
- output_state.bits_per_pixel = input_state.bits_per_pixel;
+ output_state.set_color_bits_per_pixel(input_state.bits_per_pixel_Y);
+ output_state.bits_per_pixel_alpha = input_state.bits_per_pixel_alpha;
states.emplace_back(output_state, SpeedCosts_Unoptimized);
diff --git a/tests/conversion.cc b/tests/conversion.cc
index 61a8d782..cdf096b8 100644
--- a/tests/conversion.cc
+++ b/tests/conversion.cc
@@ -172,17 +172,17 @@ std::vector<Plane> GetPlanes(const ColorState& state, int width, int height) {
std::vector<Plane> planes;
if (state.colorspace == heif_colorspace_monochrome) {
if (state.chroma != heif_chroma_monochrome) return {};
- planes.push_back({heif_channel_Y, width, height, state.bits_per_pixel});
- if (state.has_alpha) {
+ planes.push_back({heif_channel_Y, width, height, state.bits_per_pixel_Y});
+ if (state.has_alpha()) {
planes.push_back(
- {heif_channel_Alpha, width, height, state.bits_per_pixel});
+ {heif_channel_Alpha, width, height, state.bits_per_pixel_alpha});
}
} else if (state.colorspace == heif_colorspace_YCbCr) {
if (state.chroma != heif_chroma_444 && state.chroma != heif_chroma_422 &&
state.chroma != heif_chroma_420 && state.chroma != heif_chroma_monochrome) {
return {};
}
- planes.push_back({heif_channel_Y, width, height, state.bits_per_pixel});
+ planes.push_back({heif_channel_Y, width, height, state.bits_per_pixel_Y});
if (state.chroma != heif_chroma_monochrome) {
int chroma_width = state.chroma == heif_chroma_444 ? width : width / 2;
int chroma_height =
@@ -190,13 +190,13 @@ std::vector<Plane> GetPlanes(const ColorState& state, int width, int height) {
? height
: height / 2;
planes.push_back(
- {heif_channel_Cb, chroma_width, chroma_height, state.bits_per_pixel});
+ {heif_channel_Cb, chroma_width, chroma_height, state.bits_per_pixel_Cb});
planes.push_back(
- {heif_channel_Cr, chroma_width, chroma_height, state.bits_per_pixel});
+ {heif_channel_Cr, chroma_width, chroma_height, state.bits_per_pixel_Cr});
}
- if (state.has_alpha) {
+ if (state.has_alpha()) {
planes.push_back(
- {heif_channel_Alpha, width, height, state.bits_per_pixel});
+ {heif_channel_Alpha, width, height, state.bits_per_pixel_alpha});
}
} else if (state.colorspace == heif_colorspace_RGB) {
// heif_chroma_planar is a synonym for heif_chroma_444 in the RGB
@@ -212,16 +212,17 @@ std::vector<Plane> GetPlanes(const ColorState& state, int width, int height) {
return {};
}
if (state.chroma == heif_chroma_444 || state.chroma == heif_chroma_planar) {
- planes.push_back({heif_channel_R, width, height, state.bits_per_pixel});
- planes.push_back({heif_channel_G, width, height, state.bits_per_pixel});
- planes.push_back({heif_channel_B, width, height, state.bits_per_pixel});
- if (state.has_alpha) {
+ planes.push_back({heif_channel_R, width, height, state.bits_per_pixel_R});
+ planes.push_back({heif_channel_G, width, height, state.bits_per_pixel_G});
+ planes.push_back({heif_channel_B, width, height, state.bits_per_pixel_B});
+ if (state.has_alpha()) {
planes.push_back(
- {heif_channel_Alpha, width, height, state.bits_per_pixel});
+ {heif_channel_Alpha, width, height, state.bits_per_pixel_alpha});
}
} else {
+ // Interleaved formats are represented by their per-component depth in R/G/B.
planes.push_back(
- {heif_channel_interleaved, width, height, state.bits_per_pixel});
+ {heif_channel_interleaved, width, height, state.bits_per_pixel_R});
}
} else {
return {}; // Unsupported colorspace.
@@ -321,16 +322,15 @@ void TestConversion(const std::string& test_name, ColorState input_state,
REQUIRE(out_image != nullptr);
CHECK(out_image->get_colorspace() == target_state.colorspace);
CHECK(out_image->get_chroma_format() == target_state.chroma);
- CHECK(out_image->has_alpha() == target_state.has_alpha);
+ CHECK(out_image->has_alpha() == target_state.has_alpha());
for (const Plane& plane : GetPlanes(target_state, width, height)) {
INFO("Channel: " << plane.channel);
size_t stride;
CHECK(out_image->get_channel_memory(plane.channel, &stride) != nullptr);
- CHECK(out_image->get_bits_per_pixel(plane.channel) ==
- target_state.bits_per_pixel);
+ CHECK(out_image->get_bits_per_pixel(plane.channel) == plane.bit_depth);
// If an alpha plane was created from nothing, check that it's filled
// with the max alpha value.
- if (plane.channel == heif_channel_Alpha && !input_state.has_alpha) {
+ if (plane.channel == heif_channel_Alpha && !input_state.has_alpha()) {
double alpha_psnr = GetPsnr(*out_image, *out_image, heif_channel_Alpha,
/*expect_alpha_max=*/true);
REQUIRE(alpha_psnr == 100.f);
@@ -347,10 +347,10 @@ void TestConversion(const std::string& test_name, ColorState input_state,
std::shared_ptr<HeifPixelImage> recovered_image = *recovered_image_result;
// If the alpha plane was lost in the target state, it should come back
// as the max value for the given bpp, i.e. (1<<bpp)-1
- bool expect_alpha_max = !target_state.has_alpha;
+ bool expect_alpha_max = !target_state.has_alpha();
bool expect_lossless =
input_state.colorspace == target_state.colorspace &&
- input_state.bits_per_pixel == target_state.bits_per_pixel &&
+ input_state.get_color_bits_per_pixel() == target_state.get_color_bits_per_pixel() &&
(input_state.chroma == target_state.chroma ||
(input_state.chroma != heif_chroma_420 &&
input_state.chroma != heif_chroma_422 &&
@@ -777,7 +777,7 @@ TEST_CASE("Mismatched alpha bit depth - pipeline construction") {
SECTION("10-bit color, 8-bit alpha -> interleaved RGBA 8-bit") {
ColorState input_state(heif_colorspace_YCbCr, heif_chroma_420, true, 10);
- input_state.alpha_bits_per_pixel = 8;
+ input_state.bits_per_pixel_alpha = 8;
nclx_default_if_undefined(input_state);
ColorState target_state(heif_colorspace_RGB, heif_chroma_interleaved_RGBA, true, 8);
@@ -790,7 +790,7 @@ TEST_CASE("Mismatched alpha bit depth - pipeline construction") {
SECTION("8-bit color, 10-bit alpha -> interleaved RGBA 8-bit") {
ColorState input_state(heif_colorspace_YCbCr, heif_chroma_420, true, 8);
- input_state.alpha_bits_per_pixel = 10;
+ input_state.bits_per_pixel_alpha = 10;
nclx_default_if_undefined(input_state);
ColorState target_state(heif_colorspace_RGB, heif_chroma_interleaved_RGBA, true, 8);
@@ -803,11 +803,11 @@ TEST_CASE("Mismatched alpha bit depth - pipeline construction") {
SECTION("10-bit color, 8-bit alpha -> planar RGB 10-bit") {
ColorState input_state(heif_colorspace_YCbCr, heif_chroma_420, true, 10);
- input_state.alpha_bits_per_pixel = 8;
+ input_state.bits_per_pixel_alpha = 8;
nclx_default_if_undefined(input_state);
ColorState target_state(heif_colorspace_RGB, heif_chroma_444, true, 10);
- target_state.alpha_bits_per_pixel = 10;
+ target_state.bits_per_pixel_alpha = 10;
ColorConversionPipeline pipeline;
bool supported = pipeline.construct_pipeline(input_state, target_state, options, *options_ext);