Commit fc1b310f for libheif
commit fc1b310fdc62905629ff8b476fbb8d43c11668e8
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sat Mar 14 00:27:01 2026 +0100
zero-fill missing tiles to avoid uninitialized memory
diff --git a/libheif/image-items/grid.cc b/libheif/image-items/grid.cc
index 9744c1d0..bb1f4d08 100644
--- a/libheif/image-items/grid.cc
+++ b/libheif/image-items/grid.cc
@@ -24,6 +24,7 @@
#include <cstring>
#include <deque>
#include <future>
+#include <mutex>
#include <set>
#include <algorithm>
#include "api_structs.h"
@@ -303,6 +304,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
int progress_counter = 0;
bool cancelled = false;
std::shared_ptr<std::vector<Error> > warnings(new std::vector<Error>());
+ std::vector<FailedTile> failed_tiles;
for (uint32_t y = 0; y < grid.get_rows() && !cancelled; y++) {
uint32_t x0 = 0;
@@ -319,6 +321,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
heif_error_Invalid_input,
heif_suberror_Missing_grid_images,
});
+ failed_tiles.push_back({x0, y0});
reference_idx++;
x0 += tile_width;
continue;
@@ -332,6 +335,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
if (!options.strict_decoding && reference_idx != 0) {
// Skip missing tiles (unless it's the first one).
warnings->push_back(error);
+ failed_tiles.push_back({x0, y0});
reference_idx++;
x0 += tile_width;
continue;
@@ -379,7 +383,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
}
}
- err = decode_and_paste_tile_image(tileID, x0, y0, img, options, progress_counter, warnings, processed_ids);
+ err = decode_and_paste_tile_image(tileID, x0, y0, img, options, progress_counter, warnings, failed_tiles, processed_ids);
if (err) {
return err;
}
@@ -427,7 +431,7 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
errs.push_back(std::async(std::launch::async,
&ImageItem_Grid::decode_and_paste_tile_image, this,
data.tileID, data.x_origin, data.y_origin, std::ref(img), options,
- std::ref(progress_counter), warnings, processed_ids));
+ std::ref(progress_counter), warnings, std::ref(failed_tiles), processed_ids));
}
// check for decoding errors in remaining tiles
@@ -452,6 +456,9 @@ Result<std::shared_ptr<HeifPixelImage>> ImageItem_Grid::decode_full_grid_image(c
}
if (img) {
+ for (const auto& tile : failed_tiles) {
+ img->zero_region(tile.x0, tile.y0, tile_width, tile_height);
+ }
img->add_warnings(*warnings.get());
}
@@ -475,23 +482,26 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t
const heif_decoding_options& options,
int& progress_counter,
std::shared_ptr<std::vector<Error> > warnings,
+ std::vector<FailedTile>& failed_tiles,
std::set<heif_item_id> processed_ids) const
{
std::shared_ptr<HeifPixelImage> tile_img;
#if ENABLE_PARALLEL_TILE_DECODING
- static std::mutex warningsMutex;
+ static std::mutex failedTilesMutex;
#endif
auto tileItem = get_context()->get_image(tileID, true);
if (!tileItem && !options.strict_decoding) {
// We ignore missing images.
#if ENABLE_PARALLEL_TILE_DECODING
- std::lock_guard<std::mutex> lock(warningsMutex);
+ std::lock_guard<std::mutex> lock(failedTilesMutex);
#endif
- warnings->push_back(Error{
+ warnings->emplace_back(
heif_error_Invalid_input,
heif_suberror_Missing_grid_images,
- });
+ "Missing grid image"
+ );
+ failed_tiles.push_back({x0, y0});
return progress_and_return_ok(options, progress_counter);
}
@@ -505,9 +515,10 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t
if (!options.strict_decoding) {
// We ignore broken tiles.
#if ENABLE_PARALLEL_TILE_DECODING
- std::lock_guard<std::mutex> lock(warningsMutex);
+ std::lock_guard<std::mutex> lock(failedTilesMutex);
#endif
warnings->push_back(decodeResult.error());
+ failed_tiles.push_back({x0, y0});
return progress_and_return_ok(options, progress_counter);
}
diff --git a/libheif/image-items/grid.h b/libheif/image-items/grid.h
index 0555d88f..032086c2 100644
--- a/libheif/image-items/grid.h
+++ b/libheif/image-items/grid.h
@@ -167,10 +167,17 @@ private:
Result<std::shared_ptr<HeifPixelImage>> decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty, std::set<heif_item_id> processed_ids) const;
+ struct FailedTile {
+ // Top-left pixel coordinates
+ uint32_t x0;
+ uint32_t y0;
+ };
+
Error decode_and_paste_tile_image(heif_item_id tileID, uint32_t x0, uint32_t y0,
std::shared_ptr<HeifPixelImage>& inout_image,
const heif_decoding_options& options, int& progress_counter,
std::shared_ptr<std::vector<Error> > warnings,
+ std::vector<FailedTile>& failed_tiles,
std::set<heif_item_id> processed_ids) const;
};
diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc
index 3d0b8cb3..eaa2bba4 100644
--- a/libheif/pixelimage.cc
+++ b/libheif/pixelimage.cc
@@ -1255,6 +1255,41 @@ Error HeifPixelImage::copy_image_to(const std::shared_ptr<const HeifPixelImage>&
}
+void HeifPixelImage::zero_region(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h)
+{
+ uint32_t img_w = get_width();
+ uint32_t img_h = get_height();
+ heif_chroma chroma = get_chroma_format();
+
+ std::set<enum heif_channel> channels = get_channel_set();
+
+ for (heif_channel channel : channels) {
+ uint32_t cx0 = channel_width(x0, chroma, channel);
+ uint32_t cy0 = channel_height(y0, chroma, channel);
+ uint32_t cw = channel_width(w, chroma, channel);
+ uint32_t ch = channel_height(h, chroma, channel);
+
+ // clamp to plane bounds
+ uint32_t plane_w = channel_width(img_w, chroma, channel);
+ uint32_t plane_h = channel_height(img_h, chroma, channel);
+ if (cx0 >= plane_w || cy0 >= plane_h) {
+ continue;
+ }
+ cw = std::min(cw, plane_w - cx0);
+ ch = std::min(ch, plane_h - cy0);
+
+ size_t stride = 0;
+ uint8_t* data = get_plane(channel, &stride);
+ uint32_t bytes_per_pixel = get_storage_bits_per_pixel(channel) / 8;
+ uint32_t width_bytes = cw * bytes_per_pixel;
+
+ for (uint32_t y = 0; y < ch; y++) {
+ memset(data + cx0 * bytes_per_pixel + (cy0 + y) * stride, 0, width_bytes);
+ }
+ }
+}
+
+
Result<std::shared_ptr<HeifPixelImage>> HeifPixelImage::rotate_ccw(int angle_degrees, const heif_security_limits* limits)
{
// --- for some subsampled chroma colorspaces, we have to transform to 4:4:4 before rotation
diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h
index 6e14471e..56c4d7f0 100644
--- a/libheif/pixelimage.h
+++ b/libheif/pixelimage.h
@@ -508,6 +508,8 @@ public:
Error copy_image_to(const std::shared_ptr<const HeifPixelImage>& source, uint32_t x0, uint32_t y0);
+ void zero_region(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h);
+
Result<std::shared_ptr<HeifPixelImage>> rotate_ccw(int angle_degrees, const heif_security_limits* limits);
Result<std::shared_ptr<HeifPixelImage>> mirror_inplace(heif_transform_mirror_direction, const heif_security_limits* limits);