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 <martin.desruisse...@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);

Reply via email to