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}