Commit c1bc58a6 for libheif
commit c1bc58a6d0ea0e96dd679341eaa50f8d2a8c455c
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri Feb 27 12:34:58 2026 +0100
unci: support images where not all cmpd components are covered by uncC
diff --git a/examples/heif_gen_bayer.cc b/examples/heif_gen_bayer.cc
index a9841908..c59f1b81 100644
--- a/examples/heif_gen_bayer.cc
+++ b/examples/heif_gen_bayer.cc
@@ -50,7 +50,7 @@ struct PatternDefinition
std::string name;
uint16_t width;
uint16_t height;
- std::vector<heif_bayer_pattern_pixel> cpat;
+ std::vector<heif_uncompressed_component_type> cpat;
};
@@ -61,10 +61,10 @@ static const PatternDefinition patterns[] = {
{
"rggb", 2, 2,
{
- {heif_uncompressed_component_type_red, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_blue, 1.0f},
+ heif_uncompressed_component_type_red,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_blue,
}
},
@@ -77,25 +77,25 @@ static const PatternDefinition patterns[] = {
{
"rgbw", 4, 4,
{
- {heif_uncompressed_component_type_Y, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_Y, 1.0f},
- {heif_uncompressed_component_type_red, 1.0f},
-
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_Y, 1.0f},
- {heif_uncompressed_component_type_blue, 1.0f},
- {heif_uncompressed_component_type_Y, 1.0f},
-
- {heif_uncompressed_component_type_Y, 1.0f},
- {heif_uncompressed_component_type_blue, 1.0f},
- {heif_uncompressed_component_type_Y, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
-
- {heif_uncompressed_component_type_red, 1.0f},
- {heif_uncompressed_component_type_Y, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_Y, 1.0f},
+ heif_uncompressed_component_type_Y,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_Y,
+ heif_uncompressed_component_type_red,
+
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_Y,
+ heif_uncompressed_component_type_blue,
+ heif_uncompressed_component_type_Y,
+
+ heif_uncompressed_component_type_Y,
+ heif_uncompressed_component_type_blue,
+ heif_uncompressed_component_type_Y,
+ heif_uncompressed_component_type_green,
+
+ heif_uncompressed_component_type_red,
+ heif_uncompressed_component_type_Y,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_Y,
}
},
@@ -107,25 +107,25 @@ static const PatternDefinition patterns[] = {
{
"qbc", 4, 4,
{
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_red, 1.0f},
- {heif_uncompressed_component_type_red, 1.0f},
-
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_red, 1.0f},
- {heif_uncompressed_component_type_red, 1.0f},
-
- {heif_uncompressed_component_type_blue, 1.0f},
- {heif_uncompressed_component_type_blue, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
-
- {heif_uncompressed_component_type_blue, 1.0f},
- {heif_uncompressed_component_type_blue, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
- {heif_uncompressed_component_type_green, 1.0f},
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_red,
+ heif_uncompressed_component_type_red,
+
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_red,
+ heif_uncompressed_component_type_red,
+
+ heif_uncompressed_component_type_blue,
+ heif_uncompressed_component_type_blue,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_green,
+
+ heif_uncompressed_component_type_blue,
+ heif_uncompressed_component_type_blue,
+ heif_uncompressed_component_type_green,
+ heif_uncompressed_component_type_green,
}
},
};
@@ -153,14 +153,14 @@ static std::optional<PatternDefinition> parse_pattern_string(const char* str)
}
uint16_t dim = (len == 4) ? 2 : 4;
- std::vector<heif_bayer_pattern_pixel> cpat;
+ std::vector<heif_uncompressed_component_type> cpat;
cpat.reserve(len);
for (char c : s) {
switch (std::tolower(c)) {
- case 'r': cpat.push_back({heif_uncompressed_component_type_red, 1.0f}); break;
- case 'g': cpat.push_back({heif_uncompressed_component_type_green, 1.0f}); break;
- case 'b': cpat.push_back({heif_uncompressed_component_type_blue, 1.0f}); break;
+ case 'r': cpat.push_back(heif_uncompressed_component_type_red); break;
+ case 'g': cpat.push_back(heif_uncompressed_component_type_green); break;
+ case 'b': cpat.push_back(heif_uncompressed_component_type_blue); break;
default: return {};
}
}
@@ -332,7 +332,7 @@ static heif_image* create_bayer_image_from_png(const char* png_filename,
int px = x % pat->width;
int py = y % pat->height;
- uint16_t comp_type = pat->cpat[py * pat->width + px].component_type;
+ auto comp_type = pat->cpat[py * pat->width + px];
switch (comp_type) {
case heif_uncompressed_component_type_red: dst_row[x] = r; break;
@@ -363,7 +363,7 @@ static heif_image* create_bayer_image_from_png(const char* png_filename,
int px = x % pat->width;
int py = y % pat->height;
- uint16_t comp_type = pat->cpat[py * pat->width + px].component_type;
+ auto comp_type = pat->cpat[py * pat->width + px];
switch (comp_type) {
case heif_uncompressed_component_type_red: dst_row[x] = r; break;
@@ -377,10 +377,19 @@ static heif_image* create_bayer_image_from_png(const char* png_filename,
}
}
+ // Build heif_bayer_pattern_pixel array from component types.
+ // The component_index values here are the component types themselves — the encoder
+ // will resolve them to proper cmpd indices when writing the cpat box.
+ std::vector<heif_bayer_pattern_pixel> bayer_pixels(pat->cpat.size());
+ for (size_t i = 0; i < pat->cpat.size(); i++) {
+ bayer_pixels[i].component_index = static_cast<uint16_t>(pat->cpat[i]);
+ bayer_pixels[i].component_gain = 1.0f;
+ }
+
// Set Bayer pattern metadata
err = heif_image_set_bayer_pattern(bayer_img,
pat->width, pat->height,
- pat->cpat.data());
+ bayer_pixels.data());
if (err.code != heif_error_Ok) {
std::cerr << "Cannot set Bayer pattern: " << err.message << "\n";
heif_image_release(bayer_img);
diff --git a/libheif/api/libheif/heif_uncompressed.h b/libheif/api/libheif/heif_uncompressed.h
index df40d051..26b020b0 100644
--- a/libheif/api/libheif/heif_uncompressed.h
+++ b/libheif/api/libheif/heif_uncompressed.h
@@ -62,16 +62,17 @@ typedef enum heif_uncompressed_component_type
typedef struct heif_bayer_pattern_pixel
{
- uint16_t component_type; // one of heif_uncompressed_component_type values
+ uint16_t component_index; // index into the component definition (cmpd)
float component_gain;
} heif_bayer_pattern_pixel;
// Set a Bayer / filter array pattern on an image.
-// The pattern is a 2D array of component types with dimensions pattern_width x pattern_height.
+// The pattern is a 2D array of component indices with dimensions pattern_width x pattern_height.
// The number of entries in patternPixels must be pattern_width * pattern_height.
-// The component_type values correspond to the ISO 23001-17 component types
-// (e.g. heif_uncompressed_component_type_red=4, heif_uncompressed_component_type_green=5, heif_uncompressed_component_type_blue=6).
-// The encoder resolves these component types to cmpd indices when writing the cpat box.
+// The component_index values are indices into the cmpd component definition table.
+// On the encoder path, these indices are generated by heif_image_add_component() and the
+// encoder adds reference components to cmpd for pattern entries that don't have image planes.
+// On the decoder path, they come directly from the cpat box.
LIBHEIF_API
heif_error heif_image_set_bayer_pattern(heif_image*,
uint16_t pattern_width,
diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index 063c1038..e5c56fdc 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -977,7 +977,7 @@ Error Box_cpat::parse(BitstreamRange& range, const heif_security_limits* limits)
for (size_t i = 0; i < num_pixels; i++) {
heif_bayer_pattern_pixel pixel{};
- pixel.component_type = static_cast<uint16_t>(range.read32());
+ pixel.component_index = static_cast<uint16_t>(range.read32());
pixel.component_gain = range.read_float32();
m_pattern.pixels[i] = pixel;
}
@@ -995,7 +995,7 @@ std::string Box_cpat::dump(Indent& indent) const
sstr << indent << "pattern_height: " << get_pattern_height() << "\n";
for (const auto& pixel : m_pattern.pixels) {
- sstr << indent << "component index: " << pixel.component_type << ", gain: " << pixel.component_gain << "\n";
+ sstr << indent << "component index: " << pixel.component_index << ", gain: " << pixel.component_gain << "\n";
}
return sstr.str();
}
@@ -1015,7 +1015,7 @@ Error Box_cpat::write(StreamWriter& writer) const
writer.write16(m_pattern.pattern_height);
for (const auto& pixel : m_pattern.pixels) {
- writer.write32(pixel.component_type);
+ writer.write32(pixel.component_index);
writer.write_float32(pixel.component_gain);
}
diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc
index 45818d86..66326b7f 100644
--- a/libheif/codecs/uncompressed/unc_codec.cc
+++ b/libheif/codecs/uncompressed/unc_codec.cc
@@ -204,6 +204,16 @@ Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(con
colourspace,
chroma);
+ // Populate the cmpd table on the image so add_component_for_index() can look up types.
+ {
+ std::vector<uint16_t> cmpd_types;
+ cmpd_types.reserve(components.size());
+ for (const auto& c : components) {
+ cmpd_types.push_back(c.component_type);
+ }
+ img->set_cmpd_component_types(std::move(cmpd_types));
+ }
+
for (Box_uncC::Component component : uncC->get_components()) {
if (component.component_index >= components.size()) {
return Error{
@@ -217,23 +227,23 @@ Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(con
if ((component_type == heif_uncompressed_component_type_Cb) ||
(component_type == heif_uncompressed_component_type_Cr)) {
- Result<uint32_t> result = img->add_component((width / chroma_h_subsampling(chroma)),
- (height / chroma_v_subsampling(chroma)),
- component_type,
- unc_component_format_to_datatype(component.component_format),
- component.component_bit_depth,
- limits);
+ Result<uint32_t> result = img->add_component_for_index(component.component_index,
+ (width / chroma_h_subsampling(chroma)),
+ (height / chroma_v_subsampling(chroma)),
+ unc_component_format_to_datatype(component.component_format),
+ component.component_bit_depth,
+ limits);
if (result.is_error()) {
return result.error();
}
}
else {
- Result<uint32_t> result = img->add_component(width,
- height,
- component_type,
- unc_component_format_to_datatype(component.component_format),
- component.component_bit_depth,
- limits);
+ Result<uint32_t> result = img->add_component_for_index(component.component_index,
+ width,
+ height,
+ unc_component_format_to_datatype(component.component_format),
+ component.component_bit_depth,
+ limits);
if (result.is_error()) {
return result.error();
}
diff --git a/libheif/codecs/uncompressed/unc_decoder.cc b/libheif/codecs/uncompressed/unc_decoder.cc
index 34b77bd9..3aebb37e 100644
--- a/libheif/codecs/uncompressed/unc_decoder.cc
+++ b/libheif/codecs/uncompressed/unc_decoder.cc
@@ -38,6 +38,21 @@
#include "security_limits.h"
+static Error validate_component_indices(const std::vector<uint32_t>& indices,
+ size_t cmpd_size,
+ const char* box_name)
+{
+ for (uint32_t idx : indices) {
+ if (idx >= cmpd_size) {
+ return {heif_error_Invalid_input,
+ heif_suberror_Invalid_parameter_value,
+ std::string(box_name) + " component index out of range of cmpd table"};
+ }
+ }
+ return Error::Ok;
+}
+
+
// --- unc_decoder ---
unc_decoder::unc_decoder(uint32_t width, uint32_t height,
@@ -454,32 +469,46 @@ Result<std::shared_ptr<HeifPixelImage> > unc_decoder::decode_full_image(
auto img = *createImgResult;
+ size_t cmpd_size = cmpd ? cmpd->get_components().size() : 0;
+
if (properties.cpat) {
- // Resolve cpat component indices to actual component types from cmpd.
- BayerPattern pattern = properties.cpat->get_pattern();
- const auto& cmpd_components = cmpd->get_components();
- for (auto& pixel : pattern.pixels) {
- uint16_t idx = pixel.component_type;
- if (idx >= cmpd_components.size()) {
- return Error(heif_error_Invalid_input,
- heif_suberror_Invalid_parameter_value,
- "cpat component index out of range");
- }
- pixel.component_type = cmpd_components[idx].component_type;
+ const auto& pattern = properties.cpat->get_pattern();
+ std::vector<uint32_t> cpat_indices;
+ for (const auto& pixel : pattern.pixels) {
+ cpat_indices.push_back(pixel.component_index);
+ }
+ Error err = validate_component_indices(cpat_indices, cmpd_size, "cpat");
+ if (err) {
+ return err;
}
img->set_bayer_pattern(pattern);
}
for (const auto& splz_box : properties.splz) {
- img->add_polarization_pattern(splz_box->get_pattern());
+ const auto& pattern = splz_box->get_pattern();
+ Error err = validate_component_indices(pattern.component_indices, cmpd_size, "splz");
+ if (err) {
+ return err;
+ }
+ img->add_polarization_pattern(pattern);
}
for (const auto& sbpm_box : properties.sbpm) {
- img->add_sensor_bad_pixels_map(sbpm_box->get_bad_pixels_map());
+ const auto& bad_pixels_map = sbpm_box->get_bad_pixels_map();
+ Error err = validate_component_indices(bad_pixels_map.component_indices, cmpd_size, "sbpm");
+ if (err) {
+ return err;
+ }
+ img->add_sensor_bad_pixels_map(bad_pixels_map);
}
for (const auto& snuc_box : properties.snuc) {
- img->add_sensor_nuc(snuc_box->get_nuc());
+ const auto& nuc = snuc_box->get_nuc();
+ Error err = validate_component_indices(nuc.component_indices, cmpd_size, "snuc");
+ if (err) {
+ return err;
+ }
+ img->add_sensor_nuc(nuc);
}
if (properties.cloc) {
diff --git a/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc b/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc
index daf5696b..1ba6e1c4 100644
--- a/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_bytealign_component_interleave.cc
@@ -86,7 +86,7 @@ Error unc_decoder_bytealign_component_interleave::decode_tile(const std::vector<
comp[i].use = true; // map_uncompressed_component_to_channel(m_cmpd, c, &channel);
if (comp[i].use) {
- comp[i].dst_plane = img->get_component(i, &comp[i].dst_plane_stride);
+ comp[i].dst_plane = img->get_component(c.component_index, &comp[i].dst_plane_stride);
}
else {
comp[i].dst_plane = nullptr;
diff --git a/libheif/codecs/uncompressed/unc_decoder_legacybase.cc b/libheif/codecs/uncompressed/unc_decoder_legacybase.cc
index b28d9f9a..af706ae5 100644
--- a/libheif/codecs/uncompressed/unc_decoder_legacybase.cc
+++ b/libheif/codecs/uncompressed/unc_decoder_legacybase.cc
@@ -58,11 +58,9 @@ void unc_decoder_legacybase::ensureChannelList(std::shared_ptr<HeifPixelImage>&
void unc_decoder_legacybase::buildChannelList(std::shared_ptr<HeifPixelImage>& img)
{
- uint32_t idx = 0;
for (Box_uncC::Component component : m_uncC->get_components()) {
- ChannelListEntry entry = buildChannelListEntry(idx, component, img);
+ ChannelListEntry entry = buildChannelListEntry(component.component_index, component, img);
channelList.push_back(entry);
- idx++;
}
}
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
index e8f9727f..a24bc808 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
@@ -112,12 +112,17 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
if (image->has_bayer_pattern()) {
const BayerPattern& bayer = image->get_bayer_pattern();
+ // The bayer pattern stores component_index values. When the image has a cmpd
+ // table (add_component path), we look up the component type from it. When it
+ // doesn't (legacy add_plane path), the component_index IS the component type.
+
// Collect unique component types from the pattern (in order of first appearance)
std::vector<uint16_t> unique_types;
std::set<uint16_t> seen;
for (const auto& pixel : bayer.pixels) {
- if (seen.insert(pixel.component_type).second) {
- unique_types.push_back(pixel.component_type);
+ uint16_t comp_type = pixel.component_index; // legacy: index IS the type
+ if (seen.insert(comp_type).second) {
+ unique_types.push_back(comp_type);
}
}
@@ -136,7 +141,8 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
cpat_pattern.pattern_height = bayer.pattern_height;
cpat_pattern.pixels.resize(bayer.pixels.size());
for (size_t i = 0; i < bayer.pixels.size(); i++) {
- cpat_pattern.pixels[i].component_type = type_to_cmpd_index[bayer.pixels[i].component_type];
+ uint16_t comp_type = bayer.pixels[i].component_index; // legacy: index IS the type
+ cpat_pattern.pixels[i].component_index = type_to_cmpd_index[comp_type];
cpat_pattern.pixels[i].component_gain = bayer.pixels[i].component_gain;
}
diff --git a/libheif/color-conversion/bayer_bilinear.cc b/libheif/color-conversion/bayer_bilinear.cc
index 205bcf85..9e0ef309 100644
--- a/libheif/color-conversion/bayer_bilinear.cc
+++ b/libheif/color-conversion/bayer_bilinear.cc
@@ -122,7 +122,8 @@ Op_bayer_bilinear_to_RGB24_32::convert_colorspace(const std::shared_ptr<const He
// Build a lookup table: for each pattern position, which RGB channel (0=R,1=G,2=B) does it provide?
std::vector<int> pattern_channel(pw * ph);
for (int i = 0; i < pw * ph; i++) {
- pattern_channel[i] = component_type_to_rgb_index(pattern.pixels[i].component_type);
+ uint16_t comp_type = input->get_component_type(pattern.pixels[i].component_index);
+ pattern_channel[i] = component_type_to_rgb_index(comp_type);
if (pattern_channel[i] < 0) {
return Error(heif_error_Unsupported_feature,
heif_suberror_Unsupported_data_version,
diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc
index 62e6d25f..e76fc271 100644
--- a/libheif/pixelimage.cc
+++ b/libheif/pixelimage.cc
@@ -125,6 +125,37 @@ heif_channel map_uncompressed_component_to_channel(uint16_t component_type)
}
+static uint16_t map_channel_to_component_type(heif_channel channel)
+{
+ switch (channel) {
+ case heif_channel_Y:
+ return heif_uncompressed_component_type_Y;
+ case heif_channel_Cb:
+ return heif_uncompressed_component_type_Cb;
+ case heif_channel_Cr:
+ return heif_uncompressed_component_type_Cr;
+ case heif_channel_R:
+ return heif_uncompressed_component_type_red;
+ case heif_channel_G:
+ return heif_uncompressed_component_type_green;
+ case heif_channel_B:
+ return heif_uncompressed_component_type_blue;
+ case heif_channel_Alpha:
+ return heif_uncompressed_component_type_alpha;
+ case heif_channel_filter_array:
+ return heif_uncompressed_component_type_filter_array;
+ case heif_channel_depth:
+ return heif_uncompressed_component_type_depth;
+ case heif_channel_disparity:
+ return heif_uncompressed_component_type_disparity;
+ default:
+ // For interleaved and other channels without a direct match,
+ // use an internal custom value.
+ return static_cast<uint16_t>(1000 + channel);
+ }
+}
+
+
ImageExtraData::~ImageExtraData()
{
@@ -421,6 +452,8 @@ Error HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t h
{
ImageComponent plane;
plane.m_channel = channel;
+ m_cmpd_component_types.push_back(map_channel_to_component_type(channel));
+ plane.m_component_index = static_cast<uint32_t>(m_cmpd_component_types.size() - 1);
int num_interleaved_pixels = num_interleaved_components_per_plane(m_chroma);
// for backwards compatibility, allow for 24/32 bits for RGB/RGBA interleaved chromas
@@ -448,6 +481,8 @@ Error HeifPixelImage::add_channel(heif_channel channel, uint32_t width, uint32_t
{
ImageComponent plane;
plane.m_channel = channel;
+ m_cmpd_component_types.push_back(map_channel_to_component_type(channel));
+ plane.m_component_index = static_cast<uint32_t>(m_cmpd_component_types.size() - 1);
if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
return err;
}
@@ -742,54 +777,74 @@ uint32_t HeifPixelImage::get_height(enum heif_channel channel) const
uint32_t HeifPixelImage::get_width(uint32_t component_idx) const
{
- if (component_idx >= m_planes.size()) {
+ auto* comp = find_component_by_index(component_idx);
+ if (!comp) {
return 0;
}
- return m_planes[component_idx].m_width;
+ return comp->m_width;
}
uint32_t HeifPixelImage::get_height(uint32_t component_idx) const
{
- if (component_idx >= m_planes.size()) {
+ auto* comp = find_component_by_index(component_idx);
+ if (!comp) {
return 0;
}
- return m_planes[component_idx].m_height;
+ return comp->m_height;
}
uint32_t HeifPixelImage::get_primary_component() const
{
- // first pass: search for color channel
+ // first pass: search for a visual channel
for (uint32_t idx=0; idx<m_planes.size(); idx++) {
- if (m_planes[idx].m_channel == heif_channel_interleaved) {
- return idx;
- }
-
- switch (m_planes[idx].m_component_type) {
- case heif_uncompressed_component_type_Y:
- case heif_uncompressed_component_type_monochrome:
- case heif_uncompressed_component_type_red:
- case heif_uncompressed_component_type_green:
- case heif_uncompressed_component_type_blue:
- case heif_uncompressed_component_type_cyan:
- case heif_uncompressed_component_type_magenta:
- case heif_uncompressed_component_type_yellow:
- case heif_uncompressed_component_type_key_black:
- case heif_uncompressed_component_type_filter_array:
- case heif_uncompressed_component_type_palette:
- return idx;
-
+ switch (m_planes[idx].m_channel) {
+ case heif_channel_interleaved:
+ case heif_channel_Y:
+ case heif_channel_R:
+ case heif_channel_G:
+ case heif_channel_B:
+ case heif_channel_filter_array:
+ return m_planes[idx].m_component_index;
default:
; // NOP
}
}
- // second pass: allow anything
+ // second pass: if we have a cmpd table, use component types
+
+ if (!m_cmpd_component_types.empty()) {
+ for (uint32_t idx=0; idx<m_planes.size(); idx++) {
+ uint16_t comp_type = get_component_type(m_planes[idx].m_component_index);
+ switch (comp_type) {
+ case heif_uncompressed_component_type_Y:
+ case heif_uncompressed_component_type_monochrome:
+ case heif_uncompressed_component_type_red:
+ case heif_uncompressed_component_type_green:
+ case heif_uncompressed_component_type_blue:
+ case heif_uncompressed_component_type_cyan:
+ case heif_uncompressed_component_type_magenta:
+ case heif_uncompressed_component_type_yellow:
+ case heif_uncompressed_component_type_key_black:
+ case heif_uncompressed_component_type_filter_array:
+ case heif_uncompressed_component_type_palette:
+ return m_planes[idx].m_component_index;
+
+ default:
+ ; // NOP
+ }
+ }
+ }
+
+ // third pass: allow anything
+ if (!m_planes.empty()) {
+ return m_planes[0].m_component_index;
+ }
return 0;
}
#if 0
@@ -2056,38 +2111,60 @@ HeifPixelImage::extract_image_area(uint32_t x0, uint32_t y0, uint32_t w, uint32_
// --- index-based component access methods
+HeifPixelImage::ImageComponent* HeifPixelImage::find_component_by_index(uint32_t component_index)
+{
+ for (auto& plane : m_planes) {
+ if (plane.m_component_index == component_index) {
+ return &plane;
+ }
+ }
+ return nullptr;
+}
+
+
+const HeifPixelImage::ImageComponent* HeifPixelImage::find_component_by_index(uint32_t component_index) const
+{
+ return const_cast<HeifPixelImage*>(this)->find_component_by_index(component_index);
+}
+
+
heif_channel HeifPixelImage::get_component_channel(uint32_t component_idx) const
{
- assert(component_idx < m_planes.size());
- return m_planes[component_idx].m_channel;
+ auto* comp = find_component_by_index(component_idx);
+ assert(comp);
+ return comp->m_channel;
}
uint32_t HeifPixelImage::get_component_width(uint32_t component_idx) const
{
- assert(component_idx < m_planes.size());
- return m_planes[component_idx].m_width;
+ auto* comp = find_component_by_index(component_idx);
+ assert(comp);
+ return comp->m_width;
}
uint32_t HeifPixelImage::get_component_height(uint32_t component_idx) const
{
- assert(component_idx < m_planes.size());
- return m_planes[component_idx].m_height;
+ auto* comp = find_component_by_index(component_idx);
+ assert(comp);
+ return comp->m_height;
}
uint8_t HeifPixelImage::get_component_bits_per_pixel(uint32_t component_idx) const
{
- assert(component_idx < m_planes.size());
- return m_planes[component_idx].m_bit_depth;
+ auto* comp = find_component_by_index(component_idx);
+ assert(comp);
+ return comp->m_bit_depth;
}
uint8_t HeifPixelImage::get_component_storage_bits_per_pixel(uint32_t component_idx) const
{
- assert(component_idx < m_planes.size());
- uint32_t bpp = m_planes[component_idx].get_bytes_per_pixel() * 8;
+ auto* comp = find_component_by_index(component_idx);
+ assert(comp);
+ uint32_t bpp = comp->get_bytes_per_pixel() * 8;
assert(bpp <= 255);
return static_cast<uint8_t>(bpp);
}
@@ -2095,15 +2172,18 @@ uint8_t HeifPixelImage::get_component_storage_bits_per_pixel(uint32_t component_
heif_channel_datatype HeifPixelImage::get_component_datatype(uint32_t component_idx) const
{
- assert(component_idx < m_planes.size());
- return m_planes[component_idx].m_datatype;
+ auto* comp = find_component_by_index(component_idx);
+ assert(comp);
+ return comp->m_datatype;
}
uint16_t HeifPixelImage::get_component_type(uint32_t component_idx) const
{
- assert(component_idx < m_planes.size());
- return m_planes[component_idx].m_component_type;
+ if (component_idx >= m_cmpd_component_types.size()) {
+ return 0;
+ }
+ return m_cmpd_component_types[component_idx];
}
@@ -2112,15 +2192,54 @@ Result<uint32_t> HeifPixelImage::add_component(uint32_t width, uint32_t height,
heif_channel_datatype datatype, int bit_depth,
const heif_security_limits* limits)
{
+ // Auto-generate component_index by appending to cmpd table
+ m_cmpd_component_types.push_back(component_type);
+ uint32_t component_index = static_cast<uint32_t>(m_cmpd_component_types.size() - 1);
+
+ ImageComponent plane;
+ plane.m_channel = map_uncompressed_component_to_channel(component_type);
+ plane.m_component_index = component_index;
+ if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
+ return err;
+ }
+
+ m_planes.push_back(plane);
+ return component_index;
+}
+
+
+Result<uint32_t> HeifPixelImage::add_component_for_index(uint32_t component_index,
+ uint32_t width, uint32_t height,
+ heif_channel_datatype datatype, int bit_depth,
+ const heif_security_limits* limits)
+{
+ if (component_index >= m_cmpd_component_types.size()) {
+ return Error{heif_error_Usage_error, heif_suberror_Invalid_parameter_value,
+ "component_index out of range of cmpd table"};
+ }
+
+ uint16_t component_type = m_cmpd_component_types[component_index];
+
ImageComponent plane;
plane.m_channel = map_uncompressed_component_to_channel(component_type);
- plane.m_component_type = component_type;
+ plane.m_component_index = component_index;
if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits, m_memory_handle)) {
return err;
}
m_planes.push_back(plane);
- return static_cast<uint32_t>(m_planes.size() - 1);
+ return component_index;
+}
+
+
+std::vector<uint32_t> HeifPixelImage::get_component_indices() const
+{
+ std::vector<uint32_t> indices;
+ indices.reserve(m_planes.size());
+ for (const auto& plane : m_planes) {
+ indices.push_back(plane.m_component_index);
+ }
+ return indices;
}
diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h
index 471802ce..f79e4bf0 100644
--- a/libheif/pixelimage.h
+++ b/libheif/pixelimage.h
@@ -426,29 +426,44 @@ public:
uint8_t get_component_storage_bits_per_pixel(uint32_t component_idx) const;
heif_channel_datatype get_component_datatype(uint32_t component_idx) const;
+ // Look up the component type from the cmpd table. Works for any cmpd index,
+ // even those that have no image plane (e.g. bayer reference components).
uint16_t get_component_type(uint32_t component_idx) const;
+ // Encoder path: auto-generates component_index by appending to cmpd table.
Result<uint32_t> add_component(uint32_t width, uint32_t height,
uint16_t component_type,
heif_channel_datatype datatype, int bit_depth,
const heif_security_limits* limits);
+ // Decoder path: uses a pre-populated cmpd table to look up the component type.
+ Result<uint32_t> add_component_for_index(uint32_t component_index,
+ uint32_t width, uint32_t height,
+ heif_channel_datatype datatype, int bit_depth,
+ const heif_security_limits* limits);
+
+ // Populate the cmpd component types table (decoder path).
+ void set_cmpd_component_types(std::vector<uint16_t> types) { m_cmpd_component_types = std::move(types); }
+
+ // Returns the sorted list of component_indices of all planes.
+ std::vector<uint32_t> get_component_indices() const;
+
uint8_t* get_component(uint32_t component_idx, size_t* out_stride);
const uint8_t* get_component(uint32_t component_idx, size_t* out_stride) const;
template <typename T>
T* get_component_data(uint32_t component_idx, size_t* out_stride)
{
- if (component_idx >= m_planes.size()) {
+ auto* comp = find_component_by_index(component_idx);
+ if (!comp) {
if (out_stride) *out_stride = 0;
return nullptr;
}
- auto& comp = m_planes[component_idx];
if (out_stride) {
- *out_stride = comp.stride / sizeof(T);
+ *out_stride = comp->stride / sizeof(T);
}
- return static_cast<T*>(comp.mem);
+ return static_cast<T*>(comp->mem);
}
template <typename T>
@@ -519,7 +534,7 @@ private:
struct ImageComponent
{
heif_channel m_channel = heif_channel_Y;
- uint16_t m_component_type = 0; // ISO 23001-17 component type (0 = monochrome)
+ uint32_t m_component_index = 0; // index into the cmpd component definition table
// limits=nullptr disables the limits
Error alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth,
@@ -561,12 +576,16 @@ private:
ImageComponent* find_component_for_channel(heif_channel channel);
const ImageComponent* find_component_for_channel(heif_channel channel) const;
+ ImageComponent* find_component_by_index(uint32_t component_index);
+ const ImageComponent* find_component_by_index(uint32_t component_index) const;
+
uint32_t m_width = 0;
uint32_t m_height = 0;
heif_colorspace m_colorspace = heif_colorspace_undefined;
heif_chroma m_chroma = heif_chroma_undefined;
std::vector<ImageComponent> m_planes;
+ std::vector<uint16_t> m_cmpd_component_types; // indexed by cmpd index
MemoryHandle m_memory_handle;
uint32_t m_sample_duration = 0; // duration of a sequence frame