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

asf-gitbox-commits 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 0f40dab9ea Fix a wrong position of to the ZIP data to uncompress in a 
HEIF file.
0f40dab9ea is described below

commit 0f40dab9eae684fca71beb2d5c600cccf1269491
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Jun 16 18:39:32 2026 +0200

    Fix a wrong position of to the ZIP data to uncompress in a HEIF file.
---
 .../sis/storage/netcdf/classic/VariableInfo.java   |  6 +-
 .../apache/sis/io/stream/HyperRectangleWriter.java |  2 +-
 .../main/org/apache/sis/io/stream/Region.java      | 12 ++--
 .../io/stream/SubsampledRectangleWriterTest.java   |  4 +-
 .../org/apache/sis/storage/geoheif/ImageModel.java |  2 +-
 .../org/apache/sis/storage/geoheif/TiledImage.java | 26 +++++---
 .../sis/storage/geoheif/UncompressedImage.java     | 74 ++++++++++------------
 7 files changed, 63 insertions(+), 63 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
index 249d4ea4ca..f40ec4909d 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/classic/VariableInfo.java
@@ -700,7 +700,7 @@ final class VariableInfo extends Variable implements 
Comparable<VariableInfo> {
      * @see #read()
      * @see #read(GridExtent, long[])
      */
-    private Object readArray(final GridExtent area, long[] subsampling) throws 
IOException, DataStoreException {
+    private Object readArray(final GridExtent area, final long[] subsampling) 
throws IOException, DataStoreException {
         if (reader == null) {
             throw new DataStoreContentException(unknownType());
         }
@@ -734,10 +734,6 @@ final class VariableInfo extends Variable implements 
Comparable<VariableInfo> {
                 upper[i] = Math.incrementExact(area.getHigh(i));
             }
         }
-        if (subsampling == null) {
-            subsampling = new long[dimension];
-            Arrays.fill(subsampling, 1);
-        }
         final var region = new Region(size, lower, upper, subsampling);
         /*
          * If this variable uses the unlimited dimension, we have to skip the 
records of all other unlimited variables
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
index 113d9234bb..9b949a64de 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/HyperRectangleWriter.java
@@ -250,7 +250,7 @@ public class HyperRectangleWriter {
                 regionLower[0] = Math.floorDiv(regionLower[0] * 
pixelBitStride, dataSize);
                 regionUpper[0] = JDK18.ceilDiv(regionUpper[0] * 
pixelBitStride, dataSize);
             }
-            final var subset = new Region(sourceSize, regionLower, 
regionUpper, new long[] {1,1});
+            final var subset = new Region(sourceSize, regionLower, 
regionUpper, null);
             length = subset.length;
             if (bandOffsets != null) {
                 final int numBands = bandOffsets.length;
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
index 4a7bd9442f..848b83ea5f 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/Region.java
@@ -102,6 +102,10 @@ public final class Region {
      *   <li>The total length of data to read does not exceed {@link 
Integer#MAX_VALUE}.</li>
      * </ul>
      *
+     * For convenience, a null {@code regionLower} means 0 for all dimensions 
(i.e., no crop),
+     * a null {@code regionUpper} means the same values as {@code sourceSize} 
(i.e., no crop),
+     * and a null {@code subsampling} means 1 for all dimensions (i.e., no 
subsampling).
+     *
      * @param  sourceSize   the number of elements along each dimension.
      * @param  regionLower  indices of the first value to read or write along 
each dimension.
      * @param  regionUpper  indices after the last value to read or write 
along each dimension.
@@ -117,11 +121,11 @@ public final class Region {
         long stride   = 1;
         long skip     = 0;
         for (int i=0; i<dimension;) {
-            final long step  = subsampling[i];
-            final long lower = regionLower[i];
-            final long count = ceilDiv(subtractExact(regionUpper[i], lower), 
step);
-            final long upper = addExact(lower, 
incrementExact(multiplyExact(count-1, step)));
             final long span  = sourceSize[i];
+            final long step  = (subsampling != null) ? subsampling[i] : 1;
+            final long lower = (regionLower != null) ? regionLower[i] : 0;
+            final long count = ceilDiv(subtractExact(regionUpper != null ? 
regionUpper[i] : span, lower), step);
+            final long upper = addExact(lower, 
incrementExact(multiplyExact(count-1, step)));
             assert (count > 0) && (lower >= 0) && (upper > lower) && (upper <= 
span) : i;
 
             targetSize[i] = toIntExact(count);
diff --git 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
index fba9885561..3969d243e4 100644
--- 
a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
+++ 
b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/SubsampledRectangleWriterTest.java
@@ -83,9 +83,7 @@ public final class SubsampledRectangleWriterTest extends 
TestCase {
         final int    width  = (random.nextInt(9) + 3) * bandOffsets.length;
         final int    height = (random.nextInt(5) + 1);
         final int    length = width * height;
-        final long[] lower  = new long[2];
         final long[] upper  = new long[] {width, height};
-        final long[] subsm  = new long[] {1,1};
         final A source = creator.apply(length);
         for (int i=0; i<length; i++) {
             Array.setByte(source, i, (byte) (BASE + i));
@@ -94,7 +92,7 @@ public final class SubsampledRectangleWriterTest extends 
TestCase {
         final var buffer = ByteBuffer.allocate((random.nextInt(4) + 1) + 
bandOffsets.length * dataSize);
         actual = ByteBuffer.wrap(target);
         output = new ChannelDataOutput("Test", new ByteArrayChannel(target, 
false), false, buffer);
-        writer = new SubsampledRectangleWriter(new Region(upper, lower, upper, 
subsm), bandOffsets, bandOffsets.length);
+        writer = new SubsampledRectangleWriter(new Region(upper, null, null, 
null), bandOffsets, bandOffsets.length);
         return source;
     }
 
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java
index f11be26a60..0da11c285f 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/ImageModel.java
@@ -112,7 +112,7 @@ final class ImageModel {
         final int nc = (model          == null) ? 0 : model.components.length;
         final int nt = (componentTypes == null) ? 0 : componentTypes.length;
         final int ns = (bitsPerChannel == null) ? 0 : bitsPerChannel.length;
-        final int numBands = Math.max(Math.max(nc, nt), ns);
+        final int numBands = Math.max(nc, nt);  // Ignore `ns` as it is 
sometime too large (for an unknown reason).
         final var bitsPerSample = new int[numBands];
         final var sb = new SampleDimension.Builder();
         sampleDimensions = new SampleDimension[numBands];
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/TiledImage.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/TiledImage.java
index 6c881f00bc..1128ead27d 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/TiledImage.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/TiledImage.java
@@ -19,6 +19,7 @@ package org.apache.sis.storage.geoheif;
 import java.io.IOException;
 import java.awt.image.RasterFormatException;
 import org.apache.sis.io.stream.ChannelDataInput;
+import org.apache.sis.io.stream.Region;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.isobmff.ByteRanges;
 import org.apache.sis.storage.isobmff.base.ItemLocation;
@@ -78,38 +79,43 @@ final class TiledImage extends UncompressedImage {
 
     /**
      * Computes the range of bytes that will be needed for reading the tile at 
the specified index.
-     * The result is stored in the given {@code context} argument.
+     * The given region is converted to offsets relatives to the beginning of 
the <abbr>HEIF</abbr>
+     * file and the result is stored in the given {@code addTo} argument.
      *
      * @param  tileIndex  index of the tile for which to compute the range of 
bytes.
-     * @param  tileSize   ignored (replaced by the tile size which is stored 
or computed from the offset).
-     * @param  context    where to add the ranges of bytes to read as offsets 
relatives to the beginning of the file.
+     * @param  region     ignored (replaced by the tile size which is stored 
or computed from the offset).
+     * @param  addTo      where to store the offsets relatives to the 
beginning of the file.
      * @throws DataStoreException if an error occurred with the data in the 
boxes.
      * @throws ArithmeticException if an integer overflow occurred.
      */
     @Override
-    protected void computeByteRanges(final long tileIndex, long tileSize, 
final ByteRanges context)
+    protected void computeByteRanges(final long tileIndex, Region region, 
final ByteRanges addTo)
             throws DataStoreException
     {
-        final int i = Math.toIntExact(tileIndex);
+        int i = Math.toIntExact(tileIndex);
         final long offset = tileOffsets[i];
+        final long tileSize;
         if (tileSizes != null) {
             tileSize = tileSizes[i];
+        } else if (i < tileOffsets.length - 1) {
+            tileSize = tileOffsets[i+1] - offset;
         } else {
-            long next = tileOffsets[Math.incrementExact(i)];     // TODO: 
handle the case of the last tile.
-            tileSize = next - offset;
+            tileSize = -1;      // Means to read all remaining bytes in the 
box.
         }
-        locator.resolve(offset, tileSize, context);
+        locator.resolve(offset, tileSize, addTo);
     }
 
     /**
      * Returns the compression units which contains tile data.
+     * The {@code Unit.offset} value is relative to the offset
+     * computed by {@link #computeByteRanges(long, long, ByteRanges)}.
      *
      * @param  tileIndex  index of the tile for which to get the compression 
unit.
      * @return the compression unit for the tile at the given index.
      */
     @Override
     protected CompressedUnitsItemInfo.Unit compressedImageUnit(final long 
tileIndex) {
-        final int i = Math.toIntExact(tileIndex);
-        return new CompressedUnitsItemInfo.Unit(tileOffsets[i], tileSizes[i]);
+        // Set the offset to 0 because it has already been added by 
`computeByteRanges(…)`
+        return new CompressedUnitsItemInfo.Unit(0, 
tileSizes[Math.toIntExact(tileIndex)]);
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
index 47a6477193..ebdbdfd590 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/UncompressedImage.java
@@ -22,6 +22,7 @@ import static java.lang.Math.multiplyExact;
 import java.awt.image.DataBuffer;
 import java.awt.image.SampleModel;
 import java.awt.image.WritableRaster;
+import java.awt.image.BandedSampleModel;
 import org.apache.sis.image.DataType;
 import org.apache.sis.image.internal.shared.ImageUtilities;
 import org.apache.sis.image.internal.shared.RasterFactory;
@@ -118,49 +119,47 @@ class UncompressedImage extends Image {
     }
 
     /**
-     * Returns (width × number of samples per pixel) and the height, in that 
order.
+     * Returns [(width × number of samples per pixel), (height), (number of 
banks)], in that order.
+     * The banks are assumed to be consecutive.
      *
      * <p><b>Limitation:</b> current implementation ignores {@link 
java.awt.image.MultiPixelPackedSampleModel},
      * but that model does not seem to be used by <abbr>HEIF</abbr>.</p>
      *
      * @param  sampleModel  the sample model for which to get the size.
-     * @return the scanline stride and raster height, in that order.
+     * @return the scanline stride, raster height and number of banks, in that 
order.
      */
     private static long[] size(final SampleModel sampleModel) {
-        return new long[] {
-            sampleModel.getWidth() * (long) sampleModel.getNumDataElements(),
-            sampleModel.getHeight()
-        };
-    }
-
-    /**
-     * Creates a two-dimensional region without subsampling.
-     *
-     * @param  sourceSize   the number of elements along each dimension.
-     * @param  regionUpper  indices after the last value to read along each 
dimension.
-     */
-    private static Region region(final long[] sourceSize, final long[] 
regionUpper) {
-        return new Region(sourceSize, new long[2], regionUpper, new long[] {1, 
1});
+        long width    = sampleModel.getWidth();
+        long numBanks = sampleModel.getNumDataElements();
+        if (!(sampleModel instanceof BandedSampleModel)) {
+            width *= numBanks;
+            numBanks = 1;
+        }
+        return new long[] {width, sampleModel.getHeight(), numBanks};
     }
 
     /**
      * Computes the range of bytes that will be needed for reading the tile at 
the specified index.
-     * The result is stored in the given {@code context} argument.
+     * The given region is converted to offsets relatives to the beginning of 
the <abbr>HEIF</abbr>
+     * file and the result is stored in the given {@code addTo} argument.
      *
      * @param  tileIndex  index of the tile for which to compute the range of 
bytes.
-     * @param  tileSize   number of bytes of uncompressed data in each tile.
-     * @param  context    where to add the ranges of bytes to read as offsets 
relatives to the beginning of the file.
+     * @param  region     relative indexes of the uncompressed data to read in 
each tile.
+     * @param  addTo      where to store the offsets relatives to the 
beginning of the file.
      * @throws DataStoreException if an error occurred with the data in the 
boxes.
      * @throws ArithmeticException if an integer overflow occurred.
      */
-    protected void computeByteRanges(final long tileIndex, final long 
tileSize, final ByteRanges context)
+    protected void computeByteRanges(final long tileIndex, final Region 
region, final ByteRanges addTo)
             throws DataStoreException
     {
-        locator.resolve(multiplyExact(tileIndex, tileSize), tileSize, context);
+        final long tileSize = multiplyExact(region.length, dataType.bytes());
+        locator.resolve(multiplyExact(tileIndex, tileSize), tileSize, addTo);
     }
 
     /**
      * Returns the compression units which contains tile data.
+     * The {@code Unit.offset} value shall be relative to the offset
+     * computed by {@link #computeByteRanges(long, long, ByteRanges)}.
      *
      * @param  tileIndex  index of the tile for which to get the compression 
unit.
      * @return the compression unit for the tile at the given index.
@@ -185,15 +184,9 @@ class UncompressedImage extends Image {
     @Override
     protected final Reader computeByteRanges(final 
ImageResource.Coverage.ReadContext context) throws DataStoreException {
         final long[] sourceSize = size(sampleModel);
-        /*
-         * In the current implementation, we read the whole tile. If a future 
implementation allows
-         * to read a sub-region of the tile, we would need to make 
`HyperRectangleReader` accepts a
-         * `Buffer` with an arbitrary position in argument.
-         */
-        final var  region    = region(sourceSize, sourceSize);
-        final long tileSize  = multiplyExact(region.length, dataType.bytes());
-        final long tileIndex = addExact(multiplyExact(context.subTileY, 
numXTiles), context.subTileX);
-        computeByteRanges(tileIndex, tileSize, context);
+        final var    region     = new Region(sourceSize, null, null, null);
+        final long   tileIndex  = addExact(multiplyExact(context.subTileY, 
numXTiles), context.subTileX);
+        computeByteRanges(tileIndex, region, context);
         return (ChannelDataInput input) -> {
             long origin = context.offset();
             final ComputedByteChannel inflater = inflater(input);
@@ -212,17 +205,20 @@ class UncompressedImage extends Image {
             input.buffer.order(byteOrder);
             final var hr = new 
HyperRectangleReader(ImageUtilities.toNumberEnum(dataType), input);
             hr.setOrigin(origin);
-            final WritableRaster raster       = context.createRaster();
-            final long[]         rasterSize   = size(raster.getSampleModel());
-            final Region         rasterRegion = Arrays.equals(sourceSize, 
rasterSize) ? region : region(sourceSize, rasterSize);
-            final DataBuffer     data         = raster.getDataBuffer();
-            final int            numBanks     = data.getNumBanks();
+            Region bank = region;
+            final WritableRaster raster   = context.createRaster();
+            final long[]         upper    = size(raster.getSampleModel());
+            final DataBuffer     target   = raster.getDataBuffer();
+            final int            numBanks = target.getNumBanks();
             for (int b=0; b<numBanks; b++) {
-                if (b != 0) {
-                    hr.setOrigin(addExact(hr.getOrigin(), tileSize));
+                upper[2] = b + 1;
+                if (b != 0 || !Arrays.equals(sourceSize, upper)) {
+                    final long[] lower = new long[upper.length];
+                    lower[2] = b;
+                    bank = new Region(sourceSize, lower, upper, null);
                 }
-                hr.setDestination(RasterFactory.wrapAsBuffer(data, b));
-                hr.readAsBuffer(rasterRegion, 0);
+                hr.setDestination(RasterFactory.wrapAsBuffer(target, b));
+                hr.readAsBuffer(bank, 0);
             }
             if (inflater != null) {
                 context.saveForReuse(inflater.compressedInput().buffer);

Reply via email to