Commit 8936e7c0 for libheif
commit 8936e7c0875437ebac6ea0d84055f340b36d2eb8
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri Feb 20 11:35:39 2026 +0100
unci: combine the bytealign_component_interleave encoder into the component_interleave encoder
diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt
index 0bff75bd..97eca435 100644
--- a/libheif/CMakeLists.txt
+++ b/libheif/CMakeLists.txt
@@ -319,8 +319,6 @@ if (WITH_UNCOMPRESSED_CODEC)
codecs/uncompressed/unc_encoder_rgb_bytealign_pixel_interleave.h
codecs/uncompressed/unc_encoder_component_interleave.cc
codecs/uncompressed/unc_encoder_component_interleave.h
- codecs/uncompressed/unc_encoder_bytealign_component_interleave.cc
- codecs/uncompressed/unc_encoder_bytealign_component_interleave.h
codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.cc
codecs/uncompressed/unc_encoder_rgb_block_pixel_interleave.h)
endif ()
diff --git a/libheif/codecs/uncompressed/unc_encoder.cc b/libheif/codecs/uncompressed/unc_encoder.cc
index ba927a91..61d2acf5 100644
--- a/libheif/codecs/uncompressed/unc_encoder.cc
+++ b/libheif/codecs/uncompressed/unc_encoder.cc
@@ -26,7 +26,6 @@
#include "pixelimage.h"
#include "unc_boxes.h"
#include "unc_encoder_component_interleave.h"
-#include "unc_encoder_bytealign_component_interleave.h"
#include "unc_encoder_rgb_block_pixel_interleave.h"
#include "unc_encoder_rgb_pixel_interleave.h"
#include "unc_encoder_rgb_bytealign_pixel_interleave.h"
@@ -89,14 +88,12 @@ Result<std::unique_ptr<const unc_encoder> > unc_encoder_factory::get_unc_encoder
static unc_encoder_factory_rgb_block_pixel_interleave enc_rgb_block_pixel_interleave;
static unc_encoder_factory_rgb_bytealign_pixel_interleave enc_rgb_bytealign_pixel_interleave;
static unc_encoder_factory_component_interleave enc_component_interleave;
- static unc_encoder_factory_bytealign_component_interleave enc_bytealign_component_interleave;
static const unc_encoder_factory* encoders[]{
&enc_rgb_pixel_interleave,
&enc_rgb_block_pixel_interleave,
&enc_rgb_bytealign_pixel_interleave,
- &enc_component_interleave,
- &enc_bytealign_component_interleave
+ &enc_component_interleave
};
for (const unc_encoder_factory* enc : encoders) {
diff --git a/libheif/codecs/uncompressed/unc_encoder_bytealign_component_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_bytealign_component_interleave.cc
deleted file mode 100644
index 568386d3..00000000
--- a/libheif/codecs/uncompressed/unc_encoder_bytealign_component_interleave.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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 "unc_encoder_bytealign_component_interleave.h"
-
-#include <cstring>
-
-#include "pixelimage.h"
-#include "unc_boxes.h"
-
-
-bool unc_encoder_factory_bytealign_component_interleave::can_encode(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const
-{
- if (image->has_channel(heif_channel_interleaved)) {
- return false;
- }
-
- return true;
-}
-
-
-std::unique_ptr<const unc_encoder> unc_encoder_factory_bytealign_component_interleave::create(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const
-{
- return std::make_unique<unc_encoder_bytealign_component_interleave>(image, options);
-}
-
-
-unc_encoder_bytealign_component_interleave::unc_encoder_bytealign_component_interleave(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options)
-{
- bool is_nonvisual = (image->get_colorspace() == heif_colorspace_nonvisual);
- uint32_t num_components = image->get_number_of_components();
-
- for (uint32_t idx = 0; idx < num_components; idx++) {
- heif_uncompressed_component_type comp_type;
-
- if (is_nonvisual) {
- comp_type = static_cast<heif_uncompressed_component_type>(image->get_component_type(idx));
- }
- else {
- heif_channel ch = image->get_component_channel(idx);
- if (ch == heif_channel_Y && !image->has_channel(heif_channel_Cb)) {
- comp_type = component_type_monochrome;
- }
- else {
- comp_type = heif_channel_to_component_type(ch);
- }
- }
-
- uint8_t bpp = image->get_component_bits_per_pixel(idx);
- auto datatype = image->get_component_datatype(idx);
- auto comp_format = to_unc_component_format(datatype);
-
- m_components.push_back({idx, comp_type, comp_format, bpp});
- }
-
- // Build cmpd/uncC boxes
- bool little_endian = false;
-
- uint16_t box_index = 0;
- for (const auto& comp : m_components) {
- m_cmpd->add_component({comp.component_type});
-
- uint8_t component_align_size = static_cast<uint8_t>((comp.bpp + 7) / 8);
- if (comp.bpp % 8 == 0) {
- component_align_size = 0;
- }
-
- if (comp.bpp > 8) {
- little_endian = true;
- }
-
- m_uncC->add_component({box_index, comp.bpp, comp.component_format, component_align_size});
- box_index++;
- }
-
- m_uncC->set_interleave_type(interleave_mode_component);
- m_uncC->set_components_little_endian(little_endian);
-
- if (image->get_chroma_format() == heif_chroma_420) {
- m_uncC->set_sampling_type(sampling_mode_420);
- }
- else if (image->get_chroma_format() == heif_chroma_422) {
- m_uncC->set_sampling_type(sampling_mode_422);
- }
- else {
- m_uncC->set_sampling_type(sampling_mode_no_subsampling);
- }
-
- // --- compute bytes per pixel
-
- m_bytes_per_pixel_x4 = 0;
-
- for (const auto& comp : m_components) {
- int bytes_per_pixel = 4 * (comp.bpp + 7) / 8;
-
- if (!is_nonvisual) {
- heif_channel ch = image->get_component_channel(comp.component_idx);
- if (ch == heif_channel_Cb || ch == 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_bytealign_component_interleave::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;
-}
-
-
-std::vector<uint8_t> unc_encoder_bytealign_component_interleave::encode_tile(const std::shared_ptr<const HeifPixelImage>& src_image) const
-{
- // compute total size of all components
-
- uint64_t total_size = 0;
-
- for (const auto& comp : m_components) {
- int bytes_per_pixel = (comp.bpp + 7) / 8;
- uint32_t w = src_image->get_component_width(comp.component_idx);
- uint32_t h = src_image->get_component_height(comp.component_idx);
- total_size += static_cast<uint64_t>(h) * w * bytes_per_pixel;
- }
-
- std::vector<uint8_t> data;
- data.resize(total_size);
-
- // output all component planes
-
- uint64_t out_data_start_pos = 0;
-
- for (const auto& comp : m_components) {
- int bytes_per_pixel = (comp.bpp + 7) / 8;
- uint32_t w = src_image->get_component_width(comp.component_idx);
- uint32_t h = src_image->get_component_height(comp.component_idx);
-
- size_t src_stride;
- const uint8_t* src_data = src_image->get_component(comp.component_idx, &src_stride);
-
- for (uint32_t y = 0; y < h; y++) {
- memcpy(data.data() + out_data_start_pos,
- src_data + src_stride * y,
- w * bytes_per_pixel);
- out_data_start_pos += w * bytes_per_pixel;
- }
- }
-
- return data;
-}
diff --git a/libheif/codecs/uncompressed/unc_encoder_bytealign_component_interleave.h b/libheif/codecs/uncompressed/unc_encoder_bytealign_component_interleave.h
deleted file mode 100644
index b6a7eb63..00000000
--- a/libheif/codecs/uncompressed/unc_encoder_bytealign_component_interleave.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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_UNC_ENCODER_BYTEALIGN_COMPONENT_INTERLEAVE_H
-#define LIBHEIF_UNC_ENCODER_BYTEALIGN_COMPONENT_INTERLEAVE_H
-
-#include "unc_encoder.h"
-#include "unc_types.h"
-
-#include <memory>
-#include <vector>
-
-class unc_encoder_bytealign_component_interleave : public unc_encoder
-{
-public:
- unc_encoder_bytealign_component_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:
- struct encoded_component
- {
- uint32_t component_idx;
- heif_uncompressed_component_type component_type;
- heif_uncompressed_component_format component_format;
- uint8_t bpp;
- };
-
- std::vector<encoded_component> m_components;
- uint32_t m_bytes_per_pixel_x4;
-};
-
-
-class unc_encoder_factory_bytealign_component_interleave : public unc_encoder_factory
-{
-public:
-
-private:
- [[nodiscard]] bool can_encode(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const override;
-
- std::unique_ptr<const unc_encoder> create(const std::shared_ptr<const HeifPixelImage>& image,
- const heif_encoding_options& options) const override;
-};
-
-#endif //LIBHEIF_UNC_ENCODER_BYTEALIGN_COMPONENT_INTERLEAVE_H
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
index ae5837bd..0bcd1a18 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.cc
@@ -33,15 +33,7 @@ bool unc_encoder_factory_component_interleave::can_encode(const std::shared_ptr<
return false;
}
- // Check if any component has non-byte-aligned bpp
- uint32_t n = image->get_number_of_components();
- for (uint32_t i = 0; i < n; i++) {
- if (image->get_component_bits_per_pixel(i) % 8 != 0) {
- return true;
- }
- }
-
- return false;
+ return true;
}
@@ -76,18 +68,31 @@ unc_encoder_component_interleave::unc_encoder_component_interleave(const std::sh
}
uint8_t bpp = image->get_component_bits_per_pixel(idx);
- m_components.push_back({idx, ch, comp_type, bpp});
+ auto comp_format = to_unc_component_format(image->get_component_datatype(idx));
+ bool aligned = (bpp % 8 == 0);
+
+ m_components.push_back({idx, ch, comp_type, comp_format, bpp, aligned});
}
- uint16_t index = 0;
+ // Build cmpd/uncC boxes
+ bool little_endian = false;
+
+ uint16_t box_index = 0;
for (const auto& comp : m_components) {
m_cmpd->add_component({comp.component_type});
- m_uncC->add_component({index, comp.bpp, component_format_unsigned, 0});
- index++;
+
+ uint8_t component_align_size = 0;
+
+ if (comp.byte_aligned && comp.bpp > 8) {
+ little_endian = true;
+ }
+
+ m_uncC->add_component({box_index, comp.bpp, comp.component_format, component_align_size});
+ box_index++;
}
m_uncC->set_interleave_type(interleave_mode_component);
- m_uncC->set_components_little_endian(false);
+ m_uncC->set_components_little_endian(little_endian);
m_uncC->set_block_size(0);
if (image->get_chroma_format() == heif_chroma_420) {
@@ -120,7 +125,13 @@ uint64_t unc_encoder_component_interleave::compute_tile_data_size_bytes(uint32_t
}
}
- uint64_t row_bytes = (static_cast<uint64_t>(plane_width) * comp.bpp + 7) / 8;
+ uint64_t row_bytes;
+ if (comp.byte_aligned) {
+ row_bytes = static_cast<uint64_t>(plane_width) * ((comp.bpp + 7) / 8);
+ }
+ else {
+ row_bytes = (static_cast<uint64_t>(plane_width) * comp.bpp + 7) / 8;
+ }
total += row_bytes * plane_height;
}
return total;
@@ -131,7 +142,9 @@ std::vector<uint8_t> unc_encoder_component_interleave::encode_tile(const std::sh
{
uint64_t total_size = compute_tile_data_size_bytes(src_image->get_width(), src_image->get_height());
std::vector<uint8_t> data;
- data.reserve(total_size);
+ data.resize(total_size);
+
+ uint64_t out_pos = 0;
for (const auto& comp : m_components) {
uint32_t plane_width = src_image->get_component_width(comp.component_idx);
@@ -141,39 +154,53 @@ std::vector<uint8_t> unc_encoder_component_interleave::encode_tile(const std::sh
size_t src_stride;
const uint8_t* src_data = src_image->get_component(comp.component_idx, &src_stride);
- for (uint32_t y = 0; y < plane_height; y++) {
- const uint8_t* row = src_data + src_stride * y;
-
- uint64_t accumulator = 0;
- int accumulated_bits = 0;
-
- for (uint32_t x = 0; x < plane_width; x++) {
- uint32_t sample;
+ if (comp.byte_aligned) {
+ // Byte-aligned path: memcpy per row
+ int bytes_per_pixel = (bpp + 7) / 8;
- if (bpp <= 8) {
- sample = row[x];
- }
- else if (bpp <= 16) {
- sample = reinterpret_cast<const uint16_t*>(row)[x];
- }
- else {
- sample = reinterpret_cast<const uint32_t*>(row)[x];
+ for (uint32_t y = 0; y < plane_height; y++) {
+ memcpy(data.data() + out_pos,
+ src_data + src_stride * y,
+ plane_width * bytes_per_pixel);
+ out_pos += plane_width * bytes_per_pixel;
+ }
+ }
+ else {
+ // Bit-packed path: bit accumulator with row-end flush
+ for (uint32_t y = 0; y < plane_height; y++) {
+ const uint8_t* row = src_data + src_stride * y;
+
+ uint64_t accumulator = 0;
+ int accumulated_bits = 0;
+
+ for (uint32_t x = 0; x < plane_width; x++) {
+ uint32_t sample;
+
+ if (bpp <= 8) {
+ sample = row[x];
+ }
+ else if (bpp <= 16) {
+ sample = reinterpret_cast<const uint16_t*>(row)[x];
+ }
+ else {
+ sample = reinterpret_cast<const uint32_t*>(row)[x];
+ }
+
+ accumulator = (accumulator << bpp) | sample;
+ accumulated_bits += bpp;
+
+ while (accumulated_bits >= 8) {
+ accumulated_bits -= 8;
+ data[out_pos++] = static_cast<uint8_t>(accumulator >> accumulated_bits);
+ accumulator &= (uint64_t{1} << accumulated_bits) - 1;
+ }
}
- accumulator = (accumulator << bpp) | sample;
- accumulated_bits += bpp;
-
- while (accumulated_bits >= 8) {
- accumulated_bits -= 8;
- data.push_back(static_cast<uint8_t>(accumulator >> accumulated_bits));
- accumulator &= (uint64_t{1} << accumulated_bits) - 1;
+ // Flush partial byte at row end (pad with zeros in LSBs)
+ if (accumulated_bits > 0) {
+ data[out_pos++] = static_cast<uint8_t>(accumulator << (8 - accumulated_bits));
}
}
-
- // Flush partial byte at row end (pad with zeros in LSBs)
- if (accumulated_bits > 0) {
- data.push_back(static_cast<uint8_t>(accumulator << (8 - accumulated_bits)));
- }
}
}
diff --git a/libheif/codecs/uncompressed/unc_encoder_component_interleave.h b/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
index 6e837090..c5322292 100644
--- a/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
+++ b/libheif/codecs/uncompressed/unc_encoder_component_interleave.h
@@ -43,7 +43,9 @@ private:
uint32_t component_idx;
heif_channel channel;
heif_uncompressed_component_type component_type;
+ heif_uncompressed_component_format component_format;
uint8_t bpp;
+ bool byte_aligned;
};
std::vector<channel_component> m_components;