Commit 9908fd73 for libheif
commit 9908fd733b9e4fa5b5a77e1a842a1a6ad9c0cf50
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri Oct 9 22:51:23 2026 +0200
rotate_ccw: compute block ends without wrapping, drop a duplicate include, fix test ownership
Follow-ups to #1934:
- `start + block` wrapped for plane sizes close to 2^32, which is reachable
with the security limits disabled, and the block loops then restarted at 0.
Compute the block end by subtraction and step to it.
- `<bit>` was included twice.
- `rotate_ccw()` calls `shared_from_this()` for some inputs, so the test image
must be owned by a shared_ptr, like the other plane tests do.
diff --git a/libheif/image/pixelimage.cc b/libheif/image/pixelimage.cc
index 1fe9b5f7..e3735d19 100644
--- a/libheif/image/pixelimage.cc
+++ b/libheif/image/pixelimage.cc
@@ -30,7 +30,6 @@
#include <utility>
#include <limits>
#include <algorithm>
-#include <bit>
#include <map>
#include <string>
#include <sstream>
@@ -1775,6 +1774,12 @@ void HeifPixelImage::ComponentStorage::rotate_ccw(int angle_degrees,
// rotated in blocks of 64x64 samples, whose rows stay in the cache while the block is copied.
constexpr uint32_t block = 64;
+ // End of the block that starts at `start` in a dimension of `size` samples. `start + block`
+ // would wrap for sizes close to 2^32 (reachable with the security limits disabled).
+ auto block_end = [](uint32_t start, uint32_t size) {
+ return size - start > block ? start + block : size;
+ };
+
// 8-bit samples move as 8x8 tiles, read as eight 64-bit words and transposed in registers. The
// columns and rows at the edges that do not fill a tile are left to the loop below.
uint32_t w8 = 0;
@@ -1782,10 +1787,10 @@ void HeifPixelImage::ComponentStorage::rotate_ccw(int angle_degrees,
if constexpr (sizeof(T) == 1 && std::endian::native == std::endian::little) {
w8 = w & ~7U;
h8 = h & ~7U;
- for (uint32_t y0 = 0; y0 < w8; y0 += block) {
- for (uint32_t x0 = 0; x0 < h8; x0 += block) {
- for (uint32_t y = y0; y < std::min(y0 + block, w8); y += 8) {
- for (uint32_t x = x0; x < std::min(x0 + block, h8); x += 8) {
+ for (uint32_t y0 = 0; y0 < w8; y0 = block_end(y0, w8)) {
+ for (uint32_t x0 = 0; x0 < h8; x0 = block_end(x0, h8)) {
+ for (uint32_t y = y0; y < block_end(y0, w8); y += 8) {
+ for (uint32_t x = x0; x < block_end(x0, h8); x += 8) {
// Output rows y to y+7, columns x to x+7.
uint64_t r[8];
if (angle_degrees == 270) {
@@ -1811,10 +1816,10 @@ void HeifPixelImage::ComponentStorage::rotate_ccw(int angle_degrees,
}
}
- for (uint32_t y0 = 0; y0 < w; y0 += block) {
- uint32_t y1 = std::min(y0 + block, w);
- for (uint32_t x0 = 0; x0 < h; x0 += block) {
- uint32_t x1 = std::min(x0 + block, h);
+ for (uint32_t y0 = 0; y0 < w; y0 = block_end(y0, w)) {
+ uint32_t y1 = block_end(y0, w);
+ for (uint32_t x0 = 0; x0 < h; x0 = block_end(x0, h)) {
+ uint32_t x1 = block_end(x0, h);
for (uint32_t y = y0; y < y1; y++) {
T* out_row = out_data + y * out_stride;
uint32_t x_start = std::max(x0, y < w8 ? h8 : 0);
diff --git a/tests/rotate_planes.cc b/tests/rotate_planes.cc
index 56a1047c..a7a60549 100644
--- a/tests/rotate_planes.cc
+++ b/tests/rotate_planes.cc
@@ -47,13 +47,15 @@ static void check_rotation(uint32_t w, uint32_t h, int angle)
CAPTURE(sizeof(T) * 8, w, h, angle);
auto* limits = heif_get_global_security_limits();
- HeifPixelImage image;
- image.create(w, h, heif_colorspace_monochrome, heif_chroma_monochrome);
- REQUIRE(image.add_channel(heif_channel_Y, w, h, sizeof(T) * 8, limits,
- heif_component_datatype_unsigned_integer).error_code == heif_error_Ok);
+ // rotate_ccw() calls shared_from_this() for some inputs, so the image must be owned by a
+ // shared_ptr.
+ auto image = std::make_shared<HeifPixelImage>();
+ image->create(w, h, heif_colorspace_monochrome, heif_chroma_monochrome);
+ REQUIRE(image->add_channel(heif_channel_Y, w, h, sizeof(T) * 8, limits,
+ heif_component_datatype_unsigned_integer).error_code == heif_error_Ok);
size_t in_stride;
- T* in = image.get_channel_memory<T>(heif_channel_Y, &in_stride);
+ T* in = image->get_channel_memory<T>(heif_channel_Y, &in_stride);
in_stride /= sizeof(T);
for (uint32_t y = 0; y < h; y++) {
for (uint32_t x = 0; x < w; x++) {
@@ -61,7 +63,7 @@ static void check_rotation(uint32_t w, uint32_t h, int angle)
}
}
- auto rotated = image.rotate_ccw(angle, limits);
+ auto rotated = image->rotate_ccw(angle, limits);
REQUIRE(rotated.error().error_code == heif_error_Ok);
std::shared_ptr<HeifPixelImage> out_image = *rotated;