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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new e3a2732 Add integer overflow analysis in GridCoverage2D.render(…) method for BufferedImage
case. Detect when existing BufferedImage can be returned as-is instead of invoking `getSubImage`.
Add test case for https://issues.apache.org/jira/browse/SIS-495
e3a2732 is described below
commit e3a27327f3c9012df3cf05787a9ffd2e9c4b1ec1
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Jun 4 14:08:01 2020 +0200
Add integer overflow analysis in GridCoverage2D.render(…) method for BufferedImage case.
Detect when existing BufferedImage can be returned as-is instead of invoking `getSubImage`.
Add test case for https://issues.apache.org/jira/browse/SIS-495
---
.../apache/sis/coverage/grid/GridCoverage2D.java | 20 ++++++--
.../sis/coverage/grid/ResampledGridCoverage.java | 5 +-
.../sis/coverage/grid/GridCoverage2DTest.java | 57 ++++++++--------------
.../coverage/grid/ResampledGridCoverageTest.java | 33 ++++++++++++-
4 files changed, 69 insertions(+), 46 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 5da66ab..42e6834 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
@@ -578,13 +578,25 @@ public class GridCoverage2D extends GridCoverage {
* upper-left point is inside the image.
*/
if (data instanceof BufferedImage) {
- // BufferedImage origin should be (0, 0), but for consistency over image
API, we consider it variable
+ // BufferedImage origin should be (0, 0), but for consistency with image
API, we consider it variable.
final long ix = data.getMinX();
final long iy = data.getMinY();
if (xmin >= ix && ymin >= iy) {
- return ((BufferedImage) data).getSubimage(toIntExact(xmin), toIntExact(ymin),
- toIntExact(min(xmax + 1, ix + data.getWidth() ) - xmin),
- toIntExact(min(ymax + 1, iy + data.getHeight()) - ymin));
+ final int width = data.getWidth();
+ final int height = data.getHeight();
+ /*
+ * Result of `ix + width` requires at most 33 bits for any `ix` value
(same for y axis).
+ * Subtractions by `xmin` and `ymin` never overflow if `ix` and `iy`
are zero or positive,
+ * which should always be the case with BufferedImage. The +1 is applied
after subtraction
+ * instead than on `xmax` and `ymax` for avoiding overflow, since the
result of `min(…)`
+ * uses at most 33 bits.
+ */
+ final int nx = toIntExact(min(xmax, ix + width - 1) - xmin + 1);
+ final int ny = toIntExact(min(ymax, iy + height - 1) - ymin + 1);
+ if ((ix | iy) == 0 && nx == width && ny == height) {
+ return data;
+ }
+ return ((BufferedImage) data).getSubimage(toIntExact(xmin), toIntExact(ymin),
nx, ny);
}
}
/*
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
index 9f8474d..898b2d8 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ResampledGridCoverage.java
@@ -456,9 +456,8 @@ final class ResampledGridCoverage extends GridCoverage {
bounds = new Rectangle(Math.toIntExact(sliceExtent.getSize(resampledDimensions[0])),
Math.toIntExact(sliceExtent.getSize(resampledDimensions[1])));
/*
- * The transform needs to be two-dimensional. The `toSourceCenter` transform
does not met that condition,
- * otherwise `specialize(…)` would have replaced this ResampledGridCoverage
by a GridCoverage2D instance.
- * Try to extract a two-dimensional part operating only on the slice dimensions
having an extent larger
+ * The transform inputs must be two-dimensional (outputs may be more flexible).
If this is not the case,
+ * try to extract a two-dimensional part operating only on the slice dimensions
having an extent larger
* than one cell. The choice of dimensions may vary between different calls to
this `render(…)` method,
* depending on `sliceExtent` value.
*/
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
index b2ef925..ce621f9 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
@@ -16,9 +16,9 @@
*/
package org.apache.sis.coverage.grid;
-import java.awt.image.RenderedImage;
import java.util.List;
import java.util.Collections;
+import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
@@ -38,7 +38,7 @@ import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.test.TestCase;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.apache.sis.test.FeatureAssert.*;
/**
@@ -201,43 +201,26 @@ public strictfp class GridCoverage2DTest extends TestCase {
@Test
public void render_of_subextent() {
final GridCoverage coverage = createTestCoverage();
- final Raster completeRendering = coverage.render(null).getTile(0, 0);
-
+ /*
+ * Row extraction:
+ * - Expected size (2,1) is verified by `assertPixelsEqual(…)`.
+ * - Bounds of expected values is Rectangle(translation, size).
+ * - Pixel source(0, 1) → output(0, 0)
+ * - Pixel source(1, 1) → output(1, 0)
+ */
final GridExtent singleRow = new GridExtent(2, 1).translate(0, 1);
- RenderedImage subset = coverage.render(singleRow);
- assertEquals("Rendering width", 2, subset.getWidth());
- assertEquals("Rendering height", 1, subset.getHeight());
- Raster subsetTile = subset.getTile(0, 0);
- assertArrayEquals(
- "Row extraction, pixel source(0, 1) -> output(0, 0)",
- completeRendering.getPixel(0, 1, (double[])null),
- subsetTile.getPixel(0, 0, (double[])null),
- 1e-1
- );
- assertArrayEquals(
- "Row extraction, pixel source(1, 1) -> output(1, 0)",
- completeRendering.getPixel(1, 1, (double[])null),
- subsetTile.getPixel(1, 0, (double[])null),
- 1e-1
- );
-
+ assertPixelsEqual(coverage.render(null), new Rectangle(0, 1, 2, 1), // Expected
values.
+ coverage.render(singleRow), null); // Actual
values to test.
+ /*
+ * Column extraction:
+ * - Expected size (1,2) is verified by `assertPixelsEqual(…)`.
+ * - Bounds of expected values is Rectangle(translation, size).
+ * - Pixel source(1, 0) → output(0, 0)
+ * - Pixel source(1, 1) → output(0, 1)
+ */
final GridExtent singleCol = new GridExtent(1, 2).translate(1, 0);
- subset = coverage.render(singleCol);
- assertEquals("Rendering width", 1, subset.getWidth());
- assertEquals("Rendering height", 2, subset.getHeight());
- subsetTile = subset.getTile(0, 0);
- assertArrayEquals(
- "Column extraction, pixel source(1, 0) -> output(0, 0)",
- completeRendering.getPixel(1, 0, (double[])null),
- subsetTile.getPixel(0, 0, (double[])null),
- 1e-1
- );
- assertArrayEquals(
- "Column extraction, pixel source(1, 1) -> output(0, 1)",
- completeRendering.getPixel(1, 1, (double[])null),
- subsetTile.getPixel(0, 1, (double[])null),
- 1e-1
- );
+ assertPixelsEqual(coverage.render(null), new Rectangle(1, 0, 1, 2), // Expected
values.
+ coverage.render(singleCol), null); // Actual
values to test.
}
/**
diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
index c4a3156..53a78e2 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/ResampledGridCoverageTest.java
@@ -36,6 +36,7 @@ import org.apache.sis.image.Interpolation;
import org.apache.sis.image.TiledImageMock;
import org.apache.sis.internal.coverage.j2d.TiledImage;
import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.crs.HardCodedCRS;
import org.apache.sis.referencing.operation.HardCodedConversions;
@@ -61,6 +62,7 @@ import static org.apache.sis.test.FeatureAssert.*;
*
* @author Martin Desruisseaux (Geomatys)
* @author Alexis Manin (Geomatys)
+ * @author Johann Sorel (Geomatys)
* @version 1.1
* @since 1.1
* @module
@@ -520,7 +522,7 @@ public final strictfp class ResampledGridCoverageTest extends TestCase
{
* @throws TransformException if some coordinates can not be transformed to the target
grid geometry.
*/
@Test
- public void crs3D_to_crs4D() throws TransformException {
+ public void testDimensionalityIncrease() throws TransformException {
final GridCoverage source3D = createCoverageND(false);
final GridGeometry target4D = createGridGeometryND(HardCodedCRS.WGS84_4D, 0, 1, 2,
3, false);
final GridCoverage result = resample(source3D, target4D);
@@ -534,7 +536,7 @@ public final strictfp class ResampledGridCoverageTest extends TestCase
{
* @throws TransformException if some coordinates can not be transformed to the target
grid geometry.
*/
@Test
- public void crs4D_to_crs3D() throws TransformException {
+ public void testDimensionalityReduction() throws TransformException {
final GridGeometry target3D = createGridGeometryND(HardCodedCRS.WGS84_3D, 0, 1, 2,
3, false);
final GridCoverage source4D = createCoverageND(true);
final GridCoverage result = resample(source4D, target3D);
@@ -543,6 +545,33 @@ public final strictfp class ResampledGridCoverageTest extends TestCase
{
}
/**
+ * Tests resampling with a target domain larger than the source domain.
+ * Pixel outside the source domain shall be set to fill value, which is 0.
+ *
+ * @throws TransformException if some coordinates can not be transformed to the target
grid geometry.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-495">SIS-495</a>
+ */
+ @Test
+ public void testDomainIncrease() throws TransformException {
+ final int size = 2;
+ final CoordinateReferenceSystem crs = HardCodedCRS.WGS84;
+ final BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_GRAY);
+ image.getRaster().setDataElements(0, 0, size, size, new byte[] {10, 12, 16, 14});
+ final AffineTransform2D gridToCRS = new AffineTransform2D(1, 0, 0, -1, 0, 0);
+ final GridGeometry sourceGrid = new GridGeometry(null, CELL_CENTER, gridToCRS, crs);
+ final GridGeometry targetGrid = new GridGeometry(new GridExtent(4, 4), CELL_CENTER,
gridToCRS, crs);
+ final GridCoverage source = new GridCoverage2D(sourceGrid, null, image);
+ final GridCoverage target = resample(source, targetGrid);
+ assertValuesEqual(target.render(null).getData(), 0, new int[][] {
+ {10, 12, 0, 0},
+ {16, 14, 0, 0},
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0}
+ });
+ }
+
+ /**
* Returns an image with only the queries part of the given image.
* This is an helper tools which can be invoked during debugging
* session in IDE capable to display images.
|