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 <[email protected]> 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 { /**
