| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.sis.image; |
| |
| import java.util.Arrays; |
| import java.util.Optional; |
| import java.nio.Buffer; |
| import java.awt.Point; |
| import java.awt.Dimension; |
| import java.awt.Rectangle; |
| import java.awt.image.DataBuffer; |
| import java.awt.image.Raster; |
| import java.awt.image.RenderedImage; |
| import java.awt.image.WritableRaster; |
| import java.awt.image.WritableRenderedImage; |
| import java.awt.image.SampleModel; |
| import java.awt.image.SinglePixelPackedSampleModel; |
| import java.awt.image.MultiPixelPackedSampleModel; |
| import java.util.NoSuchElementException; |
| import org.apache.sis.util.resources.Errors; |
| import org.apache.sis.util.ArgumentChecks; |
| import org.apache.sis.measure.NumberRange; |
| |
| import static java.lang.Math.floorDiv; |
| import static org.apache.sis.internal.util.Numerics.ceilDiv; |
| |
| |
| /** |
| * An iterator over sample values in a raster or an image. This iterator makes easier to read and write efficiently |
| * pixel or sample values. The iterator {@linkplain RenderedImage#getTile(int,int) acquires tiles} and releases them |
| * automatically. Unless otherwise specified, iterators are free to use an {@linkplain #getIterationOrder() iteration |
| * order} that minimize the "acquire / release tile" operations (in other words, iterations are not necessarily from |
| * left to right). Iteration can be performed on a complete image or only a sub-region of it. Some optimized iterator |
| * implementations exist for a few commonly used {@linkplain java.awt.image.SampleModel sample models}. |
| * |
| * <div class="note"><b>Example:</b> |
| * {@preformat java |
| * PixelIterator it = PixelIterator.create(image); |
| * double[] samples = null; |
| * while (it.next()) { |
| * samples = it.getPixel(samples); // Get values in all bands. |
| * // Perform computation here... |
| * } |
| * } |
| * </div> |
| * |
| * @author Rémi Maréchal (Geomatys) |
| * @author Martin Desruisseaux (Geomatys) |
| * @author Johann Sorel (Geomatys) |
| * @version 1.0 |
| * @since 1.0 |
| * @module |
| */ |
| public abstract class PixelIterator { |
| /** |
| * The image in which iteration is occurring, or {@code null} if none. |
| * If {@code null}, then {@link #currentRaster} must be non-null. |
| */ |
| final RenderedImage image; |
| |
| /** |
| * The current raster in which iteration is occurring. This may change when the iterator |
| * reaches a new {@link #image} tile. May be {@code null} if not yet determined. |
| * |
| * @see RenderedImage#getTile(int, int) |
| */ |
| Raster currentRaster; |
| |
| /** |
| * Number of bands in all tiles in the {@linkplain #image}. |
| * The {@link #currentRaster} shall always have this number of bands. |
| */ |
| final int numBands; |
| |
| /** |
| * The domain, in pixel coordinates, of the region traversed by this pixel iterator. |
| * This may be smaller than the image or raster bounds, but not greater. |
| * The lower values are inclusive and the upper values exclusive. |
| * |
| * @see #getDomain() |
| */ |
| final int lowerX, lowerY, upperX, upperY; |
| |
| /** |
| * Size of all tiles in the {@link #image}. |
| */ |
| final int tileWidth, tileHeight; |
| |
| /** |
| * The X and Y coordinate of the upper-left pixel of tile (0,0). |
| * Note that tile (0,0) may not actually exist. |
| */ |
| final int tileGridXOffset, tileGridYOffset; |
| |
| /** |
| * The domain, in tile coordinates, of the region traversed by this pixel iterator. |
| * This may be smaller than the image or raster tile grid bounds, but not greater. |
| * The lower values are inclusive and the upper values exclusive. |
| */ |
| final int tileLowerX, tileLowerY, tileUpperX, tileUpperY; |
| |
| /** |
| * Size of the window to use in {@link #createWindow(TransferType)} method, or {@code 0} if none. |
| */ |
| final int windowWidth, windowHeight; |
| |
| /** |
| * Creates an iterator for the given region in the given raster. |
| * |
| * @param data the raster which contains the sample values on which to iterate. |
| * @param subArea the raster region where to perform the iteration, or {@code null} |
| * for iterating over all the raster domain. |
| * @param window size of the window to use in {@link #createWindow(TransferType)} method, or {@code null} if none. |
| */ |
| PixelIterator(final Raster data, final Rectangle subArea, final Dimension window) { |
| final Rectangle bounds; |
| image = null; |
| currentRaster = data; |
| numBands = data.getNumBands(); |
| tileWidth = data.getWidth(); |
| tileHeight = data.getHeight(); |
| tileGridXOffset = data.getMinX(); |
| tileGridYOffset = data.getMinY(); |
| tileLowerX = 0; // In this case only one raster: tile index is fixed to 0. |
| tileLowerY = 0; |
| tileUpperX = 1; |
| tileUpperY = 1; |
| bounds = intersection(tileGridXOffset, tileGridYOffset, tileWidth, tileHeight, subArea, window); |
| lowerX = bounds.x; |
| lowerY = bounds.y; |
| upperX = Math.addExact(lowerX, bounds.width); |
| upperY = Math.addExact(lowerY, bounds.height); |
| windowWidth = (window != null) ? window.width : 0; |
| windowHeight = (window != null) ? window.height : 0; |
| } |
| |
| /** |
| * Creates an iterator for the given region in the given image. |
| * |
| * @param data the image which contains the sample values on which to iterate. |
| * @param subArea the image region where to perform the iteration, or {@code null} |
| * for iterating over all the image domain. |
| * @param window size of the window to use in {@link #createWindow(TransferType)} method, or {@code null} if none. |
| */ |
| PixelIterator(final RenderedImage data, final Rectangle subArea, final Dimension window) { |
| final Rectangle bounds; |
| image = data; |
| numBands = data.getSampleModel().getNumBands(); |
| tileWidth = data.getTileWidth(); |
| tileHeight = data.getTileHeight(); |
| tileGridXOffset = data.getTileGridXOffset(); |
| tileGridYOffset = data.getTileGridYOffset(); |
| bounds = intersection(data.getMinX(), data.getMinY(), data.getWidth(), data.getHeight(), subArea, window); |
| lowerX = bounds.x; |
| lowerY = bounds.y; |
| upperX = Math.addExact(lowerX, bounds.width); |
| upperY = Math.addExact(lowerY, bounds.height); |
| tileLowerX = floorDiv(Math.subtractExact(lowerX, tileGridXOffset), tileWidth); |
| tileLowerY = floorDiv(Math.subtractExact(lowerY, tileGridYOffset), tileHeight); |
| tileUpperX = ceilDiv(Math.subtractExact(upperX, tileGridXOffset), tileWidth); |
| tileUpperY = ceilDiv(Math.subtractExact(upperY, tileGridYOffset), tileHeight); |
| windowWidth = (window != null) ? window.width : 0; |
| windowHeight = (window != null) ? window.height : 0; |
| } |
| |
| /** |
| * Computes the intersection between the given bounds and and {@code subArea} if {@code subArea} is non-null. |
| * If the result is empty, then the width and/or height are set to zero (not negative). |
| */ |
| private static Rectangle intersection(int x, int y, int width, int height, Rectangle subArea, Dimension window) { |
| if (window != null) { |
| ArgumentChecks.ensureBetween("window.width", 1, width, window.width); |
| ArgumentChecks.ensureBetween("window.height", 1, height, window.height); |
| width -= (window.width - 1); |
| height -= (window.height - 1); |
| } |
| Rectangle bounds = new Rectangle(x, y, width, height); |
| if (subArea != null) { |
| bounds = bounds.intersection(subArea); |
| if (bounds.width < 0) bounds.width = 0; |
| if (bounds.height < 0) bounds.height = 0; |
| } |
| return bounds; |
| } |
| |
| /** |
| * Builds pixel iterators for specified region of interest, window size or iteration order. |
| * By default, the builder creates iterators for all pixels in the given raster or image, |
| * with unspecified iteration order. Users can invoke setter methods for specifying |
| * desired behavior for the iterators to create. |
| * |
| * <div class="note"><b>Example:</b> |
| * {@preformat java |
| * PixelIterator iterator = new PixelIterator.Builder().setRegionOfInterest(new Rectangle(10, 10, 5, 5).create(image); |
| * } |
| * </div> |
| */ |
| public static class Builder { |
| /** |
| * The region where to perform the iteration, or {@code null} for iterating over all the domain. |
| */ |
| private Rectangle subArea; |
| |
| /** |
| * Size of the window to use in {@link PixelIterator#createWindow(TransferType)} method, |
| * or {@code null} if none. |
| */ |
| private Dimension window; |
| |
| /** |
| * The desired iteration order, or {@code null} for a default order. |
| */ |
| private SequenceType order; |
| |
| /** |
| * Creates a new iterator builder with no region of interest, no window size and default iterator order. |
| */ |
| public Builder() { |
| } |
| |
| /** |
| * Sets the region (in pixel coordinates) where to perform the iteration. |
| * By default, iterators will traverse all pixels in the given image or raster. |
| * |
| * @param subArea region where to iterator, or {@code null} for iterating over all image domain. |
| * @return {@code this} for method call chaining. |
| */ |
| public Builder setRegionOfInterest(final Rectangle subArea) { |
| this.subArea = subArea; |
| return this; |
| } |
| |
| /** |
| * Sets the size of the window to use in {@link PixelIterator#createWindow(TransferType)} method. |
| * By default, iterators do not create windows. |
| * |
| * @param window the window size, or {@code null} if no window will be created. |
| * @return {@code this} for method call chaining. |
| */ |
| public Builder setWindowSize(final Dimension window) { |
| this.window = window; |
| return this; |
| } |
| |
| /** |
| * Sets the desired iteration order. |
| * The {@code order} argument can have the following values: |
| * |
| * <table class="sis"> |
| * <caption>Supported iteration order</caption> |
| * <tr><th>Value</th> <th>Iteration order</th> <th>Supported on</th></tr> |
| * <tr><td>{@code null}</td> <td>Most efficient iteration order.</td> <td>Image and raster</td></tr> |
| * <tr><td>{@link SequenceType#LINEAR}</td> <td>From left to right, then from top to bottom.</td> <td>Raster only</td></tr> |
| * </table> |
| * |
| * Any other {@code order} value will cause an {@link IllegalArgumentException} to be thrown. |
| * More iteration orders may be supported in future Apache SIS versions. |
| * |
| * @param order the desired iteration order, or {@code null} for a default order. |
| * @return {@code this} for method call chaining. |
| */ |
| final Builder setIteratorOrder(final SequenceType order) { |
| if (order == null || order.equals(SequenceType.LINEAR)) { |
| this.order = order; |
| } else { |
| throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, order)); |
| } |
| return this; |
| } |
| |
| /** |
| * Creates a read-only iterator for the given raster. |
| * |
| * @param data the raster which contains the sample values on which to iterate. |
| * @return a new iterator traversing pixels in the given raster. |
| */ |
| public PixelIterator create(final Raster data) { |
| ArgumentChecks.ensureNonNull("data", data); |
| if (order == SequenceType.LINEAR) { |
| return new LinearIterator(data, null, subArea, window); |
| } else if (order != null) { |
| throw new IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1, order)); |
| } |
| // TODO: check here for cases that we can optimize (after we ported corresponding implementations). |
| return new DefaultIterator(data, null, subArea, window); |
| } |
| |
| /** |
| * Creates a read-only iterator for the given image. |
| * |
| * @param data the image which contains the sample values on which to iterate. |
| * @return a new iterator traversing pixels in the given image. |
| */ |
| public PixelIterator create(final RenderedImage data) { |
| ArgumentChecks.ensureNonNull("data", data); |
| if (order == SequenceType.LINEAR) { |
| return new LinearIterator(data, null, subArea, window); |
| } else if (order != null) { |
| throw new IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1, order)); |
| } |
| // TODO: check here for cases that we can optimize (after we ported corresponding implementations). |
| return new DefaultIterator(data, null, subArea, window); |
| } |
| |
| /** |
| * Creates a read/write iterator for the given raster. |
| * |
| * @param data the raster which contains the sample values on which to iterate. |
| * @return a new iterator traversing pixels in the given raster. |
| */ |
| public WritablePixelIterator createWritable(final WritableRaster data) { |
| ArgumentChecks.ensureNonNull("data", data); |
| return createWritable(data, data); |
| } |
| |
| /** |
| * Creates a read/write iterator for the given image. |
| * |
| * @param data the image which contains the sample values on which to iterate. |
| * @return a new iterator traversing pixels in the given image. |
| */ |
| public WritablePixelIterator createWritable(final WritableRenderedImage data) { |
| ArgumentChecks.ensureNonNull("data", data); |
| return createWritable(data, data); |
| } |
| |
| /** |
| * Creates an iterator which will read and write in two different rasters. |
| * |
| * @param input the raster which contains the sample values to read. |
| * @param output the raster where to write the sample values. Can be the same than {@code input}. |
| * @return a new writable iterator. |
| */ |
| public WritablePixelIterator createWritable(final Raster input, final WritableRaster output) { |
| ArgumentChecks.ensureNonNull("input", input); |
| ArgumentChecks.ensureNonNull("output", output); |
| if (order == SequenceType.LINEAR) { |
| return new LinearIterator(input, output, subArea, window); |
| } else if (order != null) { |
| throw new IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1, order)); |
| } |
| // TODO: check here for cases that we can optimize (after we ported corresponding implementations). |
| return new DefaultIterator(input, output, subArea, window); |
| } |
| |
| /** |
| * Creates an iterator which will read and write in two different images. |
| * |
| * @param input the image which contains the sample values to read. |
| * @param output the image where to write the sample values. Can be the same than {@code input}. |
| * @return a new writable iterator. |
| */ |
| public WritablePixelIterator createWritable(final RenderedImage input, final WritableRenderedImage output) { |
| ArgumentChecks.ensureNonNull("input", input); |
| ArgumentChecks.ensureNonNull("output", output); |
| if (order == SequenceType.LINEAR) { |
| return new LinearIterator(input, output, subArea, window); |
| } else if (order != null) { |
| throw new IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1, order)); |
| } |
| // TODO: check here for cases that we can optimize (after we ported corresponding implementations). |
| return new DefaultIterator(input, output, subArea, window); |
| } |
| } |
| |
| /** |
| * Creates an iterator for all pixels in the given image. |
| * This is a convenience method for {@code new Builder().create(data)}. |
| * |
| * @param data the image which contains the sample values on which to iterate. |
| * @return a new iterator traversing all pixels in the given image, in arbitrary order. |
| */ |
| public static PixelIterator create(final RenderedImage data) { |
| return new Builder().create(data); |
| } |
| |
| /** |
| * Returns {@code true} if this iterator can write pixel values (after cast to {@code WritablePixelIterator}). |
| * This method should be used instead than {@code instanceof} check because, for some implementations, being |
| * an instance of {@code WritablePixelIterator} is not a sufficient condition. |
| * |
| * @return {@code true} if this iterator can safely be casted to {@link WritablePixelIterator} and used for |
| * writing pixel values. |
| */ |
| public boolean isWritable() { |
| return false; |
| } |
| |
| /** |
| * Returns the most efficient type ({@code int}, {@code float} or {@code double}) for transferring data between the |
| * underlying rasters and this iterator. The transfer type is not necessarily the storage type used by the rasters. |
| * For example {@code int} values will be used for transferring data even if the underlying rasters store all sample |
| * values as {@code byte}s. |
| * |
| * <p>The transfer type is only a hint since all iterator methods work for any type (conversions are applied as needed). |
| * However if this method returns {@link TransferType#INT}, then {@link #getSample(int)} and {@link #getPixel(int[])} |
| * will be slightly more efficient than equivalent methods for other types. Conversely if this method returns |
| * {@link TransferType#DOUBLE}, then {@link #getSampleDouble(int)} will be both more efficient and avoid accuracy lost.</p> |
| * |
| * @return the most efficient data type for transferring data. |
| */ |
| public TransferType<?> getTransferType() { |
| return TransferType.valueOf(image != null ? image.getSampleModel().getTransferType() : currentRaster.getTransferType()); |
| } |
| |
| /** |
| * Returns the range of sample values that can be stored in each band of the rendered image or raster. |
| * The ranges depend on the data type (byte, integer, <i>etc.</i>) and the number of bits per sample. |
| * If the samples are stored as floating point values, then the ranges are infinite (unbounded). |
| * |
| * <p>Usually, the range is the same for all bands. A situation where the ranges may differ is when an |
| * image uses {@link SinglePixelPackedSampleModel}, in which case the number of bits per pixel may vary |
| * for different bands.</p> |
| * |
| * @return the ranges of valid sample values for each band. Ranges may be {@linkplain NumberRange#isBounded() unbounded}. |
| */ |
| public NumberRange<?>[] getSampleRanges() { |
| final SampleModel model = (currentRaster != null) ? currentRaster.getSampleModel() : image.getSampleModel(); |
| final NumberRange<?>[] ranges = new NumberRange<?>[model.getNumBands()]; |
| final NumberRange<?> range; |
| if (model instanceof MultiPixelPackedSampleModel) { |
| /* |
| * This model supports only unsigned integer types: DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT |
| * or DataBuffer.TYPE_INT (considered unsigned in the context of this sample model). The number |
| * of bits per sample is defined by the "pixel bit stride". |
| */ |
| final int numBits = ((MultiPixelPackedSampleModel) model).getPixelBitStride(); |
| range = NumberRange.create(0, true, (1 << numBits) - 1, true); |
| } else if (model instanceof SinglePixelPackedSampleModel) { |
| /* |
| * This model supports only unsigned integer types: TYPE_BYTE, TYPE_USHORT, TYPE_INT (considered |
| * unsigned in the context of this sample model). The number of bits may vary for each band. |
| */ |
| final int[] masks = ((SinglePixelPackedSampleModel) model).getBitMasks(); |
| for (int i=0; i<masks.length; i++) { |
| final int numBits = Integer.bitCount(masks[i]); |
| ranges[i] = NumberRange.create(0, true, (1 << numBits) - 1, true); |
| } |
| return ranges; |
| } else { |
| /* |
| * For all other sample models, the range is determined by the data type. |
| * The following cases invoke the NumberRange constructor which best fit the data type. |
| */ |
| final int type = model.getDataType(); |
| switch (type) { |
| case DataBuffer.TYPE_BYTE: range = NumberRange.create((short) 0, true, (short) 0xFF, true); break; |
| case DataBuffer.TYPE_USHORT: range = NumberRange.create( 0, true, 0xFFFF, true); break; |
| case DataBuffer.TYPE_SHORT: range = NumberRange.create(Short. MIN_VALUE, true, Short. MAX_VALUE, true); break; |
| case DataBuffer.TYPE_INT: range = NumberRange.create(Integer.MIN_VALUE, true, Integer.MAX_VALUE, true); break; |
| case DataBuffer.TYPE_FLOAT: range = NumberRange.create(Float. NEGATIVE_INFINITY, false, Float. POSITIVE_INFINITY, false); break; |
| case DataBuffer.TYPE_DOUBLE: range = NumberRange.create(Double. NEGATIVE_INFINITY, false, Double. POSITIVE_INFINITY, false); break; |
| default: throw new IllegalStateException(Errors.format(Errors.Keys.UnknownType_1, type)); |
| } |
| } |
| Arrays.fill(ranges, range); |
| return ranges; |
| } |
| |
| /** |
| * Returns the order in which pixels are traversed. {@link SequenceType#LINEAR} means that pixels on the first |
| * row are traversed from left to right, then pixels on the second row from left to right, <i>etc.</i> |
| * An empty value means that the iteration order is unspecified. |
| * |
| * @return order in which pixels are traversed. |
| */ |
| abstract Optional<SequenceType> getIterationOrder(); |
| |
| /** |
| * Returns the number of bands (samples per pixel) in the image or raster. |
| * |
| * @return number of bands. |
| */ |
| public int getNumBands() { |
| return numBands; |
| } |
| |
| /** |
| * Returns the pixel coordinates of the region where this iterator is doing the iteration. |
| * If no region was specified at construction time, then this method returns the image or raster bounds. |
| * |
| * @return pixel coordinates of the iteration region. |
| */ |
| public Rectangle getDomain() { |
| return new Rectangle(lowerX, lowerY, upperX - lowerX, upperY - lowerY); |
| } |
| |
| /** |
| * Returns the column (x) and row (y) indices of the current pixel. |
| * The {@link #next()} or {@link #moveTo(int,int)} method must have been invoked before this method. |
| * Indices of the first pixel are not necessarily zero; they can even be negative. |
| * |
| * @return column and row indices of current iterator position. |
| * @throws IllegalStateException if this method is invoked before the first call to {@link #next()} |
| * or {@link #moveTo(int,int)}, or after {@code next()} returned {@code false}. |
| */ |
| public abstract Point getPosition(); |
| |
| /** |
| * Moves the pixel iterator to the given column (x) and row (y) indices. After this method invocation, |
| * the iterator state is as if the {@link #next()} method has been invoked just before to reach the |
| * specified position. |
| * |
| * <div class="note"><b>Usage example:</b> |
| * {@preformat java |
| * iterator.moveTo(x, y); |
| * do { |
| * int sample = iterator.getSample(band); |
| * // Use sample value here... |
| * } while (iterator.next()); |
| * } |
| * </div> |
| * |
| * @param x the column index of the pixel to make current. |
| * @param y the row index of the pixel to make current. |
| * @throws IndexOutOfBoundsException if the given indices are outside the iteration domain. |
| */ |
| public abstract void moveTo(int x, int y); |
| |
| /** |
| * Moves the iterator to the next pixel. A pixel iterator is initially positioned before the first pixel. |
| * The first call to {@code next()} makes the first pixel the current one; the second call makes the second |
| * pixel the current one, <i>etc.</i> The second pixel is not necessarily on the same row than the first one; |
| * iteration order is implementation dependent. |
| * |
| * <p>When a call to {@code next()} returns {@code false}, the iterator is positioned after the last pixel. |
| * Any invocation of a {@code getSample(int)} method will result in a {@link NoSuchElementException} to be |
| * thrown.</p> |
| * |
| * @return {@code true} if the current pixel is valid, or {@code false} if there is no more pixels. |
| * @throws IllegalStateException if this iterator already reached end of iteration in a previous call |
| * to {@code next()}, and {@link #rewind()} or {@link #moveTo(int,int)} have not been invoked. |
| */ |
| public abstract boolean next(); |
| |
| /** |
| * Returns the sample value in the specified band of current pixel, rounded toward zero. |
| * The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)} method must have |
| * been invoked successfully, before this {@code getSample(int)} method is invoked. If above condition is not met, |
| * then this method behavior is undefined: it may throw any runtime exception or return a meaningless value |
| * (there is no explicit bounds check for performance reasons). |
| * |
| * @param band the band for which to get the sample value. |
| * @return sample value in specified band of current pixel. |
| * |
| * @see Raster#getSample(int, int, int) |
| */ |
| public abstract int getSample(int band); |
| |
| /** |
| * Returns the sample value in the specified band of current pixel as a single-precision floating point number. |
| * The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)} method must have |
| * been invoked successfully, before this {@code getSampleFloat(int)} method is invoked. If above condition is |
| * not met, then this method behavior is undefined: it may throw any runtime exception or return a meaningless |
| * value (there is no explicit bounds check for performance reasons). |
| * |
| * @param band the band for which to get the sample value. |
| * @return sample value in specified band of current pixel. |
| * |
| * @see Raster#getSampleFloat(int, int, int) |
| */ |
| public abstract float getSampleFloat(int band); |
| |
| /** |
| * Returns the sample value in the specified band of current pixel, without precision lost. |
| * The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)} method must have |
| * been invoked successfully, before this {@code getSampleDouble(int)} method is invoked. If above condition is |
| * not met, then this method behavior is undefined: it may throw any runtime exception or return a meaningless |
| * value (there is no explicit bounds check for performance reasons). |
| * |
| * @param band the band for which to get the sample value. |
| * @return sample value in specified band of current pixel. |
| * |
| * @see Raster#getSampleDouble(int, int, int) |
| */ |
| public abstract double getSampleDouble(int band); |
| |
| /** |
| * Returns the sample values of current pixel for all bands. |
| * The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)} method must have |
| * been invoked successfully, before this {@code getPixel(…)} method is invoked. If above condition is not met, |
| * then this method behavior is undefined: it may throw any runtime exception or return a meaningless value |
| * (there is no explicit bounds check for performance reasons). |
| * |
| * @param dest a pre-allocated array where to store the sample values, or {@code null} if none. |
| * @return the sample values for current pixel. |
| * |
| * @see Raster#getPixel(int, int, int[]) |
| */ |
| public abstract int[] getPixel(int[] dest); |
| |
| /** |
| * Returns the sample values of current pixel for all bands. |
| * The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)} method must have |
| * been invoked successfully, before this {@code getPixel(…)} method is invoked. If above condition is not met, |
| * then this method behavior is undefined: it may throw any runtime exception or return a meaningless value |
| * (there is no explicit bounds check for performance reasons). |
| * |
| * @param dest a pre-allocated array where to store the sample values, or {@code null} if none. |
| * @return the sample values for current pixel. |
| * |
| * @see Raster#getPixel(int, int, float[]) |
| */ |
| public abstract float[] getPixel(float[] dest); |
| |
| /** |
| * Returns the sample values of current pixel for all bands. |
| * The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)} method must have |
| * been invoked successfully, before this {@code getPixel(…)} method is invoked. If above condition is not met, |
| * then this method behavior is undefined: it may throw any runtime exception or return a meaningless value |
| * (there is no explicit bounds check for performance reasons). |
| * |
| * @param dest a pre-allocated array where to store the sample values, or {@code null} if none. |
| * @return the sample values for current pixel. |
| * |
| * @see Raster#getPixel(int, int, double[]) |
| */ |
| public abstract double[] getPixel(double[] dest); |
| |
| /** |
| * Returns a moving window over the sample values in a rectangular region starting at iterator position. |
| * The <cite>window size</cite> must have been specified at {@code PixelIterator} construction time. |
| * The current iterator position is the window corner having the smallest <var>x</var> and <var>y</var> coordinates. |
| * This is typically, but not necessarily (depending on axis orientations) the window upper-left corner. |
| * Sample values are stored in a sequence of length |
| * <var>(number of bands)</var> × <var>(window width)</var> × <var>(window height)</var>. |
| * Values are always stored with band index varying fastest, then column index, then row index. |
| * Columns are traversed from left to right and rows are traversed from top to bottom |
| * (linear iteration order). |
| * That order is the same regardless the {@linkplain #getIterationOrder() iteration order} of this iterator. |
| * |
| * <div class="note"><b>Example:</b> |
| * for an RGB image, the 3 first values are the red, green and blue components of the pixel at |
| * {@linkplain #getPosition() current iterator position}. The 3 next values are the red, green |
| * and blue components of the pixel at the right of current iterator position (not necessarily |
| * the position where a call to {@link #next()} would have go), <i>etc.</i></div> |
| * |
| * Calls to {@link #next()} or {@link #moveTo(int,int)} followed by {@link Window#update()} |
| * replaces the window content with values starting at the new iterator position. |
| * Before the first {@link Window#update()} invocation, the window is filled with zero values. |
| * |
| * <p>If this iterator is used for |
| * {@linkplain WritablePixelIterator#setPixel(int[]) writing pixel values at current position}, |
| * those write operations may change the content of windows at {@linkplain #next() next positions} |
| * unless the iteration order of this iterator is {@link SequenceType#LINEAR}.</p> |
| * |
| * <div class="note"><b>Usage example:</b> |
| * following code creates an iterator over the full area of given image, then a window of 5×5 pixels. |
| * The window is moved over all the image area in iteration order. Inside the window, data are copied |
| * in linear order regardless the iteration order. |
| * |
| * {@preformat java |
| * PixelIterator it = create(image, null, new Dimension(5, 5), null); // Windows size will be 5×5 pixels. |
| * PixelIterator<FloatBuffer> window = it.createWindow(TransferType.FLOAT); |
| * FloatBuffer values = window.values; |
| * while (it.next()) { |
| * window.update(); |
| * while (buffer.hasRemaining()) { |
| * float sample = buffer.get(); |
| * // use the sample value here. |
| * } |
| * } |
| * } |
| * </div> |
| * |
| * @param <T> the type of the data buffer to use for transferring data. |
| * @param type the desired type of values ({@code int}, {@code float} or {@code double}). |
| * Use {@link #getTransferType()} if the most efficient type is desired. |
| * @return a window over the sample values in the underlying image or raster. |
| * |
| * @see Raster#getPixels(int, int, int, int, double[]) |
| */ |
| public abstract <T extends Buffer> Window<T> createWindow(TransferType<T> type); |
| |
| /** |
| * Contains the sample values in a moving window over the image. Windows are created by calls to |
| * {@link PixelIterator#createWindow(TransferType)} and sample values are stored in {@link Buffer}s. |
| * The buffer content is replaced ever time {@link #update()} is invoked. |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 0.8 |
| * |
| * @param <T> the type of buffer which can be used for transferring data. |
| * |
| * @since 0.8 |
| * @module |
| */ |
| public abstract static class Window<T extends Buffer> { |
| /** |
| * A buffer containing all sample values fetched by the last call to {@link #update()}. The buffer |
| * capacity is <var>(number of bands)</var> × <var>(window width)</var> × <var>(window height)</var>. |
| * Values are always stored with band index varying fastest, then column index, then row index. |
| * Columns are traversed from left to right and rows are traversed from top to bottom |
| * (linear iteration order). |
| * That order is the same regardless the iteration order |
| * of enclosing iterator. |
| * |
| * <p>Every time that {@link #update()} is invoked, the buffer content is replaced by sample values |
| * starting at the {@linkplain PixelIterator#getPosition() current iterator position}. |
| * Before the first {@code update()} invocation, the buffer is filled with zero values.</p> |
| */ |
| public final T values; |
| |
| /** |
| * Creates a new window which will store the sample values in the given buffer. |
| */ |
| Window(final T buffer) { |
| values = buffer; |
| } |
| |
| /** |
| * Updates this window with the sample values in the region starting at current iterator position. |
| * The buffer position, limit and mark are {@linkplain Buffer#clear() cleared}. |
| * |
| * <p>The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)} method must have |
| * been invoked successfully, before this {@code update()} method is invoked. If above condition is not met, |
| * then this method behavior is undefined: it may throw any runtime exception or return meaningless values |
| * (there is no explicit bounds check for performance reasons).</p> |
| */ |
| public abstract void update(); |
| } |
| |
| /** |
| * Restores the iterator to the start position. After this method has been invoked, |
| * the iterator is in the same state than after construction. |
| */ |
| public abstract void rewind(); |
| } |