You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/04/08 16:38:09 UTC
[sis] 03/03: Result of "band select" operation should be writable if the image is writable.
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 6c85b283c4009fcd0b658dd44de43984e3125e8a
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Apr 8 16:53:23 2023 +0200
Result of "band select" operation should be writable if the image is writable.
---
.../java/org/apache/sis/image/BandSelectImage.java | 76 +++++++++++++++++++++-
.../java/org/apache/sis/image/ImageProcessor.java | 9 +++
.../org/apache/sis/image/BandSelectImageTest.java | 65 +++++++++++++++---
3 files changed, 141 insertions(+), 9 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
index 07b4f7cd12..60fbb426ec 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/BandSelectImage.java
@@ -25,10 +25,13 @@ import java.awt.image.Raster;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
import java.awt.image.ColorModel;
+import java.awt.image.TileObserver;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.internal.coverage.j2d.ImageUtilities;
+import org.apache.sis.internal.coverage.j2d.TileOpExecutor;
import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
@@ -43,7 +46,7 @@ import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
* @version 1.4
* @since 1.1
*/
-final class BandSelectImage extends SourceAlignedImage {
+class BandSelectImage extends SourceAlignedImage {
/**
* Properties to inherit from the source image, after bands reduction if applicable.
*
@@ -119,6 +122,8 @@ final class BandSelectImage extends SourceAlignedImage {
image = new BufferedImage(cm,
bi.getRaster().createWritableChild(0, 0, bi.getWidth(), bi.getHeight(), 0, 0, bands),
bi.isAlphaPremultiplied(), properties);
+ } else if (source instanceof WritableRenderedImage) {
+ image = new Writable(source, cm, bands);
} else {
image = new BandSelectImage(source, cm, bands);
}
@@ -189,6 +194,75 @@ final class BandSelectImage extends SourceAlignedImage {
return parent.createChild(x, y, parent.getWidth(), parent.getHeight(), x, y, bands);
}
+ /**
+ * Applies the band selection on the given writable raster.
+ * The child is created in the same way than {@code computeTile(…)}.
+ */
+ final WritableRaster apply(final WritableRaster parent) {
+ final int x = parent.getMinX();
+ final int y = parent.getMinY();
+ return parent.createWritableChild(x, y, parent.getWidth(), parent.getHeight(), x, y, bands);
+ }
+
+ /**
+ * A {@code BandSelectImage} where the source is a writable rendered image.
+ */
+ private static final class Writable extends BandSelectImage implements WritableRenderedImage {
+ /** Creates a new "band select" operation for the given source. */
+ Writable(final RenderedImage source, final ColorModel cm, final int[] bands) {
+ super(source, cm, bands);
+ }
+
+ /** Returns the source as a writable image. */
+ private WritableRenderedImage target() {
+ return (WritableRenderedImage) getSource();
+ }
+
+ /** Checks out a tile for writing. */
+ @Override public WritableRaster getWritableTile(final int tileX, final int tileY) {
+ markTileWritable(tileX, tileY, true);
+ final WritableRaster parent = target().getWritableTile(tileX, tileY);
+ return apply(parent);
+ }
+
+ /** Relinquishes the right to write to a tile. */
+ @Override public void releaseWritableTile(final int tileX, final int tileY) {
+ target().releaseWritableTile(tileX, tileY);
+ markTileWritable(tileX, tileY, false);
+ }
+
+ /** Adds an observer to be notified when a tile is checked out for writing. */
+ @Override public void addTileObserver(final TileObserver observer) {
+ target().addTileObserver(observer);
+ }
+
+ /** Removes an observer from the list of observers notified when a tile is checked out for writing. */
+ @Override public void removeTileObserver(final TileObserver observer) {
+ target().removeTileObserver(observer);
+ }
+
+ /** Sets a region of the image to the contents of the given raster. */
+ @Override public void setData(final Raster data) {
+ final WritableRenderedImage target = target();
+ final var executor = new TileOpExecutor(target, data.getBounds()) {
+ @Override protected void writeTo(final WritableRaster tile) {
+ apply(tile).setRect(data);
+ }
+ };
+ executor.writeTo(target);
+ }
+
+ /** Restores the identity behavior for writable image. */
+ @Override public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ /** Restores the identity behavior for writable image. */
+ @Override public boolean equals(final Object object) {
+ return object == this;
+ }
+ }
+
/**
* Returns a hash code value for this image.
*/
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 56238cb8a2..34d0e60ca3 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
@@ -899,6 +899,10 @@ public class ImageProcessor implements Cloneable {
* pixel values are not copied. Consequently, changes in the source image are reflected
* immediately in the returned image.</p>
*
+ * <p>If the given image is an instance of {@link WritableRenderedImage},
+ * then the returned image will also be a {@link WritableRenderedImage}.
+ * In such case values written in the returned image will be written directly in the source image.</p>
+ *
* <h4>Properties used</h4>
* This operation uses the following properties in addition to method parameters:
* <ul>
@@ -965,6 +969,11 @@ public class ImageProcessor implements Cloneable {
* An empty array element (i.e. zero band to select) discards the corresponding source image.
* In the latter case, the discarded element in the {@code sources} array may be {@code null}.
*
+ * <p>If all source images are {@link WritableRenderedImage} instances,
+ * then the returned image will also be a {@link WritableRenderedImage}.
+ * In such case values written in the returned image will be copied back
+ * to the source images.</p>
+ *
* <h4>Restrictions</h4>
* <ul>
* <li>All images shall use the same {@linkplain SampleModel#getDataType() data type}.</li>
diff --git a/core/sis-feature/src/test/java/org/apache/sis/image/BandSelectImageTest.java b/core/sis-feature/src/test/java/org/apache/sis/image/BandSelectImageTest.java
index df68cc3d1d..2a1d0be7ae 100644
--- a/core/sis-feature/src/test/java/org/apache/sis/image/BandSelectImageTest.java
+++ b/core/sis-feature/src/test/java/org/apache/sis/image/BandSelectImageTest.java
@@ -21,12 +21,14 @@ import java.util.Hashtable;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
import java.awt.image.RenderedImage;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
import org.apache.sis.internal.coverage.j2d.ImageUtilities;
+import org.apache.sis.test.TestUtilities;
import org.apache.sis.test.TestCase;
import org.junit.Test;
@@ -37,7 +39,7 @@ import static org.apache.sis.test.FeatureAssert.*;
* Tests {@link BandSelectImage}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
* @since 1.1
*/
public final class BandSelectImageTest extends TestCase {
@@ -46,6 +48,11 @@ public final class BandSelectImageTest extends TestCase {
*/
private static final int WIDTH = 3, HEIGHT = 4;
+ /**
+ * Random number generator used for the test.
+ */
+ private Random random;
+
/**
* The source image as an instance of custom implementation.
*/
@@ -68,7 +75,7 @@ public final class BandSelectImageTest extends TestCase {
private void createImage(final int numBands, final int checkedBand, final boolean icm) {
image = new TiledImageMock(DataBuffer.TYPE_BYTE, numBands, 0, 0, WIDTH, HEIGHT, WIDTH, HEIGHT, 0, 0, false);
image.initializeAllTiles(checkedBand);
- final Random random = new Random();
+ random = TestUtilities.createRandomNumberGenerator();
for (int i=0; i<numBands; i++) {
if (i != checkedBand) {
image.setRandomValues(i, random, 100);
@@ -91,6 +98,18 @@ public final class BandSelectImageTest extends TestCase {
bufferedImage = new BufferedImage(cm, (WritableRaster) image.getTile(0, 0), false, properties);
}
+ /**
+ * The expected sample values in the determinist band initialized by {@link #createImage(int, int, boolean)}.
+ */
+ private static int[][] expectedSampleValues() {
+ return new int[][] {
+ {100, 101, 102},
+ {110, 111, 112},
+ {120, 121, 122},
+ {130, 131, 132}
+ };
+ }
+
/**
* Computes a dummy resolution for the given band.
*/
@@ -110,12 +129,7 @@ public final class BandSelectImageTest extends TestCase {
assertEquals("numBands", numBands, tile.getNumBands());
assertEquals("numBands", numBands, ImageUtilities.getNumBands(image));
assertEquals("sampleModel", image.getSampleModel(), tile.getSampleModel());
- assertValuesEqual(tile, checkedBand, new int[][] {
- {100, 101, 102},
- {110, 111, 112},
- {120, 121, 122},
- {130, 131, 132}
- });
+ assertValuesEqual(tile, checkedBand, expectedSampleValues());
}
/**
@@ -182,4 +196,39 @@ public final class BandSelectImageTest extends TestCase {
verifySamples(test, 3, 2);
verifyProperties(test, 3, 0, 2);
}
+
+ /**
+ * Tests write operation.
+ */
+ @Test
+ public void testWritable() {
+ createImage(2, 1, true);
+ final ImageProcessor processor = new ImageProcessor();
+ RenderedImage test = processor.selectBands(image, 1);
+ final int[][] expected = expectedSampleValues();
+ final Raster data = test.getData();
+ assertValuesEqual(data, 0, expected);
+ /*
+ * Above code where read operations for making sure that we initialized the test correctly.
+ * Code below is the actual test for write operations.
+ */
+ final WritableRenderedImage writable = (WritableRenderedImage) test;
+ final int tileX = writable.getMinTileX();
+ final int tileY = writable.getMinTileY();
+ final WritableRaster tile = writable.getWritableTile(tileX, tileY);
+ for (int i=0; i<3; i++) {
+ final int x = random.nextInt(tile.getWidth());
+ final int y = random.nextInt(tile.getHeight());
+ final int s = random.nextInt(10);
+ tile.setSample(x, y, 0, s);
+ expected[y][x] = s;
+ }
+ writable.releaseWritableTile(tileX, tileY);
+ assertValuesEqual(writable.getData(), 0, expected);
+ /*
+ * Try to restore orginal values.
+ */
+ writable.setData(data);
+ assertValuesEqual(writable.getData(), 0, expectedSampleValues());
+ }
}