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 496130940f `TiledGridResource.getFillValue()` should return an array
with one value per band instead of a single value for all bands.
496130940f is described below
commit 496130940f6dedfa1797af6169655e4ff140834f
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Sep 13 15:53:57 2024 +0200
`TiledGridResource.getFillValue()` should return an array with one value
per band instead of a single value for all bands.
---
.../apache/sis/coverage/privy/ImageUtilities.java | 17 ++++-
.../apache/sis/coverage/privy/TilePlaceholder.java | 16 ++---
.../main/org/apache/sis/image/DataType.java | 39 ++++++++---
.../sis/storage/geotiff/CompressedSubset.java | 4 +-
.../org/apache/sis/storage/geotiff/DataCube.java | 5 +-
.../org/apache/sis/storage/geotiff/DataSubset.java | 38 +++++++---
.../sis/storage/geotiff/ImageFileDirectory.java | 14 +++-
.../apache/sis/storage/base/TiledGridCoverage.java | 21 ++++--
.../apache/sis/storage/base/TiledGridResource.java | 61 +++++++++++++----
.../main/org/apache/sis/math/Vector.java | 2 +-
.../org/apache/sis/storage/gdal/TiledResource.java | 80 ++++++++++++++--------
.../storage/gimi/internal/MatrixGridRessource.java | 5 --
12 files changed, 213 insertions(+), 89 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/ImageUtilities.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/ImageUtilities.java
index aa27ff7a4a..527cf5148e 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/ImageUtilities.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/ImageUtilities.java
@@ -149,7 +149,7 @@ public final class ImageUtilities extends Static {
/**
* If the given image is showing only one band, returns the index of that
band.
- * Otherwise returns -1. Image showing only one band are SIS-specific
+ * Otherwise, returns -1. Images showing only one band are SIS-specific
* (usually an image shows all its bands).
*
* @param image the image for which to get the visible band, or {@code
null}.
@@ -175,6 +175,21 @@ public final class ImageUtilities extends Static {
return -1;
}
+ /**
+ * Returns the index of the band shown by the given color model.
+ * This is zero for standard color models, but <abbr>SIS</abbr>
+ * sometime allows color models to show another band.
+ *
+ * @param cm the color model for which to get the visible band.
+ * @return index of the visible band.
+ */
+ public static int getVisibleBand(final IndexColorModel cm) {
+ if (cm instanceof MultiBandsIndexColorModel) {
+ return ((MultiBandsIndexColorModel) cm).visibleBand;
+ }
+ return 0;
+ }
+
/**
* Returns the data type of bands in rasters that use the given sample
model.
* If each band is stored in its own {@link DataBuffer} element, then this
method returns the same value
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/TilePlaceholder.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/TilePlaceholder.java
index 4a12016b7d..634f590ab9 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/TilePlaceholder.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/TilePlaceholder.java
@@ -133,20 +133,18 @@ 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.
+ * Returns a provider of empty tiles filled with the given values in all
bands.
+ * A {@code null} array 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.
+ * @param model sample model of the empty tiles.
+ * @param fillValues the values to use for filling empty spaces in
rasters, or {@code null} for the default.
* @return provider of filled tiles.
*/
- public static TilePlaceholder filled(final SampleModel model, final Number
fillValue) {
- if (fillValue == null) {
+ public static TilePlaceholder filled(final SampleModel model, final
Number[] fillValues) {
+ if (fillValues == null) {
return empty(model);
}
- final Number[] values = new Number[model.getNumBands()];
- Arrays.fill(values, fillValue);
- return filled(model, new FillValues(model, values, true));
+ return filled(model, new FillValues(model, fillValues, true));
}
/**
diff --git
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java
index be2a205ce8..cbbc0acbc1 100644
---
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java
+++
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/DataType.java
@@ -32,7 +32,7 @@ import static
org.apache.sis.util.privy.Numerics.MAX_INTEGER_CONVERTIBLE_TO_FLOA
* This is a type-safe version of the {@code TYPE_*} constants defined in
{@link DataBuffer}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.3
+ * @version 1.5
* @since 1.1
*/
public enum DataType {
@@ -45,33 +45,41 @@ public enum DataType {
/**
* Unsigned 8-bits data.
*/
- BYTE,
+ BYTE((byte) 0),
/**
* Unsigned 16-bits data.
*/
- USHORT,
+ USHORT((short) 0),
/**
* Signed 16-bits data.
*/
- SHORT,
+ SHORT((short) 0),
/**
* Signed 32-bits data. Also used for storing unsigned data; the Java2D
API such as
* {@link java.awt.image.Raster#getSample(int, int, int)} cannot
distinguish the two cases.
*/
- INT,
+ INT(0),
/**
* Single precision (32-bits) floating point data.
*/
- FLOAT,
+ FLOAT(Float.NaN),
/**
* Double precision (64-bits) floating point data.
*/
- DOUBLE;
+ DOUBLE(Double.NaN);
+
+ /**
+ * The default fill value, which is 0 for integer types and NaN for
floating point types.
+ * The class of this number is the wrapper type corresponding to the type
of sample values.
+ *
+ * @see #fillValue()
+ */
+ private final Number fillValue;
/**
* All enumeration values, cached for avoiding to recreate this array
@@ -82,7 +90,8 @@ public enum DataType {
/**
* Creates a new enumeration.
*/
- private DataType() {
+ private DataType(final Number fillValue) {
+ this.fillValue = fillValue;
}
/**
@@ -315,4 +324,18 @@ public enum DataType {
public final int toDataBufferType() {
return ordinal();
}
+
+ /**
+ * Returns the default fill value, which is 0 for integer types and NaN
for floating point types.
+ * The class of this number is the wrapper class corresponding to the type
of sample values,
+ * ignoring whether the type is signed or unsigned. For example, for
{@link #USHORT},
+ * the returned fill value is an instance of the {@link Short} class.
+ *
+ * @return 0 of NaN in an instance of the wrapper class of the sample
values.
+ *
+ * @since 1.5
+ */
+ public final Number fillValue() {
+ return fillValue;
+ }
}
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 6da055be23..5ae22e39b6 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
@@ -105,6 +105,7 @@ final class CompressedSubset extends DataSubset {
* @param rasters potentially shared cache of rasters read by this
{@code DataSubset}.
* @throws ArithmeticException if the number of tiles overflows 32 bits
integer arithmetic.
*/
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
CompressedSubset(final DataCube source, final TiledGridResource.Subset
subset) throws DataStoreException {
super(source, subset);
scanlineStride = multiplyFull(getTileSize(X_DIMENSION),
sourcePixelStride);
@@ -216,6 +217,7 @@ final class CompressedSubset extends DataSubset {
sourcePixelStride, getTileSize(X_DIMENSION),
chunksPerRow, samplesPerChunk, skipAfterChunks,
pixelsPerElement, dataType);
}
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final Inflater inflater = this.inflater;
final int capacity = getBankCapacity(pixelsPerElement);
final Buffer[] banks = new Buffer[numBanks];
@@ -243,7 +245,7 @@ final class CompressedSubset extends DataSubset {
}
inflater.skip(head); // Last iteration
without the trailing `skip(…)` calls.
inflater.uncompressRow();
- fillRemainingRows(bank.flip());
+ fillRemainingRows(bank.flip(), b);
banks[b] = bank;
}
return createWritableRaster(RasterFactory.wrap(dataType, banks),
location);
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 2891c0400f..a0f32761ab 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
@@ -170,11 +170,12 @@ abstract class DataCube extends TiledGridResource
implements ResourceOnFileSyste
* Our netCDF reader does the same thing, and we want a consistent
behavior of coverage readers.
* </div>
*
- * If this method returns a non-null value, then {@link #getFillValue()}
should return NaN.
+ * If this method returns a non-null value, then {@link
#getFillValues(int[])} should return
+ * an array of NaN.
*
* @return value to be replaced by NaN at reading time, or {@code null} if
none.
*
- * @see #getFillValue()
+ * @see #getFillValues(int[])
*/
abstract Number getReplaceableFillValue();
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 3bd7dfb457..5210fcb925 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
@@ -396,7 +396,12 @@ class DataSubset extends TiledGridCoverage implements
Localized {
final Raster r;
if (isEmpty) {
if (emptyTiles == null) {
- emptyTiles = TilePlaceholder.filled(model,
(fillValue != null) ? fillValue : 0);
+ Number[] values = fillValues;
+ if (values == null) {
+ values = new
Number[model.getNumBands()];
+ Arrays.fill(values, 0);
+ }
+ emptyTiles = TilePlaceholder.filled(model,
values);
}
r = emptyTiles.create(origin);
} else {
@@ -508,9 +513,9 @@ class DataSubset extends TiledGridCoverage implements
Localized {
* If that assumption was not true, we would have to adjust
`capacity`, `lower[0]` and `upper[0]`
* (we may do that as an optimization in a future version).
*/
- final HyperRectangleReader hr = new
HyperRectangleReader(ImageUtilities.toNumberEnum(type.toDataBufferType()),
input());
- final Region region = new Region(size, lower, upper, subsampling);
- final Buffer[] banks = new Buffer[numBanks];
+ final var hr = new
HyperRectangleReader(ImageUtilities.toNumberEnum(type.toDataBufferType()),
input());
+ final var region = new Region(size, lower, upper, subsampling);
+ final var banks = new Buffer[numBanks];
for (int b=0; b<numBanks; b++) {
if (b < byteCounts.length && length > byteCounts[b]) {
throw new
DataStoreContentException(source.reader.resources().getString(
@@ -519,7 +524,7 @@ class DataSubset extends TiledGridCoverage implements
Localized {
hr.setOrigin(offsets[b]);
assert model.getSampleSize(b) == sampleSize;
// See above comment.
final Buffer bank = hr.readAsBuffer(region, getBankCapacity(1));
- fillRemainingRows(bank);
+ fillRemainingRows(bank, b);
banks[b] = bank;
}
final DataBuffer buffer = RasterFactory.wrap(type, banks);
@@ -532,15 +537,28 @@ class DataSubset extends TiledGridCoverage implements
Localized {
* capacity if the current tile is smaller than the expected tile size
(e.g. last tile is truncated).
*
* @param bank the buffer where to fill remaining rows.
+ * @param band index of the band to fill. Same as bank index in the
particular case of {@code DataSubset} class.
*/
- final void fillRemainingRows(final Buffer bank) {
- if (fillValue != null) {
+ final void fillRemainingRows(final Buffer bank, final int band) {
+ if (fillValues != null) {
final int limit = bank.limit();
final int capacity = bank.capacity(); // Equals `this.capacity`
except for packed sample model.
if (limit != capacity) {
- Vector.create(bank.limit(capacity),
ImageUtilities.isUnsignedType(model))
- .fill(limit, capacity, fillValue);
- bank.limit(capacity);
+ final Vector v = Vector.create(bank.limit(capacity),
ImageUtilities.isUnsignedType(model));
+ final Number f = fillValues[band];
+ /*
+ * If all values are the same, we can delegate (indirectly) to
an `Arrays.fill(…)` method.
+ * Also, if the raster stores each band in a separated bank
(banded sample model),
+ * we have only one value to set in the given bank.
+ */
+ if (ArraysExt.allEquals(fillValues, f) || model instanceof
BandedSampleModel) {
+ v.fill(limit, capacity, f);
+ } else {
+ // Slow fallback for interleaved sample models.
+ for (int i=limit; i<capacity; i++) {
+ v.set(i, fillValues[i % fillValues.length]);
+ }
+ }
}
}
}
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 582b560f27..abf4f071ab 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
@@ -1778,13 +1778,21 @@ final class ImageFileDirectory extends DataCube {
}
/**
- * Returns the value to use for filling empty spaces in the raster, or
{@code null} if none,
+ * Returns the values to use for filling empty spaces in the raster, or
{@code null} if none,
* not different than zero or not valid for the target data type.
* The zero value is excluded because tiles are already initialized to
zero by default.
*/
@Override
- protected Number getFillValue() {
- return (sampleFormat != FLOAT) ? getFillValue(false) : Double.NaN;
+ protected Number[] getFillValues(final int[] bands) {
+ final Number fill;
+ if (sampleFormat == FLOAT) {
+ fill = Double.NaN;
+ } else if ((fill = getFillValue(false)) == null) {
+ return null;
+ }
+ final var values = new Number[(bands != null) ? bands.length :
getNumBands()];
+ Arrays.fill(values, fill);
+ return values;
}
/**
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 ffe450ff15..91731a7f96 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
@@ -189,18 +189,25 @@ public abstract class TiledGridCoverage extends
GridCoverage {
* The sample model for all rasters. The width and height of this sample
model are the two first elements
* of {@link #tileSize} divided by subsampling and clipped to the domain.
If user requested to read only
* a subset of the bands, then this sample model is already the subset.
+ *
+ * @see TiledGridResource#getSampleModel(int[])
*/
protected final SampleModel model;
/**
* The Java2D color model for images rendered from this coverage.
+ *
+ * @see TiledGridResource#getColorModel(int[])
*/
protected final ColorModel colors;
/**
- * The value to use for filling empty spaces in rasters, or {@code null}
if zero.
+ * The values to use for filling empty spaces in rasters, or {@code null}
if zero in all bands.
+ * If non-null, the array length is equal to the number of bands.
+ *
+ * @see TiledGridResource#getFillValues(int[])
*/
- protected final Number fillValue;
+ protected final Number[] fillValues;
/**
* Whether the reading of tiles is deferred to {@link
RenderedImage#getTile(int, int)} time.
@@ -251,10 +258,10 @@ public abstract class TiledGridCoverage extends
GridCoverage {
if (model.getWidth() != subSize[X_DIMENSION] || model.getHeight() !=
subSize[Y_DIMENSION]) {
model = model.createCompatibleSampleModel(subSize[X_DIMENSION],
subSize[Y_DIMENSION]);
}
- this.model = model;
- this.colors = subset.colorsForBandSubset;
- this.fillValue = subset.fillValue;
- forceTileSize = subSize[X_DIMENSION] * subsampling[X_DIMENSION] ==
tileSize[X_DIMENSION];
+ this.model = model;
+ this.colors = subset.colorsForBandSubset;
+ this.fillValues = subset.fillValues;
+ forceTileSize = subSize[X_DIMENSION] * subsampling[X_DIMENSION] ==
tileSize[X_DIMENSION];
}
/**
@@ -725,7 +732,7 @@ public abstract class TiledGridCoverage extends
GridCoverage {
* coordinates are the values returned by {@link #getTileOrigin(int)}
for dimensions
* of two-dimensional slices.
*
- * <p>The raster is <em>not</em> filled with {@link #fillValue}.
+ * <p>The raster is <em>not</em> filled with {@link #fillValues}.
* Filling, if needed, should be done by the caller.</p>
*
* @return a newly created, initially empty raster.
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 47cf1469e5..f4322a3daf 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
@@ -19,11 +19,13 @@ package org.apache.sis.storage.base;
import java.util.List;
import java.util.Arrays;
import java.util.Objects;
+import java.lang.reflect.Array;
import java.awt.image.DataBuffer;
import java.awt.image.ColorModel;
import java.awt.image.SampleModel;
import java.awt.image.BandedSampleModel;
import java.awt.image.ComponentSampleModel;
+import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.RasterFormatException;
@@ -35,7 +37,9 @@ import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.coverage.grid.GridRoundingMode;
import org.apache.sis.coverage.privy.ColorModelFactory;
+import org.apache.sis.coverage.privy.ImageUtilities;
import org.apache.sis.coverage.privy.RangeArgument;
+import org.apache.sis.image.DataType;
import org.apache.sis.storage.Resource;
import org.apache.sis.storage.AbstractGridCoverageResource;
import org.apache.sis.storage.DataStoreException;
@@ -222,7 +226,7 @@ public abstract class TiledGridResource extends
AbstractGridCoverageResource {
*
* @see RangeArgument#select(SampleModel, boolean)
*/
- protected boolean getDissociableBands() throws DataStoreException {
+ protected boolean canSeparateBands() throws DataStoreException {
return getSampleModel(null) instanceof ComponentSampleModel;
}
@@ -305,15 +309,44 @@ public abstract class TiledGridResource extends
AbstractGridCoverageResource {
}
/**
- * Returns the value to use for filling empty spaces in rasters,
- * or {@code null} if none, not different than zero or not valid for the
target data type.
- * This value is used if a tile contains less pixels than expected.
- * The zero value is excluded because tiles are already initialized to
zero by default.
+ * Returns the values to use for filling empty spaces in rasters, with one
value per band.
+ * The returned array can be {@code null} if there is no fill value, or if
the fill values
+ * are not different than zero, or are not valid for the image data type.
*
- * @return the value to use for filling empty spaces in rasters.
+ * <p>Fill values are used when a tile contains less pixels than expected.
+ * A null array is a shortcut for skipping the filling of new tiles,
+ * because new tiles are already initialized with zero values by
default.</p>
+ *
+ * <p>The default implementation returns an array of {@link
DataType#fillValue()} except
+ * for the {@linkplain IndexColorModel#getTransparentPixel() transparent
pixel} if any.
+ * If the array would contain only zero values, the default implementation
returns null.</p>
+ *
+ * @param bands indices (not necessarily in increasing order) of desired
bands, or {@code null} for all bands.
+ * @return the value to use for filling empty spaces in each band, or
{@code null} for defaulting to zero.
* @throws DataStoreException if an error occurred while fetching filling
information.
*/
- protected abstract Number getFillValue() throws DataStoreException;
+ protected Number[] getFillValues(final int[] bands) throws
DataStoreException {
+ final SampleModel model = getSampleModel(bands);
+ final var dataType = DataType.forDataBufferType(model.getDataType());
+ IndexColorModel icm = null;
+check: if (dataType.isInteger()) {
+ final ColorModel colors = getColorModel(bands);
+ if (colors instanceof IndexColorModel) {
+ icm = (IndexColorModel) colors;
+ if (icm.getTransparentPixel() > 0) {
+ break check;
+ }
+ }
+ return null;
+ }
+ final Number fill = dataType.fillValue();
+ final var fillValues = (Number[]) Array.newInstance(fill.getClass(),
model.getNumBands());
+ Arrays.fill(fillValues, fill);
+ if (icm != null) {
+ fillValues[ImageUtilities.getVisibleBand(icm)] =
icm.getTransparentPixel();
+ }
+ return fillValues;
+ }
/**
* Parameters that describe the resource subset to be accepted by the
{@link TiledGridCoverage} constructor.
@@ -395,10 +428,10 @@ public abstract class TiledGridResource extends
AbstractGridCoverageResource {
final ColorModel colorsForBandSubset;
/**
- * Value to use for filling empty spaces in rasters, or {@code null}
if none,
+ * Values to use for filling empty spaces in rasters, or {@code null}
if none,
* not different than zero or not valid for the target data type.
*/
- final Number fillValue;
+ final Number[] fillValues;
/**
* Cache to use for tiles loaded by the {@link TiledGridCoverage}.
@@ -477,15 +510,16 @@ public abstract class TiledGridResource extends
AbstractGridCoverageResource {
/*
* Get the bands selected by user in strictly increasing order of
source band index.
* If user has specified bands in a different order, that change
of band order will
- * be handled by the `SampleModel`, not in `includedBands` array.
+ * be handled by the `SampleModel`, not by the `includedBands`
array.
*/
+ int[] requestedBands = null; // Same as `includedBands`
but in user-specified order.
@SuppressWarnings("LocalVariableHidesMemberVariable") int[]
includedBands = null;
@SuppressWarnings("LocalVariableHidesMemberVariable") SampleModel
modelForBandSubset = null;
@SuppressWarnings("LocalVariableHidesMemberVariable") ColorModel
colorsForBandSubset = null;
boolean loadAllBands = rangeIndices.isIdentity();
if (!loadAllBands) {
bands = Arrays.asList(rangeIndices.select(bands));
- loadAllBands = !getDissociableBands();
+ loadAllBands = !canSeparateBands();
if (!loadAllBands) {
sharedCache = false;
if (!rangeIndices.hasAllBands) {
@@ -495,8 +529,7 @@ public abstract class TiledGridResource extends
AbstractGridCoverageResource {
}
assert ArraysExt.isSorted(includedBands, true);
}
- // Same as `includedBands`, but in the order requested by
the user.
- int[] requestedBands = rangeIndices.getSelectedBands();
+ requestedBands = rangeIndices.getSelectedBands();
modelForBandSubset = getSampleModel(requestedBands);
colorsForBandSubset = getColorModel (requestedBands);
}
@@ -512,7 +545,7 @@ public abstract class TiledGridResource extends
AbstractGridCoverageResource {
this.includedBands = includedBands;
this.modelForBandSubset =
Objects.requireNonNull(modelForBandSubset);
this.colorsForBandSubset = colorsForBandSubset;
- this.fillValue = getFillValue();
+ this.fillValues = getFillValues(requestedBands);
/*
* All `TiledGridCoverage` instances can share the same cache if
they read all tiles fully.
* If they read only sub-regions or apply subsampling, then they
will need their own cache.
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/Vector.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/Vector.java
index ffc29ad7b6..4bff0ab27f 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/Vector.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/Vector.java
@@ -105,7 +105,7 @@ public abstract class Vector extends AbstractList<Number>
implements RandomAcces
* <li>A {@code Number[]} array.</li>
* <li>A {@code String[]} array (not recommended, but happen with some
file formats).</li>
* <li>A {@code Vector}, in which case it is returned unchanged.</li>
- * <li>A {@link Buffer} backed by an array.</li>
+ * <li>A {@link Buffer} backed by a Java array. Wrap the part between
buffer's position and limit.</li>
* <li>The {@code null} value, in which case {@code null} is
returned.</li>
* </ul>
*
diff --git
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
index 3025ab2255..d8768ed759 100644
---
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
+++
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/TiledResource.java
@@ -34,6 +34,7 @@ import java.lang.foreign.MemorySegment;
import org.opengis.util.GenericName;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.referencing.privy.AffineTransform2D;
+import org.apache.sis.referencing.privy.ExtendedPrecisionMatrix;
import org.apache.sis.coverage.SampleDimension;
import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.grid.GridGeometry;
@@ -128,11 +129,12 @@ final class TiledResource extends TiledGridResource {
private SampleModel sampleModel;
/**
- * The fill value, fetched when first requested.
+ * The fill values, fetched when first requested. An array of length 0 is
used as a sentinel
+ * value meaning that the array has been computed and the result is {@code
null}.
*
- * @see #getFillValue()
+ * @see #getFillValues(int[])
*/
- private Number fillValue;
+ private Number[] fillValues;
/**
* Creates a new instance as a child of the given data set.
@@ -341,8 +343,27 @@ final class TiledResource extends TiledGridResource {
}
}
+ /**
+ * Always return {@code true} because <abbr>GDAL</abbr> provides a
band-oriented <abbr>API</abbr>,
+ * where each band can always be accessed separately from other bands.
+ */
+ @Override
+ protected final boolean canSeparateBands() {
+ return true;
+ }
+
+ /**
+ * Always returns 1, because the complexity of reading only a sub-region
is handled by <abbr>GDAL</abbr>.
+ */
+ @Override
+ protected final int getAtomSize(int dim) {
+ return 1;
+ }
+
/**
* Creates the color model and sample model.
+ * This method uses cached values if the {@code bandIndices} argument is
+ * equal to the values given the last time that this method has been
invoked.
*
* @param bandIndices indices of the selected bands.
*/
@@ -383,34 +404,19 @@ final class TiledResource extends TiledGridResource {
* for all number of bands from 2 to 15.
*/
}
- int visibleBand = -1;
if ((red | green | blue) >= 0) {
- visibleBand = Math.min(Math.min(red, green), blue);
colorModel = ColorModelFactory.createRGB(dataType.numBits, false,
alpha >= 0);
// TODO: needs custom color model if too many bands, or if order
is not (A)RGB.
} else if (palette != null) {
- visibleBand = paletteIndex;
- colorModel =
ColorModelFactory.createIndexColorModel(selectedBands.length, visibleBand,
palette, true, -1);
+ colorModel =
ColorModelFactory.createIndexColorModel(selectedBands.length, paletteIndex,
palette, true, -1);
} else {
- visibleBand = Math.max(gray, 0);
- final Band band = selectedBands[visibleBand];
+ gray = Math.max(gray, 0);
+ final Band band = selectedBands[gray];
final double min = band.getValue(gdal.getRasterMinimum,
MemorySegment.NULL);
final double max = band.getValue(gdal.getRasterMaximum,
MemorySegment.NULL);
- colorModel = ColorModelFactory.createGrayScale(dataType.imageType,
selectedBands.length, visibleBand, min, max);
+ colorModel = ColorModelFactory.createGrayScale(dataType.imageType,
selectedBands.length, gray, min, max);
}
sampleModel = new BandedSampleModel(dataType.imageType, width, height,
selectedBands.length);
- /*
- * Also compute the fill value here because the current method needs
the visible band.
- * TODO: we should compute the fill value for all bands instead, and
move this code to
- * the `getFillValue()` method.
- */
- try (final Arena arena = Arena.ofConfined()) {
- final MemorySegment flag = arena.allocate(ValueLayout.JAVA_INT);
- final double value =
selectedBands[visibleBand].getValue(gdal.getRasterNoDataValue, flag);
- if (value != 0 && Band.isTrue(flag)) {
- fillValue = value;
- }
- }
selectedBandIndices = bandIndices;
}
@@ -442,17 +448,35 @@ final class TiledResource extends TiledGridResource {
}
/**
- * Returns the value to use for filling empty spaces in rasters,
- * or {@code null} if none, not different than zero or not valid for the
target data type.
+ * Returns the values to use for filling empty spaces in rasters, with one
value per band.
+ * The returned array can be {@code null} if the fill values are not
different than zero.
* The zero value is excluded because tiles are already initialized to
zero by default.
*/
@Override
- protected Number getFillValue() throws DataStoreException {
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ protected Number[] getFillValues(final int[] bandIndices) throws
DataStoreException {
synchronized (getSynchronizationLock()) {
- if (fillValue == null) {
- createColorAndSampleModel(null);
+ if (fillValues == null) {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ final var fillValues = new Number[] {(bandIndices != null) ?
bandIndices.length : bands.length};
+ final GDAL gdal = parent.getProvider().GDAL();
+ boolean hasNonZero = false;
+ try (final Arena arena = Arena.ofConfined()) {
+ final MemorySegment flag =
arena.allocate(ValueLayout.JAVA_INT);
+ for (int i=0; i<fillValues.length; i++) {
+ final int b = (bandIndices != null) ? bandIndices[i] :
i;
+ final double value =
bands[b].getValue(gdal.getRasterNoDataValue, flag);
+ hasNonZero |= (value != 0);
+ if (!Band.isTrue(flag)) {
+ hasNonZero = false;
+ break;
+ }
+ }
+ }
+ // Use `ExtendedPrecisionMatrix.CREATE_ZERO` as an arbitrary
zero-length array.
+ this.fillValues = hasNonZero ? fillValues :
ExtendedPrecisionMatrix.CREATE_ZERO;
}
- return fillValue;
+ return (fillValues.length != 0) ? fillValues : null;
}
}
diff --git
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/MatrixGridRessource.java
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/MatrixGridRessource.java
index be0abc9bca..61f5328da3 100644
---
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/MatrixGridRessource.java
+++
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/MatrixGridRessource.java
@@ -84,11 +84,6 @@ public abstract class MatrixGridRessource extends
TiledGridResource {
return colorModel;
}
- @Override
- protected Number getFillValue() throws DataStoreException {
- return Double.NaN;
- }
-
@Override
public GridGeometry getGridGeometry() throws DataStoreException {
return getTileMatrix().getTilingScheme().upsample(getTileSize());