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 4322a73 Writing in ConvertedGridCoverage can now retro-propagate the
changes to original WritableRenderedImage.
4322a73 is described below
commit 4322a732dd359d5b582e72c3bba192bf89e758c5
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Wed Jan 8 19:58:40 2020 +0100
Writing in ConvertedGridCoverage can now retro-propagate the changes to
original WritableRenderedImage.
---
.../java/org/apache/sis/image/ComputedImage.java | 4 +-
.../java/org/apache/sis/image/PlanarImage.java | 23 ++--
.../coverage/j2d/BandedSampleConverter.java | 91 ++++++++++++-
.../sis/internal/coverage/j2d/ImageUtilities.java | 23 ++++
.../sis/internal/coverage/j2d/TileOpExecutor.java | 146 +++++++++++++++++++++
.../sis/internal/coverage/j2d/WriteSupport.java | 101 ++++++++++++++
.../org/apache/sis/internal/feature/Resources.java | 5 +
.../sis/internal/feature/Resources.properties | 1 +
.../sis/internal/feature/Resources_fr.properties | 1 +
.../sis/coverage/grid/GridCoverage2DTest.java | 6 +-
.../internal/coverage/j2d/ImageUtilitiesTest.java | 24 ++++
11 files changed, 401 insertions(+), 24 deletions(-)
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
b/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
index 62bcd15..f99775d 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ComputedImage.java
@@ -522,8 +522,8 @@ public abstract class ComputedImage extends PlanarImage {
* }
* }
*
- * @param tileX the X index of the tile to acquire or release.
- * @param tileY the Y index of the tile to acquire or release.
+ * @param tileX the <var>x</var> index of the tile to acquire or
release.
+ * @param tileY the <var>y</var> index of the tile to acquire or
release.
* @param writing {@code true} for acquiring the tile, or {@code false}
for releasing it.
* @return {@code true} if the tile goes from having no writers to having
one writer
* (may happen if {@code writing} is {@code true}), or goes from
having one
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 6b0a741..02b4c6b 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
@@ -31,6 +31,7 @@ import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.internal.util.Numerics;
import org.apache.sis.internal.coverage.j2d.ImageUtilities;
+import org.apache.sis.internal.coverage.j2d.TileOpExecutor;
import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
@@ -341,14 +342,6 @@ public abstract class PlanarImage implements RenderedImage
{
* @param raster the raster to hold a copy of this image, or {@code
null}.
*/
private void copyData(final Rectangle aoi, final WritableRaster raster) {
- final int tileWidth = getTileWidth();
- final int tileHeight = getTileHeight();
- final long tileGridXOffset = getTileGridXOffset(); // We want
64 bits arithmetic in operations below.
- final long tileGridYOffset = getTileGridYOffset();
- final int minTileX = Math.toIntExact(Math.floorDiv(aoi.x
- tileGridXOffset, tileWidth));
- final int minTileY = Math.toIntExact(Math.floorDiv(aoi.y
- tileGridYOffset, tileHeight));
- final int maxTileX = Math.toIntExact(Math.floorDiv(aoi.x + (aoi.width
- 1L) - tileGridXOffset, tileWidth));
- final int maxTileY = Math.toIntExact(Math.floorDiv(aoi.y +
(aoi.height - 1L) - tileGridYOffset, tileHeight));
/*
* 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.
@@ -356,11 +349,12 @@ public abstract class PlanarImage implements
RenderedImage {
* Note that `tb` should never be empty since we restrict iteration to
the tiles
* that intersect the given area of interest.
*/
- Object buffer = null;
- int bufferCapacity = 0;
- for (int ty = minTileY; ty <= maxTileY; ty++) {
- for (int tx = minTileX; tx <= maxTileX; tx++) {
- final Raster tile = getTile(tx, ty);
+ final TileOpExecutor executor = new TileOpExecutor(this, 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;
@@ -375,7 +369,8 @@ public abstract class PlanarImage implements RenderedImage {
tb.y += height;
}
}
- }
+ };
+ executor.readFrom(this);
}
/**
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
index de3ddaa..582262a 100644
---
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/BandedSampleConverter.java
@@ -17,6 +17,7 @@
package org.apache.sis.internal.coverage.j2d;
import java.awt.Dimension;
+import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.awt.image.RenderedImage;
@@ -76,8 +77,8 @@ public class BandedSampleConverter extends ComputedImage {
* @param colorModel the color model for from the expected range of
values, or {@code null}.
* @param converters the transfer functions to apply on each band of
the source image.
*/
- BandedSampleConverter(final RenderedImage source, final BandedSampleModel
sampleModel,
- final ColorModel colorModel, final MathTransform1D[]
converters)
+ private BandedSampleConverter(final RenderedImage source, final
BandedSampleModel sampleModel,
+ final ColorModel colorModel, final
MathTransform1D[] converters)
{
super(sampleModel, source);
this.colorModel = colorModel;
@@ -226,6 +227,7 @@ public class BandedSampleConverter extends ComputedImage {
/**
* A {@code BandedSampleConverter} capable to retro-propagate the changes
to the source coverage.
+ * This class contains the inverse of all {@link MathTransform1D} given to
the parent class.
*/
private static final class Writable extends BandedSampleConverter
implements WritableRenderedImage {
/**
@@ -234,6 +236,17 @@ public class BandedSampleConverter extends ComputedImage {
private final MathTransform1D[] inverses;
/**
+ * 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 #markTileWritable(int, int, boolean)}. Since this is a
copy-on-write array,
+ * it is okay to omit synchronization for that method but we still
need the memory effect.
+ */
+ @SuppressWarnings("VolatileArrayField")
+ private volatile TileObserver[] observers;
+
+ /**
* Creates a new writable image which will compute values using the
given converters.
*/
Writable(final WritableRenderedImage source, final BandedSampleModel
sampleModel,
@@ -243,26 +256,92 @@ public class BandedSampleConverter extends ComputedImage {
this.inverses = inverses;
}
+ /**
+ * 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 void addTileObserver(final TileObserver observer) {
+ public synchronized void removeTileObserver(final TileObserver
observer) {
+ observers = WriteSupport.removeTileObserver(observers, observer);
}
+ /**
+ * Sets or clears whether a tile is checked out for writing and
notifies the listener if needed.
+ *
+ * @param tileX the <var>x</var> index of the tile to acquire or
release.
+ * @param tileY the <var>y</var> index of the tile to acquire or
release.
+ * @param writing {@code true} for acquiring the tile, or {@code
false} for releasing it.
+ */
@Override
- public void removeTileObserver(final TileObserver observer) {
+ protected boolean markTileWritable(final int tileX, final int tileY,
final boolean writing) {
+ final boolean notify = super.markTileWritable(tileX, tileY,
writing);
+ if (notify) {
+ WriteSupport.fireTileUpdate(observers, this, tileX, tileY,
writing);
+ }
+ return notify;
}
+ /**
+ * Checks out a tile for writing.
+ *
+ * @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) {
- throw new UnsupportedOperationException();
+ final WritableRaster tile = (WritableRaster) getTile(tileX, tileY);
+ markTileWritable(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) {
+ if (markTileWritable(tileX, tileY, false)) {
+ setData(getTile(tileX, tileY));
+ }
}
+ /**
+ * 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) {
- throw new UnsupportedOperationException();
+ final Rectangle aoi = data.getBounds();
+ final WritableRenderedImage target = (WritableRenderedImage)
getSource(0);
+ ImageUtilities.clipBounds(target, aoi);
+ final TileOpExecutor executor = new TileOpExecutor(target, aoi) {
+ @Override protected void writeTo(final WritableRaster target)
throws TransformException {
+ Transferer.create(data, target).compute(inverses);
+ }
+ };
+ executor.writeTo(target);
}
}
}
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 437b60b..6afdb3d 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
@@ -30,6 +30,7 @@ import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import org.apache.sis.internal.feature.Resources;
import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.util.Numerics;
import org.apache.sis.util.Static;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Vocabulary;
@@ -79,6 +80,28 @@ public final class ImageUtilities extends Static {
}
/**
+ * Clips the given rectangle to the bounds of the given image.
+ *
+ * @param image the image.
+ * @param aoi a region of interest to clip to the image bounds.
+ */
+ public static void clipBounds(final RenderedImage image, final Rectangle
aoi) {
+ int low = aoi.x;
+ int min = image.getMinX();
+ if (low < min) aoi.x = min;
+ aoi.width = Numerics.clamp(Math.min(
+ ((long) min) + image.getWidth(),
+ ((long) low) + aoi.width) - aoi.x);
+
+ low = aoi.y;
+ min = image.getMinY();
+ if (low < min) aoi.y = min;
+ aoi.height = Numerics.clamp(Math.min(
+ ((long) min) + image.getHeight(),
+ ((long) low) + aoi.height) - aoi.y);
+ }
+
+ /**
* Returns the data type of the given raster.
*
* @param raster the raster for which to get the data type, or {@code
null}.
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
new file mode 100644
index 0000000..5fe1882
--- /dev/null
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/TileOpExecutor.java
@@ -0,0 +1,146 @@
+/*
+ * 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.awt.Rectangle;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
+import java.awt.image.ImagingOpException;
+import org.apache.sis.internal.feature.Resources;
+
+
+/**
+ * An action to execute on each tile of an image.
+ * Subclasses should override one of the following methods:
+ *
+ * <ul>
+ * <li>{@link #readFrom(Raster)}</li>
+ * <li>{@link #writeTo(WritableRaster)}</li>
+ * </ul>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public abstract class TileOpExecutor {
+ /**
+ * Minimum/maximum index of tiles to process, inclusive.
+ */
+ 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 in
{@code image} bounds.
+ *
+ * @param image the image from which tiles will be fetched.
+ * @param aoi region of interest.
+ */
+ protected TileOpExecutor(final RenderedImage image, final Rectangle aoi) {
+ final int tileWidth = image.getTileWidth();
+ final int tileHeight = image.getTileHeight();
+ final long tileGridXOffset = image.getTileGridXOffset(); // We want
64 bits arithmetic in operations below.
+ final long tileGridYOffset = image.getTileGridYOffset();
+ minTileX = Math.toIntExact(Math.floorDiv(aoi.x -
tileGridXOffset, tileWidth));
+ minTileY = Math.toIntExact(Math.floorDiv(aoi.y -
tileGridYOffset, tileHeight));
+ maxTileX = Math.toIntExact(Math.floorDiv(aoi.x + (aoi.width - 1L) -
tileGridXOffset, tileWidth));
+ maxTileY = Math.toIntExact(Math.floorDiv(aoi.y + (aoi.height - 1L) -
tileGridYOffset, tileHeight));
+ }
+
+ /**
+ * Executes the read operation on the given tile.
+ * The default implementation does nothing.
+ *
+ * @param source the tile to read.
+ */
+ protected void readFrom(Raster source) {
+ }
+
+ /**
+ * Executes the write operation on the given tile.
+ * The default implementation does nothing.
+ *
+ * @param target the tile where to write.
+ * @throws Exception if an error occurred while computing the values to
write.
+ */
+ protected void writeTo(WritableRaster target) throws Exception {
+ }
+
+ /**
+ * Executes the read action on tiles of the specified source image.
+ * The given source should be the same than the image specified at
construction time.
+ * Only tiles intersecting the area of interest will be processed.
+ *
+ * <p>If a tile processing throws an exception, then this method stops
immediately;
+ * remaining tiles are not processed. This policy is suited to the cases
where the
+ * caller will not return any result in case of error.</p>
+ *
+ * <p>Current implementation does not parallelize tile operations, because
this method is
+ * invoked in contexts where it should apply on exactly one tile most of
the times.</p>
+ *
+ * @param source the image to read. Should be the image specified at
construction time.
+ */
+ public final void readFrom(final RenderedImage source) {
+ for (int ty = minTileY; ty <= maxTileY; ty++) {
+ for (int tx = minTileX; tx <= maxTileX; tx++) {
+ readFrom(source.getTile(tx, ty));
+ }
+ }
+ }
+
+ /**
+ * Executes the write action on tiles of the specified target image.
+ * The given target should be the same than the image specified at
construction time.
+ * Only tiles intersecting the area of interest will be processed.
+ *
+ * <p>If a tile processing throws an exception, then this method continues
processing other tiles
+ * and will rethrow the exception only after all tiles have been
processed. This policy is suited
+ * to the cases where the target image will continue to exist after this
method call and we want
+ * to have a relatively consistent state.</p>
+ *
+ * <p>Current implementation does not parallelize tile operations, because
this method is
+ * invoked in contexts where it should apply on exactly one tile most of
the times.</p>
+ *
+ * @param target the image where to write. Should be the image specified
at construction time.
+ * @throws ImagingOpException if a {@link #writeTo(WritableRaster)} call
threw an exception.
+ */
+ public final void writeTo(final WritableRenderedImage target) {
+ ImagingOpException error = null;
+ for (int ty = minTileY; ty <= maxTileY; ty++) {
+ for (int tx = minTileX; tx <= maxTileX; tx++) {
+ final WritableRaster tile = target.getWritableTile(tx, ty);
+ try {
+ writeTo(tile);
+ } catch (Exception e) {
+ if (error == null) {
+ error = new
ImagingOpException(Resources.format(Resources.Keys.CanNotUpdateTile_2, tx, ty));
+ error.initCause(e);
+ } else {
+ error.addSuppressed(e);
+ }
+ } finally {
+ target.releaseWritableTile(tx, ty);
+ }
+ }
+ }
+ if (error != null) {
+ throw error;
+ }
+ }
+}
diff --git
a/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/WriteSupport.java
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/WriteSupport.java
new file mode 100644
index 0000000..694c68a
--- /dev/null
+++
b/core/sis-feature/src/main/java/org/apache/sis/internal/coverage/j2d/WriteSupport.java
@@ -0,0 +1,101 @@
+/*
+ * 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.Arrays;
+import java.awt.image.TileObserver;
+import java.awt.image.WritableRenderedImage;
+import org.apache.sis.util.ArraysExt;
+
+
+/**
+ * Helper methods for {@link WritableRenderedImage} implementations.
+ *
+ * <p>A future version of this class may extends {@code PlanarImage} or {@code
ComputedImage}.
+ * We have not yet decided which case would be useful.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+final class WriteSupport {
+ /**
+ * Do not allow (for now) instantiation of this class.
+ */
+ private WriteSupport() {
+ }
+
+ /**
+ * Returns a new array with the specified observer added to the array of
observers.
+ * If the observer is already present, it will receive multiple
notifications.
+ *
+ * @param observers the array where to add the observer, or {@code null}.
+ * @param observer the observer to add. Null values are ignored.
+ * @return the updated array of observers.
+ */
+ static TileObserver[] addTileObserver(TileObserver[] observers, final
TileObserver observer) {
+ if (observer != null) {
+ if (observers == null) {
+ return new TileObserver[] {observer};
+ }
+ final int n = observers.length;
+ observers = Arrays.copyOf(observers, n+1);
+ observers[n] = observer;
+ }
+ return observers;
+ }
+
+ /**
+ * Returns a new array with the specified observer removed from the
specified array of observers.
+ * If the observer was not registered, nothing happens and the given array
is returned as-is.
+ * If the observer was registered for multiple notifications, it will now
be registered for one fewer.
+ *
+ * @param observers the array where to remove the observer, or {@code
null}.
+ * @param observer the observer to remove.
+ * @return the updated array of observers.
+ */
+ static TileObserver[] removeTileObserver(final TileObserver[] observers,
final TileObserver observer) {
+ if (observers != null) {
+ for (int i=observers.length; --i >= 0;) {
+ if (observers[i] == observer) {
+ return ArraysExt.remove(observers, i, 1);
+ }
+ }
+ }
+ return observers;
+ }
+
+ /**
+ * Notifies all listeners that the specified tile has been checked out for
writing.
+ *
+ * @param observers the observers to notify, or {@code null} if none.
+ * @param image the image that owns the tile.
+ * @param tileX the <var>x</var> index of the tile that is being
updated.
+ * @param tileY the <var>y</var> index of the tile that is being
updated.
+ * @param willBeWritable if {@code true}, the tile will be grabbed for
writing; otherwise it is being released.
+ */
+ static void fireTileUpdate(final TileObserver[] observers, final
WritableRenderedImage image,
+ final int tileX, final int tileY, final boolean
willBeWritable)
+ {
+ if (observers != null) {
+ for (final TileObserver observer : observers) {
+ observer.tileUpdate(image, tileX, tileY, willBeWritable);
+ }
+ }
+ }
+}
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 a221314..4d3ed57 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
@@ -115,6 +115,11 @@ public final class Resources extends IndexedResourceBundle
{
public static final short CanNotSimplifyTransferFunction_1 = 26;
/**
+ * Can not update tile ({0}, {1}).
+ */
+ public static final short CanNotUpdateTile_2 = 69;
+
+ /**
* The two categories “{0}” and “{2}” have overlapping ranges: {1} and
{3} respectively.
*/
public static final short CategoryRangeOverlap_4 = 27;
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 e611708..3a66b14 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
@@ -22,6 +22,7 @@
AbstractFeatureType_1 = Feature type \u2018{0}\u2019 is abstract.
CanNotAssignCharacteristics_1 = Can not assign characteristics to the
\u201c{0}\u201d property.
CanNotComputeTile_2 = Can not compute tile ({0}, {1}).
+CanNotUpdateTile_2 = Can not update tile ({0}, {1}).
CanNotCreateTwoDimensionalCRS_1 = Can not create a two-dimensional reference
system from the \u201c{0}\u201d system.
CanNotEnumerateValuesInRange_1 = Can not enumerate values in the {0} range.
CanNotInstantiateProperty_1 = Property \u201c{0}\u201d is not a type
that can be instantiated.
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 972d7c8..5a6009b 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
@@ -27,6 +27,7 @@
AbstractFeatureType_1 = Le type d\u2019entit\u00e9 \u2018{0}\u2019
est abstrait.
CanNotAssignCharacteristics_1 = Ne peut pas assigner des
caract\u00e9ristiques \u00e0 la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb.
CanNotComputeTile_2 = Ne peut pas calculer la tuile ({0}, {1}).
+CanNotUpdateTile_2 = Ne peut pas mettre \u00e0 jour la tuile
({0}, {1}).
CanNotCreateTwoDimensionalCRS_1 = Ne peut pas cr\u00e9er un syst\u00e8me de
r\u00e9f\u00e9rence bidimensionnel \u00e0 partir du syst\u00e8me
\u00ab\u202f{0}\u202f\u00bb.
CanNotEnumerateValuesInRange_1 = Ne peut pas \u00e9num\u00e9rer les valeurs
dans la plage {0}.
CanNotInstantiateProperty_1 = La propri\u00e9t\u00e9
\u00ab\u202f{0}\u202f\u00bb n\u2019est pas d\u2019un type qui peut \u00eatre
instanci\u00e9.
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
index 83ccc1d..9d70119 100644
---
a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
+++
b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridCoverage2DTest.java
@@ -21,6 +21,7 @@ import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
import org.opengis.geometry.DirectPosition;
import org.opengis.coverage.PointOutsideCoverageException;
import org.opengis.referencing.operation.MathTransform1D;
@@ -106,7 +107,6 @@ public final strictfp class GridCoverage2DTest extends
TestCase {
* Tests writing values in {@link
GridCoverage2D#forConvertedValues(boolean)}.
*/
@Test
- @org.junit.Ignore("BandedSampleConverter is not yet writable")
public void testWriteConvertedValues() {
GridCoverage coverage = createTestCoverage();
coverage = coverage.forConvertedValues(true);
@@ -120,11 +120,13 @@ public final strictfp class GridCoverage2DTest extends
TestCase {
*
* 70 = p * 0.5 + 100 → (70-100)/0.5 = p → p = -60
*/
- final WritableRaster raster = ((BufferedImage)
coverage.render(null)).getRaster();
+ final WritableRenderedImage image = (WritableRenderedImage)
coverage.render(null);
+ final WritableRaster raster = image.getWritableTile(0, 0);
raster.setSample(0, 0, 0, 70);
raster.setSample(1, 0, 0, 2.5);
raster.setSample(0, 1, 0, -8);
raster.setSample(1, 1, 0, -90);
+ image.releaseWritableTile(0, 0);
assertSamplesEqual(coverage.forConvertedValues(false), new double[][] {
{ -60, -195},
{-216, -380}
diff --git
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java
index 8d5f3e1..456d57f 100644
---
a/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java
+++
b/core/sis-feature/src/test/java/org/apache/sis/internal/coverage/j2d/ImageUtilitiesTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.internal.coverage.j2d;
+import java.awt.Rectangle;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
@@ -39,6 +40,29 @@ import static org.junit.Assert.*;
*/
public final strictfp class ImageUtilitiesTest extends TestCase {
/**
+ * Tests {@link ImageUtilities#getBounds(RenderedImage)} and {@link
ImageUtilities#clipBounds(RenderedImage, Rectangle)}.
+ */
+ @Test
+ public void testClipBounds() {
+ final BufferedImage image = new BufferedImage(5, 4,
BufferedImage.TYPE_BYTE_GRAY);
+ final Rectangle bounds = ImageUtilities.getBounds(image);
+ assertEquals(0, bounds.x);
+ assertEquals(0, bounds.y);
+ assertEquals(5, bounds.width);
+ assertEquals(4, bounds.height);
+
+ bounds.x = -4;
+ bounds.y = 1;
+ bounds.width = 8;
+ bounds.height = 7;
+ ImageUtilities.clipBounds(image, bounds);
+ assertEquals(0, bounds.x);
+ assertEquals(1, bounds.y);
+ assertEquals(4, bounds.width);
+ assertEquals(3, bounds.height);
+ }
+
+ /**
* Tests {@link ImageUtilities#getDataTypeName(SampleModel)}.
*/
@Test