You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/11/05 17:09:02 UTC
(sis) 02/02: Fix the case when raster data of a tile to write does not start at the beginning of the backing array.
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
commit c835c4786f8fbaed7e80009bf044d86ece3dc97c
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Nov 5 15:10:41 2023 +0100
Fix the case when raster data of a tile to write does not start at the beginning of the backing array.
---
.../sis/storage/geotiff/writer/TileMatrix.java | 27 +++++-----
.../apache/sis/io/stream/HyperRectangleWriter.java | 57 +++++++++++++++++-----
2 files changed, 61 insertions(+), 23 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 6d45646d22..4e750053b7 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
@@ -20,7 +20,6 @@ import java.util.Arrays;
import java.util.Objects;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.DataBuffer;
@@ -211,22 +210,23 @@ public final class TileMatrix {
tileX += minTileX;
tileY += minTileY;
final Raster tile = image.getTile(tileX, tileY);
+ /*
+ * Creates the `rect` object which will be used for writing a subset of the raster data.
+ * This object depends not only on the sample model, but also on the raster coordinates.
+ * However the compressor depends on properties that change only with the sample model,
+ * so `compressor` is usually created only once and shared by all tiles.
+ */
+ final var builder = new HyperRectangleWriter.Builder();
+ rect = builder.create(tile);
+ if (rect == null) {
+ throw new UnsupportedOperationException(); // TODO: reformat using a recycled Raster.
+ }
+ bankIndices = builder.bankIndices();
if (!Objects.equals(sampleModel, sampleModel = tile.getSampleModel())) {
if (compressor != null) {
compressor.close();
compressor = null;
}
- /*
- * Creates the `rect` object which will be used for writing a subset of the raster data.
- * This object depends on the sample model, but is usually created only once because all
- * tiles should share the same sample model.
- */
- final var builder = new HyperRectangleWriter.Builder().region(new Rectangle(tileWidth, tileHeight));
- rect = builder.create(sampleModel);
- if (rect == null) {
- throw new UnsupportedOperationException(); // TODO: reformat using a recycled Raster.
- }
- bankIndices = builder.bankIndices();
/*
* Creates the data output to use for writing compressed data. The compressor will need an
* intermediate buffer, unless the `direct` flag is true, in which case we will bypass the
@@ -258,6 +258,9 @@ public final class TileMatrix {
assert predictor == Predictor.NONE : predictor; // Assumption documented in `Compression` class.
}
}
+ /*
+ * Compress and write sample values.
+ */
final DataBuffer buffer = tile.getDataBuffer();
final int[] bufferOffsets = buffer.getOffsets();
for (int j=0; j<numPlanes; j++) {
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 4effdeeded..cec18bd0b2 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
@@ -19,6 +19,7 @@ package org.apache.sis.io.stream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.awt.Rectangle;
+import java.awt.image.Raster;
import java.awt.image.DataBuffer;
import java.awt.image.SampleModel;
import java.awt.image.ComponentSampleModel;
@@ -92,7 +93,9 @@ public final class HyperRectangleWriter {
}
/**
- * A builder for {@code HyperRectangleWriter} created from a {@code SampleModel}.
+ * A builder for {@code HyperRectangleWriter} created from a {@code Raster} or a {@code SampleModel}.
+ * Each builder shall be used only once. For creating more {@code HyperRectangleWriter} instances,
+ * new builders shall be created.
*
* @author Martin Desruisseaux (Geomatys)
*/
@@ -130,9 +133,21 @@ public final class HyperRectangleWriter {
/**
* Subregion to write, or {@code null} for writing the whole raster.
+ *
+ * @see #region(Rectangle)
*/
private Rectangle region;
+ /**
+ * The translation from the coordinate system of the {@link SampleModel} to that of the {@link Raster}.
+ * To convert a pixel's coordinate from the {@link Raster} coordinate system to the {@link SampleModel}
+ * coordinate system, this value must be subtracted.
+ *
+ * @see Raster#getSampleModelTranslateX()
+ * @see Raster#getSampleModelTranslateY()
+ */
+ private int sampleModelTranslateX, sampleModelTranslateY;
+
/**
* Creates a new builder.
*/
@@ -141,6 +156,9 @@ public final class HyperRectangleWriter {
/**
* Specifies the region to write.
+ * The rectangle is in the coordinate system of the object specified to the {@code create(…)} method:
+ * {@link Raster} coordinates if the {@link #create(Raster)} method is invoked, or
+ * {@link SampleModel} coordinates if the {@link #create(SampleModel)} method is invoked.
* This method retains the given rectangle by reference, it is not copied.
*
* @param aoi the region to write, or {@code null} for writing the whole raster.
@@ -158,15 +176,17 @@ public final class HyperRectangleWriter {
private HyperRectangleWriter create(final SampleModel sm, final int subX) {
final int[] subsampling = {subX, 1};
final long[] sourceSize = {scanlineStride, sm.getHeight()};
- final long[] regionLower = new long[2];
- final long[] regionUpper = new long[2];
- if (region != null) {
- regionUpper[0] = (regionLower[0] = region.x) + region.width;
- regionUpper[1] = (regionLower[1] = region.y) + region.height;
- } else {
- regionUpper[0] = sm.getWidth();
- regionUpper[1] = sm.getHeight();
+ if (region == null) {
+ region = new Rectangle(sm.getWidth(), sm.getHeight());
}
+ final long[] regionLower = new long[] {
+ region.x - (long) sampleModelTranslateX,
+ region.y - (long) sampleModelTranslateY
+ };
+ final long[] regionUpper = new long[] {
+ regionLower[0] + region.width,
+ regionLower[1] + region.height
+ };
regionLower[0] = Math.multiplyExact(regionLower[0], pixelStride);
regionUpper[0] = Math.multiplyExact(regionUpper[0], pixelStride);
var subset = new Region(sourceSize, regionLower, regionUpper, subsampling);
@@ -277,6 +297,22 @@ public final class HyperRectangleWriter {
return null;
}
+ /**
+ * Creates a new writer for data of the specified raster.
+ * 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 raster the rasters to write.
+ * @return writer, or {@code null} if the given raster uses an unsupported sample model.
+ */
+ public HyperRectangleWriter create(final Raster raster) {
+ final Rectangle bounds = raster.getBounds();
+ region = (region != null) ? bounds.intersection(region) : bounds;
+ sampleModelTranslateX = raster.getSampleModelTranslateX();
+ sampleModelTranslateY = raster.getSampleModelTranslateY();
+ return create(raster.getSampleModel());
+ }
+
/**
* {@return the total number of elements contained in the hyper-rectangle}.
* The number of bytes to write will be this length multiplied by element size.
@@ -383,8 +419,7 @@ public final class HyperRectangleWriter {
offset = startAt(offset);
final int[] count = count();
if (direct) {
- final ByteBuffer buffer = ByteBuffer.wrap(data, offset, contiguousDataLength);
- offset = 0;
+ final ByteBuffer buffer = ByteBuffer.wrap(data);
output.flush();
do {
buffer.limit(offset + contiguousDataLength).position(offset);