sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1804002 - in /sis/branches/JDK8/core/sis-raster/src: main/java/org/apache/sis/image/ test/java/org/apache/sis/image/
Date Thu, 03 Aug 2017 14:21:48 GMT
Author: desruisseaux
Date: Thu Aug  3 14:21:48 2017
New Revision: 1804002

URL: http://svn.apache.org/viewvc?rev=1804002&view=rev
Log:
Use java.nio.Buffer for representing the window content. It allow us to use read-only buffer
(thus avoiding the need for a warning in javadoc) and make easier to support the 3 transfer
types.

Added:
    sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/TransferType.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
    sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
    sis/branches/JDK8/core/sis-raster/src/test/java/org/apache/sis/image/PixelIteratorTest.java

Modified: sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java?rev=1804002&r1=1804001&r2=1804002&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/DefaultIterator.java
[UTF-8] Thu Aug  3 14:21:48 2017
@@ -19,11 +19,17 @@ package org.apache.sis.image;
 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.RasterFormatException;
+import java.nio.Buffer;
+import java.nio.IntBuffer;
+import java.nio.FloatBuffer;
+import java.nio.DoubleBuffer;
 import org.opengis.coverage.grid.SequenceType;
 import org.apache.sis.internal.raster.Resources;
+import org.apache.sis.util.ArgumentChecks;
 
 
 /**
@@ -69,17 +75,12 @@ final class DefaultIterator extends Pixe
     private int currentLowerX, currentUpperX, currentUpperY;
 
     /**
-     * A temporary array used by {@link #window} for transferring data.
-     */
-    private transient double[] transfer;
-
-    /**
      * 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 #window()} method, or {@code
null} if none.
+     * @param  window   size of the window to use in {@link #createWindow(TransferType)}
method, or {@code null} if none.
      */
     DefaultIterator(final Raster data, final Rectangle subArea, final Dimension window) {
         super(data, subArea, window);
@@ -96,7 +97,7 @@ final class DefaultIterator extends Pixe
      * @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 #window()} method, or {@code
null} if none.
+     * @param  window   size of the window to use in {@link #createWindow(TransferType)}
method, or {@code null} if none.
      */
     DefaultIterator(final RenderedImage data, final Rectangle subArea, final Dimension window)
{
         super(data, subArea, window);
@@ -298,16 +299,176 @@ final class DefaultIterator extends Pixe
     }
 
     /**
-     * Returns the sample values in a region of the window size starting at the current pixel
position.
-     * This method assumes that {@link #next()} or {@link #moveTo(int,int)} has been invoked.
+     * Returns a moving window over the sample values in a rectangular region starting at
iterator position.
      */
     @Override
-    public double[] window() {
-        if (window == null) {
-            window   = new double[numBands * windowWidth * windowHeight];
-            transfer = new double[window.length /*- numBands * Math.min(windowWidth, windowHeight)*/];
-            // 'transfer' will always have at least one row or one column less than 'window'.
+    @SuppressWarnings("unchecked")
+    public <T extends Buffer> Window<T> createWindow(final TransferType<T>
type) {
+        ArgumentChecks.ensureNonNull("type", type);
+        final int length = numBands * windowWidth * windowHeight;
+        final int transferLength = length - numBands * Math.min(windowWidth, windowHeight);
+        // 'transfer' will always have at least one row or one column less than 'data'.
+        switch (type.dataBufferType) {
+            case DataBuffer.TYPE_INT:    return (Window<T>) new IntWindow   (new int
  [length], new int   [transferLength]);
+            case DataBuffer.TYPE_FLOAT:  return (Window<T>) new FloatWindow (new float
[length], new float [transferLength]);
+            case DataBuffer.TYPE_DOUBLE: return (Window<T>) new DoubleWindow(new double[length],
new double[transferLength]);
+            default: throw new AssertionError(type);  // Should never happen unless we updated
TransferType and forgot to update this method.
+        }
+    }
+
+    /**
+     * The base class of all {@link Window} implementations provided by {@link DefaultIterator}.
+     * This iterator defines a callback method required by {@link DefaultIterator#update(WindowBase,
Object)}.
+     *
+     * @todo keep trace of last location and use {@code System#arraycopy(…)} for moving
the values that we already have.
+     */
+    private abstract static class WindowBase<T extends Buffer> extends Window<T>
{
+        /**
+         * Creates a new window which will store the sample values in the given buffer.
+         */
+        WindowBase(final T buffer) {
+            super(buffer);
         }
+
+        /**
+         * Returns an array containing all samples for a rectangle of pixels in the given
raster, one sample
+         * per array element. Subclasses shall delegate to one of the {@code Raster#getPixels(…)}
methods
+         * depending on the buffer data type.
+         *
+         * @param  raster     the raster from which to get the pixel values.
+         * @param  subX       the X coordinate of the upper-left pixel location.
+         * @param  subY       the Y coordinate of the upper-left pixel location.
+         * @param  subWidth   width of the pixel rectangle.
+         * @param  subHeight  height of the pixel rectangle.
+         * @param  direct     {@code true} for storing directly in the final array,
+         *                     or {@code false} for using the transfer array.
+         * @return the array in which sample values have been stored.
+         */
+        abstract Object getPixels(Raster raster, int subX, int subY, int subWidth, int subHeight,
boolean direct);
+    }
+
+    /**
+     * {@link Window} implementation backed by an array of {@code int[]}.
+     */
+    private final class IntWindow extends WindowBase<IntBuffer> {
+        /**
+         * Sample values in the window ({@code data}) and a temporary array ({@code transfer}).
+         * Those arrays are overwritten when {@link #update()} is invoked.
+         */
+        private final int[] data, transfer;
+
+        /**
+         * Creates a new window which will store the sample values in the given {@code data}
array.
+         */
+        IntWindow(final int[] data, final int[] transfer) {
+            super(IntBuffer.wrap(data).asReadOnlyBuffer());
+            this.data = data;
+            this.transfer = transfer;
+        }
+
+        /**
+         * Performs the transfer between the underlying raster and this window.
+         */
+        @Override
+        Object getPixels(Raster raster, int subX, int subY, int subWidth, int subHeight,
boolean direct) {
+            return raster.getPixels(subX, subY, subWidth, subHeight, direct ? data : transfer);
+        }
+
+        /**
+         * Updates this window with the sample values in the region starting at current iterator
position.
+         * This method assumes that {@link #next()} or {@link #moveTo(int,int)} has been
invoked.
+         */
+        @Override
+        public void update() {
+            values.clear();
+            DefaultIterator.this.update(this, data);
+        }
+    }
+
+    /**
+     * {@link Window} implementation backed by an array of {@code float[]}.
+     */
+    private final class FloatWindow extends WindowBase<FloatBuffer> {
+        /**
+         * Sample values in the window ({@code data}) and a temporary array ({@code transfer}).
+         * Those arrays are overwritten when {@link #update()} is invoked.
+         */
+        private final float[] data, transfer;
+
+        /**
+         * Creates a new window which will store the sample values in the given {@code data}
array.
+         */
+        FloatWindow(final float[] data, final float[] transfer) {
+            super(FloatBuffer.wrap(data).asReadOnlyBuffer());
+            this.data = data;
+            this.transfer = transfer;
+        }
+
+        /**
+         * Performs the transfer between the underlying raster and this window.
+         */
+        @Override
+        Object getPixels(Raster raster, int subX, int subY, int subWidth, int subHeight,
boolean direct) {
+            return raster.getPixels(subX, subY, subWidth, subHeight, direct ? data : transfer);
+        }
+
+        /**
+         * Updates this window with the sample values in the region starting at current iterator
position.
+         * This method assumes that {@link #next()} or {@link #moveTo(int,int)} has been
invoked.
+         */
+        @Override
+        public void update() {
+            values.clear();
+            DefaultIterator.this.update(this, data);
+        }
+    }
+
+    /**
+     * {@link Window} implementation backed by an array of {@code double[]}.
+     */
+    private final class DoubleWindow extends WindowBase<DoubleBuffer> {
+        /**
+         * Sample values in the window ({@code data}) and a temporary array ({@code transfer}).
+         * Those arrays are overwritten when {@link #update()} is invoked.
+         */
+        private final double[] data, transfer;
+
+        /**
+         * Creates a new window which will store the sample values in the given {@code data}
array.
+         */
+        DoubleWindow(final double[] data, final double[] transfer) {
+            super(DoubleBuffer.wrap(data).asReadOnlyBuffer());
+            this.data = data;
+            this.transfer = transfer;
+        }
+
+        /**
+         * Performs the transfer between the underlying raster and this window.
+         */
+        @Override
+        Object getPixels(Raster raster, int subX, int subY, int subWidth, int subHeight,
boolean direct) {
+            return raster.getPixels(subX, subY, subWidth, subHeight, direct ? data : transfer);
+        }
+
+        /**
+         * Updates this window with the sample values in the region starting at current iterator
position.
+         * This method assumes that {@link #next()} or {@link #moveTo(int,int)} has been
invoked.
+         */
+        @Override
+        public void update() {
+            values.clear();
+            DefaultIterator.this.update(this, data);
+        }
+    }
+
+    /**
+     * Updates the content of given window with the sample values in the region starting
at current iterator position.
+     *
+     * @param window  the window to update.
+     * @param data    the array of primitive type where sample values are stored.
+     */
+    @SuppressWarnings("SuspiciousSystemArraycopy")
+    final void update(final WindowBase<?> window, final Object data) {
         Raster  raster    = currentRaster;
         int     subEndX   = (raster.getMinX() - x) + raster.getWidth();
         int     subEndY   = (raster.getMinY() - y) + raster.getHeight();
@@ -320,7 +481,11 @@ final class DefaultIterator extends Pixe
              * This is the vast majority of cases, so we perform this check soon before
              * to compute more internal variables.
              */
-            return raster.getPixels(x, y, subWidth, subHeight, window);
+            final Object transfer = window.getPixels(raster, x, y, subWidth, subHeight, true);
+            if (transfer != data) {     // Paranoiac check (arrays should always be same).
+                System.arraycopy(transfer, 0, data, 0, numBands * subWidth * subHeight);
+            }
+            return;
         }
         /*
          * At this point, we determined that the window is overlapping two or more tiles.
@@ -335,16 +500,16 @@ final class DefaultIterator extends Pixe
         final int rewind = subEndX;
         for (;;) {
             if (subWidth > 0 && subHeight > 0) {
-                final double[] data = raster.getPixels(x + subX, y + subY, subWidth, subHeight,
transfer);
+                final Object transfer = window.getPixels(raster, x + subX, y + subY, subWidth,
subHeight, false);
                 if (fullWidth) {
                     final int fullLength = stride * subHeight;
-                    System.arraycopy(data, 0, window, destOffset, fullLength);
+                    System.arraycopy(transfer, 0, data, destOffset, fullLength);
                     destOffset += fullLength;
                 } else {
                     final int  rowLength = numBands  * subWidth;
                     final int fullLength = rowLength * subHeight;
                     for (int srcOffset=0; srcOffset < fullLength; srcOffset += rowLength)
{
-                        System.arraycopy(data, srcOffset, window, destOffset, rowLength);
+                        System.arraycopy(transfer, srcOffset, data, destOffset, rowLength);
                         destOffset += stride;
                     }
                 }
@@ -359,7 +524,7 @@ final class DefaultIterator extends Pixe
                 tileSubX++;
             } else {
                 if (subEndY >= windowHeight) {
-                    return window;                          // Completed last row of tiles.
+                    return;                                 // Completed last row of tiles.
                 }
                 subY     = subEndY;
                 subEndY += tileHeight;                      // Tile on the next row.

Modified: sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java?rev=1804002&r1=1804001&r2=1804002&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
[UTF-8] Thu Aug  3 14:21:48 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.image;
 
+import java.nio.Buffer;
 import java.awt.Point;
 import java.awt.Dimension;
 import java.awt.Rectangle;
@@ -99,23 +100,17 @@ public abstract class PixelIterator {
     final int tileLowerX, tileLowerY, tileUpperX, tileUpperY;
 
     /**
-     * Size of the window to use in {@link #window()} method, or {@code 0} if none.
+     * Size of the window to use in {@link #createWindow(TransferType)} method, or {@code
0} if none.
      */
     final int windowWidth, windowHeight;
 
     /**
-     * Sample values in the window, or {@code null} if not yet extracted.
-     * Current sample values. This array is overwritten when {@link #window()} is invoked.
-     */
-    transient double[] window;
-
-    /**
      * 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 #window()} method, or {@code
null} if none.
+     * @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) {
         ArgumentChecks.ensureNonNull("data", data);
@@ -146,7 +141,7 @@ public abstract class PixelIterator {
      * @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 #window()} method, or {@code
null} if none.
+     * @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)
{
         ArgumentChecks.ensureNonNull("data", data);
@@ -198,14 +193,20 @@ public abstract class PixelIterator {
     }
 
     /**
-     * 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.
+     * 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 pixel coordinates of the iteration region.
+     * @return the most efficient data type for transferring data.
      */
-    public Rectangle getDomain() {
-        return new Rectangle(lowerX, lowerY, upperX - lowerX, upperY - lowerY);
+    public TransferType<?> getTransferType() {
+        return TransferType.valueOf(image != null ? image.getSampleModel().getTransferType()
: currentRaster.getTransferType());
     }
 
     /**
@@ -218,6 +219,16 @@ public abstract class PixelIterator {
     public abstract SequenceType getIterationOrder();
 
     /**
+     * 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.
@@ -350,9 +361,9 @@ public abstract class PixelIterator {
     public abstract int[] getPixel​(int[] dest);
 
     /**
-     * Returns the sample values in a rectangular region starting at the current pixel position.
-     * The region size is the <cite>window size</cite> specified at {@code PixelIterator} construction
time.
-     * The length of the returned array will be
+     * 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.
+     * 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
@@ -362,28 +373,88 @@ public abstract class PixelIterator {
      * <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, <i>etc.</i></div>
+     * 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>
      *
-     * The returned region will be live: calls to {@link #next()} followed by {@code window()}
returns an array
-     * (potentially the same array instance than previous call) updated with values starting
at the new iterator
-     * position. The returned array is valid only until the next call to {@code window()}.
The array shall only
-     * be read; behavior of this method become unspecified if caller modifies any values
in the array.
-     *
-     * <div class="note"><b>Rational:</b>
-     * the read-only constraint on the returned array exists for performance reasons.
-     * This method may recycle the same array in a way that avoid fetching existing values.
-     * </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.
      *
-     * The {@link #next()} method must have returned {@code true}, or the {@link #moveTo(int,int)}
method must have
-     * been invoked successfully, before this {@code window()} 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).
+     * <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 {@linkplain SequenceType#LINEAR linear order} regardless the iteration order.
+     *
+     * {@preformat java
+     *     PixelIterator it = create(image, null, new Dimension(5, 5));     // 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>
      *
-     * @return the sample values in the region starting at current iterator position.
+     * @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 double[] window();
+    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
+         * ({@link SequenceType#LINEAR} iteration order).
+         * That order is the same regardless the {@linkplain PixelIterator#getIterationOrder()
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,

Added: sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/TransferType.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/TransferType.java?rev=1804002&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/TransferType.java
(added)
+++ sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/TransferType.java
[UTF-8] Thu Aug  3 14:21:48 2017
@@ -0,0 +1,161 @@
+/*
+ * 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.nio.Buffer;
+import java.nio.IntBuffer;
+import java.nio.FloatBuffer;
+import java.nio.DoubleBuffer;
+import java.awt.image.Raster;
+import java.awt.image.DataBuffer;
+import java.io.Serializable;
+import java.io.ObjectStreamException;
+import java.io.InvalidObjectException;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * The type of data used to transfer pixels. Data transfers happen in various {@link Raster}
methods and in
+ * {@link PixelIterator#createWindow(TransferType)}. The type used for transferring data
is not necessarily
+ * the same than the type used by the raster for storing data. In particular, {@code byte}
and {@code short}
+ * (both signed and unsigned) are converted to {@code int} during the transfer.
+ *
+ * {@link Raster} and {@link PixelIterator} transfer data in {@code int[]}, {@code float[]}
and {@code double[]} arrays.
+ * Additionally, {@code PixelIterator} uses also {@link IntBuffer}, {@link FloatBuffer} and
{@link DoubleBuffer}.
+ *
+ * <div class="note"><b>Future evolution:</b>
+ * this class may be refactored as an enumeration in a future Java version if
+ * <a href="http://openjdk.java.net/jeps/301">JEP 301</a> is implemented.</div>
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ *
+ * @param  <T>  the type of buffer which can be used for transferring data.
+ *
+ * @since 0.8
+ * @module
+ *
+ * @see Raster#getTransferType()
+ * @see PixelIterator#createWindow(TransferType)
+ */
+public final class TransferType<T extends Buffer> implements Serializable {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -2891665589742927570L;
+
+    /**
+     * The enumeration name.
+     */
+    private final transient String name;
+
+    /**
+     * The type as one of the {@link DataBuffer} constants.
+     * This is the value returned by {@link Raster#getTransferType()}.
+     */
+    final int dataBufferType;
+
+    /**
+     * Specifies that sample values are transfered as 32 bits signed integer.
+     * If the raster stores sample values as {@code byte} or {@code short}, the values are
casted by a widening
+     * conversion before to be transfered. If the raster stores sample values as {@code float}
or {@code double},
+     * the values are rounded toward 0 before to be transfered.
+     *
+     * @see PixelIterator#getSample(int)
+     * @see PixelIterator#getPixel(int[])
+     */
+    public static final TransferType<IntBuffer> INT = new TransferType<>("INT",
DataBuffer.TYPE_INT);
+
+    /**
+     * Specifies that sample values are transfered as single-precision floating point number.
+     * Values of other types are casted as needed.
+     *
+     * @see PixelIterator#getSampleFloat(int)
+     * @see PixelIterator#getPixel(float[])
+     */
+    public static final TransferType<FloatBuffer> FLOAT = new TransferType<>("FLOAT",
DataBuffer.TYPE_FLOAT);
+
+    /**
+     * Specifies that sample values are transfered as double-precision floating point number.
+     * Values of other types are casted as needed. This is the safest transfer type to use
+     * when wanting to avoid any precision lost.
+     *
+     * @see PixelIterator#getSampleDouble(int)
+     * @see PixelIterator#getPixel(double[])
+     */
+    public static final TransferType<DoubleBuffer> DOUBLE = new TransferType<>("DOUBLE",
DataBuffer.TYPE_DOUBLE);
+
+    /**
+     * Creates a new enumeration.
+     */
+    private TransferType(final String name, final int dataBufferType) {
+        this.name = name;
+        this.dataBufferType = dataBufferType;
+    }
+
+    /**
+     * Returns the enumeration value for the given {@code DataBuffer} constant.
+     * This method applies the following mapping:
+     *
+     * <ul>
+     *   <li>If {@code type} is {@link DataBuffer#TYPE_DOUBLE}, returns {@link #DOUBLE}.</li>
+     *   <li>If {@code type} is {@link DataBuffer#TYPE_FLOAT}, returns {@link #FLOAT}.</li>
+     *   <li>If {@code type} is {@link DataBuffer#TYPE_INT}, {@link DataBuffer#TYPE_SHORT
TYPE_SHORT},
+     *          {@link DataBuffer#TYPE_USHORT TYPE_USHORT} or {@link DataBuffer#TYPE_BYTE
TYPE_BYTE},
+     *          returns {@link #INT}.</li>
+     *   <li>If {@code type} is {@link DataBuffer#TYPE_UNDEFINED} or any other value,
+     *       throws {@link IllegalArgumentException}.</li>
+     * </ul>
+     *
+     * The {@code type} argument given to this method is typically the {@link Raster#getTransferType()}
value.
+     *
+     * @param  type  one of {@link DataBuffer} constant.
+     * @return the enumeration value for the given constant.
+     * @throws IllegalArgumentException if (@code type} is not a supported {@code DataBuffer}
constant.
+     */
+    public static TransferType<?> valueOf(final int type) {
+        switch (type) {
+            case DataBuffer.TYPE_DOUBLE: return DOUBLE;
+            case DataBuffer.TYPE_FLOAT:  return FLOAT;
+            default: {
+                if (type >= DataBuffer.TYPE_BYTE && type <= DataBuffer.TYPE_INT)
return INT;
+                throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2,
"type", type));
+            }
+        }
+    }
+
+    /**
+     * Returns a unique instance on deserialization.
+     */
+    Object readResolve() throws ObjectStreamException {
+        try {
+            return valueOf(dataBufferType);
+        } catch (IllegalArgumentException e) {
+            throw new InvalidObjectException(e.toString());
+        }
+    }
+
+    /**
+     * Returns the name of this enumeration constant.
+     *
+     * @return the enumeration constant name.
+     */
+    @Override
+    public String toString() {
+        return name;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/TransferType.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-raster/src/main/java/org/apache/sis/image/TransferType.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-raster/src/test/java/org/apache/sis/image/PixelIteratorTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-raster/src/test/java/org/apache/sis/image/PixelIteratorTest.java?rev=1804002&r1=1804001&r2=1804002&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-raster/src/test/java/org/apache/sis/image/PixelIteratorTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-raster/src/test/java/org/apache/sis/image/PixelIteratorTest.java
[UTF-8] Thu Aug  3 14:21:48 2017
@@ -24,6 +24,7 @@ import java.awt.image.PixelInterleavedSa
 import java.awt.image.Raster;
 import java.awt.image.WritableRaster;
 import java.awt.image.WritableRenderedImage;
+import java.nio.FloatBuffer;
 import org.opengis.coverage.grid.SequenceType;
 import org.apache.sis.test.DependsOnMethod;
 import org.apache.sis.test.TestCase;
@@ -272,7 +273,7 @@ public strictfp class PixelIteratorTest
      * Tests for read-write iterators need to override.</p>
      *
      * @param  image    the data on which to perform iteration.
-     * @param  window   size of the window to use in {@link PixelIterator#window()} method.
+     * @param  window   size of the window to use in {@link PixelIterator#createWindow(TransferType)}
method.
      */
     void createWindowIterator(WritableRenderedImage image, Dimension window) {
         iterator = new DefaultIterator(image, null, window);
@@ -1009,20 +1010,21 @@ public strictfp class PixelIteratorTest
     }
 
     /**
-     * Verifies {@link PixelIterator#window()}.
+     * Verifies {@link PixelIterator#createWindow(TransferType)}.
      * This method assumes that the iterator traverses the full image (no sub-area).
      *
      * @see #verifyIteration(boolean)
      * @see #verifyIterationAfterMove(int, int)
      */
     private void verifyWindow(final Dimension window) {
+        final PixelIterator.Window<FloatBuffer> w = iterator.createWindow(TransferType.FLOAT);
+        final FloatBuffer values = w.values;
         final int tileSize   = tileWidth * tileHeight;
         final int tileStride = tileSize * (width / tileWidth);
         while (iterator.next()) {
             final Point pos = iterator.getPosition();
             pos.translate(-xmin, -ymin);
-            final double[] values = iterator.window();
-            int i = 0;
+            w.update();
             for (int y=0; y<window.height; y++) {
                 int p,t;
                 p  = pos.y + y;
@@ -1035,22 +1037,21 @@ public strictfp class PixelIteratorTest
                     p %=     tileWidth;
                     int offset = (start + t * tileSize + p) * numBands;
                     for (int b=0; b<numBands; b++) {
-                        final double e = expected[offset++];
-                        final double a = values[i++];
-                        if (Double.doubleToRawLongBits(a) != Double.doubleToRawLongBits(e))
{
+                        final float e = expected[offset++];
+                        final float a = values.get();
+                        if (Float.floatToRawIntBits(a) != Float.floatToRawIntBits(e)) {
                             fail("Index (" + x + ", " + y + ") in window starting at index
("
                                     + pos.x + ", " + pos.y + "), band " + b + ": expected
" + e + " but got " + a);
                         }
                     }
                 }
             }
-            assertEquals("window().length", i, values.length);
-            java.util.Arrays.fill(iterator.window, Double.NaN);
+            assertEquals("buffer.remaining()", 0, values.remaining());
         }
     }
 
     /**
-     * Tests {@link PixelIterator#window()} on a single tile.
+     * Tests {@link PixelIterator#createWindow(TransferType)} on a single tile.
      */
     @Test
     @DependsOnMethod("testMoveIntoImage")
@@ -1070,7 +1071,7 @@ public strictfp class PixelIteratorTest
     }
 
     /**
-     * Tests {@link PixelIterator#window()} on a tiled image.
+     * Tests {@link PixelIterator#createWindow(TransferType)} on a tiled image.
      */
     @Test
     @DependsOnMethod("testWindowOnTile")



Mime
View raw message