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 75624a737d Fix the case of GeoTIFF files not read correctly when the
image is untiled but the tile is unnecessary large (larger than the actual
image).
75624a737d is described below
commit 75624a737d2af6cfeaa8a960c1094f14a8595979
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Jul 18 16:48:54 2025 +0200
Fix the case of GeoTIFF files not read correctly when the image is untiled
but the tile is unnecessary large (larger than the actual image).
---
.../org/apache/sis/coverage/grid/GridCoverage.java | 2 +-
.../main/org/apache/sis/image/PixelIterator.java | 6 +--
.../apache/sis/image/privy/ColorModelFactory.java | 3 ++
.../sis/storage/geotiff/CompressedSubset.java | 23 +++++----
.../org/apache/sis/storage/geotiff/DataCube.java | 19 ++++++--
.../org/apache/sis/storage/geotiff/DataSubset.java | 26 +++++-----
.../sis/storage/geotiff/ImageFileDirectory.java | 20 ++++++--
.../apache/sis/storage/geotiff/inflater/LZW.java | 3 +-
.../apache/sis/storage/base/TiledGridCoverage.java | 55 +++++++++++++++-------
.../apache/sis/storage/base/TiledGridResource.java | 25 +++++++---
netbeans-project/nbproject/project.xml | 1 +
11 files changed, 125 insertions(+), 58 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage.java
index 520def8287..c224df7107 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCoverage.java
@@ -537,7 +537,7 @@ public abstract class GridCoverage extends BandedCoverage {
public TreeTable toTree(final Locale locale, final int bitmask) {
final Vocabulary vocabulary =
Vocabulary.forLocale(Objects.requireNonNull(locale));
final TableColumn<CharSequence> column = TableColumn.VALUE_AS_TEXT;
- final TreeTable tree = new DefaultTreeTable(column);
+ final var tree = new DefaultTreeTable(column);
final TreeTable.Node root = tree.getRoot();
root.setValue(column, Classes.getShortClassName(this));
TreeTable.Node branch = root.newChild();
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
index 4155780ff0..1ced8e4296 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
@@ -397,7 +397,7 @@ public class PixelIterator {
*/
static int getScanlineStride(final SampleModel sm) {
if (sm instanceof ComponentSampleModel) {
- final ComponentSampleModel csm = (ComponentSampleModel) sm;
+ final var csm = (ComponentSampleModel) sm;
if (csm.getPixelStride() == 1) {
for (final int offset : csm.getBandOffsets()) {
if (offset != 0) return 0;
@@ -407,13 +407,13 @@ public class PixelIterator {
}
}
} else if (sm instanceof SinglePixelPackedSampleModel) {
- final SinglePixelPackedSampleModel csm =
(SinglePixelPackedSampleModel) sm;
+ final var csm = (SinglePixelPackedSampleModel) sm;
final int[] offsets = csm.getBitOffsets();
if (offsets.length == 1 && offsets[0] == 0) {
return csm.getScanlineStride();
}
} else if (sm instanceof MultiPixelPackedSampleModel) {
- final MultiPixelPackedSampleModel csm =
(MultiPixelPackedSampleModel) sm;
+ final var csm = (MultiPixelPackedSampleModel) sm;
if (csm.getDataBitOffset() == 0 && csm.getPixelBitStride() ==
DataBuffer.getDataTypeSize(csm.getDataType())) {
return csm.getScanlineStride();
}
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
index d5945ed4fc..f75dd78c6f 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/privy/ColorModelFactory.java
@@ -481,6 +481,9 @@ public final class ColorModelFactory {
final double lower, final double
upper, final Color... colors)
{
ArgumentChecks.ensureNonEmpty("colors", colors);
+ if (colors.length == 2 && colors[0].getRGB() == 0xFF000000 &&
colors[1].getRGB() == 0xFFFFFFFF) {
+ return createGrayScale(dataType, numBands, visibleBand, lower,
upper);
+ }
return createPiecewise(dataType, numBands, visibleBand, new
ColorsForRange[] {
new ColorsForRange(null, new NumberRange<>(Double.class, lower,
true, upper, false), colors, true, null)
});
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
index 9c62813d72..c5ce241813 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/CompressedSubset.java
@@ -36,10 +36,13 @@ import org.apache.sis.image.privy.RasterFactory;
*/
final class CompressedSubset extends DataSubset {
/**
- * Number of sample values to skip for moving to the next row of a tile in
the GeoTIFF file.
- * This is not necessarily the same scanline stride as for the tiles
created by this class.
+ * Number of sample values to skip for moving to the next row of a tile in
the <abbr>TIFF</abbr> file.
+ * This is not necessarily the same scanline stride as the one for the
tiles created by this class.
+ *
+ * @see #sourcePixelStride
+ * @see java.awt.image.ComponentSampleModel#getScanlineStride()
*/
- private final long scanlineStride;
+ private final long sourceScanlineStride;
/**
* Number of sample values to skip before to read the first value of the
first pixel in a row.
@@ -73,7 +76,7 @@ final class CompressedSubset extends DataSubset {
private final int[] skipAfterChunks;
/**
- * Number of sample values that compose a chunk (pixel or sample) in the
GeoTIFF file.
+ * Number of sample values that compose a chunk (pixel or sample) in the
<abbr>TIFF</abbr> file.
* The value of this field can be:
*
* <ul>
@@ -107,9 +110,9 @@ final class CompressedSubset extends DataSubset {
@SuppressWarnings("LocalVariableHidesMemberVariable")
CompressedSubset(final DataCube source, final TiledGridResource.Subset
subset) throws DataStoreException {
super(source, subset);
- scanlineStride = Math.multiplyExact(sourcePixelStride,
getTileSize(X_DIMENSION));
- final int between = Math.multiplyExact(sourcePixelStride,
Math.toIntExact(getSubsampling(X_DIMENSION) - 1));
- long afterLastBand = scanlineStride - sourcePixelStride;
+ sourceScanlineStride = source.getScanlineStride(sourcePixelStride);
+ long afterLastBand = sourceScanlineStride - sourcePixelStride;
+ final int between = Math.multiplyExact(sourcePixelStride,
Math.toIntExact(getSubsampling(X_DIMENSION) - 1));
if (includedBands != null && sourcePixelStride > 1) {
final int[] skips = new int[includedBands.length];
final int m = skips.length - 1;
@@ -184,7 +187,7 @@ final class CompressedSubset extends DataSubset {
* @param upper (<var>x</var>, <var>y</var>) coordinates after the
last pixel to read relative to the tile.
* @param subsampling (<var>sx</var>, <var>sy</var>) subsampling factors.
* @param location pixel coordinates in the upper-left corner of the
tile to return.
- * @return a single tile decoded from the GeoTIFF file.
+ * @return a single tile decoded from the <abbr>TIFF</abbr> file.
*/
@Override
Raster readSlice(final long[] offsets, final long[] byteCounts, final
long[] lower, final long[] upper,
@@ -241,14 +244,14 @@ final class CompressedSubset extends DataSubset {
* by the mathematical operation identified by `predictor`.
*/
for (long y = lower[1]; --y >= 0;) {
- inflater.skip(scanlineStride); // `skip(…)` may round
to next element boundary.
+ inflater.skip(sourceScanlineStride); // `skip(…)` may round
to next element boundary.
}
for (int y = height; --y > 0;) { // (height - 1)
iterations.
inflater.skip(head);
inflater.uncompressRow();
inflater.skip(tail);
for (int j=betweenRows; --j>=0;) {
- inflater.skip(scanlineStride);
+ inflater.skip(sourceScanlineStride);
}
}
inflater.skip(head); // Last iteration
without the trailing `skip(…)` calls.
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
index b1e2774774..0b8ea082e4 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/DataCube.java
@@ -134,12 +134,25 @@ abstract class DataCube extends TiledGridResource
implements StoreResource {
abstract long getNumTiles();
/**
- * Gets the stream position and the length in bytes of compressed tile
arrays in the GeoTIFF file.
+ * Gets the stream position or the length in bytes of compressed tile
arrays in the GeoTIFF file.
* Values in the returned vector are {@code long} primitive type.
*
- * @return stream position (relative to file beginning) and length of
compressed tile arrays, in bytes.
+ * @param length {@code false} for requesting tile offsets, or {@code
true} for tile lengths.
+ * @return stream position (relative to file beginning) or length of
compressed tile arrays, in bytes.
*/
- abstract Vector[] getTileArrayInfo();
+ abstract Vector getTileArrayInfo(boolean length);
+
+ /**
+ * Returns the number of sample values for moving to the next row in a
tile of the <abbr>TIFF</abbr> file.
+ * The {@code pixelStride} argument could be computed by this class, but
is given in argument because its
+ * value is already known by the caller.
+ *
+ * @param pixelStride number of sample values for moving to the next
pixel.
+ * @return number of sample values for moving to the next row.
+ *
+ * @see java.awt.image.ComponentSampleModel#getScanlineStride()
+ */
+ abstract long getScanlineStride(int pixelStride);
/**
* Returns {@code true} if {@link Integer#reverseBytes(int)} should be
invoked on each byte read.
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 06dc570e39..bc1bd2dabe 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
@@ -55,9 +55,9 @@ import static org.apache.sis.pending.jdk.JDK18.ceilDiv;
/**
- * Raster data obtained from a GeoTIFF file in the domain requested by user.
The number of dimensions is 2
- * for standard TIFF files, but this class accepts higher number of dimensions
if 3- or 4-dimensional data
- * are stored in a GeoTIFF file using some convention. This base class
transfers uncompressed data.
+ * Raster data obtained from a GeoTIFF file in the domain requested by user.
The number of dimensions is 2 for
+ * standard <abbr>TIFF</abbr> files, but this class accepts higher number of
dimensions if 3- or 4-dimensional
+ * data are stored in a GeoTIFF file using some convention. This base class
transfers uncompressed data.
* Compressed data are handled by specialized subclasses.
*
* <h2>Cell Coordinates</h2>
@@ -114,7 +114,7 @@ class DataSubset extends TiledGridCoverage implements
Localized {
protected final int numBanks;
/**
- * Number of interleaved sample values in a pixel in the GeoTIFF file
(ignoring band subset).
+ * Number of interleaved sample values in a pixel in the <abbr>TIFF</abbr>
file (ignoring band subset).
* For planar images (banded sample model), this is equal to 1. For pixel
interleaved image,
* this is equal to the number of bands in the original image.
*
@@ -122,6 +122,7 @@ class DataSubset extends TiledGridCoverage implements
Localized {
* and 8 samples are packed in each byte. Conversely a sample may also be
1, 2, 4 or 8 bytes.</p>
*
* @see java.awt.image.ComponentSampleModel#getPixelStride()
+ * @see CompressedSubset#sourceScanlineStride
*/
protected final int sourcePixelStride;
@@ -149,11 +150,10 @@ class DataSubset extends TiledGridCoverage implements
Localized {
*/
DataSubset(final DataCube source, final TiledGridResource.Subset subset)
throws DataStoreException {
super(subset);
- this.source = source;
- this.numTiles = toIntExact(source.getNumTiles());
- final Vector[] tileArrayInfo = source.getTileArrayInfo();
- this.tileOffsets = tileArrayInfo[0];
- this.tileByteCounts = tileArrayInfo[1];
+ this.source = source;
+ this.numTiles = toIntExact(source.getNumTiles());
+ this.tileOffsets = source.getTileArrayInfo(false);
+ this.tileByteCounts = source.getTileArrayInfo(true);
/*
* "Banks" (in `java.awt.image.DataBuffer` sense) are synonymous to
"bands" for planar image only.
* Otherwise there is only one bank no matter the number of bands.
Each bank will be read separately.
@@ -229,8 +229,8 @@ class DataSubset extends TiledGridCoverage implements
Localized {
*/
protected final int getBankCapacity(final int pixelsPerElement) {
// `ceilDiv(…)` must happen before multiplication by image height.
- final int scanlineStride = ceilDiv(multiplyExact(model.getWidth(),
targetPixelStride), pixelsPerElement);
- return multiplyExact(scanlineStride, model.getHeight());
+ final int targetScanlineStride =
ceilDiv(multiplyExact(model.getWidth(), targetPixelStride), pixelsPerElement);
+ return multiplyExact(targetScanlineStride, model.getHeight());
}
/**
@@ -489,7 +489,7 @@ class DataSubset extends TiledGridCoverage implements
Localized {
* @param upper (<var>x</var>, <var>y</var>) coordinates after the
last pixel to read relative to the tile.
* @param subsampling (<var>sx</var>, <var>sy</var>) subsampling factors.
* @param location pixel coordinates in the upper-left corner of the
tile to return.
- * @return a single tile decoded from the GeoTIFF file.
+ * @return a single tile decoded from the <abbr>TIFF</abbr> file.
* @throws IOException if an I/O error occurred.
* @throws DataStoreException if a logical error occurred.
* @throws RuntimeException if the Java2D image cannot be created for
another reason
@@ -583,7 +583,7 @@ class DataSubset extends TiledGridCoverage implements
Localized {
/**
* Creates the raster with the given data buffer, which contains the
pixels that have been read.
*
- * @param buffer the sample values which have been read from the
GeoTIFF tile.
+ * @param buffer the sample values which have been read from the
<abbr>TIFF</abbr> tile.
* @param location pixel coordinates in the upper-left corner of the
tile to return.
* @return raster with the given sample values.
*/
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index 23a73e1eb9..618c6709f3 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -1640,6 +1640,19 @@ final class ImageFileDirectory extends DataCube {
return sampleModel;
}
+ /**
+ * Returns the number of sample values for moving to the next row in a
tile of the <abbr>TIFF</abbr> file.
+ * The given {@code pixelStride} argument should be {@code
sampleModel.getPixelString()} and the returned
+ * value should be {@code sampleModel.getScanlineStride()}.
+ *
+ * @param pixelStride number of sample values for moving to the next
pixel.
+ * @return number of sample values for moving to the next row.
+ */
+ @Override
+ final long getScanlineStride(final int pixelStride) {
+ return Math.multiplyFull(pixelStride, tileWidth);
+ }
+
/**
* Returns the number of components per pixel.
*/
@@ -1868,11 +1881,12 @@ final class ImageFileDirectory extends DataCube {
* Gets the stream position or the length in bytes of compressed tile
arrays in the GeoTIFF file.
* Values in the returned vector are {@code long} primitive type.
*
- * @return stream position (relative to file beginning) and length of
compressed tile arrays, in bytes.
+ * @param length {@code false} for requesting tile offsets, or {@code
true} for tile lengths.
+ * @return stream position (relative to file beginning) or length of
compressed tile arrays, in bytes.
*/
@Override
- Vector[] getTileArrayInfo() {
- return new Vector[] {tileOffsets, tileByteCounts};
+ Vector getTileArrayInfo(final boolean length) {
+ return length ? tileByteCounts : tileOffsets;
}
/**
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/LZW.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/LZW.java
index f5317e9489..0db2dc0e3a 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/LZW.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/inflater/LZW.java
@@ -193,7 +193,7 @@ final class LZW extends CompressionChannel {
/**
* Pointers to byte sequences for a code in the {@link #entriesForCodes}
array.
* Each element is a value encoded by {@link #offsetAndLength(int, int)}
method.
- * Elements are decoded by {@link #offset(int)} {@link #length(int)}
methods.
+ * Elements are decoded by {@link #offset(int)} and {@link #length(int)}
methods.
*/
private final int[] entriesForCodes;
@@ -304,6 +304,7 @@ final class LZW extends CompressionChannel {
@Override
public int read(final ByteBuffer target) throws IOException {
final int start = target.position();
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
int previousCode = this.previousCode;
/*
* If a previous invocation of this method was unable to write some
data
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 cada7ef2ac..20631fbadd 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
@@ -105,12 +105,10 @@ public abstract class TiledGridCoverage extends
GridCoverage {
protected final GridExtent readExtent;
/**
- * Whether to force the {@link #readExtent} tile intersection to the
{@link #virtualTileSize}.
- * 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 Raster}. However, there is a few exceptions
where the read extent
- * should not be forced to the tile size:
+ * Whether to enforce {@link #virtualTileSize} even if the intersection
with {@link #readExtent} is smaller.
+ * Forcing whole tile is relevant mostly (but not only) for the last
column of tile matrix, because otherwise
+ * the {@linkplain java.awt.image.ComponentSampleModel#getScanlineStride()
scanline stride} would be wrong.
+ * 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.storage.base.TiledGridResource.Subset}
@@ -120,21 +118,38 @@ public abstract class TiledGridCoverage extends
GridCoverage {
* </ul>
*
* This a list of Boolean flags packed as a bitmask with the flag for the
first dimension in the lowest bit.
- * The default implementation always sets the flag of the last dimension
to {@code false} (0), then sets the
- * flags of other dimensions to {@code true} (1) if we are not in the case
of a big untiled image.
+ * The default implementation sets the bit to 1 in all dimensions where
the read operation should be done in
+ * a tiled fashion (i.e., not reading a sub-region of an effectively
untiled image).
+ *
+ * <h4>Example</h4>
+ * The GeoTIFF reader always sets the flag of the last dimension (the
rows) to {@code false} (0),
+ * then sets the flags of other dimensions to {@code true} (1) if we are
not in the case of a big
+ * untiled image.
*/
private final long forceWholeTiles;
/**
- * Size of all tiles in the domain of this {@code TiledGridCoverage},
without clipping and subsampling.
+ * Size of all tiles in the domain of this {@code TiledGridCoverage},
without sub-sampling.
* All coverages created from the same {@link TiledGridResource} shall
have the same tile size values.
* The length of this array is the number of dimensions in the source
{@link GridExtent}.
* This is often {@value #BIDIMENSIONAL} but can also be more.
*
- * <p>The tile size may be virtual if the {@link TiledGridResource}
subclass decided to coalesce
- * many real tiles in bigger virtual tiles. This is sometime useful when a
subsampling is applied,
- * for avoiding that the subsampled tiles become too small.
- * This strategy may be convenient when coalescing is easy.</p>
+ * <h4>What is a "virtual" size</h4>
+ * The tile size stored in this field is usually the size of tiles used by
the binary encoding of the file
+ * which is read by {@link TiledGridResource}. However, this tile size may
differ in two circumstances.
+ * In such case, this tile size is said "virtual".
+ *
+ * <h5>Tiles coalescence</h5>
+ * The first circumstance is when the {@link TiledGridResource} subclass
+ * decided to coalesce many tiles from the file in bigger tiles in memory.
+ * This is sometime useful when a sub-sampling is applied,
+ * for avoiding that the sub-sampled tiles become too small.
+ * This strategy may be convenient when coalescing tiles is easy for the
subclass.
+ *
+ * <h5>Untiled image</h5>
+ * The second circumstance is when the coverage is effectively untiled.
+ * It may be because the binary file is untiled, or because the requested
region is fully inside a single tile.
+ * In such case, the virtual tile size is the size of the requested region.
*
* @see #getTileSize(int)
*/
@@ -233,7 +248,9 @@ public abstract class TiledGridCoverage extends
GridCoverage {
protected final Number[] fillValues;
/**
- * Whether the reading of tiles is deferred to {@link
RenderedImage#getTile(int, int)} time.
+ * Whether the reading of tiles is deferred until {@link
RenderedImage#getTile(int, int)} is invoked.
+ * This is true if the user explicitly {@linkplain
TiledGridResource#setLoadingStrategy requested such
+ * deferred loading strategy} and the resource considers that it is worth
to do so.
*/
private final boolean deferredTileReading;
@@ -248,7 +265,6 @@ public abstract class TiledGridCoverage extends
GridCoverage {
protected TiledGridCoverage(final TiledGridResource.Subset subset) {
super(subset.domain, subset.ranges);
final GridExtent extent = subset.domain.getExtent();
- final int dimension = subset.virtualTileSize.length;
deferredTileReading = subset.deferredTileReading(); // May be
shorter than other arrays or the grid geometry.
readExtent = subset.readExtent;
subsampling = subset.subsampling;
@@ -256,6 +272,7 @@ public abstract class TiledGridCoverage extends
GridCoverage {
includedBands = subset.includedBands;
rasters = subset.cache;
virtualTileSize = subset.virtualTileSize;
+ final int dimension = virtualTileSize.length;
tmcOfFirstTile = new long[dimension];
tileStrides = new int [dimension];
final int[] subSize = new int [dimension];
@@ -307,9 +324,11 @@ public abstract class TiledGridCoverage extends
GridCoverage {
}
/**
- * Returns the size of all tiles in the domain of this {@code
TiledGridCoverage}, without clipping and subsampling.
- * It may be a virtual tile size if the {@link TiledGridResource} subclass
decided to coalesce many real tiles into
- * fewer bigger virtual tiles.
+ * Returns the size of all tiles in the domain of this {@code
TiledGridCoverage}, without sub-sampling.
+ * This usually the same size as the tiles in the storage which is read by
{@link TiledGridResource},
+ * but not necessarily. It may be larger if the {@link TiledGridResource}
subclass decided to coalesce
+ * many real tiles into larger virtual tiles, or it may be smaller when
reading a sub-region of an
+ * effectively untiled coverage.
*
* @param dimension dimension for which to get tile size.
* @return tile size in the given dimension, without clipping and
subsampling.
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
index 104f9d1258..0b5c4e414c 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridResource.java
@@ -407,6 +407,7 @@ check: if (dataType.isInteger()) {
public final class Subset {
/**
* The full size of the coverage in the enclosing {@link
TiledGridResource}.
+ * This is taken from {@link #getGridGeometry()} and does not take
sub-sampling in account.
*/
final GridExtent sourceExtent;
@@ -463,11 +464,14 @@ check: if (dataType.isInteger()) {
final long[] subsamplingOffsets;
/**
- * Size of tiles (or chunks) in the resource, without clipping and
subsampling.
- * May be a virtual tile size (i.e., tiles larger than the real tiles)
if the
- * resource can easily coalesce many tiles in a single read operation.
+ * Size of tiles (or chunks) in the resource, without sub-sampling.
+ * May be a virtual tile size (i.e., tiles larger than the tiles in
the file)
+ * if the resource can easily coalesce many tiles in a single read
operation.
+ * Conversely, it may also be smaller than the real tile size if the
subset
+ * is effectively untiled (the requested region covers a single tile).
*
* @see #getVirtualTileSize(long[])
+ * @see TiledGridCoverage#virtualTileSize
*/
final long[] virtualTileSize;
@@ -584,7 +588,9 @@ check: if (dataType.isInteger()) {
subsamplingOffsets =
ArraysExt.resize(target.getSubsamplingOffsets(), dimension);
}
/*
- * Virtual tile size is usually the same as the real tile size.
+ * Virtual tile size is usually the same as the tile size encoded
in the binary file.
+ * The virtual size may be larger if the subclass override
`getVirtualTileSize(…)`.
+ * The loop below is where the virtual tile size may be made
smaller.
*/
virtualTileSize = getVirtualTileSize(subsampling);
for (int i=0; i < virtualTileSize.length; i++) {
@@ -638,14 +644,19 @@ check: if (dataType.isInteger()) {
/**
* Returns flags telling, for each dimension, whether the read region
should be an integer number of tiles.
+ * By default (when {@link #canReadTruncatedTiles(int, boolean)} is
not overridden), the flags are set for
+ * all dimensions except the ones where the region to read is smaller
than the tile size. The latter case
+ * happens when reading an effectively untiled coverage (when the
requested region is inside a single tile).
*
* @param subSize tile size after subsampling.
* @return a bitmask with the flag for the first dimension in the
lowest bit.
+ *
+ * @see TiledGridCoverage#forceWholeTiles
*/
final long forceWholeTiles(final int[] subSize) {
long forceWholeTiles = 0;
for (int i=0; i<subSize.length; i++) {
- if (!canReadTruncatedTiles(i,
Math.multiplyExact(subsampling[i], subSize[i]) != virtualTileSize[i])) {
+ if (!canReadTruncatedTiles(i,
Math.multiplyExact(subsampling[i], subSize[i]) < virtualTileSize[i])) {
forceWholeTiles |= Numerics.bitmask(i);
}
}
@@ -672,7 +683,9 @@ check: if (dataType.isInteger()) {
}
/**
- * Whether the reading of tiles is deferred to {@link
RenderedImage#getTile(int, int)} time.
+ * Whether the reading of tiles is deferred until {@link
RenderedImage#getTile(int, int)} is invoked.
+ * This is true if the user explicitly {@linkplain #setLoadingStrategy
requested such deferred loading
+ * strategy} and this method considers that it is worth to do so.
*/
final boolean deferredTileReading() {
if (loadingStrategy != RasterLoadingStrategy.AT_GET_TILE_TIME) {
diff --git a/netbeans-project/nbproject/project.xml
b/netbeans-project/nbproject/project.xml
index 2b218382a5..8ae7f076ad 100644
--- a/netbeans-project/nbproject/project.xml
+++ b/netbeans-project/nbproject/project.xml
@@ -34,6 +34,7 @@
<word>Geopackage</word>
<word>Molodensky</word>
<word>transformative</word>
+ <word>untiled</word>
</spellchecker-wordlist>
</configuration>
</project>