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 d485138aae50819a264eb55201555a783409733e
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Mar 23 19:52:37 2023 +0100

    Replace `PlanarImage.copyData(…)` implementation by a call to 
`WritableRaster.setRect(…)`.
    That method is optimized in various Java private subclasses of 
`WritableRaster`.
---
 .../java/org/apache/sis/image/PlanarImage.java     | 34 +++++--------
 .../main/java/org/apache/sis/image/Transferer.java | 55 +++++++++++++++++++---
 .../sis/internal/coverage/j2d/ImageUtilities.java  | 40 ----------------
 3 files changed, 60 insertions(+), 69 deletions(-)

diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java 
b/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java
index 15b2366401..5fb2e07651 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PlanarImage.java
@@ -478,35 +478,25 @@ public abstract class PlanarImage implements 
RenderedImage {
      *
      * @param  aoi     the region of the {@code source} image to copy.
      * @param  source  the source of pixel data.
-     * @param  target  the raster to hold a copy of the source image, or 
{@code null} for creating a new raster.
+     * @param  target  the raster to hold a copy of the source image.
      */
     static void copyData(final Rectangle aoi, final RenderedImage source, 
final WritableRaster target) {
         /*
-         * Iterate over all tiles that interesect the area of interest. For 
each tile,
-         * copy a few rows in a temporary buffer, then copy that buffer to 
destination.
-         * The buffer will be reused for each transfer, unless its size is 
insufficient.
-         * Note that `tb` should never be empty since we restrict iteration to 
the tiles
-         * that intersect the given area of interest.
+         * Iterate over all tiles that interesect the specified area of 
interest (AOI).
+         * For each tile, delegate to `WritableRaster.setRect(…)` because that 
method is
+         * overridden with optimized implementations in various Sun's raster 
subclasses.
+         * Note that `t` rectangle should never be empty because we restrict 
iteration
+         * to the tiles that intersect the given area of interest.
          */
         final TileOpExecutor executor = new TileOpExecutor(source, aoi) {
-            /** For copying data using data type specified by raster. */ 
private Object buffer;
-            /** For detecting if {@link #buffer} size is sufficient.  */ 
private int bufferCapacity;
-
             /** Invoked for each tile to copy to target raster. */
-            @Override protected void readFrom(final Raster tile) {
-                final Rectangle tb = aoi.intersection(tile.getBounds());       
 // Bounds of transfer buffer.
-                final int afterLastRow = 
ImageUtilities.prepareTransferRegion(tb, tile.getTransferType());
-                final int transferCapacity = tb.width * tb.height;
-                if (transferCapacity > bufferCapacity) {
-                    bufferCapacity = transferCapacity;
-                    buffer = null;                          // Will be 
allocated by Raster.getDataElements(…).
-                }
-                while (tb.y < afterLastRow) {
-                    final int height = Math.min(tb.height, afterLastRow - 
tb.y);
-                    buffer = tile.getDataElements(tb.x, tb.y, tb.width, 
height, buffer);
-                    target.setDataElements(tb.x, tb.y, tb.width, height, 
buffer);
-                    tb.y += height;
+            @Override protected void readFrom(Raster tile) {
+                final Rectangle bounds = tile.getBounds();
+                final Rectangle t = aoi.intersection(bounds);
+                if (!t.equals(bounds)) {
+                    tile = tile.createChild(t.x, t.y, t.width, t.height, t.x, 
t.y, null);
                 }
+                target.setRect(tile);
             }
         };
         executor.readFrom(source);
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/image/Transferer.java 
b/core/sis-feature/src/main/java/org/apache/sis/image/Transferer.java
index 4c9d46eec4..373acfd2c0 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/Transferer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/Transferer.java
@@ -25,10 +25,13 @@ import java.awt.image.DataBuffer;
 import java.awt.image.DataBufferFloat;
 import java.awt.image.DataBufferDouble;
 import java.awt.image.RenderedImage;
+import java.awt.image.RasterFormatException;
 import org.opengis.referencing.operation.MathTransform1D;
 import org.opengis.referencing.operation.TransformException;
 import org.apache.sis.internal.coverage.j2d.ImageUtilities;
 import org.apache.sis.internal.coverage.j2d.ImageLayout;
+import org.apache.sis.internal.system.Configuration;
+import org.apache.sis.internal.feature.Resources;
 import org.apache.sis.internal.util.Numerics;
 
 
@@ -46,10 +49,20 @@ import org.apache.sis.internal.util.Numerics;
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
  * @since   1.1
  */
 abstract class Transferer {
+    /**
+     * Approximate size of the buffer to use for copying data from/to a 
raster, in bits.
+     * The actual buffer size may be smaller or larger, depending on the 
actual tile size.
+     * This value does not need to be very large.
+     *
+     * @see #prepareTransferRegion(Rectangle, int)
+     */
+    @Configuration
+    private static final int BUFFER_SIZE = 32 * 
ImageUtilities.DEFAULT_TILE_SIZE * Byte.SIZE;
+
     /**
      * The image tile from which to read sample values.
      */
@@ -101,12 +114,40 @@ abstract class Transferer {
      *
      * @return {@code region.y + region.height}.
      *
-     * @see ImageUtilities#prepareTransferRegion(Rectangle, int)
+     * @see #prepareTransferRegion(Rectangle, int)
      */
     int prepareTransferRegion() {
         return Math.addExact(region.y, region.height);
     }
 
+    /**
+     * Suggests the height of a transfer region for a tile of the given size. 
The given region should be
+     * contained inside {@link Raster#getBounds()}. This method modifies 
{@link Rectangle#height} in-place.
+     * The {@link Rectangle#width} value is never modified, so caller can 
iterate on all raster rows without
+     * the need to check if the row is incomplete.
+     *
+     * @param  bounds    on input, the region of interest. On output, the 
suggested transfer region bounds.
+     * @param  dataType  one of {@link DataBuffer} constant. It is okay if an 
unknown constant is used since
+     *                   this information is used only as a hint for adjusting 
the {@link #BUFFER_SIZE} value.
+     * @return the maximum <var>y</var> value plus 1. This can be used as stop 
condition for iterating over rows.
+     * @throws ArithmeticException if the maximum <var>y</var> value overflows 
32 bits integer capacity.
+     * @throws RasterFormatException if the given bounds is empty.
+     */
+    static int prepareTransferRegion(final Rectangle bounds, final int 
dataType) {
+        if (bounds.isEmpty()) {
+            throw new 
RasterFormatException(Resources.format(Resources.Keys.EmptyTileOrImageRegion));
+        }
+        final int afterLastRow = Math.addExact(bounds.y, bounds.height);
+        int size;
+        try {
+            size = DataBuffer.getDataTypeSize(dataType);
+        } catch (IllegalArgumentException e) {
+            size = Short.SIZE;  // Arbitrary value is okay because this is 
only a hint for choosing a buffer size.
+        }
+        bounds.height = Math.max(1, Math.min(BUFFER_SIZE / (size * 
bounds.width), bounds.height));
+        return afterLastRow;
+    }
+
     /**
      * Computes all sample values from the source tile and writes the result 
in the target tile.
      * This method invokes {@link #computeStrip(MathTransform1D)} repetitively 
for sub-regions of the tile.
@@ -235,7 +276,7 @@ abstract class Transferer {
 
         /** Subdivides the region to process in smaller strips, for smaller 
{@linkplain #buffer}. */
         @Override int prepareTransferRegion() {
-            return ImageUtilities.prepareTransferRegion(region, 
DataBuffer.TYPE_DOUBLE);
+            return prepareTransferRegion(region, DataBuffer.TYPE_DOUBLE);
         }
 
         /** Copies source values in temporary buffer, applies conversion then 
copies to target. */
@@ -267,7 +308,7 @@ abstract class Transferer {
 
         /** Subdivides the region to process in smaller strips, for smaller 
{@linkplain #buffer}. */
         @Override int prepareTransferRegion() {
-            return ImageUtilities.prepareTransferRegion(region, 
DataBuffer.TYPE_FLOAT);
+            return prepareTransferRegion(region, DataBuffer.TYPE_FLOAT);
         }
 
         /** Copies source values in temporary buffer, applies conversion then 
copies to target. */
@@ -301,7 +342,7 @@ abstract class Transferer {
 
         /** Subdivides the region to process in smaller strips, for smaller 
{@linkplain #buffer}. */
         @Override final int prepareTransferRegion() {
-            return ImageUtilities.prepareTransferRegion(region, 
DataBuffer.TYPE_DOUBLE);
+            return prepareTransferRegion(region, DataBuffer.TYPE_DOUBLE);
         }
 
         /** Copies source values in temporary buffer, applies conversion then 
copies to target. */
@@ -408,7 +449,7 @@ abstract class Transferer {
 
         /** Subdivides the region to process in smaller strips, for smaller 
{@linkplain #buffer}. */
         @Override final int prepareTransferRegion() {
-            return ImageUtilities.prepareTransferRegion(region, 
DataBuffer.TYPE_FLOAT);
+            return prepareTransferRegion(region, DataBuffer.TYPE_FLOAT);
         }
 
         /** Copies source values in temporary buffer, applies conversion then 
copies to target. */
@@ -562,7 +603,7 @@ abstract class Transferer {
     }
 
     /**
-     * Returns {@code true} if the given raster use a data type that can be 
casted to the {@code float} type
+     * Returns {@code true} if the given raster uses a data type that can be 
casted to the {@code float} type
      * without precision lost. If the type is unknown, then this method 
returns {@code false}. Note that this
      * method also returns {@code false} for {@link DataBuffer#TYPE_INT} 
because conversion of 32 bits integer
      * to the {@code float} type may lost precision digits.
diff --git 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
index 8027050fb0..397eec973e 100644
--- 
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
+++ 
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/ImageUtilities.java
@@ -27,7 +27,6 @@ import java.awt.image.IndexColorModel;
 import java.awt.image.PackedColorModel;
 import java.awt.image.RenderedImage;
 import java.awt.image.Raster;
-import java.awt.image.RasterFormatException;
 import java.awt.image.SampleModel;
 import java.awt.image.SinglePixelPackedSampleModel;
 import java.awt.image.MultiPixelPackedSampleModel;
@@ -42,7 +41,6 @@ import org.apache.sis.util.resources.Vocabulary;
 import static java.lang.Math.abs;
 import static java.lang.Math.rint;
 import static java.lang.Math.floorDiv;
-import static java.lang.Math.addExact;
 import static java.lang.Math.toIntExact;
 import static java.lang.Math.multiplyFull;
 import static org.apache.sis.internal.util.Numerics.COMPARISON_THRESHOLD;
@@ -72,16 +70,6 @@ public final class ImageUtilities extends Static {
     @Configuration
     public static final int SUGGESTED_TILE_CACHE_SIZE = 10 * (1024 * 1024) / 
(DEFAULT_TILE_SIZE * DEFAULT_TILE_SIZE);
 
-    /**
-     * Approximate size of the buffer to use for copying data from/to a 
raster, in bits.
-     * The actual buffer size may be smaller or larger, depending on the 
actual tile size.
-     * This value does not need to be very large. The current value is 8 kb.
-     *
-     * @see #prepareTransferRegion(Rectangle, int)
-     */
-    @Configuration
-    private static final int BUFFER_SIZE = 32 * DEFAULT_TILE_SIZE * Byte.SIZE;
-
     /**
      * The logger for operations on images and rasters.
      */
@@ -594,34 +582,6 @@ public final class ImageUtilities extends Static {
         return r;
     }
 
-    /**
-     * Suggests the height of a transfer region for a tile of the given size. 
The given region should be
-     * contained inside {@link Raster#getBounds()}. This method modifies 
{@link Rectangle#height} in-place.
-     * The {@link Rectangle#width} value is never modified, so caller can 
iterate on all raster rows without
-     * the need to check if the row is incomplete.
-     *
-     * @param  bounds    on input, the region of interest. On output, the 
suggested transfer region bounds.
-     * @param  dataType  one of {@link DataBuffer} constant. It is okay if an 
unknown constant is used since
-     *                   this information is used only as a hint for adjusting 
the {@link #BUFFER_SIZE} value.
-     * @return the maximum <var>y</var> value plus 1. This can be used as stop 
condition for iterating over rows.
-     * @throws ArithmeticException if the maximum <var>y</var> value overflows 
32 bits integer capacity.
-     * @throws RasterFormatException if the given bounds is empty.
-     */
-    public static int prepareTransferRegion(final Rectangle bounds, final int 
dataType) {
-        if (bounds.isEmpty()) {
-            throw new 
RasterFormatException(Resources.format(Resources.Keys.EmptyTileOrImageRegion));
-        }
-        final int afterLastRow = addExact(bounds.y, bounds.height);
-        int size;
-        try {
-            size = DataBuffer.getDataTypeSize(dataType);
-        } catch (IllegalArgumentException e) {
-            size = Short.SIZE;  // Arbitrary value is okay because this is 
only a hint for choosing a buffer size.
-        }
-        bounds.height = Math.max(1, Math.min(BUFFER_SIZE / (size * 
bounds.width), bounds.height));
-        return afterLastRow;
-    }
-
     /**
      * If scale and shear coefficients are close to integers, replaces their 
current values by their rounded values.
      * The scale and shear coefficients are handled in a "all or nothing" way; 
either all of them or none are rounded.

Reply via email to