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
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new a095d21 Add an Area Of Interest (AOI) argument in
ImageProcessor.prefetch(…).
a095d21 is described below
commit a095d214c4e75984c084180974b02583499b840e
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat May 16 18:43:03 2020 +0200
Add an Area Of Interest (AOI) argument in ImageProcessor.prefetch(…).
---
.../apache/sis/gui/coverage/CoverageCanvas.java | 20 ++-
.../org/apache/sis/gui/coverage/RenderingData.java | 26 +++
.../org/apache/sis/coverage/grid/GridCoverage.java | 5 +-
.../java/org/apache/sis/image/AnnotatedImage.java | 1 +
.../java/org/apache/sis/image/ImageProcessor.java | 63 +------
.../java/org/apache/sis/image/PlanarImage.java | 7 +-
.../java/org/apache/sis/image/PrefetchedImage.java | 187 +++++++++++++++++++++
.../sis/internal/coverage/j2d/ImageUtilities.java | 22 +++
.../sis/internal/coverage/j2d/TileOpExecutor.java | 9 +-
.../sis/internal/coverage/j2d/TiledImage.java | 3 +-
.../sis/internal/coverage/j2d/Transferer.java | 4 +-
.../sis/referencing/operation/matrix/Matrices.java | 2 +-
12 files changed, 280 insertions(+), 69 deletions(-)
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
index b774cf0..aeb414d 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
@@ -42,6 +42,7 @@ import org.apache.sis.coverage.grid.ImageRenderer;
import org.apache.sis.internal.gui.ExceptionReporter;
import org.apache.sis.referencing.operation.transform.LinearTransform;
import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.image.PlanarImage;
import org.apache.sis.gui.map.MapCanvas;
import org.apache.sis.gui.map.MapCanvasAWT;
@@ -293,18 +294,30 @@ public class CoverageCanvas extends MapCanvasAWT {
private RenderedImage stretchedImage;
/**
- * Conversion from {@link #resampledImage} (also {@link
#stretchedImage}) pixel coordinates
- * to display coordinates.
+ * The stretched image with tiles computed in advance. The set of
prefetched
+ * tiles may differ at each rendering event. This image should not be
cached.
+ */
+ private RenderedImage prefetchedImage;
+
+ /**
+ * Conversion from {@link #resampledImage} (also {@link
#prefetchedImage})
+ * pixel coordinates to display coordinates.
*/
private AffineTransform resampledToDisplay;
/**
+ * Size and location of the display device, in pixel units.
+ */
+ private final Envelope2D displayBounds;
+
+ /**
* Creates a new renderer.
*/
Worker(final CoverageCanvas canvas) {
data = canvas.data.clone();
objectiveCRS = canvas.getObjectiveCRS();
objectiveToDisplay = canvas.getObjectiveToDisplay();
+ displayBounds = canvas.getDisplayBounds();
if (data.validateCRS(objectiveCRS)) {
resampledImage = canvas.resampledImages.get(Stretching.NONE);
stretchedImage =
canvas.resampledImages.get(data.selectedStretching);
@@ -334,6 +347,7 @@ public class CoverageCanvas extends MapCanvasAWT {
if (stretchedImage == null) {
stretchedImage = data.stretch(resampledImage);
}
+ prefetchedImage = data.prefetch(stretchedImage,
resampledToDisplay, displayBounds);
}
/**
@@ -341,7 +355,7 @@ public class CoverageCanvas extends MapCanvasAWT {
*/
@Override
protected void paint(final Graphics2D gr) {
- gr.drawRenderedImage(stretchedImage, resampledToDisplay);
+ gr.drawRenderedImage(prefetchedImage, resampledToDisplay);
}
/**
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index 0948e36..8de11f5 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -23,6 +23,7 @@ import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
import org.opengis.util.FactoryException;
import org.opengis.referencing.datum.PixelInCell;
import org.opengis.referencing.operation.CoordinateOperation;
@@ -32,6 +33,7 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.coverage.grid.GridCoverage;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.Shapes2D;
import org.apache.sis.image.Interpolation;
import org.apache.sis.image.ImageProcessor;
@@ -253,6 +255,9 @@ final class RenderingData implements Cloneable {
/**
* Creates the stretched image from the given resampled image.
+ *
+ * @param resampledImage the image computed by {@link
#resample(CoordinateReferenceSystem, LinearTransform)}.
+ * @return image with color ramp stretched. May be the same instance than
given image.
*/
final RenderedImage stretch(final RenderedImage resampledImage) {
if (selectedStretching != Stretching.NONE) {
@@ -270,6 +275,27 @@ final class RenderingData implements Cloneable {
}
/**
+ * Computes immediately, possibly using many threads, the tiles that are
going to be displayed.
+ * The returned instance should be used only for current rendering event;
it should not be cached.
+ *
+ * @param stretchedImage the image computed by {@link
#stretch(RenderedImage)}.
+ * @param resampledToDisplay the transform computed by {@link
#getTransform(LinearTransform)}.
+ * @param displayBounds size and location of the display device, in
pixel units.
+ * @return a temporary image with tiles intersecting the display region
already computed.
+ */
+ final RenderedImage prefetch(final RenderedImage stretchedImage, final
AffineTransform resampledToDisplay,
+ final Envelope2D displayBounds)
+ {
+ try {
+ return processor.prefetch(stretchedImage, (Rectangle)
AffineTransforms2D.transform(
+ resampledToDisplay.createInverse(), displayBounds, new
Rectangle()));
+ } catch (NoninvertibleTransformException e) {
+ recoverableException(e);
+ return stretchedImage;
+ }
+ }
+
+ /**
* Gets the transform to use for painting the stretched image. If the
image to draw is an instance of
* {@link BufferedImage}, then it is okay to have any transform. However
for other kinds of image,
* it is important that the transform has scale factors of 1 and integer
translations because Java2D
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index 06a859d..e4830a7 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@ -34,6 +34,7 @@ import org.apache.sis.measure.NumberRange;
import org.apache.sis.coverage.SampleDimension;
import org.apache.sis.coverage.SubspaceNotSpecifiedException;
import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
+import org.apache.sis.internal.coverage.j2d.ImageUtilities;
import org.apache.sis.util.collection.DefaultTreeTable;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.TreeTable;
@@ -314,8 +315,8 @@ public abstract class GridCoverage {
* @throws IndexOutOfBoundsException if a coordinate is out of bounds.
*/
static double[] evaluate(final RenderedImage data, final int x, final int
y, final double[] buffer) {
- final int tx = Math.toIntExact(Math.floorDiv(x - (long)
data.getTileGridXOffset(), data.getTileWidth()));
- final int ty = Math.toIntExact(Math.floorDiv(y - (long)
data.getTileGridYOffset(), data.getTileHeight()));
+ final int tx = ImageUtilities.pixelToTileX(data, x);
+ final int ty = ImageUtilities.pixelToTileY(data, y);
return data.getTile(tx, ty).getPixel(x, y, buffer);
}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/AnnotatedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/image/AnnotatedImage.java
index 761995c..57b707c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/AnnotatedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/AnnotatedImage.java
@@ -313,6 +313,7 @@ abstract class AnnotatedImage extends ImageAdapter {
* method and invoke {@code super.computeProperty(…)} with a sub-region to
compute.
*
* @param areaOfInterest pixel coordinates of the region of interest, or
{@code null} for the whole image.
+ * It is caller responsibility to ensure that this rectangle is
fully included inside image bounds.
* @return the computed property value. Note that {@code null} is a valid
result.
* @throws Exception if an error occurred while computing the property.
*/
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
index ba1e200..e2fc193 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ImageProcessor.java
@@ -25,19 +25,15 @@ import java.util.logging.LogRecord;
import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.SampleModel;
-import java.awt.image.Raster;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.ImagingOpException;
-import java.awt.image.RasterFormatException;
import org.opengis.referencing.operation.MathTransform;
import org.apache.sis.math.Statistics;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.collection.WeakHashSet;
import org.apache.sis.internal.system.Modules;
-import org.apache.sis.internal.coverage.j2d.TileOpExecutor;
import org.apache.sis.internal.coverage.j2d.TiledImage;
import org.apache.sis.referencing.operation.transform.MathTransforms;
@@ -521,7 +517,7 @@ public class ImageProcessor implements Cloneable {
}
/**
- * Computes all tiles immediately, then return an image will all tiles
ready.
+ * Computes immediately all tiles in the given region of interest, then
return an image will those tiles ready.
* Computations will use many threads if {@linkplain #getExecutionMode()
execution mode} is parallel.
*
* <div class="note"><b>Note:</b>
@@ -529,62 +525,21 @@ public class ImageProcessor implements Cloneable {
* have a mechanism for specifying which tile to produce in replacement of
tiles that can not be computed.
* This behavior may be changed in a future version.</div>
*
- * @param source the image to compute immediately (may be {@code null}).
- * @return image with all tiles computed, or {@code null} if the given
image was null.
+ * @param source the image to compute immediately (may be {@code
null}).
+ * @param areaOfInterest pixel coordinates of the region to prefetch, or
{@code null} for the whole image.
+ * @return image with all tiles intersecting the AOI computed, or {@code
null} if the given image was null.
* @throws ImagingOpException if an exception occurred during {@link
RenderedImage#getTile(int, int)} call.
* This exception wraps the original exception as its {@linkplain
ImagingOpException#getCause() cause}.
*/
- public RenderedImage prefetch(final RenderedImage source) {
+ public RenderedImage prefetch(RenderedImage source, final Rectangle
areaOfInterest) {
if (source == null || source instanceof BufferedImage || source
instanceof TiledImage) {
return source;
}
- final Prefetch worker = new Prefetch(source);
- if (parallel(source)) {
- worker.parallelReadFrom(source);
- } else {
- worker.readFrom(source);
- }
- return new TiledImage(source.getColorModel(), source.getWidth(),
source.getHeight(),
- source.getMinTileX(), source.getMinTileY(),
worker.tiles);
- }
-
- /**
- * A worker for prefetching tiles in an image.
- */
- private static final class Prefetch extends TileOpExecutor {
- /** Number of tiles in a row. */
- private final int numXTiles;
-
- /** Image properties for converting pixel coordinates to tile indices.
*/
- private final long tileWidth, tileHeight, tileGridXOffset,
tileGridYOffset;
-
- /** The tiles in a row-major fashion. */
- final Raster[] tiles;
-
- /** Prepares an instance for prefetching tiles from the given image. */
- Prefetch(final RenderedImage source) {
- super(source, null);
- numXTiles = source.getNumXTiles();
- tileWidth = source.getTileWidth();
- tileHeight = source.getTileHeight();
- tileGridXOffset = source.getTileGridXOffset();
- tileGridYOffset = source.getTileGridYOffset();
- tiles = new
Raster[Math.multiplyExact(source.getNumYTiles(), numXTiles)];
- }
-
- /** Invoked in a when a tile have been computed, possibly in a
background thread. */
- @Override protected void readFrom(final Raster source) {
- final long tx = Math.floorDiv(source.getMinX() - tileGridXOffset,
tileWidth);
- final long ty = Math.floorDiv(source.getMinY() - tileGridYOffset,
tileHeight);
- final int index = Math.toIntExact(tx + ty*numXTiles);
- synchronized (tiles) {
- if (tiles[index] != null) {
- throw new RasterFormatException(Errors.format(
- Errors.Keys.DuplicatedElement_1, "Tile[" + tx + ",
" + ty + ']'));
- }
- tiles[index] = source;
- }
+ while (source instanceof PrefetchedImage) {
+ source = ((PrefetchedImage) source).source;
}
+ final PrefetchedImage image = new PrefetchedImage(source,
areaOfInterest, parallel(source));
+ return image.isEmpty() ? source : image;
}
/**
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 bc29971..618dc9a 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
@@ -371,7 +371,7 @@ public abstract class PlanarImage implements RenderedImage {
*/
@Override
public Raster getData() {
- final Rectangle aoi = ImageUtilities.getBounds(this);
+ final Rectangle aoi = getBounds();
final WritableRaster raster = createWritableRaster(aoi);
copyData(aoi, raster);
return raster;
@@ -388,7 +388,7 @@ public abstract class PlanarImage implements RenderedImage {
@Override
public Raster getData(final Rectangle aoi) {
ArgumentChecks.ensureNonNull("aoi", aoi);
- if (!ImageUtilities.getBounds(this).contains(aoi)) {
+ if (!getBounds().contains(aoi)) {
throw new
IllegalArgumentException(Errors.format(Errors.Keys.OutsideDomainOfValidity));
}
final WritableRaster raster = createWritableRaster(aoi);
@@ -410,8 +410,9 @@ public abstract class PlanarImage implements RenderedImage {
final Rectangle aoi;
if (raster != null) {
aoi = raster.getBounds();
+ ImageUtilities.clipBounds(this, aoi);
} else {
- aoi = ImageUtilities.getBounds(this);
+ aoi = getBounds();
raster = createWritableRaster(aoi);
}
copyData(aoi, raster);
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/PrefetchedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/image/PrefetchedImage.java
new file mode 100644
index 0000000..9d7e47e
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/PrefetchedImage.java
@@ -0,0 +1,187 @@
+/*
+ * 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.Vector;
+import java.awt.Rectangle;
+import java.awt.image.ColorModel;
+import java.awt.image.SampleModel;
+import java.awt.image.RenderedImage;
+import java.awt.image.Raster;
+import java.awt.image.RasterFormatException;
+import org.apache.sis.internal.coverage.j2d.ImageUtilities;
+import org.apache.sis.internal.coverage.j2d.TileOpExecutor;
+import org.apache.sis.util.resources.Errors;
+
+
+/**
+ * An image which delegate all tile requests to another image except for some
tiles that are fetched in advance.
+ * This image has the same coordinate systems than the source image.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @see ImageProcessor#prefetch(RenderedImage, Rectangle)
+ *
+ * @since 1.1
+ * @module
+ */
+final class PrefetchedImage extends PlanarImage {
+ /**
+ * The source image from which to prefetch tiles.
+ */
+ final RenderedImage source;
+
+ /**
+ * Index of the first prefetched tile. This index is determined from the
area of interest (AOI)
+ * specified at construction time; it is not necessarily the index of the
first tile in the image.
+ */
+ private final int minTileX, minTileY;
+
+ /**
+ * Number of prefetched tiles. This number is determined from the area of
interest (AOI)
+ * specified at construction time; it is not necessarily include all tiles
in the image.
+ */
+ private final int numXTiles, numYTiles;
+
+ /**
+ * The prefetched tiles. This array contains only the tiles in the area of
interest (AOI)
+ * specified at construction time; it does not necessarily contains all
tiles in the image.
+ */
+ private final Raster[] tiles;
+
+ /**
+ * Creates a new prefetched image.
+ *
+ * @param source the image to compute immediately (may be {@code
null}).
+ * @param areaOfInterest pixel coordinates of the region to prefetch, or
{@code null} for the whole image.
+ * @param parallel whether to execute computation in parallel.
+ */
+ PrefetchedImage(final RenderedImage source, Rectangle areaOfInterest,
final boolean parallel) {
+ this.source = source;
+ if (areaOfInterest != null) {
+ areaOfInterest = new Rectangle(areaOfInterest);
+ ImageUtilities.clipBounds(source, areaOfInterest);
+ }
+ final Worker worker = new Worker(source, areaOfInterest);
+ final Rectangle ti = worker.getTileIndices();
+ minTileX = ti.x;
+ minTileY = ti.y;
+ numXTiles = ti.width;
+ numYTiles = ti.height;
+ tiles = new Raster[Math.multiplyExact(numYTiles, numXTiles)];
+ if (parallel) {
+ worker.parallelReadFrom(source);
+ } else {
+ worker.readFrom(source);
+ }
+ }
+
+ /**
+ * A worker for prefetching tiles in an image.
+ */
+ private final class Worker extends TileOpExecutor {
+ /** Image properties for converting pixel coordinates to tile indices.
*/
+ private final long tileWidth, tileHeight, tileGridXOffset,
tileGridYOffset;
+
+ /** Prepares an instance for prefetching tiles from the given image. */
+ Worker(final RenderedImage source, final Rectangle aoi) {
+ super(source, aoi);
+ tileWidth = source.getTileWidth();
+ tileHeight = source.getTileHeight();
+ tileGridXOffset = source.getTileGridXOffset();
+ tileGridYOffset = source.getTileGridYOffset();
+ }
+
+ /** Invoked in a when a tile have been computed, possibly in a
background thread. */
+ @Override protected void readFrom(final Raster source) {
+ final long tx = Math.floorDiv(source.getMinX() - tileGridXOffset,
tileWidth) - minTileX;
+ final long ty = Math.floorDiv(source.getMinY() - tileGridYOffset,
tileHeight) - minTileY;
+ assert tx >= 0 && tx < numXTiles : tx;
+ assert ty >= 0 && ty < numYTiles : ty;
+ final int index = Math.toIntExact(tx + ty*numXTiles);
+ synchronized (tiles) {
+ if (tiles[index] != null) {
+ throw new RasterFormatException(Errors.format(
+ Errors.Keys.DuplicatedElement_1, "Tile[" + tx + ",
" + ty + ']'));
+ }
+ tiles[index] = source;
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if this image does not prefetch any tiles.
+ */
+ final boolean isEmpty() {
+ return (numXTiles | numYTiles) == 0;
+ }
+
+ /**
+ * Returns the immediate source of image data for this image.
+ */
+ @Override
+ @SuppressWarnings("UseOfObsoleteCollectionType")
+ public Vector<RenderedImage> getSources() {
+ final Vector<RenderedImage> sources = new Vector<>(1);
+ sources.add(source);
+ return sources;
+ }
+
+ /**
+ * Gets a property from the source image.
+ * All properties are inherited from the source with no change.
+ */
+ @Override
+ public Object getProperty(final String name) {
+ return source.getProperty(name);
+ }
+
+ /** Delegate to the source image. */
+ @Override public String[] getPropertyNames() {return
source.getPropertyNames();}
+ @Override public ColorModel getColorModel() {return
source.getColorModel();}
+ @Override public SampleModel getSampleModel() {return
source.getSampleModel();}
+ @Override public int getWidth() {return
source.getWidth();}
+ @Override public int getHeight() {return
source.getHeight();}
+ @Override public int getMinX() {return
source.getMinX();}
+ @Override public int getMinY() {return
source.getMinY();}
+ @Override public int getNumXTiles() {return
source.getNumXTiles();}
+ @Override public int getNumYTiles() {return
source.getNumYTiles();}
+ @Override public int getMinTileX() {return
source.getMinTileX();}
+ @Override public int getMinTileY() {return
source.getMinTileY();}
+ @Override public int getTileWidth() {return
source.getTileWidth();}
+ @Override public int getTileHeight() {return
source.getTileHeight();}
+ @Override public int getTileGridXOffset() {return
source.getTileGridXOffset();}
+ @Override public int getTileGridYOffset() {return
source.getTileGridYOffset();}
+
+ /**
+ * Returns the tile at the given location in tile coordinates. If the
requested
+ * tile is in the region of prefetched tiles, that tile is returned
directly.
+ * Otherwise this method delegates to the source image.
+ */
+ @Override
+ public Raster getTile(final int tileX, final int tileY) {
+ final int tx = Math.subtractExact(tileX, minTileX);
+ if (tx >= 0 && tileX < numXTiles) {
+ final int ty = Math.subtractExact(tileY, minTileY);
+ if (ty >= 0 && tileY < numYTiles) {
+ return tiles[tx + ty * numXTiles];
+ }
+ }
+ return source.getTile(tileX, tileY);
+ }
+}
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 2fee3e2..b6cb98f 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
@@ -386,6 +386,28 @@ public final class ImageUtilities extends Static {
}
/**
+ * Converts a <var>x</var> pixel coordinates to a tile index.
+ *
+ * @param image the image containing tiles.
+ * @param x the pixel coordinate for which to get tile index.
+ * @return tile index for the given pixel coordinate.
+ */
+ public static int pixelToTileX(final RenderedImage image, final int x) {
+ return Math.toIntExact(Math.floorDiv((x - (long)
image.getTileGridXOffset()), image.getTileWidth()));
+ }
+
+ /**
+ * Converts a <var>y</var> pixel coordinates to a tile index.
+ *
+ * @param image the image containing tiles.
+ * @param y the pixel coordinate for which to get tile index.
+ * @return tile index for the given pixel coordinate.
+ */
+ public static int pixelToTileY(final RenderedImage image, final int y) {
+ return Math.toIntExact(Math.floorDiv((y - (long)
image.getTileGridYOffset()), image.getTileHeight()));
+ }
+
+ /**
* 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
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
index 5eaa015..e451af7 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
@@ -77,12 +77,15 @@ import org.apache.sis.internal.util.Strings;
public class TileOpExecutor {
/**
* Minimum/maximum index of tiles to process, inclusive.
+ *
+ * @see #getTileIndices()
*/
private final int minTileX, minTileY, maxTileX, maxTileY;
/**
* Creates a new operation for tiles in the specified region of the
specified image.
- * It is caller responsibility to ensure that {@code aoi} is contained
inside {@code image} bounds.
+ * It is caller responsibility to ensure that {@code aoi} is contained
inside {@code image} bounds
+ * (caller can invoke {@link ImageUtilities#clipBounds(RenderedImage,
Rectangle)} if needed).
*
* @param image the image from which tiles will be fetched.
* @param aoi region of interest, or {@code null} for the whole image.
@@ -126,7 +129,9 @@ public class TileOpExecutor {
* @return range of tile indices to be processed.
*/
public final Rectangle getTileIndices() {
- return new Rectangle(minTileX, minTileY, maxTileX - minTileX + 1,
maxTileY - minTileY + 1);
+ return new Rectangle(minTileX, minTileY,
+ Math.incrementExact(Math.subtractExact(maxTileX, minTileX)),
+ Math.incrementExact(Math.subtractExact(maxTileY, minTileY)));
}
/**
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TiledImage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TiledImage.java
index 0747f6b..2060815 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TiledImage.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TiledImage.java
@@ -49,8 +49,7 @@ public final class TiledImage extends PlanarImage {
private final int width, height;
/**
- * Index of the first tile in the image. Should be a non-trivial value
- * for increasing the chances to detect error in index calculation.
+ * Index of the first tile in the image.
*/
private final int minTileX, minTileY;
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Transferer.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Transferer.java
index 12fc7d5..2f802ef 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Transferer.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/Transferer.java
@@ -506,8 +506,8 @@ abstract class Transferer {
* @return object to use for applying the operation.
*/
static Transferer create(final RenderedImage source, final WritableRaster
target) {
- int tileX = Math.floorDiv((target.getMinX() -
source.getTileGridXOffset()), source.getTileWidth());
- int tileY = Math.floorDiv((target.getMinY() -
source.getTileGridYOffset()), source.getTileHeight());
+ int tileX = ImageUtilities.pixelToTileX(source, target.getMinX());
+ int tileY = ImageUtilities.pixelToTileY(source, target.getMinY());
return create(source.getTile(tileX, tileY), target,
target.getBounds());
}
diff --git
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
index 57ff192..80d194a 100644
---
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
+++
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
@@ -889,7 +889,7 @@ public final class Matrices extends Static {
if (anchor != null) {
final double p = anchor[j];
double e = matrix.getElement(j, srcDim);
- changed |= (e != (e = (e-p)*rescale + p));
+ changed |= (e != (e = (e-p)*rescale + p)); // TODO:
use Math.fma in JDK9.
matrix.setElement(j, srcDim, e);
}
}