Commit 724ad426 for libheif

commit 724ad42638c025993a0de8b53b180e465397c500
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Mon May 18 21:17:15 2026 +0200

    since image memory is now zero filled on allocation, we can remove zeroing broken tiles

diff --git a/libheif/image-items/grid.cc b/libheif/image-items/grid.cc
index 18e7bab4..b6a68bc1 100644
--- a/libheif/image-items/grid.cc
+++ b/libheif/image-items/grid.cc
@@ -310,7 +310,6 @@ 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;
@@ -327,7 +326,6 @@ 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;
@@ -341,7 +339,6 @@ 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;
@@ -391,7 +388,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, failed_tiles, processed_ids);
+        err = decode_and_paste_tile_image(tileID, x0, y0, img, options, progress_counter, warnings, processed_ids);
         if (err) {
           return err;
         }
@@ -439,7 +436,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, std::ref(failed_tiles), processed_ids));
+                                std::ref(progress_counter), warnings, processed_ids));
     }

     // check for decoding errors in remaining tiles
@@ -464,9 +461,6 @@ 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());
   }

@@ -490,26 +484,24 @@ 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 failedTilesMutex;
+  static std::mutex warningsMutex;
 #endif

   auto tileItem = get_context()->get_image(tileID, true);
   if (!tileItem && !options.strict_decoding) {
-    // We ignore missing images.
+    // We ignore missing images. The un-pasted canvas region stays zero from calloc().
 #if ENABLE_PARALLEL_TILE_DECODING
-    std::lock_guard<std::mutex> lock(failedTilesMutex);
+    std::lock_guard<std::mutex> lock(warningsMutex);
 #endif
     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);
   }

@@ -521,12 +513,11 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t
   auto decodeResult = tileItem->decode_image(options, false, 0, 0, processed_ids);
   if (!decodeResult) {
     if (!options.strict_decoding) {
-      // We ignore broken tiles.
+      // We ignore broken tiles. The un-pasted canvas region stays zero from calloc().
 #if ENABLE_PARALLEL_TILE_DECODING
-      std::lock_guard<std::mutex> lock(failedTilesMutex);
+      std::lock_guard<std::mutex> lock(warningsMutex);
 #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 37021464..3e6f9052 100644
--- a/libheif/image-items/grid.h
+++ b/libheif/image-items/grid.h
@@ -170,17 +170,10 @@ 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/image/pixelimage.cc b/libheif/image/pixelimage.cc
index ba06fc0a..7827032b 100644
--- a/libheif/image/pixelimage.cc
+++ b/libheif/image/pixelimage.cc
@@ -1150,41 +1150,6 @@ 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_channel_memory(channel, &stride);
-    uint32_t bytes_per_pixel = get_storage_bits_per_pixel(channel) / 8;
-    size_t width_bytes = static_cast<size_t>(cw) * bytes_per_pixel;
-
-    for (uint32_t y = 0; y < ch; y++) {
-      memset(data + static_cast<size_t>(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)
 {
   // TODO: Bayer pattern, polarization patterns and sensor maps reference
diff --git a/libheif/image/pixelimage.h b/libheif/image/pixelimage.h
index 8f1bbdf3..e33d9f66 100644
--- a/libheif/image/pixelimage.h
+++ b/libheif/image/pixelimage.h
@@ -274,8 +274,6 @@ 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);