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 84c4925e23f9eea038dc2bf52899c394d336059c Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Tue Nov 9 14:21:46 2021 +0100 Edit the tests (formatting, constants, some simplifications). There is no change in test data. --- .../java/org/apache/sis/image/MaskedImageTest.java | 281 +++++++++++++-------- 1 file changed, 179 insertions(+), 102 deletions(-) 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 index 441574c..7be55e7 100644 --- 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 @@ -16,99 +16,147 @@ */ package org.apache.sis.image; +import java.util.Arrays; import java.awt.Color; -import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; 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.apache.sis.internal.coverage.j2d.TiledImage; import org.apache.sis.test.TestCase; +import org.apache.sis.util.Debug; import org.junit.Test; +import static org.apache.sis.test.FeatureAssert.*; + + /** - * TODO: test with big images - * TODO: when upgrading to Junit 5 use parameterized tests to avoid duplication of test methods + * Tests {@link MaskedImage}. + * + * @author Alexis Manin (Geomatys) + * @version 1.2 + * @since 1.2 + * @module */ public final strictfp class MaskedImageTest extends TestCase { + /** + * The image width and height in pixels. + */ + private static final int WIDTH = 8, HEIGHT = 8; - private static final Polygon CONVEX_POLYGON = new Polygon( - new int[] {3, 8, 8, 3, 3}, - new int[] {3, 3, 6, 6, 3}, - 5); + /** + * The tile width and height in pixels. Note that the number of tiles + * is hard-coded to 2 in methods such as {@link #multiTile()}. + */ + private static final int TILE_WIDTH = WIDTH/2, TILE_HEIGHT = HEIGHT/2; + + /** + * Returns a rectangular shape to use as a mask. + */ + private static Rectangle rectangularMask() { + return new Rectangle(3, 3, 5, 3); + } - private static final Polygon CONCAVE_POLYGON = new Polygon( + /** + * Returns a non-rectangular polygon to use as a mask. + */ + private static Polygon concavePolygon() { + return 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 - }; - + /** + * Tests an empty mask. The result should be identical to source image. + * Actually, {@link MaskedImage} is expected to optimize by reusing the same raster. + */ @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); + final RenderedImage masked = new MaskedImage(source, new Polygon(), true, new Number[] {127}); + assertPixelsEqual(source, null, masked, null); + assertSame(source.getRaster(), masked.getTile(0,0)); // Optimization applied by MaskedImage. } + /** + * Tests masking pixels inside a rectangle on a image having a single tile. + */ @Test public void fill_MONO_tile_INside_conVEX_polygon() { - maskInsideConvexPolygon(monoTile()); + maskInsideRectangle(monoTile()); } + /** + * Tests masking pixels outside a rectangle on a image having a single tile. + */ @Test public void fill_MONO_tile_OUTside_conVEX_polygon() { maskOutsideConvexPolygon(monoTile()); } + /** + * Tests masking pixels inside a rectangle on a image having many tiles. + */ @Test public void fill_MULTI_tile_INside_conVEX_polygon() { - maskInsideConvexPolygon(multiTile()); + maskInsideRectangle(multiTile()); } + /** + * Tests masking pixels outside a rectangle on a image having many tiles. + */ @Test public void fill_MULTI_tile_OUTside_conVEX_polygon() { maskOutsideConvexPolygon(multiTile()); } + /** + * Tests masking pixels inside a non-rectangular polygon on a image having a single tile. + */ @Test public void fill_MONO_tile_INside_conCAVE_polygon() { fillInsideConcavePolygon(monoTile()); } + /** + * Tests masking pixels outside a non-rectangular polygon on a image having a single tile. + */ @Test public void fill_MONO_tile_OUTside_conCAVE_polygon() { fillOutsideConcavePolygon(monoTile()); } + /** + * Tests masking pixels inside a non-rectangular polygon on a image having many tiles. + */ @Test public void fill_MULTI_tile_INside_conCAVE_polygon() { fillInsideConcavePolygon(multiTile()); } + /** + * Tests masking pixels outside a non-rectangular polygon on a image having many tiles. + */ @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 }); + /** + * Tests masking pixels inside a rectangle on the given image. + * This method is invoked twice, for untiled image and for tiled image. + */ + private static void maskInsideRectangle(final RenderedImage source) { + final RenderedImage masked = new MaskedImage(source, rectangularMask(), 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, @@ -119,11 +167,15 @@ public final strictfp class MaskedImageTest extends TestCase { 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2 }); - FeatureAssert.assertPixelsEqual(expected, null, masked, null); + assertPixelsEqual(expected, null, masked, null); } - private void maskOutsideConvexPolygon(final RenderedImage source) { - final RenderedImage masked = new MaskedImage(source, CONVEX_POLYGON, false, new Number[]{ 4 }); + /** + * Tests masking pixels outside a rectangle on the given image. + * This method is invoked twice, for untiled image and for tiled image. + */ + private static void maskOutsideConvexPolygon(final RenderedImage source) { + final RenderedImage masked = new MaskedImage(source, rectangularMask(), 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, @@ -134,11 +186,15 @@ public final strictfp class MaskedImageTest extends TestCase { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }); - FeatureAssert.assertPixelsEqual(expected, null, masked, null); + assertPixelsEqual(expected, null, masked, null); } - private void fillInsideConcavePolygon(final RenderedImage source) { - final RenderedImage masked = new MaskedImage(source, CONCAVE_POLYGON, true, new Number[]{ 4 }); + /** + * Tests masking pixels inside a non-rectangular polygon on the given image. + * This method is invoked twice, for untiled image and for tiled image. + */ + private static void fillInsideConcavePolygon(final RenderedImage source) { + final RenderedImage masked = new MaskedImage(source, concavePolygon(), 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, @@ -149,11 +205,15 @@ public final strictfp class MaskedImageTest extends TestCase { 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2 }); - FeatureAssert.assertPixelsEqual(expected, null, masked, null); + assertPixelsEqual(expected, null, masked, null); } - private void fillOutsideConcavePolygon(final RenderedImage source) { - final RenderedImage masked = new MaskedImage(source, CONCAVE_POLYGON, false, new Number[]{ 4 }); + /** + * Tests masking pixels outside a non-rectangular polygon on the given image. + * This method is invoked twice, for untiled image and for tiled image. + */ + private static void fillOutsideConcavePolygon(final RenderedImage source) { + final RenderedImage masked = new MaskedImage(source, concavePolygon(), 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, @@ -164,24 +224,72 @@ public final strictfp class MaskedImageTest extends TestCase { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }); - FeatureAssert.assertPixelsEqual(expected, null, masked, null); + assertPixelsEqual(expected, null, masked, null); + } + + /** + * Creates the base test image with a single tile. + * Sample values are between 0 and 3 inclusive and + * the image uses an {@linkplain #colorPalette() indexed color model}. + */ + private static BufferedImage monoTile() { + return 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, 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 + }); } - private RenderedImage multiTile() { - return new WritableTiledImage(null, colorPalette(), 8, 8, 0, 0, - tile(0, 0), tile(1, 1), tile(2, 3), tile(3, 2)); + /** + * Creates an image with a single tile and the given number of values. + * Image size is {@value #WIDTH}×{@value #HEIGHT} pixels. + */ + private static BufferedImage monoTile(final int[] pixels) { + assertEquals("Input raster must be " + WIDTH + "×" + HEIGHT, WIDTH*HEIGHT, pixels.length); + final BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_BYTE_INDEXED, colorPalette()); + final WritableRaster raster = image.getRaster(); + raster.setPixels(0, 0, WIDTH, HEIGHT, pixels); + return image; } - private static WritableRaster tile(final int n, final int fillValue) { - final WritableRaster tile = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 4, 4, 1, new Point((n % 2)*4, (n / 2)*4)); - final WritablePixelIterator it = new PixelIterator.Builder().createWritable(tile); - while (it.next()) it.setSample(0, fillValue); + /** + * Creates an image with a 4 tiles and the given number of values. + * Image size is {@value #WIDTH}×{@value #HEIGHT} pixels. + */ + private static RenderedImage multiTile() { + final TiledImage image = new TiledImage(null, colorPalette(), WIDTH, HEIGHT, 0, 0, + tile( 0, 0, 0), + tile(TILE_WIDTH, 0, 1), + tile( 0, TILE_HEIGHT, 3), + tile(TILE_WIDTH, TILE_HEIGHT, 2)); + assertNull(image.verify()); + return image; + } + + /** + * Creates a tile filled with the given value. + * + * @param x <var>x</var> coordinate of upper-left corner. + * @param y <var>y</var> coordinate of upper-left corner. + * @param fillValue the value to use for filling the raster. + * @return a tile filled with the given value. + */ + private static WritableRaster tile(final int x, final int y, final int fillValue) { + final WritableRaster tile = Raster.createInterleavedRaster( + DataBuffer.TYPE_BYTE, TILE_WIDTH, TILE_HEIGHT, 1, new Point(x, y)); + final int[] pixels = new int[TILE_WIDTH * TILE_HEIGHT]; + Arrays.fill(pixels, fillValue); + tile.setPixels(x, y, TILE_WIDTH, TILE_HEIGHT, pixels); 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 }; @@ -191,65 +299,34 @@ public final strictfp class MaskedImageTest extends TestCase { } /** + * Draws the given polygon into the given image, in order to display expected result in debugger. + * If the given image is {@code null}, the {@linkplain #monoTile() untiled source image} is used. * - * @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. + * <p>Tip for IntelliJ users:</p> this method allows to display input image in debugger + * by adding a new watch calling this method on the wanted image. + * + * @param source the image to display, or {@code null} for {@link #monoTile()}. + * @param geometry the geometry to show on top of the image, or {@code null} if none. + * @return the image directly displayable through 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); + @Debug + private static BufferedImage debugGeometryImage(RenderedImage source, final Shape geometry) { + if (source == null) { + source = monoTile(); + } + final BufferedImage debugImg = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_RGB); + final Graphics2D painter = debugImg.createGraphics(); + painter.drawRenderedImage(source, new AffineTransform()); + if (geometry != 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.fill(intersection); + painter.setColor(Color.GREEN); + painter.fill(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; - } }