Commit 5897e715 for libheif
commit 5897e715c1545c37767e0d3fbebe463fae7d63da
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri Feb 27 14:51:58 2026 +0100
unci: enable compression also for non-tiled images
diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc
index ab2d9b8b..2642bf61 100644
--- a/examples/heif_enc.cc
+++ b/examples/heif_enc.cc
@@ -1853,6 +1853,7 @@ int main(int argc, char** argv)
options->color_conversion_options.only_use_preferred_chroma_algorithm = true;
}
+ options->unci_compression = unci_compression;
// --- if no output filename was given, synthesize one from the first input image filename
diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt
index ca0a0154..e7470838 100644
--- a/libheif/CMakeLists.txt
+++ b/libheif/CMakeLists.txt
@@ -25,6 +25,7 @@ set(libheif_headers
api/libheif/heif_context.h
api/libheif/heif_tiling.h
api/libheif/heif_uncompressed.h
+ api/libheif/heif_uncompressed_types.h
api/libheif/heif_text.h
api/libheif/heif_cxx.h
${CMAKE_CURRENT_BINARY_DIR}/heif_version.h)
@@ -55,6 +56,7 @@ set(libheif_sources
logging.h
logging.cc
compression.h
+ compression.cc
compression_brotli.cc
compression_zlib.cc
common_utils.cc
diff --git a/libheif/api/libheif/heif_encoding.cc b/libheif/api/libheif/heif_encoding.cc
index 609406ef..48257057 100644
--- a/libheif/api/libheif/heif_encoding.cc
+++ b/libheif/api/libheif/heif_encoding.cc
@@ -570,7 +570,7 @@ int heif_encoder_has_default(heif_encoder* encoder,
static void set_default_encoding_options(heif_encoding_options& options)
{
- options.version = 7;
+ options.version = 8;
options.save_alpha_channel = true;
options.macOS_compatibility_workaround = false;
@@ -585,6 +585,8 @@ static void set_default_encoding_options(heif_encoding_options& options)
options.color_conversion_options.only_use_preferred_chroma_algorithm = false;
options.prefer_uncC_short_form = true;
+
+ options.unci_compression = heif_unci_compression_off;
}
@@ -607,6 +609,9 @@ void heif_encoding_options_copy(heif_encoding_options* dst, const heif_encoding_
int min_version = std::min(dst->version, src->version);
switch (min_version) {
+ case 8:
+ dst->unci_compression = src->unci_compression;
+ [[fallthrough]];
case 7:
dst->prefer_uncC_short_form = src->prefer_uncC_short_form;
[[fallthrough]];
diff --git a/libheif/api/libheif/heif_encoding.h b/libheif/api/libheif/heif_encoding.h
index 5c91c416..711cf6c9 100644
--- a/libheif/api/libheif/heif_encoding.h
+++ b/libheif/api/libheif/heif_encoding.h
@@ -33,6 +33,7 @@ extern "C" {
#include <libheif/heif_context.h>
#include <libheif/heif_brands.h>
#include <libheif/heif_color.h>
+#include <libheif/heif_uncompressed_types.h>
// ----- encoder -----
@@ -308,6 +309,12 @@ typedef struct heif_encoding_options
// Set this to true to use compressed form of uncC where possible.
uint8_t prefer_uncC_short_form;
+ // version 8 options
+
+ // Set this to enable compression for 'unci' images encoded through heif_context_encode_image().
+ // Default: heif_unci_compression_off
+ heif_unci_compression unci_compression;
+
// TODO: we should add a flag to force MIAF compatible outputs. E.g. this will put restrictions on grid tile sizes and
// might add a clap box when the grid output size does not match the color subsampling factors.
// Since some of these constraints have to be known before actually encoding the image, "forcing MIAF compatibility"
diff --git a/libheif/api/libheif/heif_uncompressed.h b/libheif/api/libheif/heif_uncompressed.h
index 9e299d7f..e8ac61b3 100644
--- a/libheif/api/libheif/heif_uncompressed.h
+++ b/libheif/api/libheif/heif_uncompressed.h
@@ -21,6 +21,7 @@
#ifndef LIBHEIF_HEIF_UNCOMPRESSED_H
#define LIBHEIF_HEIF_UNCOMPRESSED_H
+#include "libheif/heif_uncompressed_types.h"
#include "libheif/heif.h"
#ifdef __cplusplus
@@ -34,37 +35,7 @@ extern "C" {
* See heif_metadata_compression for more information.
*/
-// --- ISO 23001-17 component types (Table 1)
-
-typedef 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
-} heif_uncompressed_component_type;
-
-
-// --- Bayer / filter array pattern
-
-typedef struct heif_bayer_pattern_pixel
-{
- uint16_t component_index; // index into the component definition (cmpd)
- float component_gain;
-} heif_bayer_pattern_pixel;
+// heif_uncompressed_component_type and heif_bayer_pattern_pixel are defined in heif_uncompressed_types.h.
// Set a Bayer / filter array pattern on an image.
// The pattern is a 2D array of component indices with dimensions pattern_width x pattern_height.
@@ -155,7 +126,7 @@ int heif_image_get_polarization_pattern_index_for_component(const heif_image*,
// --- Sensor bad pixels map (ISO 23001-17, Section 6.1.7)
-struct heif_bad_pixel { uint32_t row; uint32_t column; };
+// struct heif_bad_pixel is defined in heif_uncompressed_types.h.
// Add a sensor bad pixels map to an image.
// component_indices: array of component indices this map applies to (may be NULL if num_component_indices == 0,
@@ -246,21 +217,7 @@ heif_error heif_image_get_sensor_nuc_data(const heif_image*,
float* out_nuc_offsets);
-// --- Chroma sample location (ISO 23091-2 / ITU-T H.273 + ISO 23001-17)
-
-typedef enum heif_chroma420_sample_location {
- // values 0-5 according to ISO 23091-2 / ITU-T H.273
- heif_chroma420_sample_location_00_05 = 0,
- heif_chroma420_sample_location_05_05 = 1,
- heif_chroma420_sample_location_00_00 = 2,
- heif_chroma420_sample_location_05_00 = 3,
- heif_chroma420_sample_location_00_10 = 4,
- heif_chroma420_sample_location_05_10 = 5,
-
- // value 6 according to ISO 23001-17
- heif_chroma420_sample_location_00_00_01_00 = 6
-} heif_chroma420_sample_location;
-
+// heif_chroma420_sample_location is defined in heif_uncompressed_types.h.
// --- Chroma sample location (ISO 23001-17, Section 6.1.4)
@@ -280,35 +237,7 @@ uint8_t heif_image_get_chroma_location(const heif_image*);
// --- 'unci' images
-// This is similar to heif_metadata_compression. We should try to keep the integers compatible, but each enum will just
-// contain the allowed values.
-typedef enum heif_unci_compression
-{
- heif_unci_compression_off = 0,
- //heif_unci_compression_auto = 1,
- //heif_unci_compression_unknown = 2, // only used when reading unknown method from input file
- heif_unci_compression_deflate = 3,
- heif_unci_compression_zlib = 4,
- heif_unci_compression_brotli = 5
-} heif_unci_compression;
-
-
-typedef struct heif_unci_image_parameters
-{
- int version;
-
- // --- version 1
-
- uint32_t image_width;
- uint32_t image_height;
-
- uint32_t tile_width;
- uint32_t tile_height;
-
- enum heif_unci_compression compression;
-
- // TODO: interleave type, padding
-} heif_unci_image_parameters;
+// heif_unci_compression and heif_unci_image_parameters are defined in heif_uncompressed_types.h.
LIBHEIF_API
heif_unci_image_parameters* heif_unci_image_parameters_alloc(void);
@@ -346,27 +275,7 @@ heif_error heif_context_add_empty_unci_image(heif_context* ctx,
const heif_image* prototype,
heif_image_handle** out_unci_image_handle);
-// --- pixel datatype support
-
-typedef enum heif_channel_datatype
-{
- heif_channel_datatype_undefined = 0,
- heif_channel_datatype_unsigned_integer = 1,
- heif_channel_datatype_signed_integer = 2,
- heif_channel_datatype_floating_point = 3,
- heif_channel_datatype_complex_number = 4
-} heif_channel_datatype;
-
-typedef struct heif_complex32
-{
- float real, imaginary;
-} heif_complex32;
-
-typedef struct heif_complex64
-{
- double real, imaginary;
-} heif_complex64;
-
+// heif_channel_datatype, heif_complex32, heif_complex64 are defined in heif_uncompressed_types.h.
// --- index-based component access (for ISO 23001-17 multi-component images)
diff --git a/libheif/api/libheif/heif_uncompressed_types.h b/libheif/api/libheif/heif_uncompressed_types.h
new file mode 100644
index 00000000..1a00dcc9
--- /dev/null
+++ b/libheif/api/libheif/heif_uncompressed_types.h
@@ -0,0 +1,143 @@
+/*6
+ * HEIF codec.
+ * Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBHEIF_HEIF_UNCOMPRESSED_TYPES_H
+#define LIBHEIF_HEIF_UNCOMPRESSED_TYPES_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// --- ISO 23001-17 component types (Table 1)
+
+typedef 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
+} heif_uncompressed_component_type;
+
+
+// --- Bayer / filter array pattern
+
+typedef struct heif_bayer_pattern_pixel
+{
+ uint16_t component_index; // index into the component definition (cmpd)
+ float component_gain;
+} heif_bayer_pattern_pixel;
+
+
+// --- Sensor bad pixels map (ISO 23001-17, Section 6.1.7)
+
+struct heif_bad_pixel { uint32_t row; uint32_t column; };
+
+
+// --- Chroma sample location (ISO 23091-2 / ITU-T H.273 + ISO 23001-17)
+
+typedef enum heif_chroma420_sample_location {
+ // values 0-5 according to ISO 23091-2 / ITU-T H.273
+ heif_chroma420_sample_location_00_05 = 0,
+ heif_chroma420_sample_location_05_05 = 1,
+ heif_chroma420_sample_location_00_00 = 2,
+ heif_chroma420_sample_location_05_00 = 3,
+ heif_chroma420_sample_location_00_10 = 4,
+ heif_chroma420_sample_location_05_10 = 5,
+
+ // value 6 according to ISO 23001-17
+ heif_chroma420_sample_location_00_00_01_00 = 6
+} heif_chroma420_sample_location;
+
+
+// Compression methods for 'unci' (ISO 23001-17) images.
+// This is similar to heif_metadata_compression. We should try to keep the integers compatible, but each enum will just
+// contain the allowed values.
+typedef enum heif_unci_compression
+{
+ heif_unci_compression_off = 0,
+ //heif_unci_compression_auto = 1,
+ //heif_unci_compression_unknown = 2, // only used when reading unknown method from input file
+ heif_unci_compression_deflate = 3,
+ heif_unci_compression_zlib = 4,
+ heif_unci_compression_brotli = 5
+} heif_unci_compression;
+
+
+// --- 'unci' image parameters
+
+typedef struct heif_unci_image_parameters
+{
+ int version;
+
+ // --- version 1
+
+ uint32_t image_width;
+ uint32_t image_height;
+
+ uint32_t tile_width;
+ uint32_t tile_height;
+
+ heif_unci_compression compression;
+
+ // TODO: interleave type, padding
+} heif_unci_image_parameters;
+
+
+// --- pixel datatype support
+
+typedef enum heif_channel_datatype
+{
+ heif_channel_datatype_undefined = 0,
+ heif_channel_datatype_unsigned_integer = 1,
+ heif_channel_datatype_signed_integer = 2,
+ heif_channel_datatype_floating_point = 3,
+ heif_channel_datatype_complex_number = 4
+} heif_channel_datatype;
+
+typedef struct heif_complex32
+{
+ float real, imaginary;
+} heif_complex32;
+
+typedef struct heif_complex64
+{
+ double real, imaginary;
+} heif_complex64;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libheif/codecs/uncompressed/unc_enc.cc b/libheif/codecs/uncompressed/unc_enc.cc
index 4aa66f19..6a71121e 100644
--- a/libheif/codecs/uncompressed/unc_enc.cc
+++ b/libheif/codecs/uncompressed/unc_enc.cc
@@ -68,6 +68,9 @@ std::shared_ptr<class Box_VisualSampleEntry> Encoder_uncompressed::get_sample_de
case fourcc("icef"):
case fourcc("cpat"):
case fourcc("splz"):
+ case fourcc("sbpm"):
+ case fourcc("snuc"):
+ case fourcc("cloc"):
uncv->append_child_box(prop);
break;
}
diff --git a/libheif/codecs/uncompressed/unc_encoder.cc b/libheif/codecs/uncompressed/unc_encoder.cc
index 5b652fd6..a660322a 100644
--- a/libheif/codecs/uncompressed/unc_encoder.cc
+++ b/libheif/codecs/uncompressed/unc_encoder.cc
@@ -30,6 +30,7 @@
#include "unc_encoder_rgb_pixel_interleave.h"
#include "unc_encoder_rgb_bytealign_pixel_interleave.h"
#include "libheif/heif_uncompressed.h"
+#include "compression.h"
heif_uncompressed_component_type heif_channel_to_component_type(heif_channel channel)
@@ -188,7 +189,37 @@ Result<Encoder::CodedImageData> unc_encoder::encode_static(const std::shared_ptr
return codedBitstreamResult.error();
}
- codedImageData.bitstream = *codedBitstreamResult;
+ // --- optionally compress
+
+ heif_unci_compression compression = (in_options.version >= 8) ? in_options.unci_compression : heif_unci_compression_off;
+
+ if (compression != heif_unci_compression_off) {
+ uint32_t compr_fourcc = unci_compression_to_fourcc(compression);
+
+ auto compressed = compress_unci_fourcc(compr_fourcc,
+ codedBitstreamResult->data(),
+ codedBitstreamResult->size());
+ if (!compressed) {
+ return compressed.error();
+ }
+
+ auto cmpC = std::make_shared<Box_cmpC>();
+ cmpC->set_compression_type(compr_fourcc);
+ cmpC->set_compressed_unit_type(heif_cmpC_compressed_unit_type_image_tile);
+ codedImageData.properties.push_back(cmpC);
+
+ auto icef = std::make_shared<Box_icef>();
+ Box_icef::CompressedUnitInfo info;
+ info.unit_offset = 0;
+ info.unit_size = compressed->size();
+ icef->add_component(info);
+ codedImageData.properties.push_back(icef);
+
+ codedImageData.bitstream = std::move(*compressed);
+ }
+ else {
+ codedImageData.bitstream = std::move(*codedBitstreamResult);
+ }
return codedImageData;
}
diff --git a/libheif/compression.cc b/libheif/compression.cc
new file mode 100644
index 00000000..037cf926
--- /dev/null
+++ b/libheif/compression.cc
@@ -0,0 +1,61 @@
+/*
+ * HEIF codec.
+ * Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+ *
+ * This file is part of libheif.
+ *
+ * libheif is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * libheif is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libheif. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "compression.h"
+#include "common_utils.h"
+
+
+uint32_t unci_compression_to_fourcc(heif_unci_compression method)
+{
+ switch (method) {
+ case heif_unci_compression_off:
+ return 0;
+ case heif_unci_compression_deflate:
+ return fourcc("defl");
+ case heif_unci_compression_zlib:
+ return fourcc("zlib");
+ case heif_unci_compression_brotli:
+ return fourcc("brot");
+ default:
+ return 0;
+ }
+}
+
+
+Result<std::vector<uint8_t>> compress_unci_fourcc(uint32_t fourcc_code,
+ const uint8_t* data, size_t size)
+{
+ switch (fourcc_code) {
+#if HAVE_ZLIB
+ case fourcc("defl"):
+ return {compress_deflate(data, size)};
+ case fourcc("zlib"):
+ return {compress_zlib(data, size)};
+#endif
+#if HAVE_BROTLI
+ case fourcc("brot"):
+ return {compress_brotli(data, size)};
+#endif
+ default:
+ return Error{heif_error_Unsupported_feature,
+ heif_suberror_Unsupported_generic_compression_method,
+ "Unsupported unci compression method."};
+ }
+}
diff --git a/libheif/compression.h b/libheif/compression.h
index 9b007672..95e45221 100644
--- a/libheif/compression.h
+++ b/libheif/compression.h
@@ -25,6 +25,26 @@
#include <cstddef>
#include <error.h>
+#include <libheif/heif_uncompressed_types.h>
+
+/**
+ * Convert heif_unci_compression enum to a fourcc code.
+ *
+ * @param method the compression method
+ * @return the corresponding fourcc code, or 0 for heif_unci_compression_off
+ */
+uint32_t unci_compression_to_fourcc(heif_unci_compression method);
+
+/**
+ * Compress data using the compression method identified by a fourcc code.
+ *
+ * @param fourcc_code the fourcc code for the compression method (e.g. "defl", "zlib", "brot")
+ * @param data pointer to the data to be compressed
+ * @param size the length of the input array in bytes
+ * @return the corresponding compressed data, or an error
+ */
+Result<std::vector<uint8_t>> compress_unci_fourcc(uint32_t fourcc_code,
+ const uint8_t* data, size_t size);
#if HAVE_ZLIB
/**
diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc
index 00ee7d7a..760c49ce 100644
--- a/libheif/image-items/unc_image.cc
+++ b/libheif/image-items/unc_image.cc
@@ -170,30 +170,13 @@ Result<std::shared_ptr<ImageItem_uncompressed>> ImageItem_uncompressed::add_unci
unci_image->add_property(ispe, true);
if (parameters->compression != heif_unci_compression_off) {
- auto icef = std::make_shared<Box_icef>();
auto cmpC = std::make_shared<Box_cmpC>();
+ cmpC->set_compression_type(unci_compression_to_fourcc(parameters->compression));
cmpC->set_compressed_unit_type(heif_cmpC_compressed_unit_type_image_tile);
- if (false) {
- }
-#if HAVE_ZLIB
- else if (parameters->compression == heif_unci_compression_deflate) {
- cmpC->set_compression_type(fourcc("defl"));
- }
- else if (parameters->compression == heif_unci_compression_zlib) {
- cmpC->set_compression_type(fourcc("zlib"));
- }
-#endif
-#if HAVE_BROTLI
- else if (parameters->compression == heif_unci_compression_brotli) {
- cmpC->set_compression_type(fourcc("brot"));
- }
-#endif
- else {
- assert(false);
- }
-
unci_image->add_property(cmpC, true);
+
+ auto icef = std::make_shared<Box_icef>();
unci_image->add_property_without_deduplication(icef, true); // icef is empty. A normal add_property() would lead to a wrong deduplication.
}
@@ -262,38 +245,22 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c
get_file()->replace_iloc_data(get_id(), tile_idx * tile_data_size, *codedBitstreamResult, 0);
}
else {
- std::vector<uint8_t> compressed_data;
- const std::vector<uint8_t>& raw_data = std::move(*codedBitstreamResult);
- (void)raw_data;
-
- uint32_t compr = cmpC->get_compression_type();
- switch (compr) {
-#if HAVE_ZLIB
- case fourcc("defl"):
- compressed_data = compress_deflate(raw_data.data(), raw_data.size());
- break;
- case fourcc("zlib"):
- compressed_data = compress_zlib(raw_data.data(), raw_data.size());
- break;
-#endif
-#if HAVE_BROTLI
- case fourcc("brot"):
- compressed_data = compress_brotli(raw_data.data(), raw_data.size());
- break;
-#endif
- default:
- assert(false);
- break;
+ const std::vector<uint8_t>& raw_data = *codedBitstreamResult;
+
+ auto compressed = compress_unci_fourcc(cmpC->get_compression_type(),
+ raw_data.data(), raw_data.size());
+ if (!compressed) {
+ return compressed.error();
}
- get_file()->append_iloc_data(get_id(), compressed_data, 0);
+ get_file()->append_iloc_data(get_id(), *compressed, 0);
Box_icef::CompressedUnitInfo unit_info;
unit_info.unit_offset = m_next_tile_write_pos;
- unit_info.unit_size = compressed_data.size();
+ unit_info.unit_size = compressed->size();
icef->set_component(tile_idx, unit_info);
- m_next_tile_write_pos += compressed_data.size();
+ m_next_tile_write_pos += compressed->size();
}
return Error::Ok;