Commit 594bc19f for libheif
commit 594bc19fca8168b0233e72dfe022566b0d00e0e6
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sun Feb 8 17:05:31 2026 +0100
unci: move computation of tile sizes into unc_encoders
diff --git a/libheif/codecs/uncompressed/unc_boxes.cc b/libheif/codecs/uncompressed/unc_boxes.cc
index b3f49fc0..87fd6c12 100644
--- a/libheif/codecs/uncompressed/unc_boxes.cc
+++ b/libheif/codecs/uncompressed/unc_boxes.cc
@@ -414,41 +414,6 @@ Error Box_uncC::write(StreamWriter& writer) const
}
-uint64_t Box_uncC::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const
-{
- if (m_profile != 0) {
- switch (m_profile) {
- case fourcc("rgba"):
- return 4 * uint64_t{tile_width} * tile_height;
-
- case fourcc("rgb3"):
- return 3 * uint64_t{tile_width} * tile_height;
-
- default:
- assert(false);
- return 0;
- }
- }
-
- switch (m_interleave_type) {
- case interleave_mode_component:
- case interleave_mode_pixel: {
- uint32_t bytes_per_pixel = 0;
-
- for (const auto& comp : m_components) {
- assert(comp.component_bit_depth % 8 == 0); // TODO: component sizes that are no multiples of bytes
- bytes_per_pixel += comp.component_bit_depth / 8;
- }
-
- return bytes_per_pixel * uint64_t{tile_width} * tile_height;
- }
- default:
- assert(false);
- return 0;
- }
-}
-
-
Error Box_cmpC::parse(BitstreamRange& range, const heif_security_limits* limits)
{
parse_full_box_header(range);
diff --git a/libheif/codecs/uncompressed/unc_boxes.h b/libheif/codecs/uncompressed/unc_boxes.h
index 8be60abd..4a6ab69a 100644
--- a/libheif/codecs/uncompressed/unc_boxes.h
+++ b/libheif/codecs/uncompressed/unc_boxes.h
@@ -216,8 +216,6 @@ public:
uint32_t get_number_of_tiles() const { return m_num_tile_rows * m_num_tile_rows; }
- uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const;
-
protected:
Error parse(BitstreamRange& range, const heif_security_limits* limits) override;
diff --git a/libheif/codecs/uncompressed/unc_encoder.h b/libheif/codecs/uncompressed/unc_encoder.h
index 6d500afa..0e5620bc 100644
--- a/libheif/codecs/uncompressed/unc_encoder.h
+++ b/libheif/codecs/uncompressed/unc_encoder.h
@@ -42,6 +42,8 @@ public:
std::shared_ptr<Box_uncC> get_uncC() const { return m_uncC; }
+ virtual uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const = 0;
+
[[nodiscard]] virtual std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const = 0;
Result<Encoder::CodedImageData> encode_static(const std::shared_ptr<const HeifPixelImage>& src_image,
diff --git a/libheif/codecs/uncompressed/unc_encoder_planar.cc b/libheif/codecs/uncompressed/unc_encoder_planar.cc
index 5c60dff8..ebd389c8 100644
--- a/libheif/codecs/uncompressed/unc_encoder_planar.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_planar.cc
@@ -22,7 +22,7 @@
bool unc_encoder_factory_planar::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const
+ const heif_encoding_options& options) const
{
if (image->has_channel(heif_channel_interleaved)) {
return false;
@@ -32,14 +32,13 @@ bool unc_encoder_factory_planar::can_encode(const std::shared_ptr<const HeifPixe
}
-std::unique_ptr<const unc_encoder> unc_encoder_factory_planar::create(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const
+std::unique_ptr<const unc_encoder> unc_encoder_factory_planar::create(const std::shared_ptr<const HeifPixelImage>& image,
+ const heif_encoding_options& options) const
{
return std::make_unique<unc_encoder_planar>(image, options);
}
-
heif_uncompressed_component_type heif_channel_to_component_type(heif_channel channel)
{
switch (channel) {
@@ -61,60 +60,46 @@ heif_uncompressed_component_type heif_channel_to_component_type(heif_channel cha
}
-struct channel_component
-{
- heif_channel channel;
- heif_uncompressed_component_type component_type;
-};
-
-void add_channel_if_exists(const std::shared_ptr<const HeifPixelImage>& image, std::vector<channel_component>& list, heif_channel channel)
+void unc_encoder_planar::add_channel_if_exists(const std::shared_ptr<const HeifPixelImage>& image, heif_channel channel)
{
if (image->has_channel(channel)) {
- list.push_back({channel, heif_channel_to_component_type(channel)});
+ m_components.push_back({channel, heif_channel_to_component_type(channel)});
}
}
-std::vector<channel_component> get_channels(const std::shared_ptr<const HeifPixelImage>& image)
-{
- std::vector<channel_component> channels;
+unc_encoder_planar::unc_encoder_planar(const std::shared_ptr<const HeifPixelImage>& image,
+ const heif_encoding_options& options)
+{
// Special case for heif_channel_Y:
// - if this an YCbCr image, use component_type_Y,
// - otherwise, use component_type_monochrome
if (image->has_channel(heif_channel_Y)) {
if (image->has_channel(heif_channel_Cb) && image->has_channel(heif_channel_Cr)) {
- channels.push_back({heif_channel_Y, heif_uncompressed_component_type::component_type_Y});
+ m_components.push_back({heif_channel_Y, heif_uncompressed_component_type::component_type_Y});
}
else {
- channels.push_back({heif_channel_Y, heif_uncompressed_component_type::component_type_monochrome});
+ m_components.push_back({heif_channel_Y, heif_uncompressed_component_type::component_type_monochrome});
}
}
- add_channel_if_exists(image, channels, heif_channel_Cb);
- add_channel_if_exists(image, channels, heif_channel_Cr);
- add_channel_if_exists(image, channels, heif_channel_R);
- add_channel_if_exists(image, channels, heif_channel_G);
- add_channel_if_exists(image, channels, heif_channel_B);
- add_channel_if_exists(image, channels, heif_channel_Alpha);
- add_channel_if_exists(image, channels, heif_channel_filter_array);
- add_channel_if_exists(image, channels, heif_channel_depth);
- add_channel_if_exists(image, channels, heif_channel_disparity);
-
- return channels;
-}
-
+ add_channel_if_exists(image, heif_channel_Cb);
+ add_channel_if_exists(image, heif_channel_Cr);
+ add_channel_if_exists(image, heif_channel_R);
+ add_channel_if_exists(image, heif_channel_G);
+ add_channel_if_exists(image, heif_channel_B);
+ add_channel_if_exists(image, heif_channel_Alpha);
+ add_channel_if_exists(image, heif_channel_filter_array);
+ add_channel_if_exists(image, heif_channel_depth);
+ add_channel_if_exists(image, heif_channel_disparity);
-unc_encoder_planar::unc_encoder_planar(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options)
-{
- auto channels = get_channels(image);
// if we have any component > 8 bits, we enable this
bool little_endian = false;
uint16_t index = 0;
- for (channel_component channelcomponent : channels) {
+ for (channel_component channelcomponent : m_components) {
m_cmpd->add_component({channelcomponent.component_type});
uint8_t bpp = image->get_bits_per_pixel(channelcomponent.channel);
@@ -144,6 +129,30 @@ unc_encoder_planar::unc_encoder_planar(const std::shared_ptr<const HeifPixelImag
else {
m_uncC->set_sampling_type(0);
}
+
+
+ // --- compute bytes per pixel
+
+ m_bytes_per_pixel_x4 = 0;
+
+ for (channel_component channelcomponent : m_components) {
+ int bpp = image->get_bits_per_pixel(channelcomponent.channel);
+ int bytes_per_pixel = 4 * (bpp + 7) / 8;
+
+ if (channelcomponent.channel == heif_channel_Cb ||
+ channelcomponent.channel == heif_channel_Cr) {
+ int downsampling = chroma_h_subsampling(image->get_chroma_format()) * chroma_v_subsampling(image->get_chroma_format());
+ bytes_per_pixel /= downsampling;
+ }
+
+ m_bytes_per_pixel_x4 += bytes_per_pixel;
+ }
+}
+
+
+uint64_t unc_encoder_planar::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const
+{
+ return tile_width * tile_height * m_bytes_per_pixel_x4 / 4;
}
@@ -151,13 +160,11 @@ std::vector<uint8_t> unc_encoder_planar::encode_tile(const std::shared_ptr<const
{
std::vector<uint8_t> data;
- auto channels = get_channels(src_image);
-
// compute total size of all components
uint64_t total_size = 0;
- for (channel_component channelcomponent : channels) {
+ for (channel_component channelcomponent : m_components) {
int bpp = src_image->get_bits_per_pixel(channelcomponent.channel);
int bytes_per_pixel = (bpp + 7) / 8;
@@ -170,7 +177,7 @@ std::vector<uint8_t> unc_encoder_planar::encode_tile(const std::shared_ptr<const
uint64_t out_data_start_pos = 0;
- for (channel_component channelcomponent : channels) {
+ for (channel_component channelcomponent : m_components) {
int bpp = src_image->get_bits_per_pixel(channelcomponent.channel);
int bytes_per_pixel = (bpp + 7) / 8;
diff --git a/libheif/codecs/uncompressed/unc_encoder_planar.h b/libheif/codecs/uncompressed/unc_encoder_planar.h
index 3609b319..8fdbde51 100644
--- a/libheif/codecs/uncompressed/unc_encoder_planar.h
+++ b/libheif/codecs/uncompressed/unc_encoder_planar.h
@@ -17,6 +17,7 @@
#define LIBHEIF_UNC_ENCODER_PLANAR_H
#include "unc_encoder.h"
+#include "unc_types.h"
class unc_encoder_planar : public unc_encoder
{
@@ -24,7 +25,21 @@ public:
unc_encoder_planar(const std::shared_ptr<const HeifPixelImage>& image,
const heif_encoding_options& options);
+ uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const override;
+
[[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+
+private:
+ struct channel_component
+ {
+ heif_channel channel;
+ heif_uncompressed_component_type component_type;
+ };
+
+ std::vector<channel_component> m_components;
+ uint32_t m_bytes_per_pixel_x4;
+
+ void add_channel_if_exists(const std::shared_ptr<const HeifPixelImage>& image, heif_channel channel);
};
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc
index 0e681286..7227e6a7 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.cc
@@ -64,6 +64,8 @@ unc_encoder_rgb3_rgba::unc_encoder_rgb3_rgba(const std::shared_ptr<const HeifPix
m_cmpd->add_component({component_type_alpha});
}
+ m_bytes_per_pixel = save_alpha ? 4 : 3;
+
uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);
if (bpp == 8) {
@@ -91,22 +93,25 @@ unc_encoder_rgb3_rgba::unc_encoder_rgb3_rgba(const std::shared_ptr<const HeifPix
}
-std::vector<uint8_t> unc_encoder_rgb3_rgba::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
+
+uint64_t unc_encoder_rgb3_rgba::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const
{
- std::vector<uint8_t> data;
+ return tile_width * tile_height * m_bytes_per_pixel;
+}
- bool save_alpha = src_image->has_alpha();
- int bytes_per_pixel = save_alpha ? 4 : 3;
+std::vector<uint8_t> unc_encoder_rgb3_rgba::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
+{
+ std::vector<uint8_t> data;
size_t src_stride;
const uint8_t* src_data = src_image->get_plane(heif_channel_interleaved, &src_stride);
- uint64_t out_size = static_cast<uint64_t>(src_image->get_height()) * src_image->get_width() * bytes_per_pixel;
+ uint64_t out_size = static_cast<uint64_t>(src_image->get_height()) * src_image->get_width() * m_bytes_per_pixel;
data.resize(out_size);
for (uint32_t y = 0; y < src_image->get_height(); y++) {
- memcpy(data.data() + y * src_image->get_width() * bytes_per_pixel, src_data + src_stride * y, src_image->get_width() * bytes_per_pixel);
+ memcpy(data.data() + y * src_image->get_width() * m_bytes_per_pixel, src_data + src_stride * y, src_image->get_width() * m_bytes_per_pixel);
}
return data;
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h
index 2c196287..e64fdb7b 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb3_rgba.h
@@ -29,7 +29,12 @@ public:
unc_encoder_rgb3_rgba(const std::shared_ptr<const HeifPixelImage>& image,
const heif_encoding_options& options);
+ uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const override;
+
[[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+
+private:
+ uint8_t m_bytes_per_pixel = 0;
};
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc
index 993bd7eb..abd16c7e 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.cc
@@ -60,10 +60,10 @@ unc_encoder_rgb_hdr_packed_interleave::unc_encoder_rgb_hdr_packed_interleave(con
uint8_t bpp = image->get_bits_per_pixel(heif_channel_interleaved);
uint8_t nBits = static_cast<uint8_t>(3 * bpp);
- uint8_t bytes_per_pixel = static_cast<uint8_t>((nBits + 7) / 8);
+ m_bytes_per_pixel = static_cast<uint8_t>((nBits + 7) / 8);
m_uncC->set_interleave_type(interleave_mode_pixel);
- m_uncC->set_pixel_size(bytes_per_pixel);
+ m_uncC->set_pixel_size(m_bytes_per_pixel);
m_uncC->set_sampling_type(0);
m_uncC->set_components_little_endian(true);
@@ -73,20 +73,23 @@ unc_encoder_rgb_hdr_packed_interleave::unc_encoder_rgb_hdr_packed_interleave(con
}
+uint64_t unc_encoder_rgb_hdr_packed_interleave::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const
+{
+ return tile_width * tile_height * m_bytes_per_pixel;
+}
+
+
std::vector<uint8_t> unc_encoder_rgb_hdr_packed_interleave::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
{
std::vector<uint8_t> data;
uint8_t bpp = src_image->get_bits_per_pixel(heif_channel_interleaved);
- uint8_t nBits = static_cast<uint8_t>(3 * bpp);
- uint8_t bytes_per_pixel = static_cast<uint8_t>((nBits + 7) / 8);
-
size_t src_stride;
const auto* src_data = reinterpret_cast<const uint16_t*>(src_image->get_plane(heif_channel_interleaved, &src_stride));
src_stride /= 2;
- uint64_t out_size = static_cast<uint64_t>(src_image->get_height()) * src_image->get_width() * bytes_per_pixel;
+ uint64_t out_size = static_cast<uint64_t>(src_image->get_height()) * src_image->get_width() * m_bytes_per_pixel;
data.resize(out_size);
uint8_t* p = data.data();
@@ -104,7 +107,7 @@ std::vector<uint8_t> unc_encoder_rgb_hdr_packed_interleave::encode_tile(const st
*p++ = static_cast<uint8_t>((combined_pixel >> 16) & 0xFF);
*p++ = static_cast<uint8_t>((combined_pixel >> 24) & 0xFF);
- if (bytes_per_pixel > 4) {
+ if (m_bytes_per_pixel > 4) {
*p++ = static_cast<uint8_t>((combined_pixel >> 32) & 0xFF);
}
}
diff --git a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h
index 3dd69646..e3948761 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h
+++ b/libheif/codecs/uncompressed/unc_encoder_rgb_hdr_packed_interleave.h
@@ -24,7 +24,12 @@ public:
unc_encoder_rgb_hdr_packed_interleave(const std::shared_ptr<const HeifPixelImage>& image,
const heif_encoding_options& options);
+ uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const override;
+
[[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+
+private:
+ uint8_t m_bytes_per_pixel = 0;
};
diff --git a/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc b/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc
index 26d3e1aa..e5809d8d 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_rrggbb.cc
@@ -68,6 +68,8 @@ unc_encoder_rrggbb::unc_encoder_rrggbb(const std::shared_ptr<const HeifPixelImag
m_cmpd->add_component({component_type_alpha});
}
+ m_bytes_per_pixel = save_alpha ? 8 : 6;
+
bool little_endian = (image->get_chroma_format() == heif_chroma_interleaved_RRGGBB_LE ||
image->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE);
@@ -91,22 +93,24 @@ unc_encoder_rrggbb::unc_encoder_rrggbb(const std::shared_ptr<const HeifPixelImag
}
-std::vector<uint8_t> unc_encoder_rrggbb::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
+uint64_t unc_encoder_rrggbb::compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const
{
- std::vector<uint8_t> data;
+ return tile_width * tile_height * m_bytes_per_pixel;
+}
- bool save_alpha = src_image->has_alpha();
- int bytes_per_pixel = save_alpha ? 8 : 6;
+std::vector<uint8_t> unc_encoder_rrggbb::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
+{
+ std::vector<uint8_t> data;
size_t src_stride;
const uint8_t* src_data = src_image->get_plane(heif_channel_interleaved, &src_stride);
- uint64_t out_size = static_cast<uint64_t>(src_image->get_height()) * src_image->get_width() * bytes_per_pixel;
+ uint64_t out_size = static_cast<uint64_t>(src_image->get_height()) * src_image->get_width() * m_bytes_per_pixel;
data.resize(out_size);
for (uint32_t y = 0; y < src_image->get_height(); y++) {
- memcpy(data.data() + y * src_image->get_width() * bytes_per_pixel, src_data + src_stride * y, src_image->get_width() * bytes_per_pixel);
+ memcpy(data.data() + y * src_image->get_width() * m_bytes_per_pixel, src_data + src_stride * y, src_image->get_width() * m_bytes_per_pixel);
}
return data;
diff --git a/libheif/codecs/uncompressed/unc_encoder_rrggbb.h b/libheif/codecs/uncompressed/unc_encoder_rrggbb.h
index 431cb19e..3a518fdf 100644
--- a/libheif/codecs/uncompressed/unc_encoder_rrggbb.h
+++ b/libheif/codecs/uncompressed/unc_encoder_rrggbb.h
@@ -29,7 +29,12 @@ public:
unc_encoder_rrggbb(const std::shared_ptr<const HeifPixelImage>& image,
const heif_encoding_options& options);
+ uint64_t compute_tile_data_size_bytes(uint32_t tile_width, uint32_t tile_height) const override;
+
[[nodiscard]] std::vector<uint8_t> encode_tile(const std::shared_ptr<const HeifPixelImage>& image) const override;
+
+private:
+ uint8_t m_bytes_per_pixel = 0;
};
diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc
index f4b26b15..5151e00b 100644
--- a/libheif/image-items/unc_image.cc
+++ b/libheif/image-items/unc_image.cc
@@ -191,9 +191,8 @@ Result<std::shared_ptr<ImageItem_uncompressed>> ImageItem_uncompressed::add_unci
// Create empty image. If we use compression, we append the data piece by piece.
if (parameters->compression == heif_unci_compression_off) {
- assert(false); // TODO compute_tile_data_size_bytes() is too simplistic
- uint64_t tile_size = uncC->compute_tile_data_size_bytes(parameters->image_width / uncC->get_number_of_tile_columns(),
- parameters->image_height / uncC->get_number_of_tile_rows());
+ uint64_t tile_size = unci_image->m_unc_encoder->compute_tile_data_size_bytes(parameters->image_width / uncC->get_number_of_tile_columns(),
+ parameters->image_height / uncC->get_number_of_tile_rows());
std::vector<uint8_t> dummydata;
dummydata.resize(tile_size);
@@ -249,7 +248,7 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c
// uncompressed
- uint64_t tile_data_size = uncC->compute_tile_data_size_bytes(tile_width, tile_height);
+ uint64_t tile_data_size = m_unc_encoder->compute_tile_data_size_bytes(tile_width, tile_height);
get_file()->replace_iloc_data(get_id(), tile_idx * tile_data_size, *codedBitstreamResult, 0);
}