sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 03/03: Modify the semantic of TranslatedRenderedImage for storing a location instead than a translation. Rename that class as RelocatedImage to reflect that semantic change.
Date Thu, 26 Dec 2019 17:36:05 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 eb10ec8a6f1171a7edf2fbc90d6236740f10197a
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Dec 26 18:35:17 2019 +0100

    Modify the semantic of TranslatedRenderedImage for storing a location instead than a translation.
    Rename that class as RelocatedImage to reflect that semantic change.
---
 .../apache/sis/coverage/grid/GridCoverage2D.java   |  16 +-
 .../coverage/j2d/AbstractRenderedImage.java        |   4 +-
 .../sis/internal/coverage/j2d/RelocatedImage.java  | 248 +++++++++++++++++++++
 .../coverage/j2d/TranslatedRenderedImage.java      | 140 ------------
 ...deredImageTest.java => RelocatedImageTest.java} |  45 ++--
 .../apache/sis/test/suite/FeatureTestSuite.java    |   2 +-
 6 files changed, 285 insertions(+), 170 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
index 0eac980..fa8473d 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage2D.java
@@ -38,7 +38,7 @@ import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.internal.coverage.j2d.ImageUtilities;
 import org.apache.sis.internal.coverage.j2d.ConvertedGridCoverage;
-import org.apache.sis.internal.coverage.j2d.TranslatedRenderedImage;
+import org.apache.sis.internal.coverage.j2d.RelocatedImage;
 import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.util.collection.TableColumn;
@@ -498,11 +498,11 @@ public class GridCoverage2D extends GridCoverage {
             /*
              * The following code clamp values to 32 bits integers without throwing ArithmeticException
              * because any value that overflow 32 bits are sure to be outside the RenderedImage
bounds.
-             * In such case, clamping changes nothing to the result.
+             * In such case, clamping should not change the result.
              */
             final Rectangle request = bounds.intersection(new Rectangle(
-                    (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, x)),
-                    (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, y)),
+                    (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, x)),
+                    (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, y)),
                     (int) Math.min(Integer.MAX_VALUE, sliceExtent.getSize(xDimension)),
                     (int) Math.min(Integer.MAX_VALUE, sliceExtent.getSize(yDimension))));
             /*
@@ -513,7 +513,13 @@ public class GridCoverage2D extends GridCoverage {
                 final BufferedImage image = (BufferedImage) data;
                 return image.getSubimage(request.x, request.y, request.width, request.height);
             }
-            return new TranslatedRenderedImage(data,
+            /*
+             * Return the backing image almost as-is (with potentially just a wrapper) for
avoiding to copy data.
+             * As per method contract, we shall set the (x,y) location to the difference
between requested region
+             * and actual region of the returned image. For example if the user requested
an image starting at
+             * (5,5) but the image to return starts at (1,1), then we need to set its location
to (-4,-4).
+             */
+            return RelocatedImage.moveTo(data,
                     Math.toIntExact(Math.subtractExact(bounds.x, x)),
                     Math.toIntExact(Math.subtractExact(bounds.y, y)));
         } catch (ArithmeticException e) {
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/AbstractRenderedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/AbstractRenderedImage.java
index 55ae9bc..03c1000 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/AbstractRenderedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/AbstractRenderedImage.java
@@ -179,7 +179,7 @@ public abstract class AbstractRenderedImage implements RenderedImage {
 
     /**
      * Returns a copy of this image as one large tile.
-     * The returned raster will not be updated if the image is changed.
+     * The returned raster will not be updated if this image is changed.
      *
      * @return a copy of this image as one large tile.
      */
@@ -193,7 +193,7 @@ public abstract class AbstractRenderedImage implements RenderedImage {
 
     /**
      * Returns a copy of an arbitrary region of this image.
-     * The returned raster will not be updated if the image is changed.
+     * The returned raster will not be updated if this image is changed.
      *
      * @param  aoi  the region of this image to copy.
      * @return a copy of this image in the given area of interest.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RelocatedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RelocatedImage.java
new file mode 100644
index 0000000..0ffd902
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/RelocatedImage.java
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.coverage.j2d;
+
+import java.awt.Rectangle;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.awt.image.ColorModel;
+import java.awt.image.WritableRaster;
+import java.util.Vector;
+
+
+/**
+ * A view over another image with the origin relocated to a new position.
+ * If the image is tiled, this wrapper may also reduce the number of tiles.
+ * This wrapper does not change image size otherwise than by an integer amount of tiles.
+ *
+ * @author  Johann Sorel (Geomatys)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public final class RelocatedImage extends AbstractRenderedImage {
+    /**
+     * The image to translate.
+     */
+    private final RenderedImage image;
+
+    /**
+     * Coordinate of the upper-left pixel.
+     * Computed at construction time in order to detect integer overflows early.
+     */
+    private final int minX, minY;
+
+    /**
+     * Creates a new image with the same data then the given image but located at the given
coordinates.
+     *
+     * @param  image  the image to move.
+     * @param  minX   <var>x</var> coordinate of upper-left pixel.
+     * @param  minY   <var>y</var> coordinate of upper-left pixel.
+     */
+    private RelocatedImage(final RenderedImage image, final int minX, final int minY) {
+        this.image = image;
+        this.minX  = minX;
+        this.minY  = minY;
+    }
+
+    /**
+     * Returns an image with the same data then the given image but located at the given
coordinates.
+     * This method may return the given image unchanged if it is already located at the given
position.
+     *
+     * @param  image  the image to move.
+     * @param  minX   <var>x</var> coordinate of upper-left pixel.
+     * @param  minY   <var>y</var> coordinate of upper-left pixel.
+     * @return image with the same data but at the given coordinates.
+     */
+    public static RenderedImage moveTo(RenderedImage image, final int minX, final int minY)
{
+        if (minX == image.getMinX() && minY == image.getMinY()) {
+            return image;
+        }
+        if (image instanceof RelocatedImage) {
+            image = (RelocatedImage) image;
+            if (minX == image.getMinX() && minY == image.getMinY()) {
+                return image;
+            }
+        }
+        return new RelocatedImage(image, minX, minY);
+    }
+
+    /**
+     * Returns the immediate source of this image.
+     */
+    @Override
+    @SuppressWarnings("UseOfObsoleteCollectionType")
+    public Vector<RenderedImage> getSources() {
+        final Vector<RenderedImage> sources = new Vector<>(1);
+        sources.add(image);
+        return sources;
+    }
+
+    /**
+     * Delegates to the wrapped image with no change.
+     */
+    @Override public Object      getProperty(String name) {return image.getProperty(name);}
+    @Override public String[]    getPropertyNames()       {return image.getPropertyNames();}
+    @Override public ColorModel  getColorModel()          {return image.getColorModel();}
+    @Override public SampleModel getSampleModel()         {return image.getSampleModel();}
+    @Override public int         getWidth()               {return image.getWidth();}
+    @Override public int         getHeight()              {return image.getHeight();}
+    @Override public int         getNumXTiles()           {return image.getNumXTiles();}
+    @Override public int         getNumYTiles()           {return image.getNumYTiles();}
+    @Override public int         getMinTileX()            {return image.getMinTileX();}
+    @Override public int         getMinTileY()            {return image.getMinTileY();}
+    @Override public int         getTileWidth()           {return image.getTileWidth();}
+    @Override public int         getTileHeight()          {return image.getTileHeight();}
+
+    /**
+     * Returns the minimum <var>x</var> coordinate (inclusive) specified at construction
time.
+     * This coordinate may differ from the coordinate of the wrapped image.
+     */
+    @Override
+    public int getMinX() {
+        return minX;
+    }
+
+    /**
+     * Returns the minimum <var>y</var> coordinate (inclusive) specified at construction
time.
+     * This coordinate may differ from the coordinate of the wrapped image.
+     */
+    @Override
+    public int getMinY() {
+        return minY;
+    }
+
+    /**
+     * Returns the <var>x</var> coordinate of the upper-left pixel of tile (0,
0).
+     * That tile (0, 0) may not actually exist.
+     */
+    @Override
+    public int getTileGridXOffset() {
+        return offsetX(image.getTileGridXOffset());
+    }
+
+    /**
+     * Returns the <var>y</var> coordinate of the upper-left pixel of tile (0,
0).
+     * That tile (0, 0) may not actually exist.
+     */
+    @Override
+    public int getTileGridYOffset() {
+        return offsetY(image.getTileGridYOffset());
+    }
+
+    /**
+     * Converts a column index from the coordinate system of the wrapped image
+     * to the coordinate system of this image.
+     *
+     * @param  x  a column index of the wrapped image.
+     * @return the corresponding column index in this image.
+     */
+    private int offsetX(final int x) {
+        return Math.toIntExact(x + (minX - (long) image.getMinX()));
+    }
+
+    /**
+     * Converts a row index from the coordinate system of the wrapped image
+     * to the coordinate system of this image.
+     *
+     * @param  y  a row index of the wrapped image.
+     * @return the corresponding row index in this image.
+     */
+    private int offsetY(final int y) {
+        return Math.toIntExact(y + (minY - (long) image.getMinY()));
+    }
+
+    /**
+     * Returns a raster with the same data than the given raster but with coordinates translated
+     * from the coordinate system of the wrapped image to the coordinate system of this image.
+     * The returned raster will have the given raster as its parent.
+     */
+    private Raster offset(final Raster data) {
+        return data.createTranslatedChild(offsetX(data.getMinX()), offsetY(data.getMinY()));
+    }
+
+    /**
+     * Returns the tile at the given tile indices (not to be confused with pixel indices).
+     *
+     * @param  tileX  the <var>x</var> index of the requested tile in the tile
array.
+     * @param  tileY  the <var>y</var> index of the requested tile in the tile
array.
+     * @return the tile specified by the specified indices.
+     */
+    @Override
+    public Raster getTile(final int tileX, final int tileY) {
+        return offset(image.getTile(tileX, tileY));
+    }
+
+    /**
+     * Returns a copy of this image as one large tile.
+     * The returned raster will not be updated if this image is changed.
+     *
+     * @return a copy of this image as one large tile.
+     */
+    @Override
+    public Raster getData() {
+        return offset(image.getData());
+    }
+
+    /**
+     * Returns a copy of an arbitrary region of this image.
+     * The returned raster will not be updated if this image is changed.
+     *
+     * @param  aoi  the region of this image to copy.
+     * @return a copy of this image in the given area of interest.
+     */
+    @Override
+    public Raster getData(Rectangle aoi) {
+        final long offsetX = minX - (long) image.getMinX();
+        final long offsetY = minY - (long) image.getMinY();
+        aoi = new Rectangle(aoi);
+        aoi.x = Math.toIntExact(aoi.x - offsetX);       // Inverse of offsetX(int).
+        aoi.y = Math.toIntExact(aoi.y - offsetY);
+        final Raster data = image.getData(aoi);
+        return data.createTranslatedChild(Math.toIntExact(data.getMinX() + offsetX),
+                                          Math.toIntExact(data.getMinY() + offsetY));
+    }
+
+    /**
+     * Copies an arbitrary rectangular region of this image to the supplied writable raster.
+     * The region to be copied is determined from the bounds of the supplied raster.
+     *
+     * @param  raster  the raster to hold a copy of this image, or {@code null}.
+     * @return the given raster if it was not-null, or a new raster otherwise.
+     */
+    @Override
+    public WritableRaster copyData(final WritableRaster raster) {
+        final long offsetX = minX - (long) image.getMinX();
+        final long offsetY = minY - (long) image.getMinY();
+        WritableRaster data;
+        if (raster != null) {
+            data = raster.createWritableTranslatedChild(
+                    Math.toIntExact(raster.getMinX() - offsetX),
+                    Math.toIntExact(raster.getMinY() - offsetY));
+        } else {
+            data = null;
+        }
+        data = image.copyData(data);
+        if (data.getWritableParent() == raster) {
+            return raster;
+        }
+        return data.createWritableTranslatedChild(Math.toIntExact(data.getMinX() + offsetX),
+                                                  Math.toIntExact(data.getMinY() + offsetY));
+    }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TranslatedRenderedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TranslatedRenderedImage.java
deleted file mode 100644
index fc6d183..0000000
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TranslatedRenderedImage.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.sis.internal.coverage.j2d;
-
-import java.awt.image.ColorModel;
-import java.awt.image.Raster;
-import java.awt.image.RenderedImage;
-import java.awt.image.SampleModel;
-import java.util.Vector;
-
-
-/**
- * Translated RenderedImage implementation.
- *
- * @author  Johann Sorel (Geomatys)
- * @version 1.1
- * @since   1.1
- * @module
- */
-public final class TranslatedRenderedImage extends AbstractRenderedImage {
-
-    private final RenderedImage image;
-    private final int offsetX;
-    private final int offsetY;
-
-    public TranslatedRenderedImage(RenderedImage image, int offsetX, int offsetY) {
-        this.image = image;
-        this.offsetX = offsetX;
-        this.offsetY = offsetY;
-    }
-
-    @Override
-    public Vector<RenderedImage> getSources() {
-        final Vector<RenderedImage> sources = new Vector<>();
-        sources.add(image);
-        return sources;
-    }
-
-    @Override
-    public Object getProperty(String name) {
-        return image.getProperty(name);
-    }
-
-    @Override
-    public String[] getPropertyNames() {
-        return image.getPropertyNames();
-    }
-
-    @Override
-    public ColorModel getColorModel() {
-        return image.getColorModel();
-    }
-
-    @Override
-    public SampleModel getSampleModel() {
-        return image.getSampleModel();
-    }
-
-    @Override
-    public int getWidth() {
-        return image.getWidth();
-    }
-
-    @Override
-    public int getHeight() {
-        return image.getHeight();
-    }
-
-    @Override
-    public int getMinX() {
-        return Math.addExact(image.getMinX(), offsetX);
-    }
-
-    @Override
-    public int getMinY() {
-        return Math.addExact(image.getMinY(), offsetY);
-    }
-
-    @Override
-    public int getNumXTiles() {
-        return image.getNumXTiles();
-    }
-
-    @Override
-    public int getNumYTiles() {
-        return image.getNumYTiles();
-    }
-
-    @Override
-    public int getMinTileX() {
-        return image.getMinTileX();
-    }
-
-    @Override
-    public int getMinTileY() {
-        return image.getMinTileY();
-    }
-
-    @Override
-    public int getTileWidth() {
-        return image.getTileWidth();
-    }
-
-    @Override
-    public int getTileHeight() {
-        return image.getTileHeight();
-    }
-
-    @Override
-    public int getTileGridXOffset() {
-        return Math.addExact(image.getTileGridXOffset(), offsetX);
-    }
-
-    @Override
-    public int getTileGridYOffset() {
-        return Math.addExact(image.getTileGridYOffset(), offsetY);
-    }
-
-    @Override
-    public Raster getTile(int tileX, int tileY) {
-        final Raster tile = image.getTile(tileX, tileY);
-        return tile.createTranslatedChild(
-                Math.addExact(tile.getMinX(), offsetX),
-                Math.addExact(tile.getMinY(), offsetY));
-    }
-}
diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/TranslatedRenderedImageTest.java
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/RelocatedImageTest.java
similarity index 62%
rename from core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/TranslatedRenderedImageTest.java
rename to core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/RelocatedImageTest.java
index 65281f7..f35009f 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/TranslatedRenderedImageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/RelocatedImageTest.java
@@ -18,21 +18,24 @@ package org.apache.sis.internal.coverage.j2d;
 
 import java.awt.Point;
 import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import org.opengis.coverage.grid.SequenceType;
 import org.apache.sis.image.PixelIterator;
 import org.apache.sis.test.TestCase;
-import org.junit.Assert;
 import org.junit.Test;
-import org.opengis.coverage.grid.SequenceType;
+
+import static org.junit.Assert.*;
+
 
 /**
- * Tests the {@link TranslatedRenderedImage} implementation.
+ * Tests the {@link RelocatedImage} implementation.
  *
  * @author  Johann Sorel (Geomatys)
- * @version 2.0
- * @since   2.0
+ * @version 1.1
+ * @since   1.1
  * @module
  */
-public class TranslatedRenderedImageTest extends TestCase {
+public final strictfp class RelocatedImageTest extends TestCase {
 
     @Test
     public void iteratorTest() {
@@ -42,23 +45,21 @@ public class TranslatedRenderedImageTest extends TestCase {
         image.getRaster().setSample(0, 1, 0, 3);
         image.getRaster().setSample(1, 1, 0, 4);
 
-        final TranslatedRenderedImage trs = new TranslatedRenderedImage(image, -10, -20);
+        final RenderedImage trs = RelocatedImage.moveTo(image, -10, -20);
 
         final PixelIterator ite = new PixelIterator.Builder().setIteratorOrder(SequenceType.LINEAR).create(trs);
-        Assert.assertTrue(ite.next());
-        Assert.assertEquals(new Point(-10, -20), ite.getPosition());
-        Assert.assertEquals(1, ite.getSample(0));
-        Assert.assertTrue(ite.next());
-        Assert.assertEquals(new Point(-9, -20), ite.getPosition());
-        Assert.assertEquals(2, ite.getSample(0));
-        Assert.assertTrue(ite.next());
-        Assert.assertEquals(new Point(-10, -19), ite.getPosition());
-        Assert.assertEquals(3, ite.getSample(0));
-        Assert.assertTrue(ite.next());
-        Assert.assertEquals(new Point(-9, -19), ite.getPosition());
-        Assert.assertEquals(4, ite.getSample(0));
-        Assert.assertFalse(ite.next());
-
+        assertTrue(ite.next());
+        assertEquals(new Point(-10, -20), ite.getPosition());
+        assertEquals(1, ite.getSample(0));
+        assertTrue(ite.next());
+        assertEquals(new Point(-9, -20), ite.getPosition());
+        assertEquals(2, ite.getSample(0));
+        assertTrue(ite.next());
+        assertEquals(new Point(-10, -19), ite.getPosition());
+        assertEquals(3, ite.getSample(0));
+        assertTrue(ite.next());
+        assertEquals(new Point(-9, -19), ite.getPosition());
+        assertEquals(4, ite.getSample(0));
+        assertFalse(ite.next());
     }
-
 }
diff --git a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
index 4731240..66270c9 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/test/suite/FeatureTestSuite.java
@@ -90,7 +90,7 @@ import org.junit.runners.Suite;
     org.apache.sis.internal.coverage.j2d.ScaledColorSpaceTest.class,
     org.apache.sis.internal.coverage.j2d.AbstractRenderedImageTest.class,
     org.apache.sis.internal.coverage.j2d.BufferedGridCoverageTest.class,
-    org.apache.sis.internal.coverage.j2d.TranslatedRenderedImageTest.class
+    org.apache.sis.internal.coverage.j2d.RelocatedImageTest.class
 })
 public final strictfp class FeatureTestSuite extends TestSuite {
     /**


Mime
View raw message