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

commit 93542a40c716397d65780eacbd13502c06a29d21
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Oct 25 10:41:50 2021 +0200

    Apply GDAL "sparse files" convention.
    It requires relaxing `TiledGridCoverage` tile types from `WritableRaster` 
to `Raster`.
---
 .../sis/internal/coverage/j2d/TilePlaceholder.java | 17 ++++++++
 .../sis/storage/geotiff/CompressedSubset.java      |  8 ++--
 .../org/apache/sis/storage/geotiff/DataSubset.java | 45 ++++++++++++++++------
 .../sis/internal/storage/TiledGridCoverage.java    | 28 +++++++-------
 .../sis/internal/storage/TiledGridResource.java    |  8 ++--
 5 files changed, 73 insertions(+), 33 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TilePlaceholder.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TilePlaceholder.java
index 23245e8..e5ad537 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TilePlaceholder.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TilePlaceholder.java
@@ -131,6 +131,23 @@ public class TilePlaceholder {
     }
 
     /**
+     * Returns a provider of empty tiles filled with the given value in all 
bands.
+     * A value of {@code null} is interpreted as 0 for integer types or NaN 
for floating point types.
+     *
+     * @param  model      sample model of the empty tiles.
+     * @param  fillValue  the value to use for filling empty spaces in 
rasters, or {@code null} for zero.
+     * @return provider of filled tiles.
+     */
+    public static TilePlaceholder filled(final SampleModel model, final Number 
fillValue) {
+        if (fillValue == null) {
+            return empty(model);
+        }
+        final Number[] values = new Number[model.getNumBands()];
+        Arrays.fill(values, fillValue);
+        return filled(model, new FillValues(model, values, true));
+    }
+
+    /**
      * Returns a provider of empty tiles filled with the given values.
      *
      * @param  model  sample model of the empty tiles.
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
index 2f2f948..3151824 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CompressedSubset.java
@@ -20,7 +20,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.nio.Buffer;
 import java.awt.Point;
-import java.awt.image.WritableRaster;
+import java.awt.image.Raster;
 import org.apache.sis.internal.storage.TiledGridResource;
 import org.apache.sis.internal.storage.inflater.Inflater;
 import org.apache.sis.internal.coverage.j2d.RasterFactory;
@@ -191,8 +191,8 @@ final class CompressedSubset extends DataSubset {
      * @return a single tile decoded from the GeoTIFF file.
      */
     @Override
-    WritableRaster readSlice(final long[] offsets, final long[] byteCounts, 
final long[] lower, final long[] upper,
-                             final int[] subsampling, final Point location) 
throws IOException, DataStoreException
+    Raster readSlice(final long[] offsets, final long[] byteCounts, final 
long[] lower, final long[] upper,
+                     final int[] subsampling, final Point location) throws 
IOException, DataStoreException
     {
         final DataType dataType = getDataType();
         final int  width        = pixelCount(lower, upper, subsampling, 
X_DIMENSION);
@@ -250,7 +250,7 @@ final class CompressedSubset extends DataSubset {
             fillRemainingRows(bank.flip());
             banks[b] = bank;
         }
-        return WritableRaster.createWritableRaster(model, 
RasterFactory.wrap(dataType, banks), location);
+        return Raster.createWritableRaster(model, RasterFactory.wrap(dataType, 
banks), location);
     }
 
     /**
diff --git 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
index 24f969c..0bbccfb 100644
--- 
a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
+++ 
b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataSubset.java
@@ -24,7 +24,7 @@ import java.io.IOException;
 import java.awt.Point;
 import java.awt.image.BandedSampleModel;
 import java.awt.image.DataBuffer;
-import java.awt.image.WritableRaster;
+import java.awt.image.Raster;
 import org.apache.sis.image.DataType;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
@@ -32,6 +32,7 @@ import org.apache.sis.internal.storage.io.Region;
 import org.apache.sis.internal.storage.io.HyperRectangleReader;
 import org.apache.sis.internal.storage.TiledGridCoverage;
 import org.apache.sis.internal.storage.TiledGridResource;
+import org.apache.sis.internal.coverage.j2d.TilePlaceholder;
 import org.apache.sis.internal.coverage.j2d.ImageUtilities;
 import org.apache.sis.internal.coverage.j2d.RasterFactory;
 import org.apache.sis.internal.storage.io.ChannelDataInput;
@@ -64,7 +65,7 @@ import static java.lang.Math.toIntExact;
  * the same tile indices than {@link DataCube} in order to avoid integer 
overflow.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   1.1
  * @module
  */
@@ -130,6 +131,12 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
     protected final int targetPixelStride;
 
     /**
+     * Provider of empty tiles, created only if needed. Empty tiles are tiles 
with a length of 0
+     * declared in the TIFF header. This interpretation is a GDAL extension, 
not a TIFF standard.
+     */
+    private TilePlaceholder emptyTiles;
+
+    /**
      * Creates a new data subset. All parameters should have been validated
      * by {@link ImageFileDirectory#validateMandatoryTags()} before this call.
      * This constructor should be invoked inside a synchronized block.
@@ -278,7 +285,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
      * (0,0) is the tile in the upper-left corner of this {@code DataSubset} 
(not necessarily the upper-left
      * corner of the image stored in the TIFF file).
      *
-     * The {@link WritableRaster#getMinX()} and {@code getMinY()} coordinates 
of returned rasters
+     * The {@link Raster#getMinX()} and {@code getMinY()} coordinates of 
returned rasters
      * will start at the given {@code offsetAOI} values.
      *
      * <p>This method is thread-safe.</p>
@@ -291,7 +298,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
      *         (too many exception types to list them all).
      */
     @Override
-    protected final WritableRaster[] readTiles(final AOI iterator) throws 
IOException, DataStoreException {
+    protected final Raster[] readTiles(final AOI iterator) throws IOException, 
DataStoreException {
         /*
          * Prepare an array for all tiles to be returned. Tiles that are 
already in memory will be stored
          * in this array directly. Other tiles will be declared in the 
`missings` array and loaded later.
@@ -299,13 +306,13 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
          * (`sourcePixelStride` > 1) or use one separated bank per band 
(`sourcePixelStride` == 1).
          */
         final int[] includedBanks = (sourcePixelStride == 1) ? includedBands : 
null;
-        final WritableRaster[] result = new 
WritableRaster[iterator.tileCountInQuery];
+        final Raster[] result = new Raster[iterator.tileCountInQuery];
         final Tile[] missings = new Tile[iterator.tileCountInQuery];
         int numMissings = 0;
         boolean needsCompaction = false;
         synchronized (source.getSynchronizationLock()) {
             do {
-                final WritableRaster tile = iterator.getCachedTile();
+                final Raster tile = iterator.getCachedTile();
                 if (tile != null) {
                     result[iterator.getIndexInResultArray()] = tile;
                 } else {
@@ -337,10 +344,26 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
                             origin.y = tile.originY;
                             tile.copyTileInfo(tileOffsets,    offsets,    
includedBanks, numTiles);
                             tile.copyTileInfo(tileByteCounts, byteCounts, 
includedBanks, numTiles);
+                            boolean isEmpty = true;
                             for (int b=0; b<offsets.length; b++) {
+                                isEmpty &= (byteCounts[b] == 0);
                                 offsets[b] = addExact(offsets[b], 
source.reader.origin);
                             }
-                            WritableRaster r = readSlice(offsets, byteCounts, 
lower, upper, subsampling, origin);
+                            /*
+                             * If the length if zero for all bands, the GDAL 
"sparse files" convention said
+                             * that pixel values are not stored in the file 
and are assumed zero for all pixels.
+                             * This is a GDAL-specific convention but seems 
reasonable. Note that the default
+                             * fill value zero is different than 
`TilePlaceholder` default, which can be NaN.
+                             */
+                            final Raster r;
+                            if (isEmpty) {
+                                if (emptyTiles == null) {
+                                    emptyTiles = TilePlaceholder.filled(model, 
(fillValue != null) ? fillValue : 0);
+                                }
+                                r = emptyTiles.create(origin);
+                            } else {
+                                r = readSlice(offsets, byteCounts, lower, 
upper, subsampling, origin);
+                            }
                             result[tile.indexInResultArray] = tile.cache(r);
                         } else {
                             needsCompaction = true;
@@ -356,7 +379,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
          */
         if (needsCompaction) {
             int n = 0;
-            for (final WritableRaster tile : result) {
+            for (final Raster tile : result) {
                 if (tile != null) result[n++] = tile;
             }
             return Arrays.copyOf(result, n);
@@ -417,8 +440,8 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
      *
      * @see DataCube#canReadDirect(TiledGridResource.Subset)
      */
-    WritableRaster readSlice(final long[] offsets, final long[] byteCounts, 
final long[] lower, final long[] upper,
-                             final int[] subsampling, final Point location) 
throws IOException, DataStoreException
+    Raster readSlice(final long[] offsets, final long[] byteCounts, final 
long[] lower, final long[] upper,
+                     final int[] subsampling, final Point location) throws 
IOException, DataStoreException
     {
         final DataType type = getDataType();
         final int sampleSize = type.size();     // Assumed same as 
`SampleModel.getSampleSize(…)` by pre-conditions.
@@ -462,7 +485,7 @@ class DataSubset extends TiledGridCoverage implements 
Localized {
             banks[b] = bank;
         }
         final DataBuffer buffer = RasterFactory.wrap(type, banks);
-        return WritableRaster.createWritableRaster(model, buffer, location);
+        return Raster.createWritableRaster(model, buffer, location);
     }
 
     /**
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
index 3980f13..b5ffb7b 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridCoverage.java
@@ -25,7 +25,7 @@ import java.awt.image.ColorModel;
 import java.awt.image.SampleModel;
 import java.awt.image.MultiPixelPackedSampleModel;
 import java.awt.image.RenderedImage;
-import java.awt.image.WritableRaster;
+import java.awt.image.Raster;
 import org.opengis.coverage.CannotEvaluateException;
 import org.opengis.geometry.MismatchedDimensionException;
 import org.apache.sis.coverage.grid.GridCoverage;
@@ -92,8 +92,8 @@ public abstract class TiledGridCoverage extends GridCoverage {
      * This is relevant only for the last column of tile matrix, because those 
tiles may be truncated
      * if the image size is not a multiple of tile size. It is usually 
necessary to read those tiles
      * fully anyway because otherwise, the pixels read from the storage would 
not be aligned with the
-     * pixels stored in the {@link WritableRaster}. However there is a few 
exceptions where the read
-     * extent should not be forced to the tile size:
+     * pixels stored in the {@link Raster}. However there is a few exceptions 
where the read extent
+     * should not be forced to the tile size:
      *
      * <ul>
      *   <li>If the image is untiled, then the {@link 
org.apache.sis.internal.storage.TiledGridResource.Subset}
@@ -176,7 +176,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      * @see AOI#getCachedTile()
      * @see #createCacheKey(int)
      */
-    private final Map<TiledGridResource.CacheKey, WritableRaster> rasters;
+    private final Map<TiledGridResource.CacheKey, Raster> rasters;
 
     /**
      * The sample model for all rasters. The size of this sample model is the 
values of two elements
@@ -422,7 +422,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
             /*
              * Get all tiles in the specified region. I/O operations, if 
needed, happen here.
              */
-            final WritableRaster[] result = readTiles(new AOI(tileLower, 
tileUpper, offsetAOI, dimension));
+            final Raster[] result = readTiles(new AOI(tileLower, tileUpper, 
offsetAOI, dimension));
             /*
              * Wraps in an image all the tiles that we just read, together 
with the following properties:
              *    - Two-dimensional conversion from pixel coordinates to "real 
world" coordinates.
@@ -554,10 +554,10 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
          *
          * @return cached tile at current iterator position, or {@code null} 
if none.
          *
-         * @see Snapshot#cache(WritableRaster)
+         * @see Snapshot#cache(Raster)
          */
-        public WritableRaster getCachedTile() {
-            WritableRaster tile = 
rasters.get(createCacheKey(indexInTileVector));
+        public Raster getCachedTile() {
+            Raster tile = rasters.get(createCacheKey(indexInTileVector));
             if (tile != null) {
                 /*
                  * Found a tile, but the sample model may be different because 
band order may be different.
@@ -566,9 +566,9 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
                 final int x = getTileOrigin(X_DIMENSION);
                 final int y = getTileOrigin(Y_DIMENSION);
                 if (!model.equals(tile.getSampleModel())) {
-                    tile = WritableRaster.createWritableRaster(model, 
tile.getDataBuffer(), new Point(x, y));
+                    tile = Raster.createRaster(model, tile.getDataBuffer(), 
new Point(x, y));
                 } else if (tile.getMinX() != x || tile.getMinY() != y) {
-                    tile = tile.createWritableTranslatedChild(x, y);
+                    tile = tile.createTranslatedChild(x, y);
                 }
             }
             return tile;
@@ -765,8 +765,8 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
          *
          * @see AOI#getCachedTile()
          */
-        public WritableRaster cache(final WritableRaster raster) {
-            final WritableRaster existing = coverage.rasters.putIfAbsent(
+        public Raster cache(final Raster raster) {
+            final Raster existing = coverage.rasters.putIfAbsent(
                     coverage.createCacheKey(indexInTileVector), raster);
             return (existing != null) ? existing : raster;
         }
@@ -784,7 +784,7 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      * (0,0) is the tile in the upper-left corner of this {@code 
TiledGridCoverage} (not necessarily the upper-left
      * corner of the image in the {@link TiledGridResource}).
      *
-     * The {@link WritableRaster#getMinX()} and {@code getMinY()} coordinates 
of returned rasters
+     * The {@link Raster#getMinX()} and {@code getMinY()} coordinates of 
returned rasters
      * shall start at the given {@code iterator.offsetAOI} values.
      *
      * <p>This method must be thread-safe.</p>
@@ -796,5 +796,5 @@ public abstract class TiledGridCoverage extends 
GridCoverage {
      * @throws RuntimeException if the Java2D image can not be created for 
another reason
      *         (too many exception types to list them all).
      */
-    protected abstract WritableRaster[] readTiles(AOI iterator) throws 
IOException, DataStoreException;
+    protected abstract Raster[] readTiles(AOI iterator) throws IOException, 
DataStoreException;
 }
diff --git 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
index bb67d36..498abe4 100644
--- 
a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
+++ 
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/TiledGridResource.java
@@ -23,8 +23,8 @@ import java.awt.image.ColorModel;
 import java.awt.image.SampleModel;
 import java.awt.image.BandedSampleModel;
 import java.awt.image.ComponentSampleModel;
+import java.awt.image.Raster;
 import java.awt.image.RenderedImage;
-import java.awt.image.WritableRaster;
 import java.awt.image.RasterFormatException;
 import org.opengis.coverage.CannotEvaluateException;
 import org.apache.sis.coverage.SampleDimension;
@@ -100,13 +100,13 @@ public abstract class TiledGridResource extends 
AbstractGridResource {
     /**
      * All tiles loaded by any {@link TiledGridCoverage} created from this 
resource.
      * Keys contains tile indices in a row-major array of tiles.
-     * For each value, the {@link WritableRaster#getMinX()} and {@code minY} 
values
+     * For each value, the {@link Raster#getMinX()} and {@code minY} values
      * can be anything, depending which {@link TiledGridResource} was first to 
load the tile.
      *
      * @see TiledGridCoverage#rasters
      * @see TiledGridCoverage.AOI#getCachedTile()
      */
-    private final WeakValueHashMap<CacheKey, WritableRaster> rasters;
+    private final WeakValueHashMap<CacheKey, Raster> rasters;
 
     /**
      * Whether all tiles should be loaded at {@code read(…)} method call or 
deferred to a later time.
@@ -322,7 +322,7 @@ public abstract class TiledGridResource extends 
AbstractGridResource {
          * Cache to use for tiles loaded by the {@link TiledGridCoverage}.
          * It is a reference to {@link TiledGridResource#rasters} if shareable.
          */
-        final WeakValueHashMap<CacheKey, WritableRaster> cache;
+        final WeakValueHashMap<CacheKey, Raster> cache;
 
         /**
          * Creates parameters for the given domain and range.

Reply via email to