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 51fc2ddf3e Fix the writing of GeoTIFF planar image (banded sample
model).
51fc2ddf3e is described below
commit 51fc2ddf3e4a92614252896adc732df5036c370d
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Oct 31 20:49:14 2023 +0100
Fix the writing of GeoTIFF planar image (banded sample model).
---
.../sis/storage/geotiff/writer/TileMatrix.java | 33 +++-------
.../apache/sis/io/stream/HyperRectangleWriter.java | 72 ++++++++++++++++++++--
2 files changed, 74 insertions(+), 31 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
index b4ee813e6a..1ddf9f6dee 100644
---
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
+++
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/TileMatrix.java
@@ -30,9 +30,6 @@ import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferFloat;
import java.awt.image.DataBufferDouble;
import java.awt.image.SampleModel;
-import java.awt.image.ComponentSampleModel;
-import java.awt.image.MultiPixelPackedSampleModel;
-import java.awt.image.SinglePixelPackedSampleModel;
import org.apache.sis.image.DataType;
import org.apache.sis.util.internal.Numerics;
import org.apache.sis.io.stream.ChannelDataOutput;
@@ -219,6 +216,7 @@ public final class TileMatrix {
* @throws DataStoreException if the compression method is unsupported.
* @throws IOException if an error occurred while writing to the given
output.
*/
+ @SuppressWarnings("null")
public void writeRasters(final ChannelDataOutput output) throws
DataStoreException, IOException {
ChannelDataOutput compress = null;
PixelChannel cc = null;
@@ -227,42 +225,28 @@ public final class TileMatrix {
HyperRectangleWriter rect = null;
final int minTileX = image.getMinTileX();
final int minTileY = image.getMinTileY();
- int planeIndex = 0;
- while (planeIndex < offsets.length) {
+ for (int tileIndex = 0; tileIndex < numTiles; tileIndex++) {
/*
* In current implementation, we iterate from left to right then
top to bottom.
* But a future version could use Hilbert iterator (for example).
*/
- final int tileIndex = planeIndex / numPlanes;
int tileX = tileIndex % numXTiles;
int tileY = tileIndex / numXTiles;
tileX += minTileX;
tileY += minTileY;
final Raster tile = image.getTile(tileX, tileY);
if (sm != (sm = tile.getSampleModel())) {
- rect = null;
final var builder = new
HyperRectangleWriter.Builder().region(new Rectangle(tileWidth, tileHeight));
- if (sm instanceof ComponentSampleModel) {
- final var csm = (ComponentSampleModel) sm;
- rect = builder.create(csm);
- bankIndices = csm.getBankIndices();
- } else if (sm instanceof SinglePixelPackedSampleModel) {
- final var csm = (SinglePixelPackedSampleModel) sm;
- rect = builder.create(csm);
- bankIndices = new int[1];
- } else if (sm instanceof MultiPixelPackedSampleModel) {
- final var csm = (MultiPixelPackedSampleModel) sm;
- rect = builder.create(csm);
- bankIndices = new int[1];
+ rect = builder.create(sm);
+ if (rect == null) {
+ throw new UnsupportedOperationException(); // TODO:
reformat using a recycled Raster.
}
+ bankIndices = builder.bankIndices();
if (compress == null) {
compress = createCompressionChannel(output,
builder.pixelStride(), builder.scanlineStride());
if (compress != output) cc = (PixelChannel)
compress.channel;
}
}
- if (rect == null) {
- throw new UnsupportedOperationException(); // TODO:
reformat using a recycled Raster.
- }
final DataBuffer buffer = tile.getDataBuffer();
final int[] bufferOffsets = buffer.getOffsets();
for (int j=0; j<numPlanes; j++) {
@@ -281,14 +265,11 @@ public final class TileMatrix {
if (cc != null) {
cc.finish(compress);
}
+ final int planeIndex = tileIndex + j*numTiles;
offsets[planeIndex] = position;
lengths[planeIndex] =
Math.toIntExact(Math.subtractExact(output.getStreamPosition(), position));
- planeIndex++;
}
}
if (cc != null) cc.close();
- if (planeIndex != offsets.length) {
- throw new AssertionError(); // Should never happen.
- }
}
}
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 792b90e8ab..2e737fe89e 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
@@ -110,6 +110,15 @@ public final class HyperRectangleWriter {
*/
private int scanlineStride;
+ /**
+ * The indices of all banks to write with {@code HyperRectangleWriter}.
+ * A length greater than one means that the {@link
HyperRectangleWriter} instance
+ * created by this builder will need to be invoked repetitively for
each bank.
+ *
+ * @see bankIndices()
+ */
+ private int[] bankIndices;
+
/**
* Subregion to write, or {@code null} for writing the whole raster.
*/
@@ -159,20 +168,43 @@ public final class HyperRectangleWriter {
* This method supports only the writing of either a single band, or
all bands
* in the order they appear in the array.
*
+ * <p>The returned writer will need to be applied repetitively for
each bank
+ * if {@link #bankIndices()} returns an array with a length greater
than one.</p>
+ *
* @param sm the sample model of the rasters to write.
* @return writer, or {@code null} if the given sample model is not
supported.
*/
public HyperRectangleWriter create(final ComponentSampleModel sm) {
pixelStride = sm.getPixelStride();
scanlineStride = sm.getScanlineStride();
+ bankIndices = sm.getBankIndices();
final int[] d = sm.getBandOffsets();
final int subX;
- if (d.length == pixelStride && ArraysExt.isRange(0, d)) {
- subX = 1;
- } else if (d.length == 1) {
- subX = pixelStride;
+ if (ArraysExt.allEquals(bankIndices, bankIndices[0])) {
+ /*
+ * PixelInterleavedSampleModel (at least conceptually, no
matter the actual type).
+ * The returned `HyperRectangleWriter` instance will write all
sample values in a
+ * single call to a `write(…)` method, no matter the actual
number of bands.
+ */
+ bankIndices = ArraysExt.resize(bankIndices, 1);
+ if (d.length == pixelStride && ArraysExt.isRange(0, d)) {
+ subX = 1;
+ } else if (d.length == 1) {
+ subX = pixelStride;
+ } else {
+ return null;
+ }
} else {
- return null;
+ /*
+ * BandedSampleModel (at least conceptually, no matter the
actual type).
+ * The returned `HyperRectangleWriter` instance will need to
be used
+ * repetitively by the caller.
+ */
+ if (ArraysExt.allEquals(d, 0)) {
+ subX = 1;
+ } else {
+ return null;
+ }
}
return create(sm, subX);
}
@@ -185,6 +217,7 @@ public final class HyperRectangleWriter {
* @return writer, or {@code null} if the given sample model is not
supported.
*/
public HyperRectangleWriter create(final SinglePixelPackedSampleModel
sm) {
+ bankIndices = new int[1]; // Length is NOT the number of
bands.
pixelStride = 1;
scanlineStride = sm.getScanlineStride();
final int[] d = sm.getBitMasks();
@@ -205,6 +238,7 @@ public final class HyperRectangleWriter {
* @return writer, or {@code null} if the given sample model is not
supported.
*/
public HyperRectangleWriter create(final MultiPixelPackedSampleModel
sm) {
+ bankIndices = new int[1]; // Length is NOT the number of
bands.
pixelStride = 1;
scanlineStride = sm.getScanlineStride();
final int[] d = sm.getSampleSize();
@@ -217,6 +251,21 @@ public final class HyperRectangleWriter {
return null;
}
+ /**
+ * Creates a new writer for raster data described by the given sample
model.
+ * The returned writer will need to be applied repetitively for each
bank
+ * if {@link #bankIndices()} returns an array with a length greater
than one.
+ *
+ * @param sm the sample model of the rasters to write.
+ * @return writer, or {@code null} if the given sample model is not
supported.
+ */
+ public HyperRectangleWriter create(final SampleModel sm) {
+ if (sm instanceof ComponentSampleModel) return
create((ComponentSampleModel) sm);
+ if (sm instanceof SinglePixelPackedSampleModel) return
create((SinglePixelPackedSampleModel) sm);
+ if (sm instanceof MultiPixelPackedSampleModel) return
create((MultiPixelPackedSampleModel) sm);
+ return null;
+ }
+
/**
* {@return the number of elements (not necessarily bytes) between a
pixel and the next pixel}.
* This information is valid only after a {@code create(…)} method has
been invoked.
@@ -232,6 +281,19 @@ public final class HyperRectangleWriter {
public int scanlineStride() {
return scanlineStride;
}
+
+ /**
+ * Returns the indices of all banks to write with {@code
HyperRectangleWriter}.
+ * This is not necessarily the bank indices of all bands, because the
writer may be
+ * able to write all bands contiguously in a single call to a {@code
write(…)} method.
+ * This information is valid only after a {@code create(…)} method has
been invoked.
+ *
+ * @return indices of all banks to write with {@code
HyperRectangleWriter}.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public int[] bankIndices() {
+ return bankIndices;
+ }
}
/**