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 951c06a7c0f74c72dffd47d0974136a563a34a1d
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Tue Jul 28 16:12:33 2020 +0200
Add a WritableTiledImage implementation.
---
.../sis/coverage/grid/GridCoverageBuilder.java | 10 +-
.../apache/sis/coverage/grid/ImageRenderer.java | 28 ++-
.../sis/internal/coverage/j2d/TiledImage.java | 23 ++-
.../internal/coverage/j2d/WritableTiledImage.java | 218 +++++++++++++++++++++
.../org/apache/sis/internal/feature/Resources.java | 7 +-
.../sis/internal/feature/Resources.properties | 3 +-
.../sis/internal/feature/Resources_fr.properties | 3 +-
7 files changed, 273 insertions(+), 19 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
index 4133641..bef443c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridCoverageBuilder.java
@@ -36,6 +36,7 @@ import org.opengis.referencing.operation.TransformException;
import org.apache.sis.coverage.SampleDimension;
import org.apache.sis.internal.coverage.j2d.Colorizer;
import org.apache.sis.internal.coverage.j2d.TiledImage;
+import org.apache.sis.internal.coverage.j2d.WritableTiledImage;
import org.apache.sis.internal.feature.Resources;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
@@ -474,8 +475,13 @@ public class GridCoverageBuilder {
* Create an image from the raster. We favor BufferedImage instance when
possible,
* and fallback on TiledImage only if the BufferedImage can not be created.
*/
- if (colors != null && raster instanceof WritableRaster &&
(raster.getMinX() | raster.getMinY()) == 0) {
- image = new BufferedImage(colors, (WritableRaster) raster, false, properties);
+ if (raster instanceof WritableRaster) {
+ final WritableRaster wr = (WritableRaster) raster;
+ if (colors != null && (wr.getMinX() | wr.getMinY()) == 0) {
+ image = new BufferedImage(colors, wr, false, properties);
+ } else {
+ image = new WritableTiledImage(properties, colors, wr.getWidth(),
wr.getHeight(), 0, 0, wr);
+ }
} else {
image = new TiledImage(properties, colors, raster.getWidth(), raster.getHeight(),
0, 0, raster);
}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
index 231ff8f..68c6200 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ImageRenderer.java
@@ -29,6 +29,7 @@ import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.awt.image.ImagingOpException;
import java.awt.image.RasterFormatException;
+import java.awt.image.Raster;
import org.opengis.util.FactoryException;
import org.opengis.geometry.MismatchedDimensionException;
import org.apache.sis.coverage.SubspaceNotSpecifiedException;
@@ -38,6 +39,7 @@ import org.apache.sis.internal.coverage.j2d.Colorizer;
import org.apache.sis.internal.coverage.j2d.DeferredProperty;
import org.apache.sis.internal.coverage.j2d.RasterFactory;
import org.apache.sis.internal.coverage.j2d.TiledImage;
+import org.apache.sis.internal.coverage.j2d.WritableTiledImage;
import org.apache.sis.internal.feature.Resources;
import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.util.NullArgumentException;
@@ -571,13 +573,14 @@ public class ImageRenderer {
/**
* Creates a raster with the data specified by the last call to a {@code setData(…)}
method.
* The raster upper-left corner is located at the position given by {@link #getBounds()}.
+ * The returned raster is often an instance of {@link WritableRaster}, but read-only
rasters are also allowed.
*
- * @return the raster.
+ * @return the raster, usually (but not necessarily) an instance of {@link WritableRaster}.
* @throws IllegalStateException if no {@code setData(…)} method has been invoked before
this method call.
- * @throws RasterFormatException if a call to a {@link WritableRaster} factory method
failed.
+ * @throws RasterFormatException if a call to a {@link Raster} factory method failed.
* @throws ArithmeticException if a property of the raster to construct exceeds the capacity
of 32 bits integers.
*/
- public WritableRaster raster() {
+ public Raster raster() {
if (buffer == null) {
throw new IllegalStateException(Resources.format(Resources.Keys.UnspecifiedRasterData));
}
@@ -614,14 +617,18 @@ public class ImageRenderer {
* The two-dimensional {@linkplain #getImageGeometry(int) image geometry} is stored as
* a property associated to the {@value org.apache.sis.image.PlanarImage#GRID_GEOMETRY_KEY}
key.
*
+ * <p>The default implementation returns an instance of {@link java.awt.image.WritableRenderedImage}
+ * if the {@link #raster()} return value is an instance of {@link WritableRaster}, or
a read-only
+ * {@link RenderedImage} otherwise.</p>
+ *
* @return the image.
* @throws IllegalStateException if no {@code setData(…)} method has been invoked before
this method call.
- * @throws RasterFormatException if a call to a {@link WritableRaster} factory method
failed.
+ * @throws RasterFormatException if a call to a {@link Raster} factory method failed.
* @throws ArithmeticException if a property of the image to construct exceeds the capacity
of 32 bits integers.
*/
@SuppressWarnings("UseOfObsoleteCollectionType")
public RenderedImage image() {
- final WritableRaster raster = raster();
+ final Raster raster = raster();
final Colorizer colorizer = new Colorizer(Colorizer.GRAYSCALE);
final ColorModel colors;
if (colorizer.initialize(bands[visibleBand]) || colorizer.initialize(raster.getSampleModel(),
visibleBand)) {
@@ -637,14 +644,19 @@ public class ImageRenderer {
supplier = new SliceGeometry(geometry, sliceExtent, gridDimensions, null);
}
}
- if (colors != null && (imageX | imageY) == 0) {
- return new Untiled(colors, raster, properties, imageGeometry, supplier);
+ final WritableRaster wr = (raster instanceof WritableRaster) ? (WritableRaster) raster
: null;
+ if (wr != null && colors != null && (imageX | imageY) == 0) {
+ return new Untiled(colors, wr, properties, imageGeometry, supplier);
}
if (properties == null) {
properties = new Hashtable<>();
}
properties.putIfAbsent(GRID_GEOMETRY_KEY, (supplier != null) ? new DeferredProperty(supplier)
: imageGeometry);
- return new TiledImage(properties, colors, width, height, 0, 0, raster);
+ if (wr != null) {
+ return new WritableTiledImage(properties, colors, width, height, 0, 0, wr);
+ } else {
+ return new TiledImage(properties, colors, width, height, 0, 0, raster);
+ }
}
/**
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 1d40cc0..8cce878 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
@@ -25,6 +25,7 @@ import java.awt.image.SampleModel;
import org.apache.sis.image.PlanarImage;
import org.apache.sis.internal.jdk9.JDK9;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
/**
@@ -41,7 +42,7 @@ import org.apache.sis.util.ArgumentChecks;
* @since 1.1
* @module
*/
-public final class TiledImage extends PlanarImage {
+public class TiledImage extends PlanarImage {
/**
* The color model, or {@code null} if none.
*/
@@ -80,6 +81,7 @@ public final class TiledImage extends PlanarImage {
* @param minTileX minimum tile index in the X direction.
* @param minTileY minimum tile index in the Y direction.
* @param tiles the tiles. Must contains at least one element.
+ * This array is not cloned.
*/
public TiledImage(final Map<String,Object> properties, final ColorModel colorModel,
final int width, final int height, final int minTileX, final int minTileY,
@@ -211,11 +213,20 @@ public final class TiledImage extends PlanarImage {
public Raster getTile(int tileX, int tileY) {
final int numXTiles = getNumXTiles();
final int numYTiles = getNumYTiles();
- if ((tileX -= minTileX) < 0 || tileX >= numXTiles ||
- (tileY -= minTileY) < 0 || tileY >= numYTiles)
- {
- throw new IndexOutOfBoundsException();
- }
+ tileX = verifyTileIndex("tileX", tileX, minTileX, numXTiles);
+ tileY = verifyTileIndex("tileY", tileY, minTileY, numYTiles);
return tiles[tileX + tileY * numXTiles];
}
+
+ /**
+ * Verifies that the given tile index is inside expected bounds, then returns is zero-based
value.
+ */
+ private static int verifyTileIndex(final String name, final int value, final int min,
final int count) {
+ final int r = value - min;
+ if (r >= 0 && r < count) {
+ return r;
+ }
+ throw new IndexOutOfBoundsException(Errors.format(
+ Errors.Keys.ValueOutOfRange_4, name, min, min + count - 1, value));
+ }
}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/WritableTiledImage.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/WritableTiledImage.java
new file mode 100644
index 0000000..16b5078
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/WritableTiledImage.java
@@ -0,0 +1,218 @@
+/*
+ * 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.internal.coverage.j2d;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.TileObserver;
+import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
+import org.apache.sis.internal.feature.Resources;
+
+
+/**
+ * A writable version of {@link TiledImage}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public class WritableTiledImage extends TiledImage implements WritableRenderedImage {
+ /**
+ * The observers, or {@code null} if none. This is a copy-on-write array:
+ * values are never modified after construction (new arrays are created).
+ *
+ * This field is declared volatile because it is read without synchronization by
+ * {@link #getWritableTile(int, int)} and {@link #releaseWritableTile(int, int)}.
+ * Since this is a copy-on-write array, it is okay to omit synchronization for
+ * those methods but we still need the memory effect.
+ */
+ @SuppressWarnings("VolatileArrayField")
+ private volatile TileObserver[] observers;
+
+ /**
+ * Indices of tiles taken for a write operation.
+ * Values are counter of calls to {@link #getWritableTile(int, int)}.
+ * All accesses to this map shall be synchronized on the map instance.
+ */
+ private final Map<Point,Integer> writables;
+
+ /**
+ * Creates a new tiled image. The first tile in the given array must be the one located
at the minimal tile
+ * indices. All tiles must have the same size and the same sample model and must be sorted
in row-major fashion.
+ *
+ * @param properties image properties, or {@code null} if none.
+ * @param colorModel the color model, or {@code null} if none.
+ * @param width number of pixels along X axis in the whole rendered image.
+ * @param height number of pixels along Y axis in the whole rendered image.
+ * @param minTileX minimum tile index in the X direction.
+ * @param minTileY minimum tile index in the Y direction.
+ * @param tiles the tiles. Must contains at least one element.
+ * This array is not cloned.
+ */
+ public WritableTiledImage(final Map<String,Object> properties, final ColorModel
colorModel,
+ final int width, final int height, final int minTileX, final
int minTileY,
+ final WritableRaster... tiles)
+ {
+ super(properties, colorModel, width, height, minTileX, minTileY, tiles);
+ writables = new LinkedHashMap<>();
+ }
+
+ /**
+ * Adds an observer to be notified when a tile is checked out for writing.
+ * If the observer is already present, it will receive multiple notifications.
+ *
+ * @param observer the observer to notify.
+ */
+ @Override
+ public synchronized void addTileObserver(final TileObserver observer) {
+ observers = WriteSupport.addTileObserver(observers, observer);
+ }
+
+ /**
+ * Removes an observer from the list of observers notified when a tile is checked out
for writing.
+ * If the observer was not registered, nothing happens. If the observer was registered
for multiple
+ * notifications, it will now be registered for one fewer.
+ *
+ * @param observer the observer to stop notifying.
+ */
+ @Override
+ public synchronized void removeTileObserver(final TileObserver observer) {
+ observers = WriteSupport.removeTileObserver(observers, observer);
+ }
+
+ /**
+ * Checks out a tile for writing. If the same tile is checked out many times
+ * before to be released, only the first checkout is notified to listeners.
+ *
+ * @param tileX the <var>x</var> index of the tile.
+ * @param tileY the <var>y</var> index of the tile.
+ * @return the specified tile as a writable tile.
+ */
+ @Override
+ public WritableRaster getWritableTile(final int tileX, final int tileY) {
+ final WritableRaster tile = (WritableRaster) super.getTile(tileX, tileY);
+ final Point key = new Point(tileX, tileY);
+ final Integer count;
+ synchronized (writables) {
+ count = writables.merge(key, 1, (old, one) -> old + 1);
+ }
+ if (count <= 1) {
+ WriteSupport.fireTileUpdate(observers, this, tileX, tileY, true);
+ }
+ return tile;
+ }
+
+ /**
+ * Relinquishes the right to write to a tile. If the tile goes from having one writer
to
+ * having no writers, the values are inverse converted and written in the original image.
+ * If the caller continues to write to the tile, the results are undefined.
+ *
+ * @param tileX the <var>x</var> index of the tile.
+ * @param tileY the <var>y</var> index of the tile.
+ */
+ @Override
+ public void releaseWritableTile(final int tileX, final int tileY) {
+ final Point key = new Point(tileX, tileY);
+ final Integer count;
+ final boolean close;
+ synchronized (writables) {
+ count = writables.computeIfPresent(key, (k, old) -> old - 1);
+ close = (count != null && count <= 0);
+ if (close) {
+ writables.remove(key);
+ }
+ }
+ if (count == null) {
+ throw new IllegalArgumentException(Resources.format(Resources.Keys.TileNotWritable_2,
tileX, tileY));
+ }
+ if (close) {
+ WriteSupport.fireTileUpdate(observers, this, tileX, tileY, false);
+ }
+ }
+
+ /**
+ * Returns whether a tile is currently checked out for writing.
+ *
+ * @param tileX the <var>x</var> index of the tile.
+ * @param tileY the <var>y</var> index of the tile.
+ * @return {@code true} if specified tile is checked out for writing, {@code false} otherwise.
+ */
+ @Override
+ public boolean isTileWritable(final int tileX, final int tileY) {
+ final Point key = new Point(tileX, tileY);
+ synchronized (writables) {
+ return writables.containsKey(key);
+ }
+ }
+
+ /**
+ * Returns the indices of all tiles checked out for writing.
+ * Returns null if none are checked out.
+ *
+ * @return indices of tiles that are checked out for writing, or {@code null} if none.
+ */
+ @Override
+ public Point[] getWritableTileIndices() {
+ final Point[] indices;
+ synchronized (writables) {
+ final int n = writables.size();
+ if (n == 0) return null;
+ indices = writables.keySet().toArray(new Point[n]);
+ }
+ for (int i=0; i<indices.length; i++) {
+ indices[i] = new Point(indices[i]);
+ }
+ return indices;
+ }
+
+ /**
+ * Returns whether any tile is checked out for writing.
+ *
+ * @return {@code true} if any tiles are checked out for writing, or {@code false} otherwise.
+ */
+ @Override
+ public boolean hasTileWriters() {
+ synchronized (writables) {
+ return !writables.isEmpty();
+ }
+ }
+
+ /**
+ * Sets a region of the image to the contents of the given raster.
+ * The raster is assumed to be in the same coordinate space as this image.
+ * The operation is clipped to the bounds of this image.
+ *
+ * @param data the values to write in this image.
+ */
+ @Override
+ public void setData(final Raster data) {
+ final Rectangle bounds = data.getBounds();
+ ImageUtilities.clipBounds(this, bounds);
+ final TileOpExecutor op = new TileOpExecutor(this, bounds) {
+ @Override protected void writeTo(final WritableRaster target) {
+ target.setRect(data);
+ }
+ };
+ op.writeTo(this);
+ }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
index bf46750..fa4a909 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java
@@ -367,11 +367,16 @@ public final class Resources extends IndexedResourceBundle {
public static final short PropertyNotFound_2 = 16;
/**
- * Tile ({0}, {1}) has the error flag set.
+ * Tile ({0}, {1}) is unavailable because of error in a previous calculation attempt.
*/
public static final short TileErrorFlagSet_2 = 68;
/**
+ * Tile ({0}, {1}) is not writable.
+ */
+ public static final short TileNotWritable_2 = 78;
+
+ /**
* Too many qualitative categories.
*/
public static final short TooManyQualitatives = 48;
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
index 4f08663..1072738 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties
@@ -78,7 +78,8 @@ OptionalLibraryNotFound_2 = The {0} optional library is not available.
G
OutOfIteratorDomain_2 = The ({0,number}, {1,number}) pixel coordinate is outside
iterator domain.
PropertyAlreadyExists_2 = Property \u201c{1}\u201d already exists in feature \u201c{0}\u201d.
PropertyNotFound_2 = No property named \u201c{1}\u201d has been found in \u201c{0}\u201d
feature.
-TileErrorFlagSet_2 = Tile ({0}, {1}) has the error flag set.
+TileErrorFlagSet_2 = Tile ({0}, {1}) is unavailable because of error in a
previous calculation attempt.
+TileNotWritable_2 = Tile ({0}, {1}) is not writable.
TooManyQualitatives = Too many qualitative categories.
TransformDependsOnDimension_1 = Coordinate operation depends on grid dimension {0}.
UnavailableGeometryLibrary_1 = The {0} geometry library is not available in current
runtime environment.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
index 63935a4..1f31415 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
+++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties
@@ -84,7 +84,8 @@ OutOfIteratorDomain_2 = La coordonn\u00e9e pixel ({0,number},
{1,num
PointOutsideCoverageDomain_1 = Le point ({0}) est en dehors du domaine de la couverture
de donn\u00e9es.
PropertyAlreadyExists_2 = La propri\u00e9t\u00e9 \u00ab\u202f{1}\u202f\u00bb existe
d\u00e9j\u00e0 dans l\u2019entit\u00e9 \u00ab\u202f{0}\u202f\u00bb.
PropertyNotFound_2 = Aucune propri\u00e9t\u00e9 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb
n\u2019a \u00e9t\u00e9 trouv\u00e9e dans l\u2019entit\u00e9 \u00ab\u202f{0}\u202f\u00bb.
-TileErrorFlagSet_2 = La tuile ({0}, {1}) est marqu\u00e9e comme ayant une
erreur.
+TileErrorFlagSet_2 = La tuile ({0}, {1}) est indisponible pour cause d\u2019erreur
lors d\u2019une tentative ant\u00e9rieure de calcul.
+TileNotWritable_2 = La tuile ({0}, {1}) n\u2019est pas d\u00e9clar\u00e9e
en \u00e9criture.
TooManyQualitatives = Trop de cat\u00e9gories qualitatives.
TransformDependsOnDimension_1 = L\u2019op\u00e9ration sur les coordonn\u00e9es d\u00e9pend
de la dimension {0} de la grille.
UnavailableGeometryLibrary_1 = La biblioth\u00e8que de g\u00e9om\u00e9tries {0} n\u2019est
pas disponible dans l\u2019environnement d\u2019ex\u00e9cution actuel.
|