sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 03/03: Make possible to use `BandedIterator` with multi-tiled images. It required to retrofit `LinearIterator` into `PixelIterator`.
Date Tue, 29 Dec 2020 17:27:26 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit eceea7ffb6eb9e94f2efa14accd05385c03c610e
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Dec 29 18:18:40 2020 +0100

    Make possible to use `BandedIterator` with multi-tiled images.
    It required to retrofit `LinearIterator` into `PixelIterator`.
---
 .../java/org/apache/sis/image/BandedIterator.java  |  65 +++------
 .../java/org/apache/sis/image/LinearIterator.java  | 124 -----------------
 .../java/org/apache/sis/image/PixelIterator.java   | 154 ++++++++++++---------
 .../apache/sis/image/WritablePixelIterator.java    |  11 +-
 .../org/apache/sis/image/BandedIteratorTest.java   |   8 +-
 .../org/apache/sis/image/LinearIteratorTest.java   |  40 +-----
 .../org/apache/sis/image/PixelIteratorTest.java    |  19 ++-
 7 files changed, 135 insertions(+), 286 deletions(-)

diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/BandedIterator.java b/core/sis-feature/src/main/java/org/apache/sis/image/BandedIterator.java
index 312ac7b..2f9fce8 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/BandedIterator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/BandedIterator.java
@@ -28,6 +28,7 @@ import java.awt.image.Raster;
 import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
 import java.awt.image.WritableRenderedImage;
+import org.opengis.coverage.grid.SequenceType;
 
 
 /**
@@ -80,7 +81,7 @@ final class BandedIterator extends WritablePixelIterator {
      *
      * Then {@code xToBuffer} is above value with <var>x</var> = 0. This value
may be negative.
      *
-     * @see #updateForRowChange()
+     * @see #changedRowOrTile()
      */
     private int xToBuffer;
 
@@ -97,15 +98,16 @@ final class BandedIterator extends WritablePixelIterator {
      * @param  output   the raster where to write the sample values, or {@code null} for
read-only iterator.
      * @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.
+     * @param  order    {@code null} or {@link SequenceType#LINEAR}. Other values may be
added in future versions.
      * @param  scanlineStride  value of {@code getScanlineStride(input.getSampleModel()}.
Shall be greater than zero.
      */
     BandedIterator(final Raster input, final WritableRaster output, final Rectangle subArea,
-                   final Dimension window, final int scanlineStride)
+                   final Dimension window, final SequenceType order, final int scanlineStride)
     {
-        super(input, output, subArea, window);
+        super(input, output, subArea, window, order);
         this.scanlineStride = scanlineStride;
         acquiredTile(input);
-        updateForRowChange();
+        changedRowOrTile();
     }
 
     /**
@@ -115,32 +117,25 @@ final class BandedIterator extends WritablePixelIterator {
      * @param  output   the image where to write the sample values, or {@code null} for read-only
iterator.
      * @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.
+     * @param  order    {@code null} or {@link SequenceType#LINEAR}. Other values may be
added in future versions.
      * @param  scanlineStride  value of {@code getScanlineStride(input.getSampleModel()}.
Shall be greater than zero.
      */
     BandedIterator(final RenderedImage input, final WritableRenderedImage output, final Rectangle
subArea,
-                   final Dimension window, final int scanlineStride)
+                   final Dimension window, final SequenceType order, final int scanlineStride)
     {
-        super(input, output, subArea, window);
+        super(input, output, subArea, window, order);
         this.scanlineStride = scanlineStride;
         // `acquiredTile(…)` will be invoked later by `fetchTile()`.
     }
 
     /**
      * Recomputes {@link #xToBuffer} for the new {@link #y} value. This method shall be invoked
-     * when the iterator moved to new row, or when the iterator fetched a new tile but only
after
-     * the (x,y) coordinates have been updated.
-     */
-    private void updateForRowChange() {
-        xToBuffer = (y - sampleModelTranslateY) * scanlineStride - sampleModelTranslateX;
-    }
-
-    /**
-     * Restores the iterator to the start position.
+     * when the iterator moved to a new row, or when the iterator fetched a new tile but
only
+     * after the (x,y) coordinates have been updated.
      */
     @Override
-    public void rewind() {
-        super.rewind();
-        updateForRowChange();
+    final void changedRowOrTile() {
+        xToBuffer = (y - sampleModelTranslateY) * scanlineStride - sampleModelTranslateX;
     }
 
     /**
@@ -150,47 +145,19 @@ final class BandedIterator extends WritablePixelIterator {
      */
     @Override
     public void moveTo(final int px, final int py) {
-        if (py == y && px >= currentLowerX() && px < currentUpperX())
{
+        if (isSameRowAndTile(px, py)) {
             x = px;
         } else {
             super.moveTo(px, py);
-            updateForRowChange();
-        }
-    }
-
-    /**
-     * Moves the iterator to the next pixel. This implementation is a copy of {@link PixelIterator#next()}
-     * with only a call to {@link #updateForRowChange()} added when the {@link #y} value
changed.
-     *
-     * @return {@code true} if the current pixel is valid, or {@code false} if there is no
more pixels.
-     */
-    @Override
-    public boolean next() {
-        if (++x >= currentUpperX()) {
-            if (++y >= currentUpperY()) {
-                releaseTile();
-                if (++tileX >= tileUpperX) {
-                    if (++tileY >= tileUpperY) {
-                        endOfIteration();
-                        return false;
-                    }
-                    tileX = tileLowerX;
-                }
-                y = fetchTile();
-                updateForRowChange();
-            } else {
-                xToBuffer += scanlineStride;
-            }
-            x = currentLowerX();
+            changedRowOrTile();
         }
-        return true;
     }
 
     /**
      * Invoked when the iterator fetched a new tile. This method updates {@code BandedIterator}
fields
      * with raster properties, except {@link #xToBuffer} which is not updated here because
{@link #y}
      * is not yet updated to its new value. {@code BandedIterator} must override all methods
invoking
-     * {@link #fetchTile()} ane ensure that {@link #updateForRowChange()} is invoked after
(x,y) have
+     * {@link #fetchTile()} ane ensure that {@link #changedRowOrTile()} is invoked after
(x,y) have
      * been updated.
      */
     @Override
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/LinearIterator.java b/core/sis-feature/src/main/java/org/apache/sis/image/LinearIterator.java
deleted file mode 100644
index e1d4fdd..0000000
--- a/core/sis-feature/src/main/java/org/apache/sis/image/LinearIterator.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.Optional;
-import java.awt.Dimension;
-import java.awt.Rectangle;
-import java.awt.image.Raster;
-import java.awt.image.RenderedImage;
-import java.awt.image.WritableRaster;
-import java.awt.image.WritableRenderedImage;
-import java.awt.image.RasterFormatException;
-import org.opengis.coverage.grid.SequenceType;
-import org.apache.sis.internal.feature.Resources;
-
-
-/**
- * Iterator for the {@link SequenceType#LINEAR} traversal order.
- * This iterator behaves as is the whole image was a single tile.
- * Calls to {@link #next()} move the current position by increasing the following values,
in order:
- *
- * <ol>
- *   <li>Column index in image (from left to right)</li>
- *   <li>Row index in image (from top to bottom).</li>
- * </ol>
- *
- * This class uses the {@link Raster} API for traversing the pixels of the image,
- * i.e. it does not yet provide optimization for commonly used sample models.
- *
- * @author  Johann Sorel (Geomatys)
- * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
- * @since   1.0
- * @module
- */
-final class LinearIterator extends WritablePixelIterator {
-    /**
-     * Creates an iterator for the given region in the given raster.
-     *
-     * @param  input    the raster which contains the sample values to read.
-     * @param  output   the raster where to write the sample values, or {@code null} for
read-only iterator.
-     * @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.
-     */
-    LinearIterator(final Raster input, final WritableRaster output, final Rectangle subArea,
final Dimension window) {
-        super(input, output, subArea, window);
-    }
-
-    /**
-     * Creates an iterator for the given region in the given image.
-     *
-     * @param  input    the image which contains the sample values to read.
-     * @param  output   the image where to write the sample values, or {@code null} for read-only
iterator.
-     * @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.
-     */
-    LinearIterator(final RenderedImage input, final WritableRenderedImage output, final Rectangle
subArea, final Dimension window) {
-        super(input, output, subArea, window);
-    }
-
-    /**
-     * Returns the order in which pixels are traversed.
-     */
-    @Override
-    public Optional<SequenceType> getIterationOrder() {
-        return Optional.of(SequenceType.LINEAR);
-    }
-
-    /**
-     * Moves the iterator to the next pixel on the current row, or to the next row.
-     * This method behaves as if the whole image was a single tile.
-     *
-     * @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.
-     */
-    @Override
-    public boolean next() {
-        if (++x >= currentUpperX()) {               // Move to next column, potentially
on a different tile.
-            if (x < upperX) {
-                releaseTile();                      // Must be invoked before `tileX` change.
-                tileX++;
-            } else {
-                x = lowerX;                         // Beginning of next row.
-                if (++y >= currentUpperY()) {       // Move to next line.
-                    releaseTile();                  // Must be invoked before `tileY` change.
-                    if (++tileY >= tileUpperY) {
-                        endOfIteration();
-                        return false;
-                    }
-                } else if (tileX == tileLowerX) {
-                    return true;                    // Beginning of next row is in the same
tile.
-                }
-                releaseTile();                      // Must be invoked before `tileX` change.
-                tileX = tileLowerX;
-            }
-            /*
-             * At this point the (x,y) pixel coordinates have been updated and are inside
the domain of validity.
-             * We may need to change tile, either because we moved to the tile on the right
or because we start a
-             * new row (in which case we need to move to the leftmost tile). The only case
where we can skip tile
-             * change is when the image has only one tile width (in which case tileX == tileLowerX)
and the next
-             * row is still on the same tile.
-             */
-            if (fetchTile() > y) {
-                throw new RasterFormatException(Resources.format(Resources.Keys.IncompatibleTile_2,
tileX, tileY));
-            }
-        }
-        return true;
-    }
-}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
index de88021..7dd9366 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PixelIterator.java
@@ -133,7 +133,7 @@ public class PixelIterator {
      * The tile index ranges may be smaller than the ranges of valid indices in image,
      * but not greater. The lower values are inclusive and the upper values exclusive.
      */
-    final int tileLowerX, tileLowerY, tileUpperX, tileUpperY;
+    private final int tileLowerX, tileLowerY, tileUpperX, tileUpperY;
 
     /**
      * Size of the window to use in {@link #createWindow(TransferType)} method, or {@code
0} if none.
@@ -156,9 +156,6 @@ public class PixelIterator {
      * Bounds of the region traversed by the iterator in {@linkplain #currentRaster current
raster}.
      * When iteration reaches the upper coordinates, the iterator needs to move to next tile.
      * This the raster bounds clipped to the area of interest.
-     *
-     * @see #currentUpperX()
-     * @see #currentUpperY()
      */
     private int currentLowerX, currentUpperX, currentUpperY;
 
@@ -176,14 +173,21 @@ public class PixelIterator {
     private int windowLimitX, windowLimitY;
 
     /**
+     * {@code true} for default iteration order, or {@code false} for {@link SequenceType#LINEAR}.
+     * Note that the order is equivalent to linear order when there is only one tile.
+     */
+    private final boolean isDefaultOrder;
+
+    /**
      * 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.
+     * @param  order    {@code null} or {@link SequenceType#LINEAR}. Other values may be
added in future versions.
      */
-    PixelIterator(final Raster data, final Rectangle subArea, final Dimension window) {
+    PixelIterator(final Raster data, final Rectangle subArea, final Dimension window, final
SequenceType order) {
         final Rectangle bounds;
         image           = null;
         currentRaster   = data;
@@ -210,6 +214,7 @@ public class PixelIterator {
         windowLimitY    = Math.addExact(tileGridYOffset, tileHeight);
         x               = Math.decrementExact(lowerX);                  // Set to the position
before first pixel.
         y               = lowerY;
+        isDefaultOrder  = true;
     }
 
     /**
@@ -219,8 +224,9 @@ public class PixelIterator {
      * @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.
+     * @param  order    {@code null} or {@link SequenceType#LINEAR}. Other values may be
added in future versions.
      */
-    PixelIterator(final RenderedImage data, final Rectangle subArea, final Dimension window)
{
+    PixelIterator(final RenderedImage data, final Rectangle subArea, final Dimension window,
final SequenceType order) {
         final Rectangle bounds;
         image           = data;
         numBands        = data.getSampleModel().getNumBands();
@@ -246,6 +252,7 @@ public class PixelIterator {
         currentUpperY   = lowerY;
         x               = Math.decrementExact(lowerX);          // Set to the position before
first pixel.
         y               = lowerY;
+        isDefaultOrder  = (order == null) || (tileUpperX - tileLowerX) <= 1;
         /*
          * We need to ensure that `tileUpperY+1 > tileUpperY` will alway be true because
`tileY` may be equal
          * to `tileUpperY` when the `if (++tileY >= tileUpperY)` statement is excuted
in the `next()` method.
@@ -420,18 +427,11 @@ public class PixelIterator {
          */
         public PixelIterator create(final Raster data) {
             ArgumentChecks.ensureNonNull("data", data);
-            if (order != null && order != SequenceType.LINEAR) {
-                throw new IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1,
order));
-            }
-            /*
-             * No need to instantiate `LinearIterator` because the default iterator
-             * has the same iteration order when there is only one tile.
-             */
             final int scanlineStride = getScanlineStride(data.getSampleModel());
             if (scanlineStride > 0) {
-                return new BandedIterator(data, null, subArea, window, scanlineStride);
+                return new BandedIterator(data, null, subArea, window, order, scanlineStride);
             } else {
-                return new PixelIterator(data, subArea, window);
+                return new PixelIterator(data, subArea, window, order);
             }
         }
 
@@ -445,7 +445,7 @@ public class PixelIterator {
             ArgumentChecks.ensureNonNull("data", data);
             data = unwrap(data);
             /*
-             * Note: As of Java 14, `BufferedImage.getTileGridXOffset()` and `getTileGridYOffset()`
have a bug.
+             * Note: Before Java 16, `BufferedImage.getTileGridXOffset()` and `getTileGridYOffset()`
had a bug.
              * They should return `BufferedImage.getMinX()` (which is always 0) because the
image contains only
              * one tile at index (0,0).  But they return `raster.getSampleModelTranslateX()`
instead, which may
              * be non-zero if the image is a sub-region of another image.  Delegating to
`create(Raster)` avoid
@@ -456,16 +456,11 @@ public class PixelIterator {
             if (data instanceof BufferedImage) {
                 return create(((BufferedImage) data).getRaster());
             }
-            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));
-            }
             final int scanlineStride = getScanlineStride(data.getSampleModel());
             if (scanlineStride > 0) {
-                return new BandedIterator(data, null, subArea, window, scanlineStride);
+                return new BandedIterator(data, null, subArea, window, order, scanlineStride);
             } else {
-                return new PixelIterator(data, subArea, window);
+                return new PixelIterator(data, subArea, window, order);
             }
         }
 
@@ -505,18 +500,11 @@ public class PixelIterator {
         public WritablePixelIterator createWritable(final Raster input, final WritableRaster
output) {
             ArgumentChecks.ensureNonNull("input",  input);
             ArgumentChecks.ensureNonNull("output", output);
-            if (order != null && order != SequenceType.LINEAR) {
-                throw new IllegalStateException(Errors.format(Errors.Keys.UnsupportedType_1,
order));
-            }
-            /*
-             * No need to instantiate `LinearIterator` because the default iterator
-             * has the same iteration order when there is only one tile.
-             */
             final int scanlineStride = getScanlineStride(input.getSampleModel());
             if (scanlineStride > 0) {
-                return new BandedIterator(input, output, subArea, window, scanlineStride);
+                return new BandedIterator(input, output, subArea, window, order, scanlineStride);
             } else {
-                return new WritablePixelIterator(input, output, subArea, window);
+                return new WritablePixelIterator(input, output, subArea, window, order);
             }
         }
 
@@ -532,16 +520,11 @@ public class PixelIterator {
             ArgumentChecks.ensureNonNull("input",  input);
             ArgumentChecks.ensureNonNull("output", output);
             input = unwrap(input);
-            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));
-            }
             final int scanlineStride = getScanlineStride(input.getSampleModel());
             if (scanlineStride > 0) {
-                return new BandedIterator(input, output, subArea, window, scanlineStride);
+                return new BandedIterator(input, output, subArea, window, order, scanlineStride);
             } else {
-                return new WritablePixelIterator(input, output, subArea, window);
+                return new WritablePixelIterator(input, output, subArea, window, order);
             }
         }
     }
@@ -656,10 +639,10 @@ public class PixelIterator {
      * @return order in which pixels are traversed.
      */
     public Optional<SequenceType> getIterationOrder() {
-        if (image == null || (tileUpperX - tileLowerX) <= 1 && (tileUpperY - tileLowerY)
<= 1) {
-            return Optional.of(SequenceType.LINEAR);
+        if (isDefaultOrder && (tileUpperX - tileLowerX) > 1) {
+            return Optional.empty();                                // Undefined iteration
order.
         } else {
-            return Optional.empty();                // Undefined order.
+            return Optional.of(SequenceType.LINEAR);
         }
     }
 
@@ -757,44 +740,80 @@ public class PixelIterator {
      *         to {@code next()}, and {@link #rewind()} or {@link #moveTo(int,int)} have
not been invoked.
      */
     public boolean next() {
+        /*
+         * Current implementation supports two iteration orders: default and SequenceType.LINEAR.
+         * They are the two most frequent orders and have in common an iteration over x values
of
+         * current tile before to make any decision. It is reasonably cheap to implement
them in
+         * the same method because the cost of checking for iteration order happens only
once per
+         * tile row (instead than at every pixel). Providing the two implementations here
makes
+         * easier for subclasses such as `BandedIterator` to support those two iteration
orders.
+         *
+         * All other iteration orders (Cantor, Morton, Hilbert, etc.) should have a dedicated
+         * class overriding this method. We do not intent to support all iteration order
here.
+         */
         if (++x >= currentUpperX) {
-            if (++y >= currentUpperY) {             // Strict equality (==) would work,
but use >= as a safety.
-                releaseTile();                      // Release current writable raster, if
any.
-                if (++tileX >= tileUpperX) {        // Strict equality (==) would work,
but use >= as a safety.
-                    if (++tileY >= tileUpperY) {
-                        endOfIteration();
-                        return false;
+            if (isDefaultOrder) {
+                if (++y >= currentUpperY) {             // Strict equality (==) would
work, but use >= as a safety.
+                    releaseTile();                      // Release current writable raster,
if any.
+                    if (++tileX >= tileUpperX) {        // Strict equality (==) would
work, but use >= as a safety.
+                        if (++tileY >= tileUpperY) {
+                            endOfIteration();
+                            return false;
+                        }
+                        tileX = tileLowerX;
+                    }
+                    y = fetchTile();
+                }
+                x = currentLowerX;
+            } else {
+                /*
+                 * SequenceType.LINEAR iteration order: before to move to next row, verify
if there is
+                 * more tiles to traverse on the right side.
+                 */
+                releaseTile();                          // Must be invoked before (tileX,
tileY) change.
+                if (x < upperX) {
+                    tileX++;
+                } else {
+                    if (++y >= currentUpperY) {         // Move to next line only after
full image row.
+                        if (++tileY >= tileUpperY) {
+                            endOfIteration();
+                            return false;
+                        }
                     }
                     tileX = tileLowerX;
+                    x = lowerX;                         // Beginning of next row.
+                }
+                /*
+                 * At this point the (x,y) pixel coordinates have been updated and are inside
the domain of validity.
+                 * We need to change tile, either because we moved to the tile on the right
or because we started a
+                 * new row (in which case we need to move to the leftmost tile).
+                 */
+                if (fetchTile() > y) {
+                    throw new RasterFormatException(Resources.format(Resources.Keys.IncompatibleTile_2,
tileX, tileY));
                 }
-                y = fetchTile();
             }
-            x = currentLowerX;
+            changedRowOrTile();
         }
         return true;
     }
 
     /**
-     * Returns the lower limit of the region traversed by the iterator in current raster.
-     */
-    final int currentLowerX() {
-        return currentLowerX;
-    }
-
-    /**
-     * Returns the upper limit of the region traversed by the iterator in current raster.
-     * When iteration reaches this limit, the iterator needs to move to next row or next
tile.
+     * Invoked by the default {@link #next()} implementation when the iterator moved to a
new row or a new tile.
+     * Subclasses can override for updating some <var>y</var>-dependent cached
values.
+     *
+     * <p>Note that this method is not invoked by {@link #moveTo(int, int)} for performance
reason.
+     * Subclasses can get equivalent functionality by overriding {@code moveTo(…)} and
checking
+     * {@code isSameRowAndTile(px, py)}.</p>
      */
-    final int currentUpperX() {
-        return currentUpperX;
+    void changedRowOrTile() {
     }
 
     /**
-     * Returns the upper limit of the region traversed by the iterator in current raster.
-     * When iteration reaches this limit, the iterator needs to move to next tile.
+     * Returns whether given position is on the same row and same tile than current (x,y)
position.
+     * This method is provided as a complement to {@link #changedRowOrTile()}.
      */
-    final int currentUpperY() {
-        return currentUpperY;
+    final boolean isSameRowAndTile(final int px, final int py) {
+        return (py == y) && px >= currentLowerX && px < currentUpperX;
     }
 
     /**
@@ -810,7 +829,7 @@ public class PixelIterator {
      *
      * @return the {@link #y} value of the first row of new tile.
      */
-    final int fetchTile() {
+    private int fetchTile() {
         Raster tile = fetchWritableTile();
         if (tile == null) {
             tile = image.getTile(tileX, tileY);
@@ -870,7 +889,7 @@ public class PixelIterator {
      * <p>Note: {@link #releaseTile()} is always invoked before this method.
      * Consequently {@link #currentRaster} is already {@code null}.</p>
      */
-    final void endOfIteration() {
+    private void endOfIteration() {
         /*
          * The `tileY` value is used for checking if next() is invoked again, in order to
avoid a
          * common misuse pattern. In principle `tileY` needs to be compared only to `tileUpperY`,
@@ -1430,5 +1449,6 @@ public class PixelIterator {
         }
         x = lowerX - 1;                 // Set to the position before first pixel.
         y = lowerY;
+        changedRowOrTile();
     }
 }
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/WritablePixelIterator.java
b/core/sis-feature/src/main/java/org/apache/sis/image/WritablePixelIterator.java
index 6ee7598..986c354 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/WritablePixelIterator.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/WritablePixelIterator.java
@@ -23,6 +23,7 @@ import java.awt.image.Raster;
 import java.awt.image.RenderedImage;
 import java.awt.image.WritableRaster;
 import java.awt.image.WritableRenderedImage;
+import org.opengis.coverage.grid.SequenceType;
 import org.apache.sis.internal.feature.Resources;
 
 
@@ -80,11 +81,12 @@ public class WritablePixelIterator extends PixelIterator implements Closeable
{
      * @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.
+     * @param  order    {@code null} or {@link SequenceType#LINEAR}. Other values may be
added in future versions.
      */
     WritablePixelIterator(final Raster input, final WritableRaster output,
-                          final Rectangle subArea, final Dimension window)
+                          final Rectangle subArea, final Dimension window, final SequenceType
order)
     {
-        super(input, subArea, window);
+        super(input, subArea, window, order);
         destRaster  = output;
         destination = null;
         if (output != null) {
@@ -104,11 +106,12 @@ public class WritablePixelIterator extends PixelIterator implements
Closeable {
      * @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.
+     * @param  order    {@code null} or {@link SequenceType#LINEAR}. Other values may be
added in future versions.
      */
     WritablePixelIterator(final RenderedImage input, final WritableRenderedImage output,
-                          final Rectangle subArea, final Dimension window)
+                          final Rectangle subArea, final Dimension window, final SequenceType
order)
     {
-        super(input, subArea, window);
+        super(input, subArea, window, order);
         destRaster  = null;
         destination = output;
         if (output != null) {
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/BandedIteratorTest.java b/core/sis-feature/src/test/java/org/apache/sis/image/BandedIteratorTest.java
index 3221552..4d70f46 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/BandedIteratorTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/BandedIteratorTest.java
@@ -38,7 +38,7 @@ public final strictfp class BandedIteratorTest extends PixelIteratorTest
{
      * Creates a new test case.
      */
     public BandedIteratorTest() {
-        super(DataBuffer.TYPE_FLOAT);
+        super(DataBuffer.TYPE_FLOAT, null);
         useBandedSampleModel = true;
     }
 
@@ -49,7 +49,7 @@ public final strictfp class BandedIteratorTest extends PixelIteratorTest
{
     void createPixelIterator(final WritableRaster raster, final Rectangle subArea) {
         final int scanlineStride = PixelIterator.Builder.getScanlineStride(raster.getSampleModel());
         assertTrue(scanlineStride >= raster.getWidth());
-        iterator = new BandedIterator(raster, isWritable ? raster : null, subArea, null,
scanlineStride);
+        iterator = new BandedIterator(raster, isWritable ? raster : null, subArea, null,
null, scanlineStride);
         assertEquals("getIterationOrder()", SequenceType.LINEAR, iterator.getIterationOrder().get());
         assertEquals("isWritable", isWritable, iterator.isWritable());
     }
@@ -61,7 +61,7 @@ public final strictfp class BandedIteratorTest extends PixelIteratorTest
{
     void createPixelIterator(final WritableRenderedImage image, final Rectangle subArea)
{
         final int scanlineStride = PixelIterator.Builder.getScanlineStride(image.getSampleModel());
         assertTrue(scanlineStride >= image.getTileWidth());
-        iterator = new BandedIterator(image, isWritable ? image : null, subArea, null, scanlineStride);
+        iterator = new BandedIterator(image, isWritable ? image : null, subArea, null, null,
scanlineStride);
         assertEquals("isWritable", isWritable, iterator.isWritable());
     }
 
@@ -73,7 +73,7 @@ public final strictfp class BandedIteratorTest extends PixelIteratorTest
{
     void createWindowIterator(final WritableRenderedImage image, final Dimension window)
{
         final int scanlineStride = PixelIterator.Builder.getScanlineStride(image.getSampleModel());
         assertTrue(scanlineStride >= image.getTileWidth());
-        iterator = new BandedIterator(image, isWritable ? image : null, null, window, scanlineStride);
+        iterator = new BandedIterator(image, isWritable ? image : null, null, window, null,
scanlineStride);
         assertEquals("isWritable", isWritable, iterator.isWritable());
     }
 }
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/LinearIteratorTest.java b/core/sis-feature/src/test/java/org/apache/sis/image/LinearIteratorTest.java
index 35ff72c..f7e0e92 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/LinearIteratorTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/LinearIteratorTest.java
@@ -19,8 +19,6 @@ package org.apache.sis.image;
 import java.awt.Dimension;
 import java.awt.Rectangle;
 import java.awt.image.DataBuffer;
-import java.awt.image.WritableRaster;
-import java.awt.image.WritableRenderedImage;
 import org.opengis.coverage.grid.SequenceType;
 
 import static org.junit.Assert.*;
@@ -29,9 +27,14 @@ import static org.junit.Assert.*;
 /**
  * Tests the linear read-write iterator on signed short integer values.
  *
+ * <p>Historical note: in a previous version, this iteration order was implemented
in a separated class
+ * named {@code LinearIterator}. Linear order has been retrofitted in {@link PixelIterator}
but we keep
+ * that test class separated as an easy way to execute the same set tests with the two iteration
orders
+ * (default and linear).</p>
+ *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   1.0
  */
 public final strictfp class LinearIteratorTest extends PixelIteratorTest {
@@ -39,7 +42,7 @@ public final strictfp class LinearIteratorTest extends PixelIteratorTest
{
      * Creates a new test case.
      */
     public LinearIteratorTest() {
-        super(DataBuffer.TYPE_SHORT);
+        super(DataBuffer.TYPE_SHORT, SequenceType.LINEAR);
     }
 
     /**
@@ -93,35 +96,6 @@ public final strictfp class LinearIteratorTest extends PixelIteratorTest
{
     }
 
     /**
-     * Creates a {@code PixelIterator} for a sub-area of given raster.
-     */
-    @Override
-    void createPixelIterator(WritableRaster raster, Rectangle subArea) {
-        iterator = new LinearIterator(raster, isWritable ? raster : null, subArea, null);
-        assertEquals("getIterationOrder()", SequenceType.LINEAR, iterator.getIterationOrder().get());
-        assertEquals("isWritable", isWritable, iterator.isWritable());
-    }
-
-    /**
-     * Creates a {@code PixelIterator} for a sub-area of given image.
-     */
-    @Override
-    void createPixelIterator(WritableRenderedImage image, Rectangle subArea) {
-        iterator = new LinearIterator(image, isWritable ? image : null, subArea, null);
-        assertEquals("isWritable", isWritable, iterator.isWritable());
-    }
-
-    /**
-     * Creates a {@code PixelIterator} for a window in the given image.
-     * The iterator shall be assigned to the {@link #iterator} field.
-     */
-    @Override
-    void createWindowIterator(WritableRenderedImage image, Dimension window) {
-        iterator = new LinearIterator(image, isWritable ? image : null, null, window);
-        assertEquals("isWritable", isWritable, iterator.isWritable());
-    }
-
-    /**
      * Returns the values of the given sub-region, organized in a {@link SequenceType#LINEAR}
fashion.
      * This method is invoked for {@link #verifyWindow(Dimension)} purpose.
      *
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/PixelIteratorTest.java b/core/sis-feature/src/test/java/org/apache/sis/image/PixelIteratorTest.java
index bf67736..9f8c71e 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/PixelIteratorTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/PixelIteratorTest.java
@@ -111,6 +111,13 @@ public strictfp class PixelIteratorTest extends TestCase {
     private float[] expected;
 
     /**
+     * Iteration order to request at construction time. This is not necessarily the same
+     * than the actual iteration order chosen by the iterator because the iterator may
+     * replace default order by a more specific one.
+     */
+    private final SequenceType requestedOrder;
+
+    /**
      * {@code true} for testing write operations in addition of read operations.
      */
     boolean isWritable;
@@ -125,15 +132,17 @@ public strictfp class PixelIteratorTest extends TestCase {
      *
      * @param  dataType  the raster or image data type as one of the {@link DataBuffer} constants.
      */
-    PixelIteratorTest(final int dataType) {
+    PixelIteratorTest(final int dataType, final SequenceType requestedOrder) {
         this.dataType = dataType;
+        this.requestedOrder = requestedOrder;
     }
 
     /**
      * Creates a new test case.
      */
     public PixelIteratorTest() {
-        this(DataBuffer.TYPE_SHORT);
+        dataType = DataBuffer.TYPE_SHORT;
+        requestedOrder = null;
     }
 
     /**
@@ -341,7 +350,7 @@ public strictfp class PixelIteratorTest extends TestCase {
      * @param  subArea  the boundary of the raster sub-area where to perform iteration.
      */
     void createPixelIterator(WritableRaster raster, Rectangle subArea) {
-        iterator = new WritablePixelIterator(raster, isWritable ? raster : null, subArea,
null);
+        iterator = new WritablePixelIterator(raster, isWritable ? raster : null, subArea,
null, requestedOrder);
         assertEquals("getIterationOrder()", SequenceType.LINEAR, iterator.getIterationOrder().get());
         assertEquals("isWritable", isWritable, iterator.isWritable());
     }
@@ -357,7 +366,7 @@ public strictfp class PixelIteratorTest extends TestCase {
      * @param  subArea  the boundary of the image sub-area where to perform iteration.
      */
     void createPixelIterator(WritableRenderedImage image, Rectangle subArea) {
-        iterator = new WritablePixelIterator(image, isWritable ? image : null, subArea, null);
+        iterator = new WritablePixelIterator(image, isWritable ? image : null, subArea, null,
requestedOrder);
         assertEquals("isWritable", isWritable, iterator.isWritable());
     }
 
@@ -372,7 +381,7 @@ public strictfp class PixelIteratorTest extends TestCase {
      * @param  window   size of the window to use in {@link PixelIterator#createWindow(TransferType)}
method.
      */
     void createWindowIterator(WritableRenderedImage image, Dimension window) {
-        iterator = new WritablePixelIterator(image, isWritable ? image : null, null, window);
+        iterator = new WritablePixelIterator(image, isWritable ? image : null, null, window,
requestedOrder);
         assertEquals("isWritable", isWritable, iterator.isWritable());
     }
 


Mime
View raw message