/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2001-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; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; import java.awt.image.RasterFormatException; import javax.media.jai.PlanarImage; import javax.media.jai.iterator.RectIter; import javax.media.jai.iterator.RectIterFactory; import javax.media.jai.iterator.WritableRectIter; /** * A {@linkplain WritableRectIter writable iterator} that read pixel values from an image, and * write pixel values to a different image. Every {@code get} methods read values from the * <cite>source</cite> image specified at {@linkplain #create creation time}. Every {@code set} * methods write values to the <cite>destination</cite> image specified at {@linkplain #create * creation time}, which may or may not be the same than the <cite>source</cite> image. This is * different than the usual {@link WritableRectIter} contract, which read and write values in * the same image. * <p> * The {@code create(...)} methods return an instance of {@code TransfertRectIter} only if the * source and target rasters are different. Callers can use this contract for optimizing their * code. For example implementations of {@link javax.media.jai.PointOpImage} that copy some * pixel values from source to target rasters can skip the copy phase if the iterator is not * an instance of {@code TransfertRectIter}. * * @author Martin Desruisseaux (IRD, Geomatys) * @version 3.11 * * @since 2.3 * @module */ public class TransfertRectIter implements WritableRectIter { /** * The string for error message. * * @todo Localize. */ private static final String ERROR = "Size mismatch"; /** * The iterator to use for reading from the source. * * @since 3.11 */ protected final RectIter src; /** * The iterator to use for writing to the destination destination. * * @since 3.11 */ protected final WritableRectIter dst; /** * Constructs a {@code TransfertRectIter} object. * * @param src The iterator to use for reading from the source. * @param dst The iterator to use for writing to the destination destination. * * @since 3.11 */ protected TransfertRectIter(final RectIter src, final WritableRectIter dst) { this.src = src; this.dst = dst; } /** * Creates a {@code WritableRectIter} for the specified source and destination iterator. * The two iterators must iterate over a rectangle of the same size, otherwise a * {@link RasterFormatException} will be thrown during the iteration. * * @param src The source iterator. * @param dst The destination iterator. * @return An iterator that read samples from {@code src} and write samples to {@code dst}. * If {@code src == dst}, then the destination iterator itself is returned. */ public static WritableRectIter create(final RectIter src, final WritableRectIter dst) { if (src == dst) { return dst; } return new TransfertRectIter(src, dst); } /** * Creates a {@code WritableRectIter} for the specified source and destination images. * The given rectangle must be in the bounds of both images (this will not be verified). * * @param src The source image. * @param dst The destination image. * @param bounds The region of the images to iterate over. * @return An iterator that read samples from {@code src} and write samples to {@code dst}. * It will be an instance of {@code TransfertRectIter} if and only if the source * and destination images are not the same. * * @since 3.00 */ public static WritableRectIter create(RenderedImage src, WritableRenderedImage dst, Rectangle bounds) { if (dst instanceof BufferedImage) { /* * BufferedImage are always backed by a single raster. Consequently we are better to * delegate the work to create(RenderedImage, WritableRaster, Rectangle), which will * detects if the source uses the same raster than the destination. */ return create(src, ((BufferedImage) dst).getRaster(), bounds); } WritableRectIter iter = RectIterFactory.createWritable(dst, bounds); if (src != dst) { iter = new TransfertRectIter(RectIterFactory.create(src, bounds), iter); } return iter; } /** * Creates a {@code WritableRectIter} for the specified source image and destination raster. * The given rectangle must be in the bounds of both arguments (this will not be verified). * * @param src The source image. * @param dst The destination raster. * @param bounds The region of the image or raster to iterate over. * @return An iterator that read samples from {@code src} and write samples to {@code dst}. * It will be an instance of {@code TransfertRectIter} if and only if the source * and destination rasters are not the same. * * @since 3.00 */ public static WritableRectIter create(RenderedImage src, WritableRaster dst, Rectangle bounds) { final Raster tile = uniqueTile(src, bounds); if (tile == dst) { return create(tile, dst, bounds); } return new TransfertRectIter(RectIterFactory.create(src, bounds), RectIterFactory.createWritable(dst, bounds)); } /** * Creates a {@code WritableRectIter} for the specified source and destination rasters. * The given rectangle must be in the bounds of both rasters (this will not be verified). * * @param src The source raster. * @param dst The destination raster. * @param bounds The region of the rasters to iterate over. * @return An iterator that read sample from {@code src} and write sample to {@code dst}. * It will be an instance of {@code TransfertRectIter} if and only if the source * and destination rasters are not the same. * * @since 3.00 */ public static WritableRectIter create(Raster src, WritableRaster dst, Rectangle bounds) { WritableRectIter iter = RectIterFactory.createWritable(dst, bounds); if (src != dst) { iter = new TransfertRectIter(RectIterFactory.create(src, bounds), iter); } return iter; } /** * If the given bounds encompass only one tile of the given image, returns that tile. * Otherwise returns {@code null}. The given bounds would be inside the image bounds; * this will not be verified. * * @param image The image for which to get a singleton tile. * @param bounds The bounds of the region of interest, or {@code null} for the whole image. * @return The raster inside the given bounds, or {@code null} if there is more than one raster * in those bounds. */ private static Raster uniqueTile(final RenderedImage image, final Rectangle bounds) { if (bounds == null) { if (image.getNumXTiles() == 1 && image.getNumYTiles() == 1) { return image.getTile(image.getMinTileX(), image.getMinTileY()); } } else { int x = bounds.x; final int width = image.getTileWidth(); final int xOffset = image.getTileGridXOffset(); final int tx = PlanarImage.XToTileX(x, xOffset, width); x -= PlanarImage.tileXToX(tx, xOffset, width); if (x + bounds.width <= width) { int y = bounds.y; final int height = image.getTileHeight(); final int yOffset = image.getTileGridYOffset(); final int ty = PlanarImage.YToTileY(y, yOffset, height); y -= PlanarImage.tileYToY(ty, yOffset, height); if (y + bounds.height <= height) { return image.getTile(tx, ty); } } } return null; } /** * Sets the iterator to the first line of its bounding rectangle. */ @Override public void startLines() { src.startLines(); dst.startLines(); } /** * Sets the iterator to the leftmost pixel of its bounding rectangle. */ @Override public void startPixels() { src.startPixels(); dst.startPixels(); } /** * Sets the iterator to the first band of the image. */ @Override public void startBands() { src.startBands(); dst.startBands(); } /** * Jumps downward num lines from the current position. * * @param num The number of lines to jump. */ @Override public void jumpLines(int num) { src.jumpLines(num); dst.jumpLines(num); } /** * Jumps rightward num pixels from the current position. * * @param num The number of pixels to jump. */ @Override public void jumpPixels(int num) { src.jumpPixels(num); dst.jumpPixels(num); } /** * Sets the iterator to the next line of the image. */ @Override public void nextLine() { src.nextLine(); dst.nextLine(); } /** * Sets the iterator to the next pixel in image (that is, move rightward). */ @Override public void nextPixel() { src.nextPixel(); dst.nextPixel(); } /** * Sets the iterator to the next band in the image. */ @Override public void nextBand() { src.nextBand(); dst.nextBand(); } /** * Sets the iterator to the next line in the image, * and returns {@code true} if the bottom row of the bounding rectangle has been passed. * * @return {@code true} if the iteration over lines is finished. */ @Override public boolean nextLineDone() { boolean check = src.nextLineDone(); if (check == dst.nextLineDone()) { return check; } throw new RasterFormatException(ERROR); } /** * Sets the iterator to the next pixel in the image (that is, move rightward). * * @return {@code true} if the iteration over pixels is finished. */ @Override public boolean nextPixelDone() { boolean check = src.nextPixelDone(); if (check == dst.nextPixelDone()) { return check; } throw new RasterFormatException(ERROR); } /** * Sets the iterator to the next band in the image, * and returns {@code true} if the max band has been exceeded. * * @return {@code true} if the iteration over bands is finished. */ @Override public boolean nextBandDone() { boolean check = src.nextBandDone(); if (check == dst.nextBandDone()) { return check; } throw new RasterFormatException(ERROR); } /** * Returns {@code true} if the bottom row of the bounding rectangle has been passed. * * @return {@code true} if the iteration over lines is finished. */ @Override public boolean finishedLines() { boolean check = src.finishedLines(); if (check == dst.finishedLines()) { return check; } throw new RasterFormatException(ERROR); } /** * Returns {@code true} if the right edge of the bounding rectangle has been passed. * * @return {@code true} if the iteration over pixels is finished. */ @Override public boolean finishedPixels() { boolean check = src.finishedPixels(); if (check == dst.finishedPixels()) { return check; } throw new RasterFormatException(ERROR); } /** * Returns {@code true} if the max band in the image has been exceeded. * * @return {@code true} if the iteration over bands is finished. */ @Override public boolean finishedBands() { boolean check = src.finishedBands(); if (check == dst.finishedBands()) { return check; } throw new RasterFormatException(ERROR); } /** * Returns the samples of the current pixel from the image in an array of int. * * @param array The array where to store the sample values. * @return The array of sample values. */ @Override public int[] getPixel(int[] array) { return src.getPixel(array); } /** * Returns the samples of the current pixel from the image in an array of float. * * @param array The array where to store the sample values. * @return The array of sample values. */ @Override public float[] getPixel(float[] array) { return src.getPixel(array); } /** * Returns the samples of the current pixel from the image in an array of double. * * @param array The array where to store the sample values. * @return The array of sample values. */ @Override public double[] getPixel(double[] array) { return src.getPixel(array); } /** * Returns the current sample as an integer. * * @return The current sample value. */ @Override public int getSample() { return src.getSample(); } /** * Returns the specified sample of the current pixel as an integer. * * @param b The band for which to get the sample value. * @return The sample value at the given band. */ @Override public int getSample(int b) { return src.getSample(b); } /** * Returns the current sample as a float. * * @return The current sample value. */ @Override public float getSampleFloat() { return src.getSampleFloat(); } /** * Returns the specified sample of the current pixel as a float. * * @param b The band for which to get the sample value. * @return The sample value at the given band. */ @Override public float getSampleFloat(int b) { return src.getSampleFloat(b); } /** * Returns the current sample as a double. * * @return The current sample value. */ @Override public double getSampleDouble() { return src.getSampleDouble(); } /** * Returns the specified sample of the current pixel as a double. * * @param b The band for which to get the sample value. * @return The sample value at the given band. */ @Override public double getSampleDouble(int b) { return src.getSampleDouble(b); } /** * Sets all samples of the current pixel to a set of int values. * * @param array The new pixel values. */ @Override public void setPixel(int[] array) { dst.setPixel(array); } /** * Sets all samples of the current pixel to a set of float values. * * @param array The new pixel values. */ @Override public void setPixel(float[] array) { dst.setPixel(array); } /** * Sets all samples of the current pixel to a set of double values. * * @param array The new pixel values. */ @Override public void setPixel(double[] array) { dst.setPixel(array); } /** * Sets the current sample to an integral value. * * @param s The new sample value. */ @Override public void setSample(int s) { dst.setSample(s); } /** * Sets the current sample to a float value. * * @param s The new sample value. */ @Override public void setSample(float s) { dst.setSample(s); } /** * Sets the current sample to a double value. * * @param s The new sample value. */ @Override public void setSample(double s) { dst.setSample(s); } /** * Sets the specified sample of the current pixel to an integral value. * * @param b The band for which to set the sample value. * @param s The new sample value. */ @Override public void setSample(int b, int s) { dst.setSample(b, s); } /** * Sets the specified sample of the current pixel to a float value. * * @param b The band for which to set the sample value. * @param s The new sample value. */ @Override public void setSample(int b, float s) { dst.setSample(b, s); } /** * Sets the specified sample of the current pixel to a double value. * * @param b The band for which to set the sample value. * @param s The new sample value. */ @Override public void setSample(int b, double s) { dst.setSample(b, s); } }