001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * http://www.geoapi.org 004 * 005 * Copyright (C) 2012-2019 Open Geospatial Consortium, Inc. 006 * All Rights Reserved. http://www.opengeospatial.org/ogc/legal 007 * 008 * Permission to use, copy, and modify this software and its documentation, with 009 * or without modification, for any purpose and without fee or royalty is hereby 010 * granted, provided that you include the following on ALL copies of the software 011 * and documentation or portions thereof, including modifications, that you make: 012 * 013 * 1. The full text of this NOTICE in a location viewable to users of the 014 * redistributed or derivative work. 015 * 2. Notice of any changes or modifications to the OGC files, including the 016 * date changes were made. 017 * 018 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE 019 * NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 020 * TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT 021 * THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY 022 * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 023 * 024 * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 025 * CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. 026 * 027 * The name and trademarks of copyright holders may NOT be used in advertising or 028 * publicity pertaining to the software without specific, written prior permission. 029 * Title to copyright in this software and any associated documentation will at all 030 * times remain with copyright holders. 031 */ 032package org.opengis.test.coverage.image; 033 034import java.util.Random; 035import java.io.Closeable; 036import java.io.IOException; 037import java.awt.Rectangle; 038import java.awt.image.RenderedImage; 039import javax.imageio.IIOParam; 040 041 042/** 043 * Base class for all image I/O tests. 044 * 045 * @author Martin Desruisseaux (Geomatys) 046 * @version 3.1 047 * @since 3.1 048 */ 049public strictfp abstract class ImageIOTestCase extends ImageBackendTestCase { 050 /** 051 * Default number of iterations when testing the image read operations. 052 */ 053 static final int DEFAULT_NUM_ITERATIONS = 10; 054 055 /** 056 * {@code true} if the reader or writer takes in account the parameter value given to 057 * {@link IIOParam#setSourceRegion(Rectangle)}. The default value is {@code true}. 058 * Subclasses can set this flag to {@code false} when testing an incomplete implementation. 059 */ 060 protected boolean isSubregionSupported = true; 061 062 /** 063 * {@code true} if the reader or writer takes in account the two first parameter values given to 064 * {@link IIOParam#setSourceSubsampling(int, int, int, int)}. The default value is {@code true}. 065 * Subclasses can set this flag to {@code false} when testing an incomplete implementation. 066 */ 067 protected boolean isSubsamplingSupported = true; 068 069 /** 070 * {@code true} if the reader or writer takes in account the two last parameter values given to 071 * {@link IIOParam#setSourceSubsampling(int, int, int, int)}. The default value is {@code true}. 072 * Subclasses can set this flag to {@code false} when testing an incomplete implementation. 073 */ 074 protected boolean isSubsamplingOffsetSupported = true; 075 076 /** 077 * {@code true} if the reader or writer takes in account the parameter value given to 078 * {@link IIOParam#setSourceBands(int[])}. The default value is {@code true}. 079 * Subclasses can set this flag to {@code false} if this feature can not be tested for 080 * the current implementation. 081 * 082 * <p>Note that this feature can not be tested with some standard readers like PNG, because 083 * those readers require an explicit destination image to be specified if the number of bands 084 * to read differs from the number of bands in the source image.</p> 085 */ 086 protected boolean isSourceBandsSupported = true; 087 088 /** 089 * The tolerance threshold to use when comparing floating point numbers. The default value 090 * is 0. Subclasses can relax this tolerance threshold if needed. 091 * 092 * <p>This threshold applies only to values of type {@code float} and {@code double}; 093 * it doesn't apply to integer types.</p> 094 */ 095 protected double sampleToleranceThreshold; 096 097 /** 098 * The random number generator. 099 */ 100 final Random random; 101 102 /** 103 * Creates a new test case using a default random number generator. 104 * The sub-regions, sub-samplings and source bands will be different 105 * for every test execution. If reproducible subsetting sequences are 106 * needed, use the {@link #ImageIOTestCase(long)} constructor instead. 107 */ 108 protected ImageIOTestCase() { 109 random = new Random(); 110 } 111 112 /** 113 * Creates a new test case using a random number generator initialized to the given seed. 114 * 115 * @param seed the initial seed for the random numbers generator. Use a constant value if 116 * the tests need to be reproduced with the same sequence of image parameters. 117 */ 118 protected ImageIOTestCase(final long seed) { 119 random = new Random(seed); 120 } 121 122 /** 123 * Returns an iterator on a random region, subsampling and source bands of the given image. 124 * The selected parameters are set in the given {@code param} object. 125 * 126 * @param image the image for which to returns an iterator. 127 * @param param the parameter where to set the random region, subsampling and source bands. 128 */ 129 final PixelIterator getIteratorOnRandomSubset(final RenderedImage image, final IIOParam param) { 130 final Rectangle region = getBounds(image); 131 if (isSubregionSupported) { 132 final int dx = random.nextInt(region.width); 133 final int dy = random.nextInt(region.height); 134 region.x += random.nextInt(region.width - dx); 135 region.y += random.nextInt(region.height - dy); 136 region.width = dx + 1; 137 region.height = dy + 1; 138 param.setSourceRegion(region); 139 } 140 int xSubsampling=1, ySubsampling=1; 141 if (isSubsamplingSupported) { 142 xSubsampling = random.nextInt(region.width /2 + 1) + 1; 143 ySubsampling = random.nextInt(region.height/2 + 1) + 1; 144 int xOffset=0, yOffset=0; 145 if (isSubsamplingOffsetSupported) { 146 xOffset = random.nextInt(xSubsampling); 147 yOffset = random.nextInt(ySubsampling); 148 region.x += xOffset; 149 region.y += yOffset; 150 region.width -= xOffset; 151 region.height -= yOffset; 152 } 153 param.setSourceSubsampling(xSubsampling, ySubsampling, xOffset, yOffset); 154 } 155 int[] sourceBands = null; 156 if (isSourceBandsSupported) { 157 final int numBands = image.getSampleModel().getNumBands(); 158 sourceBands = new int[random.nextInt(numBands) + 1]; 159 for (int i=0; i<sourceBands.length; i++) { 160 int band; 161 do band = random.nextInt(numBands); 162 while (contains(sourceBands, i, band)); 163 sourceBands[i] = band; 164 } 165 param.setSourceBands(sourceBands); 166 } 167 return new PixelIterator(image, region, xSubsampling, ySubsampling, sourceBands); 168 } 169 170 /** 171 * Closes the given input or output stream if it implements the {@link Closeable} interface. 172 * Do nothing otherwise. 173 * 174 * @param stream the input or output stream to close, or {@code null} if none. 175 * @throws IOException if an error occurred while closing the stream. 176 */ 177 static void close(final Object stream) throws IOException { 178 if (stream instanceof Closeable) { 179 ((Closeable) stream).close(); 180 } 181 } 182}