/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-2012, Open Source Geospatial Foundation (OSGeo) * (C) 2009-2012, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.image.io.mosaic; import java.awt.Color; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.image.ComponentColorModel; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.ImageReadParam; import javax.imageio.spi.ImageReaderSpi; import javax.imageio.stream.ImageInputStream; import org.geotoolkit.nio.IOUtilities; import org.geotoolkit.test.Commons; import org.geotoolkit.test.TestData; import org.geotoolkit.internal.image.io.Formats; import org.geotoolkit.internal.io.TemporaryFile; import org.geotoolkit.test.image.ImageTestBase; import org.junit.*; import static org.junit.Assert.*; /** * Tests reading and writing of a mosaic. This tests is the only one performing * real read/write operations. The input tiles are opaque RGB images. The output * should be opaque as well, except in the case of {@link #testTransparency()} * which should have added an alpha channel. * * @author Martin Desruisseaux (Geomatys) * @version 3.17 * * @since 3.01 */ public final strictfp class MosaicReadWriteTest extends ImageTestBase { /** * The width and height (in pixels) of source tiles. */ private static final int S = 90; /** * Checksum of input files. Many checksums may be declared for the same * image because the sample model changed in different Java version: * <p> * <ol> * <li>Java 6 update 17 and before</li> * <li>Java 6 update 18 and after</li> * </ol> */ private static final long[][] TILE_CHECKSUMS = { {3489461482L, 3995241366L}, // A1 {3282954537L, 3034917950L}, // B1 {3175519999L, 4097683706L}, // C1 { 504243661L, 2349894252L}, // D1 { 546121330L, 2654069663L}, // A2 {3926870361L, 4125865354L}, // B2 { 963864334L, 3858155692L}, // C2 { 919637760L, 1047037325L} // D2 }; /** * Potential checksums of the whole image. */ public static final long[] IMAGE_CHECKSUMS = {1800014439L, 2327013649L, 3333171052L}; /** * The source mosaic. */ private TileManager sourceMosaic; /** * The directory which will contains the target tiles. * If non-null, this directory will be cleared after the test. */ private Path targetDirectory; /** * Creates a new test suite. */ public MosaicReadWriteTest() { super(Tile.class); } /** * Clears the target directory. This method does not scan in sub-directories. */ @After public void clearTargetDirectory() { if (targetDirectory != null) { try { IOUtilities.deleteRecursively(targetDirectory); } catch (IOException e) { fail(e.getMessage()); } targetDirectory = null; } } /** * Initialize {@link #sourceMosaic}. * * @throws IOException Should not occur. */ @Before public void setupSourceMosaic() throws IOException { final ImageReaderSpi spi = Formats.getReaderByFormatName("png", null); final TileManager[] managers = TileManagerFactory.DEFAULT.create( new Tile(spi, TestData.url(MosaicReadWriteTest.class, "A1.png"), 0, new Rectangle(0*S, 0, S, S)), new Tile(spi, TestData.url(MosaicReadWriteTest.class, "B1.png"), 0, new Rectangle(1*S, 0, S, S)), new Tile(spi, TestData.url(MosaicReadWriteTest.class, "C1.png"), 0, new Rectangle(2*S, 0, S, S)), new Tile(spi, TestData.url(MosaicReadWriteTest.class, "D1.png"), 0, new Rectangle(3*S, 0, S, S)), new Tile(spi, TestData.url(MosaicReadWriteTest.class, "A2.png"), 0, new Rectangle(0*S, S, S, S)), new Tile(spi, TestData.url(MosaicReadWriteTest.class, "B2.png"), 0, new Rectangle(1*S, S, S, S)), new Tile(spi, TestData.url(MosaicReadWriteTest.class, "C2.png"), 0, new Rectangle(2*S, S, S, S)), new Tile(spi, TestData.url(MosaicReadWriteTest.class, "D2.png"), 0, new Rectangle(3*S, S, S, S)) ); assertEquals(1, managers.length); sourceMosaic = managers[0]; } /** * Performs a checksum on the input tiles. This is not yet the mosaic. * If this test fails, then all other tests in the file are likely to * fail as well. * * @throws IOException If an I/O error occurred. */ @Test public void testInputTiles() throws IOException { int i=0; for (final Tile tile : sourceMosaic.getTiles()) { final ImageReader reader = tile.getImageReader(); image = reader.read(0); Tile.dispose(reader); final String name = tile.getInputName(); assertEquals(name, S, image.getWidth()); assertEquals(name, S, image.getHeight()); assertEquals(3, image.getSampleModel().getNumBands()); assertEquals(Transparency.OPAQUE, image.getColorModel().getTransparency()); assertCurrentChecksumEquals(name, TILE_CHECKSUMS[i++]); } } /** * Creates an image reader from the input mosaic and test reading the image. * First we read the image as a whole. Then we read each individual tile. * Finally we read at a few different subsampling a region values. * <p> * Note that this test does not tests the * {@linkplain MosaicImageReadParam#setSubsamplingChangeAllowed(boolean) subsampling changes}. * This is aimed to be a relatively simple and straightforward test. * * @throws IOException If an I/O error occurred. */ @Test public void testInputMosaic() throws IOException { final MosaicImageReader reader = new MosaicImageReader(); reader.setInput(sourceMosaic); assertEquals("Num images", 1, reader.getNumImages(false)); assertEquals("Width", 4*S, reader.getWidth (0)); assertEquals("Height", 2*S, reader.getHeight(0)); assertTrue(reader.getRawImageType(0).getColorModel() instanceof ComponentColorModel); image = reader.read(0); assertEquals("Width", 4*S, image.getWidth ()); assertEquals("Height", 2*S, image.getHeight()); showCurrentImage("testInputMosaic()"); assertCurrentChecksumEquals("testInputMosaic", IMAGE_CHECKSUMS); for (int i=0,y=0; y<2; y++) { for (int x=0; x<4; x++) { ImageReadParam param = reader.getDefaultReadParam(); param.setSourceRegion(new Rectangle(x*S, y*S, S, S)); image = reader.read(0, param); assertEquals(S, image.getWidth()); assertEquals(S, image.getHeight()); assertEquals(3, image.getSampleModel().getNumBands()); assertEquals(Transparency.OPAQUE, image.getColorModel().getTransparency()); assertCurrentChecksumEquals("Tile("+x+','+y+')', TILE_CHECKSUMS[i++]); } } /* * Loads the mosaic with a subsampling. */ ImageReadParam param = reader.getDefaultReadParam(); param.setSourceSubsampling(4, 2, 0, 0); image = reader.read(0, param); showCurrentImage("testInputMosaic() - subsampling"); assertEquals("Width", S, image.getWidth ()); assertEquals("Height", S, image.getHeight()); assertEquals("Checksum", 329430756L, Commons.checksum(image)); /* * Loads a sub-region of the mosaic. */ param = reader.getDefaultReadParam(); param.setSourceRegion(new Rectangle(S/2, S/4, 3*S, S+S/2)); image = reader.read(0, param); showCurrentImage("testInputMosaic() - subregion"); assertEquals("Width", 3*S, image.getWidth ()); assertEquals("Height", S+S/2, image.getHeight()); assertEquals("Checksum", 4259662989L, Commons.checksum(image)); reader.dispose(); } /** * Returns the builder to use for creating the target mosaic. The subsampling levels to create * must be supplied in argument. Some tests do not create tiles at the finest subsampling in * order to reduce the amount of data to write (and thus make the test slightly faster). The * levels {@code 3, 6, 9} create 3 levels with tiles of 30x30 pixels, except the last level * which will create an image of 20 pixels height. */ private MosaicBuilder builder(final int... subsamplings) throws IOException { targetDirectory = TemporaryFile.getSharedTemporaryDirectory().resolve("mosaic-test"); Files.createDirectory(targetDirectory); final MosaicBuilder builder = new MosaicBuilder(); builder.setUntiledImageBounds(new Rectangle(4*S, 2*S)); builder.setTileDirectory(targetDirectory); builder.setTileSize(new Dimension(30, 30)); builder.setSubsamplings(subsamplings); builder.setTileReaderSpi("png"); return builder; } /** * Tests writing the mosaic. After the test, the created tiles will be * read and their pixel values compared against the expected checksum. * * @throws IOException If an I/O error occurred. */ @Test public void testWriteMosaic() throws IOException { final MosaicImageWriter writer = new MosaicImageWriter(); writer.setOutput(builder(3, 6, 9).createTileManager()); writer.writeFromInput(sourceMosaic, null); writer.dispose(); /* * Now ensure that the files that we created are the expected ones. * We expect PNG format. */ final String[] files = { "L1_A1.png", "L1_B1.png", "L1_C1.png", "L1_D1.png", "L1_A2.png", "L1_B2.png", "L1_C2.png", "L1_D2.png", "L2_A1.png", "L2_B1.png", "L3_A1.png", "L3_B1.png" }; final long[][] checksums = { {3823973597L, 4171709854L}, {1268989087L, 3973907355L}, { 90746832L, 4072319012L}, {3244609990L, 161414722L}, {3803928042L, 3436002999L}, {1195337429L, 4167432L}, {3165544981L, 3079689620L}, {2981893502L, 2387513289L}, {1536584446L, 3113480928L}, {1796661935L, 1246616471L}, {2442125326L, 1852799903L}, {1672159374L, 89694022L} }; int i=0; final ImageReader reader = ImageIO.getImageReadersByFormatName("png").next(); for (final String filename : files) { final Path file = targetDirectory.resolve(filename); assertTrue(filename, Files.isRegularFile(file)); try (ImageInputStream in = ImageIO.createImageInputStream(file)) { assertNotNull("File not found", in); reader.setInput(in); image = reader.read(0); } assertEquals(3, image.getSampleModel().getNumBands()); assertEquals(Transparency.OPAQUE, image.getColorModel().getTransparency()); assertCurrentChecksumEquals(filename, checksums[i++]); } reader.dispose(); } /** * Tests again writing a mosaic, but this time with an operation applied. * This operation add an alpha channel to the tiles. * * @throws IOException If an I/O error occurred. */ @Test @org.junit.Ignore public void testTransparency() throws IOException { final TileManager targetMosaic = builder(1, 6).createTileManager(); /* * The colors to replace by transparent pixels. There is a few occurrences of this color * on the last row of the source image. This artifact provides a convenient opportunity * for testing this operation. Note only a few black strips on the last row will be made * transparent - some will not be changed if they do not appear on a corner of a source * tile. */ final Color[] opaqueColors = { Color.BLACK, }; final MosaicImageWriter writer = new MosaicImageWriter(); final MosaicImageWriteParam param = writer.getDefaultWriteParam(); param.setOpaqueBorderFilter(opaqueColors); writer.setOutput(targetMosaic); writer.writeFromInput(sourceMosaic, param); writer.dispose(); /* * Verifies that every tiles has an alpha channel. */ for (final Tile tile : targetMosaic.getTiles()) { final ImageReader reader = tile.getImageReader(); image = reader.read(0); Tile.dispose(reader); assertEquals(4, image.getSampleModel().getNumBands()); assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency()); } /* * Now read the target mosaic as one single image. */ final MosaicImageReader reader = new MosaicImageReader(); reader.setInput(targetMosaic); image = reader.read(0); reader.dispose(); assertEquals(4, image.getSampleModel().getNumBands()); assertEquals(Transparency.TRANSLUCENT, image.getColorModel().getTransparency()); assertCurrentChecksumEquals("testTransparency", IMAGE_CHECKSUMS); /* * Visual test (if enabled). */ showCurrentImage("testTransparency()"); } }