sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/02: Bug fix: when `ImageRenderer` is applied on a 3-dimensional image (or more), it shall compute an offset taking in account all supplemental dimensions.
Date Thu, 15 Jul 2021 16:05:24 GMT
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 57901c3d7ef368fc3f9ea3ed8e1733f1aa40d1a8
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Jul 15 18:01:13 2021 +0200

    Bug fix: when `ImageRenderer` is applied on a 3-dimensional image (or more), it shall
compute an offset taking in account all supplemental dimensions.
---
 .../apache/sis/coverage/grid/ImageRenderer.java    | 70 ++++++++++++++++------
 .../coverage/grid/BufferedGridCoverageTest.java    | 56 +++++++++++++++++
 2 files changed, 107 insertions(+), 19 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
index 403553d..24b1b0e 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
@@ -51,6 +51,11 @@ import org.apache.sis.util.ComparisonMode;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.math.Vector;
 
+import static java.lang.Math.addExact;
+import static java.lang.Math.subtractExact;
+import static java.lang.Math.multiplyExact;
+import static java.lang.Math.incrementExact;
+import static java.lang.Math.toIntExact;
 import static org.apache.sis.image.PlanarImage.GRID_GEOMETRY_KEY;
 
 
@@ -127,6 +132,12 @@ public class ImageRenderer {
     private GridGeometry imageGeometry;
 
     /**
+     * Offset to add to {@link #buffer} offset for reaching the first sample value for the
slice to render.
+     * This is zero for a two-dimensional image, but may be greater for cube having more
dimensions.
+     */
+    private final long offsetZ;
+
+    /**
      * Location of the first image pixel relative to the grid coverage extent. The (0,0)
offset means that the first pixel
      * in the {@code sliceExtent} (specified at construction time) is the first pixel in
the whole {@link GridCoverage}.
      *
@@ -275,12 +286,12 @@ public class ImageRenderer {
         bands = CollectionsExt.toArray(coverage.getSampleDimensions(), SampleDimension.class);
         geometry = coverage.getGridGeometry();
         final GridExtent source = geometry.getExtent();
+        final int dimension = source.getDimension();
         this.sliceExtent = sliceExtent;
         if (sliceExtent != null) {
-            final int dimension = sliceExtent.getDimension();
-            if (source.getDimension() != dimension) {
+            if (sliceExtent.getDimension() != dimension) {
                 throw new MismatchedDimensionException(Errors.format(
-                        Errors.Keys.MismatchedDimension_3, "sliceExtent", source.getDimension(),
dimension));
+                        Errors.Keys.MismatchedDimension_3, "sliceExtent", dimension, sliceExtent.getDimension()));
             }
         } else {
             sliceExtent = source;
@@ -300,26 +311,46 @@ public class ImageRenderer {
             final int d = (xmax < xmin) ? xd : yd;
             throw new DisjointExtentException(source, sliceExtent, d);
         }
-        width   = Math.incrementExact(Math.toIntExact(xmax - xmin));
-        height  = Math.incrementExact(Math.toIntExact(ymax - ymin));
-        imageX  = Math.toIntExact(Math.subtractExact(xmin, xreq));
-        imageY  = Math.toIntExact(Math.subtractExact(ymin, yreq));
-        offsetX = Math.subtractExact(xmin, xcov);
-        offsetY = Math.subtractExact(ymin, ycov);
+        width   = incrementExact(toIntExact(xmax - xmin));
+        height  = incrementExact(toIntExact(ymax - ymin));
+        imageX  = toIntExact(subtractExact(xmin, xreq));
+        imageY  = toIntExact(subtractExact(ymin, yreq));
+        offsetX = subtractExact(xmin, xcov);
+        offsetY = subtractExact(ymin, ycov);
+        /*
+         * If we are rendering a slice in a cube having more than 2 dimensions,
+         * there is a "global" offset to add for reachining the beginning of the slice.
+         */
+        if (dimension > GridCoverage2D.BIDIMENSIONAL) {
+            long base = 0, stride = 1;
+            for (int i=0; i<dimension; i++) {
+                if (i != xd && i != yd) {
+                    final long min = source.getLow(i);
+                    final long c = sliceExtent.getLow(i);
+                    if (c > min) {
+                        base = addExact(base, multiplyExact(stride, c - min));
+                    }
+                }
+                stride = multiplyExact(stride, source.getSize(i));
+            }
+            offsetZ = base;
+        } else {
+            offsetZ = 0;
+        }
         /*
          * At this point, the RenderedImage properties have been computed on the assumption
          * that the returned image will be a single tile. Now compute SampleModel properties.
          */
         long pixelStride  = 1;
         for (int i=0; i<xd; i++) {
-            pixelStride = Math.multiplyExact(pixelStride, source.getSize(i));
+            pixelStride = multiplyExact(pixelStride, source.getSize(i));
         }
         long scanlineStride = pixelStride;
         for (int i=xd; i<yd; i++) {
-            scanlineStride = Math.multiplyExact(scanlineStride, source.getSize(i));
+            scanlineStride = multiplyExact(scanlineStride, source.getSize(i));
         }
-        this.pixelStride    = Math.toIntExact(pixelStride);
-        this.scanlineStride = Math.toIntExact(scanlineStride);
+        this.pixelStride    = toIntExact(pixelStride);
+        this.scanlineStride = toIntExact(scanlineStride);
     }
 
     /**
@@ -617,23 +648,24 @@ public class ImageRenderer {
         if (bandOffsets == null) {
             strideFactor = isInterleaved ? getNumBands() : 1;
         }
-        final int ls = Math.multiplyExact(scanlineStride, strideFactor);    // Real scanline
stride.
-        final int ps = pixelStride * strideFactor;                          // Can not fail
if above operation did not fail.
+        final int ls = multiplyExact(scanlineStride, strideFactor);     // Real scanline
stride.
+        final int ps = pixelStride * strideFactor;                      // Can not fail if
above operation did not fail.
         /*
          * Number of data elements from the first element of the bank to the first sample
of the band.
          * This is usually 0 for all bands, unless the upper-left corner (minX, minY) is
not (0,0).
          */
         final int[] offsets = new int[getNumBands()];
-        Arrays.fill(offsets, Math.toIntExact(Math.addExact(
-                Math.multiplyExact(offsetX, ps),
-                Math.multiplyExact(offsetY, ls))));
+        Arrays.fill(offsets, toIntExact(addExact(addExact(
+                multiplyExact(offsetX, ps),
+                multiplyExact(offsetY, ls)),
+                              offsetZ)));
         /*
          * Add the offset specified by the user (if any), or the default offset. The default
is 0, 1, 2…
          * for interleaved sample model (all bands in one bank) and 0, 0, 0… for banded
sample model.
          */
         if (bandOffsets != null || isInterleaved) {
             for (int i=0; i<offsets.length; i++) {
-                offsets[i] = Math.addExact(offsets[i], (bandOffsets != null) ? bandOffsets[i]
: i);
+                offsets[i] = addExact(offsets[i], (bandOffsets != null) ? bandOffsets[i]
: i);
             }
         }
         final Point location = new Point(imageX, imageY);
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/BufferedGridCoverageTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/BufferedGridCoverageTest.java
index 7ed1c2d..949fbcb 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/BufferedGridCoverageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/BufferedGridCoverageTest.java
@@ -17,10 +17,20 @@
 package org.apache.sis.coverage.grid;
 
 import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferInt;
+import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
+import org.opengis.referencing.datum.PixelInCell;
 import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.util.iso.Names;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
 
 
 /**
@@ -61,4 +71,50 @@ public final strictfp class BufferedGridCoverageTest extends GridCoverage2DTest
         raster.setSample(1, 1, 0, -10);
         return coverage;
     }
+
+    /**
+     * Tests the creation of a three-dimensional coverage.
+     */
+    @Test
+    public void testMultidimensional() {
+        final int width  = 5;
+        final int height = 7;
+        final int nbTime = 3;
+        final GridExtent extent = new GridExtent(null, null, new long[] {width, height, nbTime},
false);
+        final GridGeometry domain = new GridGeometry(extent, PixelInCell.CELL_CENTER, MathTransforms.scale(2,
3, 5), null);
+        final SampleDimension band = new SampleDimension(Names.createLocalName(null, null,
"Data"), null, Collections.emptyList());
+        /*
+         * Fill slices with all values set to 10, 11 and 12 at time t=0, 1 and 2 respectively.
+         * All values are stored in a single bank.
+         */
+        final int sliceSize = width*height;
+        final int size = sliceSize*nbTime;
+        final int[] buffer = new int[size];
+        for (int t=0, i=0; t<nbTime; t++) {
+            Arrays.fill(buffer, i, i += sliceSize, t + 10);
+        }
+        final DataBufferInt data = new DataBufferInt(buffer, size);
+        final GridCoverage coverage = new BufferedGridCoverage(domain, Collections.singletonList(band),
data);
+        /*
+         * Verify a value in each temporal slice.
+         */
+        GridExtent query = new GridExtent(null, null, new long[] {width, height, 0}, true);
+        RenderedImage slice = coverage.render(query);
+        assertSampleEquals(10, slice);
+
+        query = new GridExtent(null, new long[] {0,0,1}, new long[] {width, height, 1}, true);
+        slice = coverage.render(query);
+        assertSampleEquals(11, slice);
+
+        query = new GridExtent(null, new long[] {0,0,2}, new long[] {width, height, 2}, true);
+        slice = coverage.render(query);
+        assertSampleEquals(12, slice);
+    }
+
+    /**
+     * Verifies that an arbitrary pixel taken from the given slice has a value equals to
the expected value.
+     */
+    private static void assertSampleEquals(final int expected, final RenderedImage slice)
{
+        assertEquals(expected, slice.getTile(0,0).getSample(1, 1, 0));
+    }
 }

Mime
View raw message