This is an automated email from the ASF dual-hosted git repository.

amanin pushed a commit to branch fix/mask-image
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 7b1ee54c0f2638cb6793a31db347fc65dd222dc9
Author: Alexis Manin <alexis.ma...@geomatys.com>
AuthorDate: Fri Nov 5 16:38:49 2021 +0100

    chore(Core): Add a simple unit test for image masks.
---
 .../java/org/apache/sis/image/MaskedImageTest.java | 237 +++++++++++++++++++++
 1 file changed, 237 insertions(+)

diff --git 
a/core/sis-feature/src/test/java/org/apache/sis/image/MaskedImageTest.java 
b/core/sis-feature/src/test/java/org/apache/sis/image/MaskedImageTest.java
new file mode 100644
index 0000000..dc9e8ec
--- /dev/null
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/MaskedImageTest.java
@@ -0,0 +1,237 @@
+package org.apache.sis.image;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
+import java.awt.image.IndexColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import org.apache.sis.internal.coverage.j2d.WritableTiledImage;
+import org.apache.sis.test.FeatureAssert;
+import org.apache.sis.util.ArgumentChecks;
+import org.junit.Test;
+
+/**
+ * TODO: test with big images
+ * TODO: when upgrading to Junit 5 use parameterized tests to avoid 
duplication of test methods
+ */
+public class MaskedImageTest {
+
+    private static final Polygon CONVEX_POLYGON = new Polygon(
+            new int[] {3, 8, 8, 3, 3},
+            new int[] {3, 3, 6, 6, 3},
+            5);
+
+    private static final Polygon CONCAVE_POLYGON = new Polygon(
+            new int[] { 1, 1, 7, 7, 5, 5, 2, 2 },
+            new int[] { 1, 6, 6, 3, 3, 4, 4, 1 },
+            8);
+
+    private static final int[] BASE_CANVAS = {
+            0, 0, 0, 0, 1, 1, 1, 1,
+            0, 0, 0, 0, 1, 1, 1, 1,
+            0, 0, 0, 0, 1, 1, 1, 1,
+            0, 0, 0, 0, 1, 1, 1, 1,
+            3, 3, 3, 3, 2, 2, 2, 2,
+            3, 3, 3, 3, 2, 2, 2, 2,
+            3, 3, 3, 3, 2, 2, 2, 2,
+            3, 3, 3, 3, 2, 2, 2, 2
+    };
+
+    @Test
+    public void noErrorOnEmptyMasks() {
+        final BufferedImage source = monoTile();
+        final RenderedImage masked = new MaskedImage(source, new Polygon(), 
true, new Number[]{ 127 });
+        FeatureAssert.assertPixelsEqual(source, null, masked, null);
+    }
+
+    @Test
+    public void fill_MONO_tile_INside_conVEX_polygon() {
+        maskInsideConvexPolygon(monoTile());
+    }
+
+    @Test
+    public void fill_MONO_tile_OUTside_conVEX_polygon() {
+        maskOutsideConvexPolygon(monoTile());
+    }
+
+    @Test
+    public void fill_MULTI_tile_INside_conVEX_polygon() {
+        maskInsideConvexPolygon(multiTile());
+    }
+
+    @Test
+    public void fill_MULTI_tile_OUTside_conVEX_polygon() {
+        maskOutsideConvexPolygon(multiTile());
+    }
+
+    @Test
+    public void fill_MONO_tile_INside_conCAVE_polygon() {
+        fillInsideConcavePolygon(monoTile());
+    }
+
+    @Test
+    public void fill_MONO_tile_OUTside_conCAVE_polygon() {
+        fillOutsideConcavePolygon(monoTile());
+    }
+
+    @Test
+    public void fill_MULTI_tile_INside_conCAVE_polygon() {
+        fillInsideConcavePolygon(multiTile());
+    }
+
+    @Test
+    public void fill_MULTI_tile_OUTside_conCAVE_polygon() {
+        fillOutsideConcavePolygon(multiTile());
+    }
+
+    private void maskInsideConvexPolygon(RenderedImage source) {
+        final RenderedImage masked = new MaskedImage(source, CONVEX_POLYGON, 
true, new Number[]{ 4 });
+        final RenderedImage expected = monoTile(new int[] {
+                0, 0, 0, 0, 1, 1, 1, 1,
+                0, 0, 0, 0, 1, 1, 1, 1,
+                0, 0, 0, 0, 1, 1, 1, 1,
+                0, 0, 0, 4, 4, 4, 4, 4,
+                3, 3, 3, 4, 4, 4, 4, 4,
+                3, 3, 3, 4, 4, 4, 4, 4,
+                3, 3, 3, 3, 2, 2, 2, 2,
+                3, 3, 3, 3, 2, 2, 2, 2
+        });
+        FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+    }
+
+    private void maskOutsideConvexPolygon(final RenderedImage source) {
+        final RenderedImage masked = new MaskedImage(source, CONVEX_POLYGON, 
false, new Number[]{ 4 });
+        final RenderedImage expected = monoTile(new int[] {
+                4, 4, 4, 4, 4, 4, 4, 4,
+                4, 4, 4, 4, 4, 4, 4, 4,
+                4, 4, 4, 4, 4, 4, 4, 4,
+                4, 4, 4, 0, 1, 1, 1, 1,
+                4, 4, 4, 3, 2, 2, 2, 2,
+                4, 4, 4, 3, 2, 2, 2, 2,
+                4, 4, 4, 4, 4, 4, 4, 4,
+                4, 4, 4, 4, 4, 4, 4, 4
+        });
+        FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+    }
+
+    private void fillInsideConcavePolygon(final RenderedImage source) {
+        final RenderedImage masked = new MaskedImage(source, CONCAVE_POLYGON, 
true, new Number[]{ 4 });
+        final RenderedImage expected = monoTile(new int[] {
+                0, 0, 0, 0, 1, 1, 1, 1,
+                0, 4, 0, 0, 1, 1, 1, 1,
+                0, 4, 0, 0, 1, 1, 1, 1,
+                0, 4, 0, 0, 1, 4, 4, 1,
+                3, 4, 4, 4, 4, 4, 4, 2,
+                3, 4, 4, 4, 4, 4, 4, 2,
+                3, 3, 3, 3, 2, 2, 2, 2,
+                3, 3, 3, 3, 2, 2, 2, 2
+        });
+        FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+    }
+
+    private void fillOutsideConcavePolygon(final RenderedImage source) {
+        final RenderedImage masked = new MaskedImage(source, CONCAVE_POLYGON, 
false, new Number[]{ 4 });
+        final RenderedImage expected = monoTile(new int[] {
+                4, 4, 4, 4, 4, 4, 4, 4,
+                4, 0, 4, 4, 4, 4, 4, 4,
+                4, 0, 4, 4, 4, 4, 4, 4,
+                4, 0, 4, 4, 4, 1, 1, 4,
+                4, 3, 3, 3, 2, 2, 2, 4,
+                4, 3, 3, 3, 2, 2, 2, 4,
+                4, 4, 4, 4, 4, 4, 4, 4,
+                4, 4, 4, 4, 4, 4, 4, 4
+        });
+        FeatureAssert.assertPixelsEqual(expected, null, masked, null);
+    }
+
+    private RenderedImage multiTile() {
+        return new WritableTiledImage(null, colorPalette(), 8, 8, 0, 0,
+                tile(0), tile(1), tile(3), tile(2));
+    }
+
+    private WritableRaster tile(int fillValue) {
+        final WritableRaster tile = 
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 4, 4, 1, null);
+        final WritablePixelIterator it = new 
PixelIterator.Builder().createWritable(tile);
+        while (it.next()) it.setSample(0, fillValue);
+        return tile;
+    }
+
+    /**
+     * Base colors are shades of blue. The last (fifth) color used for masks 
is green.
+     * @return
+     */
+    private static IndexColorModel colorPalette() {
+        byte[] reds   = { 0,  0,  0,   0,   0 };
+        byte[] greens = { 0,  0,  0,   0, 127 };
+        byte[] blues  = { 0, 50, 95, 127,   0 };
+        return new IndexColorModel(Byte.SIZE, 5, reds, greens, blues);
+    }
+
+    /**
+     *
+     * @return Base test image, based on {@link #colorPalette() indexed color 
model}.
+     */
+    private static BufferedImage monoTile() {
+        return monoTile(BASE_CANVAS);
+    }
+
+    private static BufferedImage monoTile(int[] pixels) {
+        ArgumentChecks.ensureDimensionMatches("Input raster must be 8x8", 64, 
pixels);
+        final BufferedImage image = new BufferedImage(8, 8, 
BufferedImage.TYPE_BYTE_INDEXED, colorPalette());
+        final WritableRaster raster = image.getRaster();
+        raster.setPixels(0, 0, 8, 8, pixels);
+        return image;
+    }
+
+    /**
+     * Draw given polygon into source image, to display expected result in 
debugger.
+     */
+    private static BufferedImage debugGeometryImage(final Polygon geometry) {
+        final BufferedImage source = monoTile();
+        final BufferedImage debugImg = new BufferedImage(source.getWidth(), 
source.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
+        final Graphics painter = debugImg.getGraphics();
+        painter.drawImage(source, 0, 0, null);
+
+        final Rectangle bounds = geometry.getBounds();
+        final Rectangle enlargedBounds = new Rectangle(bounds.x - 1, bounds.y 
- 1, bounds.width + 2, bounds.height + 2);
+        final Rectangle intersection = 
enlargedBounds.intersection(debugImg.getRaster().getBounds());
+        painter.setColor(Color.RED);
+        painter.fillRect(intersection.x, intersection.y, intersection.width, 
intersection.height);
+        painter.setColor(Color.GREEN);
+        painter.fillPolygon(geometry);
+        painter.dispose();
+
+        return debugImg;
+    }
+
+
+    /**
+     * Useful in IntelliJ: allow to display input image in debugger: Add a new 
watch calling this method on wanted image.
+     * <em>WARNINGS:</em> works only for current test case:
+     * <ul>
+     *     <li> it assume input image is compatible with test {@link 
#colorPalette() color palette}.</li>
+     *     <li>Work only on single tile images.</li>
+     * </ul>
+     *
+     * @param source The image to display.
+     * @return The image directly displayable through debugger.
+     */
+    private static BufferedImage debug(final RenderedImage source) {
+        Raster tile = source.getTile(source.getMinTileX(), 
source.getMinTileY());
+        final int width, height;
+
+        tile = tile.createTranslatedChild(0, 0);
+        width = tile.getWidth();
+        height = tile.getHeight();
+
+        final BufferedImage view = new BufferedImage(width, height, 
BufferedImage.TYPE_BYTE_INDEXED, colorPalette());
+        view.getRaster().setRect(tile);
+
+        return view;
+    }
+}

Reply via email to