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.