/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2016, 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.plugin;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRenderedImage;
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Random;
import javax.imageio.IIOParam;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import org.apache.sis.test.TestUtilities;
import org.geotoolkit.coverage.io.CoverageIO;
import org.geotoolkit.image.internal.ImageUtils;
import org.geotoolkit.image.internal.PhotometricInterpretation;
import org.geotoolkit.image.internal.PlanarConfiguration;
import org.geotoolkit.image.internal.SampleType;
import org.geotoolkit.image.io.UnsupportedImageFormatException;
import org.geotoolkit.image.iterator.*;
import org.geotoolkit.nio.IOUtilities;
import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.geotoolkit.image.io.plugin.ImageOrientation.*;
/**
* Primary test class to test {@link TiffImageWriter} and {@link TiffImageReader}. <br/><br/>
*
* All tests use random image boundary and random internales datas.<br/><br/>
*
* Proposed tests : <br/>
*
* - Simply Reading / writing without any sub sampling or other, with 1 band, in all sample format (Byte, Short, Integer, Float, Double).<br/>
* - Simply Reading / writing without any sub sampling or other, with 3, 4 bands RGB.<br/>
* - Simply Reading / writing without any sub sampling or other, with 1 bands Color Map.<br/><br/>
*
* All following tests, test internaly all precedently tests with random sub sampling, destination offset, sub sample offsets.<br/>
* - Reading / Writing with a random source region situated on image lower left corner.<br/>
* - Reading / Writing with a random source region situated on image lower right corner.<br/>
* - Reading / Writing with a random source region situated on image upper left corner.<br/>
* - Reading / Writing with a random source region situated on image upper right corner.<br/>
* - Reading / Writing with a random source region situated on image center.<br/>
*
* @author Remi Marechal (Geomatys).
*/
public strictfp abstract class TestTiffImageReaderWriter {
protected final double DEFAULT_TOLERANCE = 1E-15;
/**
* Tested {@link ImageReader} implementation.
*/
protected TiffImageReader reader;
/**
* Adapted {@link IIOParam} for reading operations.
*/
protected ImageReadParam readerParam;
/**
* Tested {@link ImageWriter} implementation.
*/
protected TiffImageWriter writer;
/**
* Adapted {@link IIOParam} for writing operations.
*/
protected ImageWriteParam writerParam;
/**
* Random number generator used for tests.
*/
protected final Random random;
protected final File tempDir;
/**
* @param compression choosen compression to write image.
*/
public TestTiffImageReaderWriter(final String compression) throws IOException {
tempDir = Files.createTempDirectory("tiffTests").toFile();
this.reader = new TiffImageReader(null);
this.readerParam = reader.getDefaultReadParam();
this.writer = new TiffImageWriter(null);
this.writerParam = writer.getDefaultWriteParam();
if (compression != null) {
writerParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writerParam.setCompressionType(compression);
}
random = TestUtilities.createRandomNumberGenerator();
}
@After
public void deleteTempFiles() throws IOException {
Files.walkFileTree(tempDir.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return super.visitFile(file, attrs);
}
});
}
/**
* Effectuate distinct test in function of Reader or Writer implementation
* and return result image from Reading writing action.
*
* @param fileTest the place to be
* @param sourceImage image which will be written
* @param sourceRegion Reading or writing region.
* @param sourceXSubsampling Reading or writing subsample in X direction.
* @param sourceYsubsampling Reading or writing subsample in Y direction.
* @param sourceXOffset Reading or writing offset in X direction.
* @param sourceYOffset Reading or writing offset in Y direction.
* @return Result image from reading writing action.
* @throws IOException if problem during reading / writing action.
*/
protected abstract RenderedImage effectuateTest(final File fileTest, final RenderedImage sourceImage, final Rectangle sourceRegion,
final int sourceXSubsampling, final int sourceYsubsampling, final int sourceXOffset, final int sourceYOffset, final Point destOffset) throws IOException;
/**
* Test which write and read after an image with only one band and test all sample type.
*
* @throws IOException if problem during reading/writing action.
*/
@Test
// @Ignore
public void default1BandTest() throws IOException {
Path fileTest = Files.createTempFile(tempDir.toPath(),"default1BandTest", "tiff");
//-- test : 1 band type : byte grayscale --//
defaultTest("default1BandTest : 1 band type : Byte grayscale : ", fileTest,
SampleType.BYTE, 1, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : short grayscale --//
defaultTest("default1BandTest : 1 band type : Short grayscale : ", fileTest,
SampleType.USHORT, 1, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : int grayscale --//
defaultTest("default1BandTest : 1 band type : Integer grayscale : ", fileTest,
SampleType.INTEGER, 1, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : float grayscale --//
defaultTest("default1BandTest : 1 band type : Float grayscale : ", fileTest,
SampleType.FLOAT, 1, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : Double grayscale --//
defaultTest("default1BandTest : 1 band type : Double grayscale : ", fileTest,
SampleType.DOUBLE, 1, PhotometricInterpretation.GRAYSCALE);
}
/**
* Test volontary ignored because scaled color space is not supported yet.
*
* @throws IOException if problem during reading/writing action.
*/
@Test
//@Ignore
public void default4BandTest() throws IOException {
Path fileTest = Files.createTempFile(tempDir.toPath(),"default4BandTest", "tiff");
//-- test : 1 band type : byte grayscale --//
defaultTest("default1BandTest : 4 band type : Byte grayscale : ", fileTest,
SampleType.BYTE, 4, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : short grayscale --//
defaultTest("default1BandTest : 4 band type : Short grayscale : ", fileTest,
SampleType.USHORT, 4, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : int grayscale --//
defaultTest("default1BandTest : 4 band type : Integer grayscale : ", fileTest,
SampleType.INTEGER, 4, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : float grayscale --//
defaultTest("default1BandTest : 4 band type : Float grayscale : ", fileTest,
SampleType.FLOAT, 4, PhotometricInterpretation.GRAYSCALE);
//-- test : 1 band type : Double grayscale --//
defaultTest("default1BandTest : 10 band type : Double grayscale : ", fileTest,
SampleType.DOUBLE, 10, PhotometricInterpretation.GRAYSCALE);
}
/**
* Test writing/ reading action with an RGB image.
*
* @throws IOException if problem during reading/writing action.
*/
@Test
// @Ignore
public void defaultRGBTest() throws IOException {
Path fileTest = Files.createTempFile(tempDir.toPath(),"defaultRGBTest", "tiff");
//-- test : 3 bands type : byte RGB --//
defaultTest("defaultRGBTest : 3 bands type : Byte RGB: ", fileTest,
SampleType.BYTE, 3, PhotometricInterpretation.RGB);
//-- test : 4 bands type : byte RGB --//
defaultTest("defaultRGBTest : 4 bands type : Byte RGB: ", fileTest,
SampleType.BYTE, 4, PhotometricInterpretation.RGB);
}
/**
* Test writing/ reading action with an color map image.
*
* @throws IOException if problem during reading/writing action.
*/
@Test
// @Ignore
public void defaultColorMapTest() throws IOException {
Path fileTest = Files.createTempFile(tempDir.toPath(),"defaultColorMapTest", "tiff");
//-- test : 1 bands type : byte palette --//
defaultTest("defaultColorMapTest : 1 band type : Byte Palette: ", fileTest,
SampleType.BYTE, 1, PhotometricInterpretation.PALETTE);
//-- uncomment this code when a solution for multi band with color palette will be approuved.
// //-- test : 3 bands type : byte RGB --//
// defaultTest("defaultColorMapTest : 3 bands type : Byte Palette: ", fileTest,
// Byte.SIZE, 3, PHOTOMETRIC_PALETTE, SAMPLEFORMAT_UINT);
//
// //-- test : 4 bands type : byte RGB --//
// defaultTest("defaultColorMapTest : 4 bands type : Byte RGB: ", fileTest,
// Byte.SIZE, 4, PHOTOMETRIC_PALETTE, SAMPLEFORMAT_UINT);
}
/**
* Improve Reader / Writer with random image dimension and subsampling values.
* Moreover source region is near source image upper left corner.
*
* @see #regionTest(java.lang.String, org.geotoolkit.image.io.plugin.ImageOrientation)
*/
@Test
// @Ignore
public void upperLeftCornerTest() throws IOException {
regionTest("UpperLeftCornerTest", IMAGE_UPPER_LEFT_CORNER);
}
/**
* Improve Reader / Writer with random image dimension and subsampling values.
* Moreover source region is near source image upper right corner.
*
* @see #regionTest(java.lang.String, org.geotoolkit.image.io.plugin.ImageOrientation)
*/
@Test
// @Ignore
public void upperRightCornerTest() throws IOException {
regionTest("upperRightCornerTest", IMAGE_UPPER_RIGHT_CORNER);
}
/**
* Improve Reader / Writer with random image dimension and subsampling values.
* Moreover source region is near source image lower left corner.
*
* @see #regionTest(java.lang.String, org.geotoolkit.image.io.plugin.ImageOrientation)
*/
@Test
// @Ignore
public void lowerLeftCornerTest() throws IOException {
regionTest("lowerLeftCornerTest", IMAGE_LOWER_LEFT_CORNER);
}
/**
* Improve Reader / Writer with random image dimension and subsampling values.
* Moreover source region is near source image lower right corner.
*
* @see #regionTest(java.lang.String, org.geotoolkit.image.io.plugin.ImageOrientation)
*/
@Test
// @Ignore
public void lowerRightCornerTest() throws IOException {
regionTest("lowerRightCornerTest", IMAGE_LOWER_RIGHT_CORNER);
}
/**
* Improve Reader / Writer with random image dimension and subsampling values.
* Moreover source region is near source image center.
*
* @see #regionTest(java.lang.String, org.geotoolkit.image.io.plugin.ImageOrientation)
*/
@Test
// @Ignore
public void centerTest() throws IOException {
regionTest("CenterAreaTest", IMAGE_CENTER);
}
/**
* Effectuate some tests on the random region near the specified imageOrientation.
*
* @param message in case of error first part of error message.
* @param imageOrientation an enum to stipulate witch source image region will be written or read.
* @see #IMAGE_LOWER_LEFT_CORNER
* @see #IMAGE_LOWER_RIGHT_CORNER
* @see #IMAGE_UPPER_LEFT_CORNER
* @see #IMAGE_UPPER_RIGHT_CORNER
* @see #IMAGE_CENTER
* @throws IOException if problem during reading/writing action.
*/
protected void regionTest(final String message, final ImageOrientation imageOrientation) throws IOException {
final File fileTest = File.createTempFile(message, "tiff", tempDir);
//-------------------- test : 1 band -----------------------------------//
//-- type byte
generalTest(message+" : 1 band, type : byte.", fileTest, SampleType.BYTE, 1,
PhotometricInterpretation.GRAYSCALE, imageOrientation);
//-- type short
generalTest(message + " : 1 band, type : short.", fileTest, SampleType.USHORT, 1,
PhotometricInterpretation.GRAYSCALE, imageOrientation);
//-- type int
generalTest(message + " : 1 band, type : int.", fileTest, SampleType.INTEGER, 1,
PhotometricInterpretation.GRAYSCALE, imageOrientation);
//-- type Float
generalTest(message + " : 1 band, type : float.", fileTest, SampleType.FLOAT, 1,
PhotometricInterpretation.GRAYSCALE, imageOrientation);
//-- type double
generalTest(message + " : 1 band, type : double.", fileTest, SampleType.DOUBLE, 1,
PhotometricInterpretation.GRAYSCALE, imageOrientation);
//-- RGB --//
//-- type Byte RGB
generalTest(message+" : 3 bands RGB, type : Byte.", fileTest, SampleType.BYTE, 3,
PhotometricInterpretation.RGB, imageOrientation);
generalTest(message + " : 4 bands RGB, type : Byte.", fileTest, SampleType.BYTE, 4,
PhotometricInterpretation.RGB, imageOrientation);
//-- color Map --//
//-- type Byte
generalTest(message + " : 1 bands ColorMap, type : Byte.", fileTest, SampleType.BYTE, 1,
PhotometricInterpretation.PALETTE, imageOrientation);
//-- uncomment this code when a solution for multi band with color palette will be approuved.
// generalTest(message + " : 4 bands ColorMap, type : Byte.", fileTest, Byte.SIZE, 4,
// PHOTOMETRIC_PALETTE, SAMPLEFORMAT_UINT, imageOrientation);
}
/**
* Create an image with expected properties given by followed attributs : <br/>
* - random width and height<br/>
* - sampleBitsSize<br/>
* - numBand<br/>
* - photometricInterpretation<br/>
* - sampleFormat<br/>
* and fill it by appropriate random sample values.<br/>
* Then image is writen at "filetest" adress and read.<br/>
* To finish read image is compare to itself before writing.<br/><br/>
*
* Moreover, test different input / output type setted.
*
* @param message in case of assertion error.
* @param fileTest the place to be.
* @param sampleBitsSize sample bit number.
* @param numBand band number.
* @param photometricInterpretation define RGB or 1 band or also color map.
* @param sampleFormat define sample format integer or floating point.
* @see #PHOTOMETRIC_MINISBLACK
* @see #PHOTOMETRIC_PALETTE
* @see #PHOTOMETRIC_RGB
* @see #SAMPLEFORMAT_IEEEFP
* @see #SAMPLEFORMAT_UINT
* @throws IOException if problem during reading/writing action.
*/
private void defaultTest(final String message, final Path fileTest, final SampleType sampleType,
final int numBand, final PhotometricInterpretation photometricInterpretation)
throws IOException {
final int width = random.nextInt(256) + 16;
final int height = random.nextInt(256) + 16;
final RenderedImage expected = createImageTest(width, height, sampleType, numBand, photometricInterpretation);
String messageSize = message + "\n Test image size(w/h) "+width+"/"+height+".";
//test writing with different output type from Spi
for (Class type : writer.getOriginatingProvider().getOutputTypes()) {
Object out = null;
try {
if (type.equals(Path.class)) {
out = fileTest;
} else if (type.equals(File.class)) {
out = fileTest.toFile();
} else if (type.equals(String.class)) {
out = fileTest.toString();
} else if (type.equals(OutputStream.class)) {
out = Files.newOutputStream(fileTest, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
} else if (type.equals(ImageOutputStream.class)) {
out = CoverageIO.createImageOutputStream(fileTest);
} else if (type.equals(FileOutputStream.class)) {
out = new FileImageOutputStream(fileTest.toFile());
}
writer.setOutput(out); //-- to initialize writer
writer.write(expected, writerParam);
writer.dispose();
//close if output is a closable stream
if (out != null && !IOUtilities.canProcessAsPath(out) && out instanceof Closeable) {
((Closeable) out).close();
}
//test image (read using Path input)
reader.setInput(fileTest); //-- to initialize reader
final RenderedImage tested = reader.read(0);
reader.close();
//test image
checkImage(messageSize, expected, tested);
} catch (Exception e) {
String messageType = messageSize + "\n Writer output : "+type.getCanonicalName()
+ "\n Reader input : "+fileTest.getClass().getCanonicalName()+"."
+ "\n Cause : "+e.getMessage();
fail(messageType);
}
}
//test reading with every reader spy inputTypes (except Path because already tested)
for (Class type : reader.getOriginatingProvider().getInputTypes()) {
Object in = null;
try {
if (type.equals(File.class)) {
in = fileTest.toFile();
} else if (type.equals(String.class)) {
in = fileTest.toString();
} else if (type.equals(InputStream.class)) {
in = Files.newInputStream(fileTest);
} else if (type.equals(ImageInputStream.class)) {
in = CoverageIO.createImageInputStream(fileTest);
}
reader.setInput(fileTest); //-- to initialize reader
final RenderedImage tested = reader.read(0);
reader.close();
//close if input is a closable stream
if (in != null && !IOUtilities.canProcessAsPath(in) && in instanceof Closeable) {
((Closeable) in).close();
}
//test image
checkImage(messageSize, expected, tested);
} catch (Exception e) {
String messageType = messageSize + "\n Reader input : "+type.getCanonicalName()+"."
+ "\n Cause : "+e.getMessage();
fail(messageType);
}
}
}
/**
* Effectuate a test in function of given parameter.
* Internaly, a source image is generate with width height region and subsampling random values.
*
* @param message in case of error first part of error message.
* @param fileTest the place to be.
* @param sampleType
* @param numBand source image numband.
* @param photometricInterpretation define RGB or 1 band or also color map.
* @param imageOrientation an enum to stipulate witch source image region will be written or read.
* @see #PHOTOMETRIC_MINISBLACK
* @see #PHOTOMETRIC_PALETTE
* @see #PHOTOMETRIC_RGB
* @see #SAMPLEFORMAT_IEEEFP
* @see #SAMPLEFORMAT_UINT
* @see #IMAGE_LOWER_LEFT_CORNER
* @see #IMAGE_LOWER_RIGHT_CORNER
* @see #IMAGE_UPPER_LEFT_CORNER
* @see #IMAGE_UPPER_RIGHT_CORNER
* @see #IMAGE_CENTER
* @throws IOException if problem during reading/writing action.
*/
protected void generalTest(final String message, final File fileTest,
final SampleType sampleType, final int numBand,
final PhotometricInterpretation photometricInterpretation,
final ImageOrientation imageOrientation) throws IOException {
int width = random.nextInt(256) + 16;
int height = random.nextInt(256) + 16;
final RenderedImage sourceImage = createImageTest(width, height, sampleType, numBand, photometricInterpretation);
final int srcRegionX, srcRegionY;
switch (imageOrientation) {
case IMAGE_LOWER_RIGHT_CORNER : {
srcRegionX = width >> 1;
srcRegionY = 0;
break;
}
case IMAGE_UPPER_LEFT_CORNER : {
srcRegionX = 0;
srcRegionY = height >> 1;
break;
}
case IMAGE_UPPER_RIGHT_CORNER : {
srcRegionX = width >> 1;
srcRegionY = height >> 1;
break;
}
case IMAGE_CENTER : {
srcRegionX = (width >> 2) + random.nextInt(width >> 2);
srcRegionY = (height >> 2) + random.nextInt(height >> 2);
break;
}
case IMAGE_LOWER_LEFT_CORNER : //-- stipulate only to better view
default : {
srcRegionX = srcRegionY = 0;
break;
}
}
final Rectangle sourceRegion = new Rectangle(srcRegionX, srcRegionY, width >> 1, height >> 1);
final int subsampleX = random.nextInt((width >> 1) - 1) + 1;
final int subsampleY = random.nextInt((height >> 1) - 1) + 1;
final int subsampleXOffset = Math.max(0, random.nextInt(subsampleX) - 1);
final int subsampleYOffset = Math.max(0, random.nextInt(subsampleY) - 1);
final Point destOffset = new Point(random.nextInt(width), random.nextInt(height));
StringBuilder seedMessage = new StringBuilder(message);
seedMessage.append("\n").append("Test image size (w/h) : ").append(width).append("/").append(height).append("\n");
seedMessage.append("SourceRegion : ").append(sourceRegion).append("\n");
seedMessage.append("subsampleX/subsampleY : ").append(subsampleX).append("/").append(subsampleY).append("\n");
seedMessage.append("subsampleXOffset/subsampleYOffset : ").append(subsampleXOffset).append("/").append(subsampleYOffset).append("\n");
seedMessage.append("destOffset : ").append(destOffset).append("\n");
try {
final RenderedImage testedImage = effectuateTest(fileTest, sourceImage, sourceRegion,
subsampleX, subsampleY, subsampleXOffset, subsampleYOffset, destOffset);
checkImages(message, sourceImage, sourceRegion, subsampleX, subsampleXOffset, subsampleY, subsampleYOffset, destOffset, testedImage);
} catch (Exception e) {
fail(seedMessage.toString()+"Cause : "+e.getMessage());
}
}
/**
* Compare two {@link RenderedImage} and throw an assertion exception if comparison criterion are not respected.
*
* @param message in case of error first part of error message.
* @param sourceImage source image
* @param testedImage image which will be compare than source.
*/
protected void checkImage(final String message, final RenderedImage sourceImage, final RenderedImage testedImage) {
checkImages(message, sourceImage, null, 1, 0, 1, 0, null, testedImage);
}
/**
* Compare two {@link RenderedImage} in function of given parameter
* and throw an assertion exception if comparison criterion are not respected.
*
* @param message in case of error first part of error message.
* @param sourceImage source image
* @param sourceRegion area from source image which will be written or read.
* @param sourceXsubsampling subsampling in X direction.
* @param sourceXOffset offset in X direction
* @param sourceYsubsampling subsample in Y direction
* @param sourceYOffset offset in Y direction
* @param testedImage I/O result which will be test.
*/
protected void checkImages(final String message, final RenderedImage sourceImage, Rectangle sourceRegion,
final int sourceXsubsampling, final int sourceXOffset, final int sourceYsubsampling, final int sourceYOffset,
final Point destOffset, final RenderedImage testedImage) {
if (sourceRegion == null) sourceRegion = new Rectangle(0, 0, sourceImage.getWidth(), sourceImage.getHeight());
int destOffsetX = 0;
int destOffsetY = 0;
if (destOffset != null) {
destOffsetX = destOffset.x;
destOffsetY = destOffset.y;
}
sourceRegion.translate(sourceXOffset, sourceYOffset);
sourceRegion.width -= sourceXOffset;
sourceRegion.height -= sourceYOffset;
final int srcMinX = Math.max(sourceRegion.x, sourceImage.getMinX());
final int srcMinY = Math.max(sourceRegion.y, sourceImage.getMinY());
final int srcMaxX = Math.min(sourceRegion.x + sourceRegion.width, sourceImage.getMinX() + sourceImage.getWidth());
final int srcMaxY = Math.min(sourceRegion.y + sourceRegion.height, sourceImage.getMinY() + sourceImage.getHeight());
final int expectedWidth = (srcMaxX - srcMinX + sourceXsubsampling - 1) / sourceXsubsampling;
final int expectedHeight = (srcMaxY - srcMinY + sourceYsubsampling - 1) / sourceYsubsampling;
assertEquals(message+"image width ", destOffsetX + expectedWidth, testedImage.getWidth(), DEFAULT_TOLERANCE);
assertEquals(message+"image height ", destOffsetY + expectedHeight, testedImage.getHeight(), DEFAULT_TOLERANCE);
final SampleModel expectedSm = sourceImage.getSampleModel();
final int expectedNumband = expectedSm.getNumBands();
final SampleModel testedSm = testedImage .getSampleModel();
assertEquals(message+"numband : ", expectedNumband, testedSm.getNumBands());
assertEquals(message+"numDataElement : ", expectedSm.getNumDataElements(), testedSm.getNumDataElements());
assertEquals(message+"datatype : ", expectedSm.getDataType(), testedSm.getDataType());
final PixelIterator sourcePix = PixelIteratorFactory.createRowMajorIterator(sourceImage, sourceRegion);
final Rectangle testedRegion = new Rectangle(destOffsetX, destOffsetY, expectedWidth, expectedHeight);
final PixelIterator testedPix = PixelIteratorFactory.createRowMajorIterator(testedImage, testedRegion);
for (int y = sourceRegion.y; y < sourceRegion.y + sourceRegion.height; y += sourceYsubsampling) {
for (int x = sourceRegion.x; x < sourceRegion.x + sourceRegion.width; x += sourceXsubsampling) {
sourcePix.moveTo(x, y, 0);
int b = 0;
while (b++ < expectedNumband) {
testedPix.next();
assertEquals(message+"pixel at coordinate : (x, y, b) : ("+sourcePix.getX()+", "+sourcePix.getY()+", "+(b - 1)+") : ",
sourcePix.getSampleDouble(), testedPix.getSampleDouble(), DEFAULT_TOLERANCE);
sourcePix.next();
}
}
}
}
/**
* Build an appropriate {@link ImageTypeSpecifier} in function of given parameter.
*
* @param sampleType
* @param numBand expected band number
* @param photometricInterpretation
* @return {@link ImageTypeSpecifier}.
* @throws UnsupportedImageFormatException if photometricInterpretation or sampleFormat are not in accordance with other parameters.
*/
protected ImageTypeSpecifier buildImageTypeSpecifier(final SampleType sampleType, final int numBand,
final PhotometricInterpretation photometricInterpretation)
throws UnsupportedImageFormatException {
return ImageUtils.buildImageTypeSpecifier(sampleType, numBand, photometricInterpretation, PlanarConfiguration.INTERLEAVED, buildColorMapArray(sampleType));
}
/**
* Create an appropriate {@link RenderedImage} adapted for test which respond from all properties parameters.<br>
* PlanarConfiguration is define as {@link PlanarConfiguration#INTERLEAVED}.
*
* @param width width of created image.
* @param height height of created image
* @param sampleType
* @param numBand samples number for each pixel.
* @param photometricInterpretation
* @return created {@link RenderedImage}.
* @throws UnsupportedImageFormatException if sampleBitsSize has a wrong value of photometricInterpretation is not supported.
*/
protected WritableRenderedImage createImageTest(final int width, final int height,
final SampleType sampleType,
final int numBand,
final PhotometricInterpretation photometricInterpretation)
throws UnsupportedImageFormatException {
final ImageTypeSpecifier imgType = buildImageTypeSpecifier(sampleType, numBand, photometricInterpretation);
final BufferedImage buffImg = imgType.createBufferedImage(width, height);
fillImage(buffImg, imgType.getSampleModel().getDataType());
return buffImg;
}
/**
* Create a map array which contain random {@link Integer} values
* adapted to create an appropriate {@link IndexColorModel}.
*
* @param sampleType define sample type Byte, short ...
* @return map array.
*/
protected int[] buildColorMapArray(final SampleType sampleType) {
final int size = (SampleType.BYTE.equals(sampleType)) ? ((1 << Byte.SIZE) - 1) : (1 << Short.SIZE) - 1;
final int[] result = new int[size];
for (int i = 0; i < size; i++) {
result[i] = random.nextInt();
}
return result;
}
/**
* Fill all image samples, with random values in fonction of its {@link DataBuffer} type.
*
* @param image image which will be filled.
* @param databufferType type of internal samples data.
*/
private void fillImage(final WritableRenderedImage image, final int databufferType) {
if (databufferType == DataBuffer.TYPE_BYTE) {
final int numXTile = image.getNumXTiles();
final int numYTile = image.getNumYTiles();
final int tXOffset = image.getTileGridXOffset();
final int tYOffset = image.getTileGridYOffset();
for (int ty = tYOffset; ty < tYOffset + numYTile; ty++) {
for (int tx = tXOffset; tx < tXOffset + numXTile; tx++) {
final Raster raster = image.getTile(tx, ty);
final DataBuffer databuff = raster.getDataBuffer();
final int numBanks = databuff.getNumBanks();
for (int bank = 0; bank < numBanks; bank ++) {
random.nextBytes(((DataBufferByte)databuff).getData(bank));
}
}
}
return;
}
final PixelIterator pix = PixelIteratorFactory.createDefaultWriteableIterator(image, image);
switch (databufferType) {
case DataBuffer.TYPE_USHORT :
case DataBuffer.TYPE_SHORT :
case DataBuffer.TYPE_INT : while (pix.next()) pix.setSample(random.nextInt()); break;
case DataBuffer.TYPE_FLOAT : while (pix.next()) pix.setSampleFloat(random.nextFloat()); break;
case DataBuffer.TYPE_DOUBLE : while (pix.next()) pix.setSampleDouble(random.nextDouble()); break;
default: throw new AssertionError(databufferType);
}
}
}