This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 7913e12a60 Notify GDAL in advance of the region which will be read 
(all tiles). This commit contains again renaming of some `TiledGridCoverage` 
methods in an attempt to make them less ambiguous.
7913e12a60 is described below

commit 7913e12a603d9ff74c668f40efd8725553f898b0
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Wed Sep 18 17:10:40 2024 +0200

    Notify GDAL in advance of the region which will be read (all tiles).
    This commit contains again renaming of some `TiledGridCoverage` methods in 
an attempt to make them less ambiguous.
---
 .../org/apache/sis/storage/geotiff/DataSubset.java |   2 +-
 .../apache/sis/storage/base/TiledGridCoverage.java | 197 ++++++++++++++-------
 .../main/org/apache/sis/storage/gdal/Band.java     |  69 ++++++--
 .../main/org/apache/sis/storage/gdal/GDAL.java     |  22 ++-
 .../org/apache/sis/storage/gdal/TiledCoverage.java |  36 +++-
 5 files changed, 236 insertions(+), 90 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
index 6140124f95..b64a104605 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataSubset.java
@@ -61,7 +61,7 @@ import static org.apache.sis.pending.jdk.JDK18.ceilDiv;
  * <h2>Cell Coordinates</h2>
  * When there is no subsampling, {@code DataSubset} uses the same cell 
coordinates as {@link DataCube}.
  * When there is a subsampling, cell coordinates in this subset are divided by 
the subsampling factors.
- * Conversion is done by {@link #pixelToResourceCoordinate(long, int)}.
+ * Conversion is done by {@link #coverageToResourceCoordinate(long, int)}.
  *
  * <h2>Tile Matrix Coordinates</h2>
  * In each {@code DataSubset}, indices of tiles starts at (0, 0, …). This 
class does not use
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
index dab74aaff7..499fa4056e 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java
@@ -64,7 +64,7 @@ import org.opengis.coordinate.MismatchedDimensionException;
  * <h2>Cell coordinates</h2>
  * When there is no subsampling, this coverage uses the same cell coordinates 
as the originating resource.
  * When there is a subsampling, cell coordinates in this coverage are divided 
by the subsampling factors.
- * Conversions are done by {@link #pixelToResourceCoordinates(Rectangle)}.
+ * Conversions are done by {@link #coverageToResourceCoordinate(long, int)}.
  *
  * <p><b>DEsign note:</b> {@code TiledGridCoverage} use the same cell 
coordinates as the originating
  * {@link TiledGridResource} (when no subsampling) because those two classes 
use {@code long} integers.
@@ -95,7 +95,7 @@ public abstract class TiledGridCoverage extends GridCoverage {
      * This is the intersection between user-specified domain and the source
      * {@link TiledGridResource} domain, expanded to an integer number of 
tiles.
      */
-    private final GridExtent readExtent;
+    protected final GridExtent readExtent;
 
     /**
      * Whether to force the {@link #readExtent} tile intersection to the 
{@link #virtualTileSize}.
@@ -148,7 +148,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
     /**
      * The Tile Matrix Coordinates (<abbr>TMC</abbr>) that tile (0,0) of this 
coverage
      * would have in the originating {@code TiledGridResource}.
-     * This is the value to subtract from tile indices computed from pixel 
coordinates.
+     * This is the value to subtract from tile indices computed from cell 
coordinates.
      *
      * <p>The current implementation assumes that the tile (0,0) in the 
resource starts
      * at cell coordinates (0,0) of the resource.</p>
@@ -159,7 +159,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
     private final long[] tmcOfFirstTile;
 
     /**
-     * Conversion from pixel coordinates in this (potentially subsampled) 
coverage
+     * Conversion from cell coordinates in this (potentially subsampled) 
coverage
      * to cell coordinates in the originating resource coverage at full 
resolution.
      * The conversion from (<var>x</var>, <var>y</var>) to (<var>x′</var>, 
<var>y′</var>) is as below,
      * where <var>s</var> are subsampling factors and <var>t</var> are 
subsampling offsets:
@@ -172,7 +172,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      * This transform maps {@linkplain 
org.apache.sis.coverage.grid.PixelInCell#CELL_CORNER pixel corners}.
      *
      * @see #getSubsampling(int)
-     * @see #pixelToResourceCoordinate(long, int)
+     * @see #coverageToResourceCoordinate(long, int)
      */
     private final long[] subsampling, subsamplingOffsets;
 
@@ -321,50 +321,58 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
     /**
      * Converts a cell coordinate from this coverage to the {@code 
TiledGridResource} coordinate space.
      * This method removes the subsampling effect, i.e. returns the coordinate 
that we would have if this
-     * coverage was at full resolution. Such unsampled {@code 
TiledGridCoverage} uses the same coordinates
-     * as the originating {@link TiledGridResource}.
-     *
-     * <p>This method uses the "pixel" word for simplicity and because this 
method is used mostly
-     * for the first two dimensions, but "pixel" should be understood as "grid 
coverage cell".</p>
+     * coverage was at full resolution. When there is no subsampling, {@code 
TiledGridCoverage} uses the
+     * same coordinates as the originating {@link TiledGridResource}.
      *
      * @param  coordinate  coordinate in this {@code TiledGridCoverage} domain.
-     * @param  dimension   dimension of the coordinate.
+     * @param  dimension   the dimension of the coordinate to convert.
      * @return coordinate in this {@code TiledGridResource} with no 
subsampling applied.
      * @throws ArithmeticException if the coordinate cannot be represented as 
a long integer.
-     *
-     * @see #pixelToResourceCoordinates(Rectangle)
      */
-    private long pixelToResourceCoordinate(final long coordinate, final int 
dimension) {
+    protected final long coverageToResourceCoordinate(final long coordinate, 
final int dimension) {
         return addExact(multiplyExact(coordinate, subsampling[dimension]), 
subsamplingOffsets[dimension]);
     }
 
     /**
      * Converts a cell coordinate from {@link TiledGridResource} space to 
{@code TiledGridCoverage} coordinate.
-     * This is the converse of {@link #pixelToResourceCoordinate(long, int)}.
+     * This method is the converse of {@link 
#coverageToResourceCoordinate(long, int)}.
      * Note that there is a possible accuracy lost.
      *
      * @param  coordinate  coordinate in the {@code TiledGridResource} domain.
-     * @param  dimension   dimension of the coordinate.
+     * @param  dimension   the dimension of the coordinate to convert.
      * @return coordinates in this subsampled {@code TiledGridCoverage} domain.
      * @throws ArithmeticException if the coordinate cannot be represented as 
a long integer.
      */
-    private long resourceToPixelCoordinate(final long coordinate, final int 
dimension) {
+    private long resourceToCoverageCoordinate(final long coordinate, final int 
dimension) {
         return floorDiv(subtractExact(coordinate, 
subsamplingOffsets[dimension]), subsampling[dimension]);
     }
 
+    /**
+     * Converts a tile index from the <abbr>TMC</abbr> of this coverage to a 
cell coordinate in the originating resource.
+     * Note that the computation (like all methods in this class) uses the 
<em>virtual</em> tile size.
+     * This is usually the same as the real tile size, but not always.
+     *
+     * @param  tileIndex  tile index from the <abbr>TMC</abbr> of this 
coverage.
+     * @param  dimension  the dimension of the coordinate to convert.
+     * @return cell coordinate of the tile lower coordinate in the originating 
resource.
+     */
+    final long coverageTileToResourceCell(final long tileIndex, final int 
dimension) {
+        return multiplyExact(addExact(tileIndex, tmcOfFirstTile[dimension]), 
virtualTileSize[dimension]);
+    }
+
     /**
      * Converts a cell coordinate from this {@code TiledGridCoverage} 
coordinate space to
      * the Tile Matrix Coordinate (<abbr>TMC</abbr>) of the tile which 
contains that cell.
-     * The <abbr>TMC</abbr> is relative to the full {@link TiledGridResource},
+     * The returned <abbr>TMC</abbr> is relative to the full {@link 
TiledGridResource},
      * i.e. without subtraction of {@link #tmcOfFirstTile}.
      *
      * @param  coordinate  coordinates in this {@code TiledGridCoverage} 
domain.
-     * @param  dimension   dimension of the coordinate.
+     * @param  dimension   the dimension of the coordinate to convert.
      * @return Tile Matrix Coordinate (TMC) of the tile which contains the 
specified cell.
      * @throws ArithmeticException if the coordinate cannot be represented as 
an integer.
      */
-    private long toResourceTileMatrixCoordinate(final long coordinate, final 
int dimension) {
-        return floorDiv(pixelToResourceCoordinate(coordinate, dimension), 
virtualTileSize[dimension]);
+    private long coverageCellToResourceTile(final long coordinate, final int 
dimension) {
+        return floorDiv(coverageToResourceCoordinate(coordinate, dimension), 
virtualTileSize[dimension]);
     }
 
     /**
@@ -411,6 +419,25 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
         return 1;
     }
 
+    /**
+     * Returns the two-dimensional slice of the given grid extent, converted 
to 32-bits integers.
+     * By default, the <var>x</var> axis is the grid dimension at index 0 and 
the <var>y</var> axis
+     * is the grid dimension at index 1. Other dimensions are ignored.
+     *
+     * @param  extent  the grid extent to slice, or {@code null}.
+     * @return two-dimensional slice of the given extent, or {@code null} if 
the given extent was null.
+     * @throws ArithmeticException if the extent exceeds the capacity of 
32-bits integers.
+     */
+    protected final Rectangle bidimensional(final GridExtent extent) {
+        if (extent == null) {
+            return null;
+        }
+        return new Rectangle(toIntExact(extent.getLow (X_DIMENSION)),
+                             toIntExact(extent.getLow (Y_DIMENSION)),
+                             toIntExact(extent.getSize(X_DIMENSION)),
+                             toIntExact(extent.getSize(Y_DIMENSION)));
+    }
+
     /**
      * Returns a two-dimensional slice of grid data as a rendered image.
      *
@@ -446,8 +473,8 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                 final long max    = available  .getHigh(i);     // Highest 
valid coordinate, inclusive.
                 final long aoiMin = sliceExtent.getLow (i);     // Requested 
coordinate in subsampled image.
                 final long aoiMax = sliceExtent.getHigh(i);
-                final long tileUp = 
incrementExact(toResourceTileMatrixCoordinate(Math.min(aoiMax, max), i));
-                final long tileLo =                
toResourceTileMatrixCoordinate(Math.max(aoiMin, min), i);
+                final long tileUp = 
incrementExact(coverageCellToResourceTile(Math.min(aoiMax, max), i));
+                final long tileLo =                
coverageCellToResourceTile(Math.max(aoiMin, min), i);
                 if (tileUp <= tileLo) {
                     final String message = Errors.forLocale(getLocale())
                             .getString(Errors.Keys.IllegalRange_2, aoiMin, 
aoiMax);
@@ -458,8 +485,8 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                     }
                 }
                 // Lower and upper coordinates in subsampled image, rounded to 
integer number of tiles and clipped to available data.
-                final long lower = /* inclusive 
*/Math.max(resourceToPixelCoordinate(/* inclusive */multiplyExact(tileLo, 
virtualTileSize[i]),  i), min);
-                final long upper = 
incrementExact(Math.min(resourceToPixelCoordinate(decrementExact(multiplyExact(tileUp,
 virtualTileSize[i])), i), max));
+                final long lower = /* inclusive 
*/Math.max(resourceToCoverageCoordinate(/* inclusive */multiplyExact(tileLo, 
virtualTileSize[i]),  i), min);
+                final long upper = 
incrementExact(Math.min(resourceToCoverageCoordinate(decrementExact(multiplyExact(tileUp,
 virtualTileSize[i])), i), max));
                 imageSize[i] = toIntExact(subtractExact(upper, lower));
                 offsetAOI[i] = toIntExact(subtractExact(lower, aoiMin));
                 tileLower[i] = toIntExact(subtractExact(tileLo, 
tmcOfFirstTile[i]));
@@ -812,12 +839,13 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
         /**
          * Pixel coordinates to assign to the upper-left corner of the region 
to render, with subsampling applied.
          * This is the difference between the region requested by user and the 
region which will be rendered.
+         * This is often 0 or negative. May be positive if the image has been 
clipped.
          */
         private final int[] offsetAOI;
 
         /**
          * Cell coordinates of current iterator position relative to the Area 
Of Interest specified by user.
-         * Those coordinates are in units of the coverage at full resolution.
+         * Those coordinates are in units of the coverage at full resolution 
(except for the translation).
          * Initial position is {@link #offsetAOI} multiplied by {@link 
#subsampling}.
          * This array is modified by calls to {@link #next()}.
          */
@@ -903,31 +931,93 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
         }
 
         /**
-         * Returns the extent of the current tile in units of the full 
coverage resource (without subsampling).
-         * This method is a generalization to <var>n</var> dimensions of the 
rectangle computed by the
-         * following code:
-         *
-         * {@snippet lang="java" :
-         *     WritableRaster tile = createRaster();
-         *     Rectangle target = tile.getBounds();
-         *     Rectangle source = pixelToResourceCoordinates(bounds);
-         * }
+         * Returns the extent of the full region to read (all tiles) in units 
of the originating resource.
+         * The returned extent does not change during the iteration process 
(this method is not available
+         * in {@link Snapshot} for that reason). This method is typically 
invoked only at the beginning of
+         * the iteration process, when knowing in advance the full read region 
allows some optimizations.
          *
-         * @return extent of this tile in units of the full coverage resource.
+         * <p>The returned region is based on the user's request in her/his 
call to {@link #render(GridExtent)},
+         * but is not necessarily identical. The render method may have 
expanded or clipped the user's request.</p>
          *
-         * @see #pixelToResourceCoordinates(Rectangle)
+         * @return extent of all tiles to be traversed by this iterator, in 
units of the originating resource.
          */
-        public final GridExtent getTileExtentInResource() {
-            final int dimension = tileOffsetFull.length;
+        public GridExtent getFullRegionInResourceCoordinates() {
+            final int dimension = tileLower.length;
             final var    axes  = new DimensionNameType[dimension];
             final long[] lower = new long[dimension];
             final long[] upper = new long[dimension];
             for (int i=0; i<dimension; i++) {
-                lower[i] = pixelToResourceCoordinate(getTileOrigin(i), i);
-                upper[i] = addExact(lower[i], getTileSize(i));
                 axes [i] = readExtent.getAxisType(i).orElse(null);
+                lower[i] = Math.max(coverageTileToResourceCell(tileLower[i], 
i),   readExtent.getLow(i));
+                upper[i] = Math.min(coverageTileToResourceCell(tileUpper[i], 
i)-1, readExtent.getHigh(i));
             }
-            return new GridExtent(axes, lower, upper, false);
+            return new GridExtent(axes, lower, upper, true);
+        }
+
+        /**
+         * Converts the given cell coordinate from the originating resource to 
the pixel coordinate.
+         * The destination coordinate system is the coordinate system of the 
{@link RenderedImage}
+         * to be rendered. Note that the top-left corner of the image is not 
necessarily (0,0).
+         *
+         * @param  coordinate  pixel coordinate in the {@link RenderedImage} 
coordinate system.
+         * @param  dimension   the dimension of the coordinate to convert.
+         * @return cell coordinate in the {@link TiledGridResource} coordinate 
system.
+         */
+        public long resourceToImage(long coordinate, final int dimension) {
+            coordinate = Math.subtractExact(coordinate, 
coverageTileToResourceCell(tileLower[dimension], dimension));
+            coordinate = Math.floorDiv(coordinate, getSubsampling(dimension));
+            coordinate = Math.addExact(coordinate, offsetAOI[dimension]);
+            return coordinate;
+        }
+
+        /**
+         * Converts the given pixel coordinate to the coordinate in the 
originating resource.
+         * The source coordinate system is the coordinate system of the {@link 
RenderedImage}
+         * to be rendered. Note that the top-left corner of the image is not 
necessarily (0,0).
+         *
+         * @param  coordinate  pixel coordinate in the {@link RenderedImage} 
coordinate system.
+         * @param  dimension   the dimension of the coordinate to convert.
+         * @return cell coordinate in the {@link TiledGridResource} coordinate 
system.
+         */
+        public long imageToResource(long coordinate, final int dimension) {
+            coordinate = subtractExact(coordinate, offsetAOI[dimension]);      
 // (0,0) at image origin instead of AOI.
+            coordinate = multiplyExact(coordinate, getSubsampling(dimension)); 
 // Full resolution, like in the resource.
+            coordinate = addExact(coordinate, 
coverageTileToResourceCell(tileLower[dimension], dimension));
+            return coordinate;
+        }
+
+        /**
+         * Converts cell coordinates from the originating resource to pixel 
coordinates.
+         *
+         * @param  bounds  the coordinates to convert.
+         * @return the converted coordinates.
+         * @throws ArithmeticException if the result cannot be expressed as 
32-bits integers.
+         */
+        public Rectangle resourceToImage(final Rectangle bounds) {
+            long x, y;      // Convenience for casting `int` to `long`.
+            final var r = new Rectangle();
+            r.x      = toIntExact(resourceToImage(x = bounds.x,      
X_DIMENSION));
+            r.y      = toIntExact(resourceToImage(y = bounds.y,      
Y_DIMENSION));
+            r.width  = toIntExact(resourceToImage(x + bounds.width,  
X_DIMENSION) - r.x);
+            r.height = toIntExact(resourceToImage(y + bounds.height, 
Y_DIMENSION) - r.y);
+            return r;
+        }
+
+        /**
+         * Converts cell coordinates from pixel coordinates to the originating 
resource.
+         *
+         * @param  bounds  the coordinates to convert.
+         * @return the converted coordinates.
+         * @throws ArithmeticException if the result cannot be expressed as 
32-bits integers.
+         */
+        public Rectangle imageToResource(final Rectangle bounds) {
+            long x, y;      // Convenience for casting `int` to `long`.
+            final var r = new Rectangle();
+            r.x      = toIntExact(imageToResource(x = bounds.x,      
X_DIMENSION));
+            r.y      = toIntExact(imageToResource(y = bounds.y,      
Y_DIMENSION));
+            r.width  = toIntExact(imageToResource(x + bounds.width,  
X_DIMENSION) - r.x);
+            r.height = toIntExact(imageToResource(y + bounds.height, 
Y_DIMENSION) - r.y);
+            return r;
         }
 
         /**
@@ -1120,27 +1210,4 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      *         (too many exception types to list them all).
      */
     protected abstract Raster[] readTiles(TileIterator iterator) throws 
IOException, DataStoreException;
-
-    /**
-     * Converts raster coordinate from this coverage to {@code 
TiledGridResource} coordinate space.
-     * This method removes the subsampling effect, i.e. returns the 
coordinates that we would have if this
-     * coverage was at full resolution. Such unsampled {@code 
TiledGridCoverage} uses the same coordinates
-     * as the originating {@link TiledGridResource}.
-     *
-     * <p>This method uses the "pixel" word for simplicity and because this 
method is used for
-     * the two-dimensional case, but "pixel" should be understood as "grid 
coverage cell".</p>
-     *
-     * @param  bounds  the rectangle to convert.
-     * @return the converted rectangle.
-     * @throws ArithmeticException if the coordinate cannot be represented as 
an integer.
-     *
-     * @see TileIterator#getTileExtentInResource()
-     */
-    protected final Rectangle pixelToResourceCoordinates(final Rectangle 
bounds) {
-        return new Rectangle(
-                toIntExact(pixelToResourceCoordinate(bounds.x, X_DIMENSION)),
-                toIntExact(pixelToResourceCoordinate(bounds.y, Y_DIMENSION)),
-                toIntExact(multiplyExact(subsampling[X_DIMENSION], 
bounds.width)),
-                toIntExact(multiplyExact(subsampling[Y_DIMENSION], 
bounds.height)));
-    }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
index c8f06f505c..9e46ccdac1 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Band.java
@@ -266,7 +266,7 @@ final class Band {
      * @param  raster           the Java2D raster where to store of fetch the 
values to read or write.
      * @param  rasterBounds     region to write or read in raster coordinates.
      * @param  transferBuffer   a temporary buffer used for copying data.
-     * @return whether  the operation was successful according 
<abbr>GDAL</abbr>.
+     * @return whether the operation was successful according 
<abbr>GDAL</abbr>.
      * @throws ClassCastException if an above-documented prerequisite is not 
true.
      * @throws DataStoreException if <var>GDAL</var> reported a warning or 
fatal error.
      */
@@ -283,10 +283,13 @@ final class Band {
         if (readWriteFlags == OpenFlag.READ && !(raster instanceof 
WritableRaster)) {
             throw new ClassCastException();
         }
-        final var   sampleModel = (ComponentSampleModel) 
raster.getSampleModel();   // See prerequisites in Javadoc.
-        final var   dataBuffer  = raster.getDataBuffer();
-        final int   dataSize    = 
DataBuffer.getDataTypeSize(dataBuffer.getDataType()) / Byte.SIZE;
-        final int[] bankIndices = sampleModel.getBankIndices();
+        final var   dataBuffer     = raster.getDataBuffer();
+        final var   rasterType     = 
resourceType.forDataBufferType(dataBuffer.getDataType());
+        final int   dataSize       = 
DataBuffer.getDataTypeSize(dataBuffer.getDataType()) / Byte.SIZE;
+        final var   sampleModel    = (ComponentSampleModel) 
raster.getSampleModel();   // See prerequisites in Javadoc.
+        final int   pixelStride    = Math.multiplyExact(dataSize, 
sampleModel.getPixelStride());
+        final int   scanlineStride = Math.multiplyExact(dataSize, 
sampleModel.getScanlineStride());
+        final int[] bankIndices    = sampleModel.getBankIndices();
         /*
          * The following assertions are critical: if those conditions are not 
true, it may crash the JVM.
          * For that reason, we test them unconditionally instead of using the 
`assert` statement.
@@ -304,14 +307,19 @@ final class Band {
                             rasterBounds.y - 
raster.getSampleModelTranslateY(), i));
             final int err;
             try {
-                err = (int) gdal.rasterIO.invokeExact(selectedBands[i].handle, 
readWriteFlags,
-                        resourceBounds.x, resourceBounds.y, 
resourceBounds.width, resourceBounds.height,
-                        transferBuffer,
-                        rasterBounds.width,
-                        rasterBounds.height,
-                        
resourceType.forDataBufferType(dataBuffer.getDataType()).ordinal(),
-                        Math.multiplyExact(dataSize, 
sampleModel.getPixelStride()),
-                        Math.multiplyExact(dataSize, 
sampleModel.getScanlineStride()));
+                err = (int) gdal.rasterIO.invokeExact(
+                        selectedBands[i].handle,
+                        readWriteFlags,         // Either GF_Read to read a 
region of data, or GF_Write to write a region of data.
+                        resourceBounds.x,       // First column of the region 
to be accessed. Zero to start from the left side.
+                        resourceBounds.y,       // First row of the region to 
be accessed. Zero to start from the top.
+                        resourceBounds.width,   // The width of the region of 
the band to be accessed in pixels.
+                        resourceBounds.height,  // The height of the region of 
the band to be accessed in lines.
+                        transferBuffer,         // The buffer into which the 
data is read, or from which it is written.
+                        rasterBounds.width,     // The width of the region of 
the Java2D raster to be accessed.
+                        rasterBounds.height,    // The height of the region of 
the Java2D raster to be accessed.
+                        rasterType.ordinal(),   // The type of the pixel 
values in the destinaton image.
+                        pixelStride,            // The byte offset from the 
start of one pixel to the start of the next pixel.
+                        scanlineStride);        // The byte offset from the 
start of one scanline to the start of the next.
             } catch (Throwable e) {
                 throw GDAL.propagate(e);
             }
@@ -322,4 +330,39 @@ final class Band {
         }
         return true;
     }
+
+    /**
+     * Advise driver of upcoming read requests. Contrarily to the above {@code 
read(…)} method which receives
+     * a rectangle for one tile at a time, the rectangle received by this 
method is for all tiles to be read.
+     *
+     * @param  gdal             set of handles for invoking <abbr>GDAL</abbr> 
functions.
+     * @param  resourceBounds   region to read in resource coordinates. (0,0) 
is the upper-left pixel.
+     * @param  imageBounds      region to write in image coordinates (may 
cover more than one tile).
+     * @param  imageType       the <abbr>GDAL</abbr> data type of the 
destination raster.
+     * @return whether the operation was successful according 
<abbr>GDAL</abbr>.
+     * @throws DataStoreException if <var>GDAL</var> reported a warning or 
fatal error.
+     */
+    final boolean adviseRead(final GDAL      gdal,
+                             final Rectangle resourceBounds,
+                             final Rectangle imageBounds,       // Not the 
same as `rasterBounds`.
+                             final DataType  imageType)
+            throws DataStoreException
+    {
+        final int err;
+        try {
+            err = (int) gdal.adviseRead.invokeExact(
+                    handle,
+                    resourceBounds.x,       // First column of the region to 
be accessed. Zero to start from the left side.
+                    resourceBounds.y,       // First row of the region to be 
accessed. Zero to start from the top.
+                    resourceBounds.width,   // The width of the region of the 
band to be accessed in pixels.
+                    resourceBounds.height,  // The height of the region of the 
band to be accessed in lines.
+                    imageBounds.width,      // The width of the destination 
image.
+                    imageBounds.height,     // The height of the destination 
image.
+                    imageType.ordinal(),    // The type of the pixel values in 
the destinaton image.
+                    MemorySegment.NULL);    // A list of name=value strings 
with special control options.
+        } catch (Throwable e) {
+            throw GDAL.propagate(e);
+        }
+        return ErrorHandler.checkCPLErr(err);
+    }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
index 1857780d47..246aeb96b9 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
@@ -21,20 +21,20 @@ import java.util.List;
 import java.util.ArrayList;
 import java.util.Optional;
 import java.util.NoSuchElementException;
-import java.lang.foreign.Arena;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
+import java.lang.foreign.Arena;
 import java.lang.foreign.Linker;
 import java.lang.foreign.ValueLayout;
 import java.lang.foreign.SymbolLookup;
 import java.lang.foreign.MemorySegment;
 import java.lang.foreign.FunctionDescriptor;
 import java.lang.invoke.MethodHandle;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.panama.LibraryLoader;
 import org.apache.sis.storage.panama.LibraryStatus;
 import org.apache.sis.storage.panama.NativeFunctions;
-import org.apache.sis.util.logging.Logging;
 
 
 /**
@@ -267,6 +267,12 @@ final class GDAL extends NativeFunctions {
      */
     final MethodHandle rasterIO;
 
+    /**
+     * <abbr>GDAL</abbr> {@code CPLErr GDALRasterAdviseRead(GDALRasterBandH 
hRBand, ...)}.
+     * Advise driver of upcoming read requests.
+     */
+    final MethodHandle adviseRead;
+
     /**
      * Creates the handles for all <abbr>GDAL</abbr> functions which will be 
needed.
      *
@@ -376,6 +382,18 @@ final class GDAL extends NativeFunctions {
                 ValueLayout.JAVA_INT,       // int nPixelSpace
                 ValueLayout.JAVA_INT));     // int nLineSpace
 
+        adviseRead = lookup(linker, "GDALRasterAdviseRead", 
FunctionDescriptor.of(
+                ValueLayout.JAVA_INT,       // CPLErr error code (return value)
+                ValueLayout.ADDRESS,        // GDALRasterBandH hRBand
+                ValueLayout.JAVA_INT,       // int nDSXOff
+                ValueLayout.JAVA_INT,       // int nDSYOff
+                ValueLayout.JAVA_INT,       // int nDSXSize
+                ValueLayout.JAVA_INT,       // int nDSYSize
+                ValueLayout.JAVA_INT,       // int nBXSize
+                ValueLayout.JAVA_INT,       // int nBYSize
+                ValueLayout.JAVA_INT,       // GDALDataType eBDataType
+                ValueLayout.ADDRESS));      // CSLConstList papszOptions
+
         // Set error handling first in order to redirect initialization 
warnings.
         setErrorHandler(linker, null);
 
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
index 733e42d4aa..e7b58649e9 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledCoverage.java
@@ -23,6 +23,7 @@ import java.awt.image.WritableRaster;
 import java.lang.foreign.Arena;
 import java.lang.foreign.MemorySegment;
 import org.opengis.util.GenericName;
+import org.apache.sis.util.logging.Logging;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.base.TiledGridCoverage;
 import org.apache.sis.storage.base.TiledGridResource;
@@ -78,20 +79,37 @@ final class TiledCoverage extends TiledGridCoverage {
      */
     @Override
     protected Raster[] readTiles(final TileIterator iterator) throws 
IOException, DataStoreException {
+        Rectangle resourceBounds = 
bidimensional(iterator.getFullRegionInResourceCoordinates());
+        Rectangle imageBounds;
+        try {
+            imageBounds = iterator.resourceToImage(resourceBounds);
+        } catch (ArithmeticException e) {
+            // Ignore, this is used only as a hint.
+            Logging.ignorableException(GDALStoreProvider.LOGGER, 
GDALStore.class, "read", e);
+            imageBounds = null;
+        }
         synchronized (owner.getSynchronizationLock()) {
-            final Band[] bands  = owner.bands(includedBands);
-            final GDAL   gdal   = owner.parent.getProvider().GDAL();
-            final var    result = new 
WritableRaster[iterator.tileCountInQuery];
+            final Band[]   bands      = owner.bands(includedBands);
+            final GDAL     gdal       = owner.parent.getProvider().GDAL();
+            final var      result     = new 
WritableRaster[iterator.tileCountInQuery];
+            final DataType rasterType = 
owner.dataType.forDataBufferType(model.getDataType());
+            if (imageBounds != null) {
+                // Give a chance to the GDAL driver to prepare itself for the 
reading of all tiles in the AOI.
+                for (final Band band : bands) {
+                    if (!band.adviseRead(gdal, resourceBounds, imageBounds, 
rasterType)) break;
+                }
+            }
             try (Arena arena = Arena.ofConfined()) {
                 final MemorySegment transferBuffer = 
arena.allocate(getTileLength());
                 do {
                     final WritableRaster tile = iterator.createRaster();
-                    final Rectangle target = 
iterator.getRegionInsideTile(true);
-                    if (target != null) {
-                        target.x = Math.addExact(target.x, tile.getMinX());
-                        target.y = Math.addExact(target.y, tile.getMinY());
-                        final Rectangle source = 
pixelToResourceCoordinates(target);
-                        if (!Band.transfer(gdal, OpenFlag.READ, bands, 
owner.dataType, source, tile, target, transferBuffer)) {
+                    final Rectangle rasterBounds = 
iterator.getRegionInsideTile(true);
+                    if (rasterBounds != null) {
+                        rasterBounds.x += tile.getMinX();
+                        rasterBounds.y += tile.getMinY();
+                        resourceBounds = 
iterator.imageToResource(rasterBounds);
+                        assert (imageBounds == null) || 
imageBounds.contains(rasterBounds) : rasterBounds + " not in " + imageBounds;
+                        if (!Band.transfer(gdal, OpenFlag.READ, bands, 
owner.dataType, resourceBounds, tile, rasterBounds, transferBuffer)) {
                             break;      // Exception will be thrown by 
`throwOnFailure(…)`
                         }
                     }


Reply via email to