Commit 92a4a312 for libheif
commit 92a4a31209fe8a8d8454dbaab993a7abd99b2176
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Thu Feb 26 01:17:45 2026 +0100
unci: implement writing filter-array images. Add heif-gen-bayer example to encode PNG as filter-array
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index f6e76805..9eb96b7b 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -75,6 +75,14 @@ if (WITH_HEADER_COMPRESSION)
endif ()
+if (PNG_FOUND)
+ add_executable(heif-gen-bayer
+ heif_gen_bayer.cc)
+ target_link_libraries(heif-gen-bayer PRIVATE heif heifio)
+ target_include_directories(heif-gen-bayer PRIVATE ${libheif_SOURCE_DIR})
+endif()
+
+
if (WITH_EXAMPLE_HEIF_THUMB AND PNG_FOUND)
add_executable(heif-thumbnailer ${getopt_sources}
heif_thumbnailer.cc
diff --git a/examples/heif_gen_bayer.cc b/examples/heif_gen_bayer.cc
new file mode 100644
index 00000000..adc17e6e
--- /dev/null
+++ b/examples/heif_gen_bayer.cc
@@ -0,0 +1,357 @@
+/*
+ libheif example application "heif-gen-bayer".
+
+ MIT License
+
+ Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <getopt.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <libheif/heif.h>
+#include <libheif/heif_uncompressed.h>
+
+#include "heifio/decoder_png.h"
+
+
+struct PatternDefinition
+{
+ const char* name;
+ uint16_t width;
+ uint16_t height;
+ std::vector<heif_bayer_pattern_pixel> cpat;
+};
+
+
+static const PatternDefinition patterns[] = {
+ // RGGB (standard Bayer)
+ // R G
+ // G B
+ {
+ "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},
+ }
+ },
+
+ // GBRG
+ // G B
+ // R G
+ {
+ "gbrg", 2, 2,
+ {
+ {heif_uncompressed_component_type_green, 1.0f},
+ {heif_uncompressed_component_type_blue, 1.0f},
+ {heif_uncompressed_component_type_red, 1.0f},
+ {heif_uncompressed_component_type_green, 1.0f},
+ }
+ },
+
+ // RGBW (Red-Green-Blue-White) — 4×4
+ // W G W R
+ // G W B W
+ // W B W G
+ // R W G W
+ // White is an unfiltered (panchromatic) pixel → Y component type.
+ {
+ "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},
+ }
+ },
+
+ // QBC (Quad Bayer Coding) — 4×4
+ // G G R R
+ // G G R R
+ // B B G G
+ // B B G G
+ {
+ "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},
+ }
+ },
+};
+
+static constexpr int num_patterns = sizeof(patterns) / sizeof(patterns[0]);
+
+
+static const PatternDefinition* find_pattern(const char* name)
+{
+ for (int i = 0; i < num_patterns; i++) {
+ if (strcasecmp(patterns[i].name, name) == 0) {
+ return &patterns[i];
+ }
+ }
+ return nullptr;
+}
+
+
+static void print_usage()
+{
+ std::cerr << "Usage: heif-gen-bayer [options] <input.png> <output.heif>\n\n"
+ << "Options:\n"
+ << " -h, --help show this help\n"
+ << " -p, --pattern <name> filter array pattern (default: rggb)\n\n"
+ << "Patterns:\n";
+ for (int i = 0; i < num_patterns; i++) {
+ std::cerr << " " << patterns[i].name
+ << " (" << patterns[i].width << "x" << patterns[i].height << ")"
+ << (i == 0 ? " [default]" : "")
+ << "\n";
+ }
+}
+
+
+static struct option long_options[] = {
+ {(char* const) "help", no_argument, nullptr, 'h'},
+ {(char* const) "pattern", required_argument, nullptr, 'p'},
+ {nullptr, 0, nullptr, 0}
+};
+
+
+int main(int argc, char* argv[])
+{
+ const PatternDefinition* pat = &patterns[0]; // default: RGGB
+
+ while (true) {
+ int option_index = 0;
+ int c = getopt_long(argc, argv, "hp:", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ return 0;
+
+ case 'p':
+ pat = find_pattern(optarg);
+ if (!pat) {
+ std::cerr << "Unknown pattern: " << optarg << "\n";
+ print_usage();
+ return 1;
+ }
+ break;
+
+ default:
+ print_usage();
+ return 1;
+ }
+ }
+
+ if (argc - optind != 2) {
+ print_usage();
+ return 1;
+ }
+
+ const char* input_filename = argv[optind];
+ const char* output_filename = argv[optind + 1];
+
+ // --- Load PNG
+
+ InputImage input_image;
+ heif_error err = loadPNG(input_filename, 8, &input_image);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Cannot load PNG: " << err.message << "\n";
+ return 1;
+ }
+
+ heif_image* src_img = input_image.image.get();
+
+ int width = heif_image_get_primary_width(src_img);
+ int height = heif_image_get_primary_height(src_img);
+
+ int bpp = heif_image_get_bits_per_pixel_range(src_img, heif_channel_interleaved);
+ if (bpp != 8) {
+ std::cerr << "Only 8-bit PNG input is supported. Got " << bpp << " bits per pixel.\n";
+ return 1;
+ }
+
+ if (width % pat->width != 0 || height % pat->height != 0) {
+ std::cerr << "Image dimensions must be multiples of the pattern size ("
+ << pat->width << "x" << pat->height << "). Got "
+ << width << "x" << height << "\n";
+ return 1;
+ }
+
+ // --- Get source RGB data
+
+ int src_stride;
+ const uint8_t* src_data = heif_image_get_plane_readonly(src_img, heif_channel_interleaved, &src_stride);
+ if (!src_data) {
+ std::cerr << "Failed to get interleaved RGB plane from PNG.\n";
+ return 1;
+ }
+
+ // --- Create Bayer image
+
+ heif_image* bayer_img = nullptr;
+ err = heif_image_create(width, height,
+ heif_colorspace_monochrome,
+ heif_chroma_monochrome,
+ &bayer_img);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Cannot create image: " << err.message << "\n";
+ return 1;
+ }
+
+ err = heif_image_add_plane(bayer_img, heif_channel_filter_array, width, height, 8);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Cannot add plane: " << err.message << "\n";
+ heif_image_release(bayer_img);
+ return 1;
+ }
+
+ int dst_stride;
+ uint8_t* dst_data = heif_image_get_plane(bayer_img, heif_channel_filter_array, &dst_stride);
+
+ // --- Convert RGB to filter array using the selected pattern
+
+ for (int y = 0; y < height; y++) {
+ const uint8_t* src_row = src_data + y * src_stride;
+ uint8_t* dst_row = dst_data + y * dst_stride;
+
+ for (int x = 0; x < width; x++) {
+ uint8_t r = src_row[x * 3 + 0];
+ uint8_t g = src_row[x * 3 + 1];
+ uint8_t b = src_row[x * 3 + 2];
+
+ int px = x % pat->width;
+ int py = y % pat->height;
+ uint16_t comp_type = pat->cpat[py * pat->width + px].component_type;
+
+ switch (comp_type) {
+ case heif_uncompressed_component_type_red: dst_row[x] = r; break;
+ case heif_uncompressed_component_type_green: dst_row[x] = g; break;
+ case heif_uncompressed_component_type_blue: dst_row[x] = b; break;
+ case heif_uncompressed_component_type_Y: dst_row[x] = static_cast<uint8_t>((r + g + b) / 3); break; // Y / white
+ default:
+ assert(false);
+ }
+ }
+ }
+
+ // --- Set Bayer pattern metadata
+
+ err = heif_image_set_bayer_pattern(bayer_img,
+ pat->width, pat->height,
+ pat->cpat.data());
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Cannot set Bayer pattern: " << err.message << "\n";
+ heif_image_release(bayer_img);
+ return 1;
+ }
+
+ // --- Encode
+
+ heif_context* ctx = heif_context_alloc();
+
+ heif_encoder* encoder = nullptr;
+ err = heif_context_get_encoder_for_format(ctx, heif_compression_uncompressed, &encoder);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Cannot get uncompressed encoder: " << err.message << "\n";
+ heif_image_release(bayer_img);
+ heif_context_free(ctx);
+ return 1;
+ }
+
+ heif_encoding_options* options = heif_encoding_options_alloc();
+
+ heif_image_handle* handle = nullptr;
+ err = heif_context_encode_image(ctx, bayer_img, encoder, options, &handle);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Cannot encode image: " << err.message << "\n";
+ heif_encoding_options_free(options);
+ heif_encoder_release(encoder);
+ heif_image_release(bayer_img);
+ heif_context_free(ctx);
+ return 1;
+ }
+
+ heif_encoding_options_free(options);
+ heif_encoder_release(encoder);
+ heif_image_handle_release(handle);
+ heif_image_release(bayer_img);
+
+ // --- Write file
+
+ err = heif_context_write_to_file(ctx, output_filename);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Cannot write file: " << err.message << "\n";
+ heif_context_free(ctx);
+ return 1;
+ }
+
+ heif_context_free(ctx);
+
+ std::cout << "Wrote " << pat->name << " ("
+ << pat->width << "x" << pat->height
+ << ") Bayer image to " << output_filename << "\n";
+
+ return 0;
+}
diff --git a/libheif/api/libheif/heif_uncompressed.cc b/libheif/api/libheif/heif_uncompressed.cc
index 0064ab35..c9b21f98 100644
--- a/libheif/api/libheif/heif_uncompressed.cc
+++ b/libheif/api/libheif/heif_uncompressed.cc
@@ -21,6 +21,7 @@
#include "heif_uncompressed.h"
#include "context.h"
#include "api_structs.h"
+#include "pixelimage.h"
#include "image-items/unc_image.h"
#include <array>
@@ -28,6 +29,82 @@
#include <algorithm>
+heif_error heif_image_set_bayer_pattern(heif_image* image,
+ uint16_t pattern_width,
+ uint16_t pattern_height,
+ const struct heif_bayer_pattern_pixel* patternPixels)
+{
+ if (image == nullptr || patternPixels == nullptr) {
+ return heif_error_null_pointer_argument;
+ }
+
+ if (pattern_width == 0 || pattern_height == 0) {
+ return {heif_error_Usage_error,
+ heif_suberror_Invalid_parameter_value,
+ "Bayer pattern dimensions must be non-zero."};
+ }
+
+ BayerPattern pattern;
+ pattern.pattern_width = pattern_width;
+ pattern.pattern_height = pattern_height;
+
+ size_t num_pixels = size_t{pattern_width} * pattern_height;
+ pattern.pixels.assign(patternPixels, patternPixels + num_pixels);
+
+ image->image->set_bayer_pattern(pattern);
+
+ return heif_error_success;
+}
+
+
+int heif_image_has_bayer_pattern(const heif_image* image,
+ uint16_t* out_pattern_width,
+ uint16_t* out_pattern_height)
+{
+ if (image == nullptr || !image->image->has_bayer_pattern()) {
+ if (out_pattern_width) {
+ *out_pattern_width = 0;
+ }
+ if (out_pattern_height) {
+ *out_pattern_height = 0;
+ }
+ return 0;
+ }
+
+ const BayerPattern& pattern = image->image->get_bayer_pattern();
+
+ if (out_pattern_width) {
+ *out_pattern_width = pattern.pattern_width;
+ }
+ if (out_pattern_height) {
+ *out_pattern_height = pattern.pattern_height;
+ }
+
+ return 1;
+}
+
+
+heif_error heif_image_get_bayer_pattern(const heif_image* image,
+ struct heif_bayer_pattern_pixel* out_patternPixels)
+{
+ if (image == nullptr || out_patternPixels == nullptr) {
+ return heif_error_null_pointer_argument;
+ }
+
+ if (!image->image->has_bayer_pattern()) {
+ return {heif_error_Usage_error,
+ heif_suberror_Invalid_parameter_value,
+ "Image does not have a Bayer pattern."};
+ }
+
+ const BayerPattern& pattern = image->image->get_bayer_pattern();
+ size_t num_pixels = size_t{pattern.pattern_width} * pattern.pattern_height;
+ std::copy(pattern.pixels.begin(), pattern.pixels.begin() + num_pixels, out_patternPixels);
+
+ return heif_error_success;
+}
+
+
heif_unci_image_parameters* heif_unci_image_parameters_alloc()
{
auto* params = new heif_unci_image_parameters();
diff --git a/libheif/api/libheif/heif_uncompressed.h b/libheif/api/libheif/heif_uncompressed.h
index 427c7a1f..5273f8c3 100644
--- a/libheif/api/libheif/heif_uncompressed.h
+++ b/libheif/api/libheif/heif_uncompressed.h
@@ -34,6 +34,66 @@ extern "C" {
* See heif_metadata_compression for more information.
*/
+// --- ISO 23001-17 component types (Table 1)
+
+enum heif_uncompressed_component_type
+{
+ heif_uncompressed_component_type_monochrome = 0,
+ heif_uncompressed_component_type_Y = 1,
+ heif_uncompressed_component_type_Cb = 2,
+ heif_uncompressed_component_type_Cr = 3,
+ heif_uncompressed_component_type_red = 4,
+ heif_uncompressed_component_type_green = 5,
+ heif_uncompressed_component_type_blue = 6,
+ heif_uncompressed_component_type_alpha = 7,
+ heif_uncompressed_component_type_depth = 8,
+ heif_uncompressed_component_type_disparity = 9,
+ heif_uncompressed_component_type_palette = 10,
+ heif_uncompressed_component_type_filter_array = 11,
+ heif_uncompressed_component_type_padded = 12,
+ heif_uncompressed_component_type_cyan = 13,
+ heif_uncompressed_component_type_magenta = 14,
+ heif_uncompressed_component_type_yellow = 15,
+ heif_uncompressed_component_type_key_black = 16
+};
+
+
+// --- Bayer / filter array pattern
+
+struct heif_bayer_pattern_pixel
+{
+ uint16_t component_type; // one of heif_uncompressed_component_type values
+ float component_gain;
+};
+
+// 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 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.
+LIBHEIF_API
+heif_error heif_image_set_bayer_pattern(heif_image*,
+ uint16_t pattern_width,
+ uint16_t pattern_height,
+ const struct heif_bayer_pattern_pixel* patternPixels);
+
+// Returns whether the image has a Bayer / filter array pattern.
+// If the image has a pattern, out_pattern_width and out_pattern_height are set.
+// Either output pointer may be NULL if the caller does not need that value.
+LIBHEIF_API
+int heif_image_has_bayer_pattern(const heif_image*,
+ uint16_t* out_pattern_width,
+ uint16_t* out_pattern_height);
+
+// Get the Bayer / filter array pattern pixels.
+// The caller must provide an array large enough for pattern_width * pattern_height entries
+// (use heif_image_has_bayer_pattern() to query the dimensions first).
+// Returns heif_error_Ok on success, or an error if no pattern is set.
+LIBHEIF_API
+heif_error heif_image_get_bayer_pattern(const heif_image*,
+ struct heif_bayer_pattern_pixel* out_patternPixels);
+
// --- 'unci' images
// This is similar to heif_metadata_compression. We should try to keep the integers compatible, but each enum will just
diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index 67797b78..26396f6d 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -96,27 +96,27 @@ bool is_predefined_component_type(uint16_t type)
{
// check whether the component type can be mapped to heif_uncompressed_component_type and we have a name defined for
// it in sNames_uncompressed_component_type.
- return type <= component_type_max_valid;
+ return type <= heif_uncompressed_component_type_max_valid;
}
static std::map<heif_uncompressed_component_type, const char*> sNames_uncompressed_component_type{
- {component_type_monochrome, "monochrome"},
- {component_type_Y, "Y"},
- {component_type_Cb, "Cb"},
- {component_type_Cr, "Cr"},
- {component_type_red, "red"},
- {component_type_green, "green"},
- {component_type_blue, "blue"},
- {component_type_alpha, "alpha"},
- {component_type_depth, "depth"},
- {component_type_disparity, "disparity"},
- {component_type_palette, "palette"},
- {component_type_filter_array, "filter-array"},
- {component_type_padded, "padded"},
- {component_type_cyan, "cyan"},
- {component_type_magenta, "magenta"},
- {component_type_yellow, "yellow"},
- {component_type_key_black, "key (black)"}
+ {heif_uncompressed_component_type_monochrome, "monochrome"},
+ {heif_uncompressed_component_type_Y, "Y"},
+ {heif_uncompressed_component_type_Cb, "Cb"},
+ {heif_uncompressed_component_type_Cr, "Cr"},
+ {heif_uncompressed_component_type_red, "red"},
+ {heif_uncompressed_component_type_green, "green"},
+ {heif_uncompressed_component_type_blue, "blue"},
+ {heif_uncompressed_component_type_alpha, "alpha"},
+ {heif_uncompressed_component_type_depth, "depth"},
+ {heif_uncompressed_component_type_disparity, "disparity"},
+ {heif_uncompressed_component_type_palette, "palette"},
+ {heif_uncompressed_component_type_filter_array, "filter-array"},
+ {heif_uncompressed_component_type_padded, "padded"},
+ {heif_uncompressed_component_type_cyan, "cyan"},
+ {heif_uncompressed_component_type_magenta, "magenta"},
+ {heif_uncompressed_component_type_yellow, "yellow"},
+ {heif_uncompressed_component_type_key_black, "key (black)"}
};
template <typename T> const char* get_name(T val, const std::map<T, const char*>& table)
@@ -444,9 +444,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_red});
- cmpd->add_component({component_type_green});
- cmpd->add_component({component_type_blue});
+ cmpd->add_component({heif_uncompressed_component_type_red});
+ cmpd->add_component({heif_uncompressed_component_type_green});
+ cmpd->add_component({heif_uncompressed_component_type_blue});
uncC->set_sampling_type(sampling_mode_no_subsampling);
uncC->set_interleave_type(interleave_mode_pixel);
}
@@ -456,10 +456,10 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
uncC->add_component({3, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_red});
- cmpd->add_component({component_type_green});
- cmpd->add_component({component_type_blue});
- cmpd->add_component({component_type_alpha});
+ cmpd->add_component({heif_uncompressed_component_type_red});
+ cmpd->add_component({heif_uncompressed_component_type_green});
+ cmpd->add_component({heif_uncompressed_component_type_blue});
+ cmpd->add_component({heif_uncompressed_component_type_alpha});
uncC->set_sampling_type(sampling_mode_no_subsampling);
uncC->set_interleave_type(interleave_mode_pixel);
}
@@ -469,10 +469,10 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
uncC->add_component({3, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_alpha});
- cmpd->add_component({component_type_blue});
- cmpd->add_component({component_type_green});
- cmpd->add_component({component_type_red});
+ cmpd->add_component({heif_uncompressed_component_type_alpha});
+ cmpd->add_component({heif_uncompressed_component_type_blue});
+ cmpd->add_component({heif_uncompressed_component_type_green});
+ cmpd->add_component({heif_uncompressed_component_type_red});
uncC->set_sampling_type(sampling_mode_no_subsampling);
uncC->set_interleave_type(interleave_mode_pixel);
}
@@ -483,9 +483,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_multi_y);
}
@@ -496,9 +496,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_multi_y);
}
@@ -509,9 +509,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
- cmpd->add_component({component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_multi_y);
}
@@ -522,9 +522,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Cr});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_multi_y);
}
@@ -537,9 +537,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_411);
uncC->set_interleave_type(interleave_mode_multi_y);
}
@@ -549,9 +549,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Cr});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
uncC->set_sampling_type(sampling_mode_no_subsampling);
uncC->set_interleave_type(interleave_mode_pixel);
}
@@ -562,10 +562,10 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
uncC->add_component({3, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
- cmpd->add_component({component_type_alpha});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_alpha});
uncC->set_sampling_type(sampling_mode_no_subsampling);
uncC->set_interleave_type(interleave_mode_pixel);
}
@@ -577,9 +577,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 10, component_format_unsigned, 0});
uncC->add_component({0, 10, component_format_unsigned, 0});
uncC->add_component({2, 10, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_multi_y);
uncC->set_block_size(2);
@@ -593,9 +593,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 10, component_format_unsigned, 0});
uncC->add_component({1, 10, component_format_unsigned, 0});
uncC->add_component({2, 10, component_format_unsigned, 0});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_no_subsampling);
uncC->set_interleave_type(interleave_mode_pixel);
uncC->set_block_size(4);
@@ -611,9 +611,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({1, 10, component_format_unsigned, 0});
uncC->add_component({2, 10, component_format_unsigned, 0});
uncC->add_component({1, 10, component_format_unsigned, 0});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_multi_y);
uncC->set_block_size(4);
@@ -626,9 +626,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_420);
uncC->set_interleave_type(interleave_mode_component);
}
@@ -638,9 +638,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_420);
uncC->set_interleave_type(interleave_mode_mixed);
}
@@ -650,9 +650,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
- cmpd->add_component({component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
uncC->set_sampling_type(sampling_mode_420);
uncC->set_interleave_type(interleave_mode_mixed);
}
@@ -662,9 +662,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cb});
- cmpd->add_component({component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_component);
}
@@ -674,9 +674,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
- cmpd->add_component({component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
uncC->set_sampling_type(sampling_mode_422);
uncC->set_interleave_type(interleave_mode_component);
}
@@ -686,9 +686,9 @@ void fill_uncC_and_cmpd_from_profile(const std::shared_ptr<Box_uncC>& uncC,
uncC->add_component({0, 8, component_format_unsigned, 0});
uncC->add_component({1, 8, component_format_unsigned, 0});
uncC->add_component({2, 8, component_format_unsigned, 0});
- cmpd->add_component({component_type_Y});
- cmpd->add_component({component_type_Cr});
- cmpd->add_component({component_type_Cb});
+ cmpd->add_component({heif_uncompressed_component_type_Y});
+ cmpd->add_component({heif_uncompressed_component_type_Cr});
+ cmpd->add_component({heif_uncompressed_component_type_Cb});
uncC->set_sampling_type(sampling_mode_420);
uncC->set_interleave_type(interleave_mode_component);
}
@@ -956,31 +956,30 @@ Error Box_cpat::parse(BitstreamRange& range, const heif_security_limits* limits)
return unsupported_version_error("cpat");
}
- m_pattern.m_pattern_width = range.read16();
- m_pattern.m_pattern_height = range.read16();
+ m_pattern.pattern_width = range.read16();
+ m_pattern.pattern_height = range.read16();
- if (m_pattern.m_pattern_width == 0 || m_pattern.m_pattern_height == 0) {
+ if (m_pattern.pattern_width == 0 || m_pattern.pattern_height == 0) {
return {heif_error_Invalid_input,
heif_suberror_Invalid_parameter_value,
"Zero Bayer pattern size."};
}
auto max_bayer_pattern_size = limits->max_bayer_pattern_pixels;
- if (max_bayer_pattern_size && m_pattern.m_pattern_height > max_bayer_pattern_size / m_pattern.m_pattern_width) {
+ if (max_bayer_pattern_size && m_pattern.pattern_height > max_bayer_pattern_size / m_pattern.pattern_width) {
return {heif_error_Invalid_input,
heif_suberror_Security_limit_exceeded,
"Maximum Bayer pattern size exceeded."};
}
- m_pattern.m_components.resize(size_t{m_pattern.m_pattern_width} * m_pattern.m_pattern_height);
+ size_t num_pixels = size_t{m_pattern.pattern_width} * m_pattern.pattern_height;
+ m_pattern.pixels.resize(num_pixels);
- for (uint16_t i = 0; i < m_pattern.m_pattern_height; i++) {
- for (uint16_t j = 0; j < m_pattern.m_pattern_width; j++) {
- BayerPattern::PatternComponent component{};
- component.component_index = range.read32();
- component.component_gain = range.read_float32();
- m_pattern.m_components[i] = component;
- }
+ 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_gain = range.read_float32();
+ m_pattern.pixels[i] = pixel;
}
return range.get_error();
@@ -995,8 +994,8 @@ std::string Box_cpat::dump(Indent& indent) const
sstr << indent << "pattern_width: " << get_pattern_width() << "\n";
sstr << indent << "pattern_height: " << get_pattern_height() << "\n";
- for (const auto& component : m_pattern.m_components) {
- sstr << indent << "component index: " << component.component_index << ", gain: " << component.component_gain << "\n";
+ for (const auto& pixel : m_pattern.pixels) {
+ sstr << indent << "component index: " << pixel.component_type << ", gain: " << pixel.component_gain << "\n";
}
return sstr.str();
}
@@ -1006,28 +1005,21 @@ Error Box_cpat::write(StreamWriter& writer) const
{
size_t box_start = reserve_box_header_space(writer);
- if (m_pattern.m_pattern_width * size_t{m_pattern.m_pattern_height} != m_pattern.m_components.size()) {
- // needs to be rectangular
+ if (m_pattern.pattern_width * size_t{m_pattern.pattern_height} != m_pattern.pixels.size()) {
return {heif_error_Usage_error,
heif_suberror_Invalid_parameter_value,
"incorrect number of pattern components"};
}
- writer.write16(m_pattern.m_pattern_width);
- writer.write16(m_pattern.m_pattern_height);
+ writer.write16(m_pattern.pattern_width);
+ writer.write16(m_pattern.pattern_height);
- for (const auto& component : m_pattern.m_components) {
- writer.write32(component.component_index);
- writer.write_float32(component.component_gain);
+ for (const auto& pixel : m_pattern.pixels) {
+ writer.write32(pixel.component_type);
+ writer.write_float32(pixel.component_gain);
}
prepend_header(writer, box_start);
return Error::Ok;
}
-
-
-void Box_cpat::set_bayer_pattern(const BayerPattern& pattern)
-{
- m_pattern = pattern;
-}
diff --git a/libheif/codecs/uncompressed/unc_boxes.h b/libheif/codecs/uncompressed/unc_boxes.h
index 85d7e803..013f357d 100644
--- a/libheif/codecs/uncompressed/unc_boxes.h
+++ b/libheif/codecs/uncompressed/unc_boxes.h
@@ -24,6 +24,7 @@
#include "box.h"
#include "bitstream.h"
+#include "pixelimage.h"
#include "unc_types.h"
#include "sequences/seq_boxes.h"
@@ -354,19 +355,13 @@ public:
set_short_type(fourcc("cpat"));
}
- uint16_t get_pattern_width() const
- {
- return m_pattern.m_pattern_width;
- }
+ uint16_t get_pattern_width() const { return m_pattern.pattern_width; }
- uint16_t get_pattern_height() const
- {
- return m_pattern.m_pattern_height;
- }
+ uint16_t get_pattern_height() const { return m_pattern.pattern_height; }
- const BayerPattern& get_bayer_pattern() const { return m_pattern; }
+ const BayerPattern& get_pattern() const { return m_pattern; }
- void set_bayer_pattern(const BayerPattern& pattern);
+ void set_pattern(const BayerPattern& pattern) { m_pattern = pattern; }
std::string dump(Indent&) const override;
diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc
index b3253a86..76ea42bf 100644
--- a/libheif/codecs/uncompressed/unc_codec.cc
+++ b/libheif/codecs/uncompressed/unc_codec.cc
@@ -87,27 +87,27 @@ Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr
uint16_t component_index = component.component_index;
uint16_t component_type = cmpd->get_components()[component_index].component_type;
- if (component_type > component_type_max_valid) {
+ if (component_type > heif_uncompressed_component_type_max_valid) {
std::stringstream sstr;
- sstr << "a component_type > " << component_type_max_valid << " is not supported";
+ sstr << "a component_type > " << heif_uncompressed_component_type_max_valid << " is not supported";
return {heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, sstr.str()};
}
- if (component_type == component_type_padded) {
+ if (component_type == heif_uncompressed_component_type_padded) {
// not relevant for determining chroma
continue;
}
componentSet |= (1 << component_type);
}
- *out_has_alpha = (componentSet & (1 << component_type_alpha)) != 0;
+ *out_has_alpha = (componentSet & (1 << heif_uncompressed_component_type_alpha)) != 0;
- if (componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue)) ||
- componentSet == ((1 << component_type_red) | (1 << component_type_green) | (1 << component_type_blue) | (1 << component_type_alpha))) {
+ if (componentSet == ((1 << heif_uncompressed_component_type_red) | (1 << heif_uncompressed_component_type_green) | (1 << heif_uncompressed_component_type_blue)) ||
+ componentSet == ((1 << heif_uncompressed_component_type_red) | (1 << heif_uncompressed_component_type_green) | (1 << heif_uncompressed_component_type_blue) | (1 << heif_uncompressed_component_type_alpha))) {
*out_chroma = heif_chroma_444;
*out_colourspace = heif_colorspace_RGB;
}
- if (componentSet == ((1 << component_type_Y) | (1 << component_type_Cb) | (1 << component_type_Cr))) {
+ if (componentSet == ((1 << heif_uncompressed_component_type_Y) | (1 << heif_uncompressed_component_type_Cb) | (1 << heif_uncompressed_component_type_Cr))) {
switch (uncC->get_sampling_type()) {
case sampling_mode_no_subsampling:
*out_chroma = heif_chroma_444;
@@ -122,15 +122,16 @@ Error UncompressedImageCodec::get_heif_chroma_uncompressed(const std::shared_ptr
*out_colourspace = heif_colorspace_YCbCr;
}
- if (componentSet == ((1 << component_type_monochrome)) || componentSet == ((1 << component_type_monochrome) | (1 << component_type_alpha))) {
+ if (componentSet == ((1 << heif_uncompressed_component_type_monochrome)) || componentSet == ((1 << heif_uncompressed_component_type_monochrome) | (1 << heif_uncompressed_component_type_alpha))) {
// mono or mono + alpha input, mono output.
*out_chroma = heif_chroma_monochrome;
*out_colourspace = heif_colorspace_monochrome;
}
- if (componentSet == (1 << component_type_filter_array)) {
- *out_chroma = heif_chroma_monochrome; // TODO: ?
- *out_colourspace = heif_colorspace_filter_array;
+ if (componentSet == (1 << heif_uncompressed_component_type_filter_array)) {
+ // TODO - we should look up the components
+ *out_chroma = heif_chroma_monochrome;
+ *out_colourspace = heif_colorspace_monochrome;
}
// TODO: more combinations
@@ -214,8 +215,8 @@ Result<std::shared_ptr<HeifPixelImage>> UncompressedImageCodec::create_image(con
auto component_type = components[component.component_index].component_type;
- if ((component_type == heif_uncompressed_component_type::component_type_Cb) ||
- (component_type == heif_uncompressed_component_type::component_type_Cr)) {
+ 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,
@@ -326,7 +327,7 @@ Error UncompressedImageCodec::check_header_validity(std::optional<const std::sha
}
uint16_t component_type = cmpd->get_components()[comp.component_index].component_type;
- if (component_type > 7 && component_type != component_type_padded && component_type != component_type_filter_array) {
+ if (component_type > 7 && component_type != heif_uncompressed_component_type_padded && component_type != heif_uncompressed_component_type_filter_array) {
std::stringstream sstr;
sstr << "Uncompressed image with component_type " << ((int) component_type) << " is not implemented yet";
return {heif_error_Unsupported_feature,
diff --git a/libheif/codecs/uncompressed/unc_dec.cc b/libheif/codecs/uncompressed/unc_dec.cc
index 49295730..194398c7 100644
--- a/libheif/codecs/uncompressed/unc_dec.cc
+++ b/libheif/codecs/uncompressed/unc_dec.cc
@@ -65,14 +65,14 @@ int Decoder_uncompressed::get_luma_bits_per_pixel() const
}
auto component_type = m_cmpd->get_components()[component_index].component_type;
switch (component_type) {
- case component_type_monochrome:
- case component_type_red:
- case component_type_green:
- case component_type_blue:
- case component_type_filter_array:
+ 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_filter_array:
alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth);
break;
- case component_type_Y:
+ case heif_uncompressed_component_type_Y:
luma_bits = std::max(luma_bits, (int) component.component_bit_depth);
break;
// TODO: there are other things we'll need to handle eventually, like palette.
@@ -109,15 +109,15 @@ int Decoder_uncompressed::get_chroma_bits_per_pixel() const
}
auto component_type = m_cmpd->get_components()[component_index].component_type;
switch (component_type) {
- case component_type_monochrome:
- case component_type_red:
- case component_type_green:
- case component_type_blue:
- case component_type_filter_array:
+ 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_filter_array:
alternate_channel_bits = std::max(alternate_channel_bits, (int) component.component_bit_depth);
break;
- case component_type_Cb:
- case component_type_Cr:
+ case heif_uncompressed_component_type_Cb:
+ case heif_uncompressed_component_type_Cr:
chroma_bits = std::max(chroma_bits, (int) component.component_bit_depth);
break;
// TODO: there are other things we'll need to handle eventually, like palette.
diff --git a/libheif/codecs/uncompressed/unc_decoder.cc b/libheif/codecs/uncompressed/unc_decoder.cc
index 5b287da2..78659e6b 100644
--- a/libheif/codecs/uncompressed/unc_decoder.cc
+++ b/libheif/codecs/uncompressed/unc_decoder.cc
@@ -455,7 +455,7 @@ Result<std::shared_ptr<HeifPixelImage> > unc_decoder::decode_full_image(
auto img = *createImgResult;
if (properties.cpat) {
- img->set_bayer_pattern(properties.cpat->get_bayer_pattern());
+ img->set_bayer_pattern(properties.cpat->get_pattern());
}
auto decoderResult = unc_decoder_factory::get_unc_decoder(width, height, cmpd, uncC);
diff --git a/libheif/codecs/uncompressed/unc_encoder.cc b/libheif/codecs/uncompressed/unc_encoder.cc
index 060877fa..f5ccc0b5 100644
--- a/libheif/codecs/uncompressed/unc_encoder.cc
+++ b/libheif/codecs/uncompressed/unc_encoder.cc
@@ -35,22 +35,22 @@
heif_uncompressed_component_type heif_channel_to_component_type(heif_channel channel)
{
switch (channel) {
- case heif_channel_Y: return heif_uncompressed_component_type::component_type_Y;
- case heif_channel_Cb: return heif_uncompressed_component_type::component_type_Cb;
- case heif_channel_Cr: return heif_uncompressed_component_type::component_type_Cr;
- case heif_channel_R: return heif_uncompressed_component_type::component_type_red;
- case heif_channel_G: return heif_uncompressed_component_type::component_type_green;
- case heif_channel_B: return heif_uncompressed_component_type::component_type_blue;
- case heif_channel_Alpha: return heif_uncompressed_component_type::component_type_alpha;
+ 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_interleaved: assert(false);
break;
- case heif_channel_filter_array: return heif_uncompressed_component_type::component_type_filter_array;
- case heif_channel_depth: return heif_uncompressed_component_type::component_type_depth;
- case heif_channel_disparity: return heif_uncompressed_component_type::component_type_disparity;
- case heif_channel_unknown: return heif_uncompressed_component_type::component_type_padded;
+ 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;
+ case heif_channel_unknown: return heif_uncompressed_component_type_padded;
}
- return heif_uncompressed_component_type::component_type_padded;
+ return heif_uncompressed_component_type_padded;
}
@@ -160,6 +160,10 @@ Result<Encoder::CodedImageData> unc_encoder::encode_static(const std::shared_ptr
codedImageData.properties.push_back(this->get_cmpd());
}
+ if (m_cpat) {
+ codedImageData.properties.push_back(m_cpat);
+ }
+
// --- encode image
diff --git a/libheif/codecs/uncompressed/unc_encoder.h b/libheif/codecs/uncompressed/unc_encoder.h
index 099953ef..d9565ebf 100644
--- a/libheif/codecs/uncompressed/unc_encoder.h
+++ b/libheif/codecs/uncompressed/unc_encoder.h
@@ -31,6 +31,7 @@
class Box_uncC;
class Box_cmpd;
+class Box_cpat;
class HeifPixelImage;
heif_uncompressed_component_type heif_channel_to_component_type(heif_channel channel);
@@ -47,6 +48,7 @@ public:
std::shared_ptr<Box_cmpd> get_cmpd() const { return m_cmpd; }
std::shared_ptr<Box_uncC> get_uncC() const { return m_uncC; }
+ std::shared_ptr<Box_cpat> get_cpat() const { return m_cpat; }
virtual uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const = 0;
@@ -62,6 +64,7 @@ public:
protected:
std::shared_ptr<Box_cmpd> m_cmpd;
std::shared_ptr<Box_uncC> m_uncC;
+ std::shared_ptr<Box_cpat> m_cpat;
};
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
index 0bcd1a18..ad2bdf52 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
@@ -21,6 +21,8 @@
#include "unc_encoder_component_interleave.h"
#include <cstring>
+#include <map>
+#include <set>
#include "pixelimage.h"
#include "unc_boxes.h"
@@ -60,7 +62,7 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
else {
ch = image->get_component_channel(idx);
if (ch == heif_channel_Y && !image->has_channel(heif_channel_Cb)) {
- comp_type = component_type_monochrome;
+ comp_type = heif_uncompressed_component_type_monochrome;
}
else {
comp_type = heif_channel_to_component_type(ch);
@@ -104,6 +106,43 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
else {
m_uncC->set_sampling_type(sampling_mode_no_subsampling);
}
+
+ // --- Bayer pattern: add reference components to cmpd and generate cpat box
+
+ if (image->has_bayer_pattern()) {
+ const BayerPattern& bayer = image->get_bayer_pattern();
+
+ // 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);
+ }
+ }
+
+ // Add reference components to cmpd (these have no uncC entries).
+ // box_index is already at the next available index after data components.
+ std::map<uint16_t, uint16_t> type_to_cmpd_index;
+ for (uint16_t type : unique_types) {
+ type_to_cmpd_index[type] = box_index;
+ m_cmpd->add_component({type});
+ box_index++;
+ }
+
+ // Build cpat box with resolved cmpd indices
+ BayerPattern cpat_pattern;
+ cpat_pattern.pattern_width = bayer.pattern_width;
+ 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];
+ cpat_pattern.pixels[i].component_gain = bayer.pixels[i].component_gain;
+ }
+
+ m_cpat = std::make_shared<Box_cpat>();
+ m_cpat->set_pattern(cpat_pattern);
+ }
}
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
index 02d24679..16be6a0e 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
@@ -58,9 +58,9 @@ std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb_block_pixel_interleav
unc_encoder_rgb_block_pixel_interleave::unc_encoder_rgb_block_pixel_interleave(const std::shared_ptr<const HeifPixelImage>& image,
const heif_encoding_options& options)
{
- m_cmpd->add_component({component_type_red});
- m_cmpd->add_component({component_type_green});
- m_cmpd->add_component({component_type_blue});
+ m_cmpd->add_component({heif_uncompressed_component_type_red});
+ m_cmpd->add_component({heif_uncompressed_component_type_green});
+ m_cmpd->add_component({heif_uncompressed_component_type_blue});
uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
index a93750c3..d103abb4 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.cc
@@ -58,14 +58,14 @@ std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb_bytealign_pixel_inter
unc_encoder_rgb_bytealign_pixel_interleave::unc_encoder_rgb_bytealign_pixel_interleave(const std::shared_ptr<const HeifPixelImage>& image,
const heif_encoding_options& options)
{
- m_cmpd->add_component({component_type_red});
- m_cmpd->add_component({component_type_green});
- m_cmpd->add_component({component_type_blue});
+ m_cmpd->add_component({heif_uncompressed_component_type_red});
+ m_cmpd->add_component({heif_uncompressed_component_type_green});
+ m_cmpd->add_component({heif_uncompressed_component_type_blue});
bool save_alpha = image->has_alpha();
if (save_alpha) {
- m_cmpd->add_component({component_type_alpha});
+ m_cmpd->add_component({heif_uncompressed_component_type_alpha});
}
m_bytes_per_pixel = save_alpha ? 8 : 6;
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
index 268e220d..6e2fc457 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_pixel_interleave.cc
@@ -54,14 +54,14 @@ std::unique_ptr<const unc_encoder> unc_encoder_factory_rgb_pixel_interleave::cre
unc_encoder_rgb_pixel_interleave::unc_encoder_rgb_pixel_interleave(const std::shared_ptr<const HeifPixelImage>& image,
const heif_encoding_options& options)
{
- m_cmpd->add_component({component_type_red});
- m_cmpd->add_component({component_type_green});
- m_cmpd->add_component({component_type_blue});
+ m_cmpd->add_component({heif_uncompressed_component_type_red});
+ m_cmpd->add_component({heif_uncompressed_component_type_green});
+ m_cmpd->add_component({heif_uncompressed_component_type_blue});
bool save_alpha = image->has_alpha();
if (save_alpha) {
- m_cmpd->add_component({component_type_alpha});
+ m_cmpd->add_component({heif_uncompressed_component_type_alpha});
}
m_bytes_per_pixel = save_alpha ? 4 : 3;
diff --git a/libheif/codecs/uncompressed/unc_types.h b/libheif/codecs/uncompressed/unc_types.h
index de63f0da..7dfb16c0 100644
--- a/libheif/codecs/uncompressed/unc_types.h
+++ b/libheif/codecs/uncompressed/unc_types.h
@@ -23,107 +23,10 @@
#define LIBHEIF_UNC_TYPES_H
#include <cstdint>
-#include <vector>
+#include <libheif/heif_uncompressed.h>
-/**
- * Component type.
- *
- * See ISO/IEC 23001-17 Table 1.
-*/
-enum heif_uncompressed_component_type : uint16_t
-{
- /**
- * Monochrome component.
- */
- component_type_monochrome = 0,
-
- /**
- * Luma component (Y).
- */
- component_type_Y = 1,
-
- /**
- * Chroma component (Cb / U).
- */
- component_type_Cb = 2,
-
- /**
- * Chroma component (Cr / V).
- */
- component_type_Cr = 3,
-
- /**
- * Red component (R).
- */
- component_type_red = 4,
-
- /**
- * Green component (G).
- */
- component_type_green = 5,
-
- /**
- * Blue component (B).
- */
- component_type_blue = 6,
-
- /**
- * Alpha / transparency component (A).
- */
- component_type_alpha = 7,
-
- /**
- * Depth component (D).
- */
- component_type_depth = 8,
-
- /**
- * Disparity component (Disp).
- */
- component_type_disparity = 9,
-
- /**
- * Palette component (P).
- *
- * The {@code component_format} value for this component shall be 0.
- */
- component_type_palette = 10,
-
- /**
- * Filter Array (FA) component such as Bayer, RGBW, etc.
- */
- component_type_filter_array = 11,
-
- /**
- * Padded component (unused bits/bytes).
- */
- component_type_padded = 12,
-
- /**
- * Cyan component (C).
- */
- component_type_cyan = 13,
-
- /**
- * Magenta component (M).
- */
- component_type_magenta = 14,
-
- /**
- * Yellow component (Y).
- */
- component_type_yellow = 15,
-
- /**
- * Key (black) component (K).
- */
- component_type_key_black = 16,
-
- /**
- * Maximum valid component type value.
- */
- component_type_max_valid = component_type_key_black
-};
+// Update this when adding new values to heif_uncompressed_component_type.
+#define heif_uncompressed_component_type_max_valid heif_uncompressed_component_type_key_black
/**
* HEIF uncompressed component format.
@@ -288,18 +191,4 @@ enum heif_uncompressed_interleave_mode
};
-struct BayerPattern
-{
- struct PatternComponent
- {
- uint32_t component_index;
- float component_gain;
- };
-
- uint16_t m_pattern_width = 0;
- uint16_t m_pattern_height = 0;
- std::vector<PatternComponent> m_components;
-};
-
-
#endif //LIBHEIF_UNC_TYPES_H
diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc
index 232b6c20..0cc6c700 100644
--- a/libheif/image-items/image_item.cc
+++ b/libheif/image-items/image_item.cc
@@ -321,7 +321,10 @@ Result<Encoder::CodedImageData> ImageItem::encode_to_bitstream_and_boxes(const s
// --- write PIXI property
std::shared_ptr<Box_pixi> pixi = std::make_shared<Box_pixi>();
- if (colorspace == heif_colorspace_monochrome) {
+ if (colorspace == heif_colorspace_monochrome && image->has_channel(heif_channel_filter_array)) {
+ // Skip pixi for filter array images — bit depth info is in uncC
+ }
+ else if (colorspace == heif_colorspace_monochrome) {
pixi->add_channel_bits(image->get_bits_per_pixel(heif_channel_Y));
}
else if (colorspace == heif_colorspace_YCbCr) {
diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc
index e482e6b1..89f2a69d 100644
--- a/libheif/image-items/unc_image.cc
+++ b/libheif/image-items/unc_image.cc
@@ -157,6 +157,11 @@ Result<std::shared_ptr<ImageItem_uncompressed>> ImageItem_uncompressed::add_unci
}
+ // Add cpat property if Bayer pattern is set
+ if ((*uncEncoder)->get_cpat()) {
+ unci_image->add_property((*uncEncoder)->get_cpat(), true);
+ }
+
// Add `ispe` property
auto ispe = std::make_shared<Box_ispe>();
diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc
index 600c87b8..588e68d7 100644
--- a/libheif/pixelimage.cc
+++ b/libheif/pixelimage.cc
@@ -96,29 +96,29 @@ uint32_t channel_height(uint32_t h, heif_chroma chroma, heif_channel channel)
heif_channel map_uncompressed_component_to_channel(uint16_t component_type)
{
switch (component_type) {
- case component_type_monochrome:
- case component_type_Y:
+ case heif_uncompressed_component_type_monochrome:
+ case heif_uncompressed_component_type_Y:
return heif_channel_Y;
- case component_type_Cb:
+ case heif_uncompressed_component_type_Cb:
return heif_channel_Cb;
- case component_type_Cr:
+ case heif_uncompressed_component_type_Cr:
return heif_channel_Cr;
- case component_type_red:
+ case heif_uncompressed_component_type_red:
return heif_channel_R;
- case component_type_green:
+ case heif_uncompressed_component_type_green:
return heif_channel_G;
- case component_type_blue:
+ case heif_uncompressed_component_type_blue:
return heif_channel_B;
- case component_type_alpha:
+ case heif_uncompressed_component_type_alpha:
return heif_channel_Alpha;
- case component_type_filter_array:
+ case heif_uncompressed_component_type_filter_array:
return heif_channel_filter_array;
- case component_type_depth:
+ case heif_uncompressed_component_type_depth:
return heif_channel_depth;
- case component_type_disparity:
+ case heif_uncompressed_component_type_disparity:
return heif_channel_disparity;
- case component_type_padded:
+ case heif_uncompressed_component_type_padded:
default:
return heif_channel_unknown;
}
@@ -767,17 +767,17 @@ uint32_t HeifPixelImage::get_primary_component() const
}
switch (m_planes[idx].m_component_type) {
- case component_type_Y:
- case component_type_monochrome:
- case component_type_red:
- case component_type_green:
- case component_type_blue:
- case component_type_cyan:
- case component_type_magenta:
- case component_type_yellow:
- case component_type_key_black:
- case component_type_filter_array:
- case component_type_palette:
+ 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;
default:
diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h
index 2d5fba0a..77f0c128 100644
--- a/libheif/pixelimage.h
+++ b/libheif/pixelimage.h
@@ -26,6 +26,7 @@
#include "error.h"
#include "nclx.h"
#include <libheif/heif_experimental.h>
+#include <libheif/heif_uncompressed.h>
#if HEIF_WITH_OMAF
#include "omaf_boxes.h"
#endif
@@ -34,12 +35,18 @@
#include <vector>
#include <memory>
#include <map>
+#include <optional>
#include <set>
#include <utility>
#include <cassert>
#include <string>
-#include "codecs/uncompressed/unc_types.h"
+struct BayerPattern
+{
+ uint16_t pattern_width = 0;
+ uint16_t pattern_height = 0;
+ std::vector<heif_bayer_pattern_pixel> pixels;
+};
heif_chroma chroma_from_subsampling(int h, int v);
@@ -182,6 +189,15 @@ public:
std::string get_gimi_sample_content_id() const { assert(has_gimi_sample_content_id()); return *m_gimi_sample_content_id; }
+
+ // --- bayer pattern
+
+ bool has_bayer_pattern() const { return m_bayer_pattern.has_value(); }
+
+ const BayerPattern& get_bayer_pattern() const { assert(has_bayer_pattern()); return *m_bayer_pattern; }
+
+ virtual void set_bayer_pattern(const BayerPattern& pattern) { m_bayer_pattern = pattern; }
+
#if HEIF_WITH_OMAF
bool has_omaf_image_projection() const {
return (m_omaf_image_projection != heif_omaf_image_projection_flat);
@@ -196,12 +212,6 @@ public:
}
#endif
- void set_bayer_pattern(const BayerPattern& pattern) { m_bayer_pattern = pattern; }
-
- bool has_bayer_pattern() const { return m_bayer_pattern.has_value(); }
-
- const BayerPattern& get_bayer_pattern() const { assert(m_bayer_pattern); return *m_bayer_pattern; }
-
private:
bool m_premultiplied_alpha = false;
nclx_profile m_color_profile_nclx = nclx_profile::undefined();
@@ -216,12 +226,12 @@ private:
std::optional<std::string> m_gimi_sample_content_id;
+ std::optional<BayerPattern> m_bayer_pattern;
+
#if HEIF_WITH_OMAF
heif_omaf_image_projection m_omaf_image_projection = heif_omaf_image_projection::heif_omaf_image_projection_flat;
#endif
- std::optional<BayerPattern> m_bayer_pattern;
-
protected:
std::shared_ptr<Box_clli> get_clli_box() const;
diff --git a/libheif/sequences/track_visual.cc b/libheif/sequences/track_visual.cc
index 9be3d948..ee982a26 100644
--- a/libheif/sequences/track_visual.cc
+++ b/libheif/sequences/track_visual.cc
@@ -158,7 +158,7 @@ bool Track_Visual::has_alpha_channel() const
if (sampleEntry) {
if (auto box_uncv = std::dynamic_pointer_cast<const Box_uncv>(sampleEntry)) {
if (auto cmpd = box_uncv->get_child_box<const Box_cmpd>()) {
- if (cmpd->has_component(component_type_alpha)) {
+ if (cmpd->has_component(heif_uncompressed_component_type_alpha)) {
return true;
}
}