This is an automated email from the ASF dual-hosted git repository. jsorel pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 84fd34f2b31e53821b45339b1dd2b7bb7f58efc3 Author: jsorel <[email protected]> AuthorDate: Thu Nov 14 17:26:57 2019 +0100 Image : add TranslatedRenderedImage --- .../sis/internal/coverage/GridCoverage2D.java | 16 +-- .../sis/internal/image/AbstractRenderedImage.java | 153 +++++++++++++++++++++ .../internal/image/TranslatedRenderedImage.java | 138 +++++++++++++++++++ .../image/TranslatedRenderedImageTest.java | 64 +++++++++ .../apache/sis/test/suite/FeatureTestSuite.java | 3 +- 5 files changed, 362 insertions(+), 12 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java index 5f8d76f..b2b9bf8 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/GridCoverage2D.java @@ -18,14 +18,13 @@ package org.apache.sis.internal.coverage; import java.awt.Rectangle; import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; import java.awt.image.RenderedImage; -import java.awt.image.WritableRaster; import java.util.Collection; import org.apache.sis.coverage.SampleDimension; import org.apache.sis.coverage.grid.GridCoverage; import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.coverage.grid.GridGeometry; +import org.apache.sis.internal.image.TranslatedRenderedImage; import org.apache.sis.referencing.CRS; import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.util.ArgumentChecks; @@ -132,17 +131,12 @@ public final class GridCoverage2D extends GridCoverage { final BufferedImage bi = (BufferedImage) image; return bi.getSubimage(subX, subY, subWidth, subHeight); } else { - //todo : current approach makes a copy of the datas, a better solution should be found - final WritableRaster raster = image.getTile(image.getMinTileX(), image.getMinTileY()).createCompatibleWritableRaster(subWidth, subHeight); - final WritableRaster derivate = raster.createWritableTranslatedChild(subX, subY); - image.copyData(derivate); - ColorModel cm = image.getColorModel(); - return new BufferedImage(cm, raster, cm.isAlphaPremultiplied(), null); + return new TranslatedRenderedImage(image, subX, subY); } } } - public double[] evaluate(DirectPosition position, double[] pixel) throws CannotEvaluateException { + public double[] evaluate(DirectPosition position, double[] buffer) throws CannotEvaluateException { try { position = toGridCoord(position); @@ -167,7 +161,7 @@ public final class GridCoverage2D extends GridCoverage { } if (getBounds().contains(x,y)) { - return image.getTile(XToTileX(x), YToTileY(y)).getPixel(x, y, pixel); + return image.getTile(XToTileX(x), YToTileY(y)).getPixel(x, y, buffer); } throw new PointOutsideCoverageException(""); } catch (FactoryException | TransformException ex) { @@ -177,6 +171,7 @@ public final class GridCoverage2D extends GridCoverage { /** * Converts the specified point to grid coordinate. + * * @param point point to transform to grid coordinate * @return point in grid coordinate * @throws org.opengis.util.FactoryException if creating transformation fails @@ -193,7 +188,6 @@ public final class GridCoverage2D extends GridCoverage { } else { trs = getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER); } - return trs.transform(point, null); } diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/image/AbstractRenderedImage.java b/core/sis-feature/src/main/java/org/apache/sis/internal/image/AbstractRenderedImage.java new file mode 100644 index 0000000..06786bd --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/image/AbstractRenderedImage.java @@ -0,0 +1,153 @@ +/* + * 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.image; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.util.Vector; + +/** + * + * @author Johann Sorel (Geomatys) + * @version 2.0 + * @since 2.0 + * @module + */ +public abstract class AbstractRenderedImage implements RenderedImage { + + @Override + public Vector<RenderedImage> getSources() { + return new Vector<>(); + } + + @Override + public Object getProperty(String name) { + return null; + } + + @Override + public String[] getPropertyNames() { + return new String[0]; + } + + @Override + public Raster getData() { + final SampleModel sm = getSampleModel().createCompatibleSampleModel(getWidth(), getHeight()); + final Raster rasterOut = Raster.createWritableRaster(sm, new Point(getMinX(), getMinY())); + + // Clear dataBuffer to 0 value for all bank + for (int s = 0; s < rasterOut.getDataBuffer().getSize(); s++) { + for (int b = 0; b < rasterOut.getDataBuffer().getNumBanks(); b++) { + rasterOut.getDataBuffer().setElem(b, s, 0); + } + } + + for (int y = 0; y < this.getNumYTiles(); y++) { + for (int x = 0; x < this.getNumYTiles(); x++) { + final Raster rasterIn = getTile(x, y); + rasterOut.getSampleModel() + .setDataElements( + x * this.getTileWidth(), + y * this.getTileHeight(), + this.getTileWidth(), + this.getTileHeight(), + rasterIn.getSampleModel().getDataElements(0, 0, this.getTileWidth(), this.getTileHeight(), null, rasterIn.getDataBuffer()), + rasterOut.getDataBuffer()); + } + } + + return rasterOut; + } + + @Override + public Raster getData(Rectangle rect) { + final SampleModel sm = getSampleModel().createCompatibleSampleModel(rect.width, rect.height); + final Raster rasterOut = Raster.createWritableRaster(sm, null); + + // Clear dataBuffer to 0 value for all bank + for (int s = 0; s < rasterOut.getDataBuffer().getSize(); s++) { + for (int b = 0; b < rasterOut.getDataBuffer().getNumBanks(); b++) { + rasterOut.getDataBuffer().setElem(b, s, 0); + } + } + + final Point upperLeftPosition = this.getPositionOf(rect.x, rect.y); + final Point lowerRightPosition = this.getPositionOf(rect.x+rect.width-1, rect.y+rect.height-1); + + for (int y = Math.max(upperLeftPosition.y,0); y < Math.min(lowerRightPosition.y + 1,this.getNumYTiles()); y++) { + for (int x = Math.max(upperLeftPosition.x,0); x < Math.min(lowerRightPosition.x + 1, this.getNumXTiles()); x++) { + final Rectangle tileRect = new Rectangle(x * this.getTileWidth(), y * this.getTileHeight(), this.getTileWidth(), this.getTileHeight()); + + final int minX, maxX, minY, maxY; + minX = clamp(rect.x, tileRect.x, tileRect.x + tileRect.width); + maxX = clamp(rect.x + rect.width, tileRect.x, tileRect.x + tileRect.width); + minY = clamp(rect.y, tileRect.y, tileRect.y + tileRect.height); + maxY = clamp(rect.y + rect.height, tileRect.y, tileRect.y + tileRect.height); + + final Rectangle rectIn = new Rectangle(minX, minY, maxX-minX, maxY-minY); + rectIn.translate(-tileRect.x, -tileRect.y); + final Rectangle rectOut = new Rectangle(minX, minY, maxX-minX, maxY-minY); + rectOut.translate(-rect.x, -rect.y); + + if (rectIn.width <= 0 || rectIn.height <= 0 || rectOut.width <= 0 || rectOut.height <= 0){ + continue; + } + + final Raster rasterIn = getTile(x, y); + + rasterOut.getSampleModel().setDataElements(rectOut.x, rectOut.y, rectOut.width, rectOut.height, + rasterIn.getSampleModel().getDataElements(rectIn.x, rectIn.y, rectIn.width, rectIn.height, null, rasterIn.getDataBuffer()), + rasterOut.getDataBuffer()); + } + } + + return rasterOut; + } + + + @Override + public WritableRaster copyData(WritableRaster raster) { + //TODO + throw new UnsupportedOperationException("Not supported yet."); + } + + /** + * Get the tile column and row position for a pixel. + * Return value can be out of the gridSize + */ + protected Point getPositionOf(int x, int y){ + final int posX = (int) (Math.floor(x / this.getTileWidth())); + final int posY = (int) (Math.floor(y / this.getTileHeight())); + return new Point(posX, posY); + } + + /** + * Clamps a value between min value and max value. + * + * @param val the value to clamp + * @param min the minimum value + * @param max the maximum value + * @return val clamped between min and max + */ + private static int clamp(int val, int min, int max) { + return Math.min(Math.max(val, min), max); + } +} diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/image/TranslatedRenderedImage.java b/core/sis-feature/src/main/java/org/apache/sis/internal/image/TranslatedRenderedImage.java new file mode 100644 index 0000000..2d08aec --- /dev/null +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/image/TranslatedRenderedImage.java @@ -0,0 +1,138 @@ +/* + * 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.image; + +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 2.0 + * @since 2.0 + * @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 image.getMinX() + offsetX; + } + + @Override + public int getMinY() { + return 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 image.getTileGridXOffset() + offsetX; + } + + @Override + public int getTileGridYOffset() { + return image.getTileGridYOffset() + offsetY; + } + + @Override + public Raster getTile(int tileX, int tileY) { + final Raster tile = image.getTile(tileX, tileY); + return tile.createTranslatedChild(offsetX, offsetY); + } + +} diff --git a/core/sis-feature/src/test/java/org/apache/sis/internal/image/TranslatedRenderedImageTest.java b/core/sis-feature/src/test/java/org/apache/sis/internal/image/TranslatedRenderedImageTest.java new file mode 100644 index 0000000..6c3d251 --- /dev/null +++ b/core/sis-feature/src/test/java/org/apache/sis/internal/image/TranslatedRenderedImageTest.java @@ -0,0 +1,64 @@ +/* + * 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.image; + +import java.awt.Point; +import java.awt.image.BufferedImage; +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; + +/** + * Tests the {@link TranslatedRenderedImage} implementation. + * + * @author Johann Sorel (Geomatys) + * @version 2.0 + * @since 2.0 + * @module + */ +public class TranslatedRenderedImageTest extends TestCase { + + @Test + public void iteratorTest() { + final BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_BYTE_GRAY); + image.getRaster().setSample(0, 0, 0, 1); + image.getRaster().setSample(1, 0, 0, 2); + image.getRaster().setSample(0, 1, 0, 3); + image.getRaster().setSample(1, 1, 0, 4); + + final TranslatedRenderedImage trs = new TranslatedRenderedImage(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()); + + } + +} 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 9bf8ffa..b735d58 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 @@ -86,7 +86,8 @@ import org.junit.runners.Suite; org.apache.sis.coverage.SampleRangeFormatTest.class, org.apache.sis.internal.coverage.ScaledColorSpaceTest.class, org.apache.sis.internal.coverage.BufferedGridCoverageTest.class, - org.apache.sis.internal.coverage.GridCoverage2DTest.class + org.apache.sis.internal.coverage.GridCoverage2DTest.class, + org.apache.sis.internal.image.TranslatedRenderedImageTest.class }) public final strictfp class FeatureTestSuite extends TestSuite { /**
