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);