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 2021/11/28 13:08:05 UTC
[sis] branch geoapi-4.0 updated (2034647 -> 9edee11)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git.
from 2034647 Fix an `IndexOutOfBoundsException` when stretching the colors of an `IndexColorModel`.
new 739b246 Remove the 100 pixels margin which was added to the image rendered by `CoverageCanvas`. The margin intend was to have some content ready in advance when user translates the view. But it also consumes more memory, slows down the rendering (because of larger surface) and makes the `ImagePropertyExplorer` content a little bit more confusing.
new 8ceffa0 Better rendering of `ImagePropertyExplorer` content when the property value is another image.
new 9edee11 In `PlanarImage.verify()`, an unexpected value for "width" and "height" properties may be normal. So those "anomalies" should be reported only if everything else is okay.
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../apache/sis/gui/coverage/CoverageCanvas.java | 1 -
.../sis/gui/coverage/ImagePropertyExplorer.java | 10 +-
.../apache/sis/internal/gui/ImageConverter.java | 137 +++++++++++++++------
.../org/apache/sis/internal/gui/PropertyView.java | 62 +++++++---
.../org/apache/sis/internal/gui/Resources.java | 2 +-
.../apache/sis/internal/gui/Resources.properties | 2 +-
.../sis/internal/gui/Resources_fr.properties | 2 +-
.../apache/sis/coverage/grid/ReshapedImage.java | 16 +--
.../java/org/apache/sis/image/PlanarImage.java | 39 +++---
.../java/org/apache/sis/image/ResampledImage.java | 10 +-
.../sis/internal/coverage/j2d/TiledImage.java | 7 +-
.../sis/internal/map/coverage/RenderingData.java | 2 +
12 files changed, 198 insertions(+), 92 deletions(-)
[sis] 02/03: Better rendering of `ImagePropertyExplorer` content when the property value is another image.
Posted by de...@apache.org.
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 8ceffa01786c878f81c5420428990d58012ac5fe
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Nov 28 12:14:46 2021 +0100
Better rendering of `ImagePropertyExplorer` content when the property value is another image.
---
.../sis/gui/coverage/ImagePropertyExplorer.java | 2 +-
.../apache/sis/internal/gui/ImageConverter.java | 137 +++++++++++++++------
.../org/apache/sis/internal/gui/PropertyView.java | 62 +++++++---
.../sis/internal/map/coverage/RenderingData.java | 2 +
4 files changed, 147 insertions(+), 56 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
index 39e72dc..eb1208d 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
@@ -530,7 +530,7 @@ public class ImagePropertyExplorer extends Widget {
/**
* Invoked when an image is selected in the tree of image sources. The selected image
- * is not necessarily {@link #image} property value; it may be one of its sources.
+ * is not necessarily the {@link #image} property value; it may be one of its sources.
* If no image is explicitly selected, defaults to the root image.
*/
private void imageSelected(final RenderedImage selected) {
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ImageConverter.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ImageConverter.java
index a407d7f..5c96296 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ImageConverter.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ImageConverter.java
@@ -26,6 +26,8 @@ import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.RenderedImage;
import javafx.concurrent.Task;
+import javafx.scene.layout.Region;
+import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelWriter;
@@ -35,6 +37,7 @@ import org.apache.sis.image.PlanarImage;
import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
import org.apache.sis.internal.coverage.j2d.ImageUtilities;
import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.internal.util.Numerics;
import org.apache.sis.internal.jdk9.JDK9;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.measure.NumberRange;
@@ -46,26 +49,23 @@ import org.apache.sis.math.Statistics;
* This task should be used only for small images (thumbnail) because some potentially costly resources
* are created each time.
*
+ * <p>The {@link ImageView} should not be modified by caller. Instead, the {@link #clear(ImageView)}
+ * method should be invoked for setting the image to null, because this class also manages properties
+ * associated to the {@link ImageView}.</p>
+ *
* <p>Current implementation returns statistics on sample values as a side-product.
- * It may change in any future version.</p>
+ * The statistics may be null if they were not computed.
+ * This policy may change in any future version.</p>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
* @since 1.1
* @module
*/
final class ImageConverter extends Task<Statistics[]> {
/**
- * The maximal image width and height, as an arbitrary value that may change in any future version.
- * This size should be a typical size expected for the JavaFX control showing the image.
- * If the {@link ImageView} is larger than this size, the image will be rescaled by JavaFX.
- * The quality is lower than if we had let the "resample" operation do its work at full resolution,
- * but {@link PropertyView} should be used only of overview anyway.
- */
- private static final int MAX_SIZE = 600;
-
- /**
* Colors to apply on the mask image when that image is overlay on top of another image.
+ * Current value is a transparent yellow color.
*/
private static final Map<NumberRange<?>,Color[]> MASK_TRANSPARENCY = JDK9.mapOf(
NumberRange.create(0, true, 0, true), new Color[] {ColorModelFactory.TRANSPARENT},
@@ -79,18 +79,23 @@ final class ImageConverter extends Task<Statistics[]> {
/**
* Pixel coordinates of the region to render, or {@code null} for the whole image.
*/
- private Rectangle bounds;
+ private Rectangle sourceAOI;
/**
- * Size of the image actually rendered. This is the {@link #bounds} size,
- * unless that size is greater than {@link #MAX_SIZE} in which case it is scaled down.
+ * On input, the canvas size. On output, the actual image size rendered in the canvas.
+ * One of the width or height may be modified on output for preserving image proportions.
*/
private int width, height;
/**
+ * Value to give to {@link ImageView} x/y properties after the image has been created.
+ */
+ private int xpos, ypos;
+
+ /**
* Where to write the image. This will be updated in JavaFX thread.
*/
- private final ImageView canvas;
+ private final ImageView view;
/**
* The ARGB values to be copied in the JavaFX image.
@@ -100,38 +105,85 @@ final class ImageConverter extends Task<Statistics[]> {
/**
* Creates a new task for converting the given image.
*/
- ImageConverter(final RenderedImage source, final Rectangle bounds, final ImageView canvas) {
- this.source = source;
- this.bounds = bounds;
- this.canvas = canvas;
+ ImageConverter(final RenderedImage source, final Rectangle sourceAOI, final ImageView view, final Region canvas) {
+ this.source = source;
+ this.sourceAOI = sourceAOI;
+ this.view = view;
+ this.width = Numerics.clamp(Math.round(canvas.getWidth()));
+ this.height = Numerics.clamp(Math.round(canvas.getHeight()));
+ }
+
+ /**
+ * Sets the image property to {@code null} on the given view.
+ * This method also clears other properties managed by {@link ImageConverter}.
+ *
+ * @param view the view on which to set the image property to {@code null}.
+ */
+ public static void clear(final ImageView view) {
+ view.setImage(null);
+ view.setUserData(null);
+ }
+
+ /**
+ * Returns {@code true} if the {@link #call()} method needs to be invoked in a background thread.
+ * This method may return {@code false} because the canvas size is zero, or because current content can be reused.
+ * The content can be reused if the canvas size did not changed, or changed in a way that does not require repaint
+ * (i.e. if the size that changed is the size that was already too wide for fitting the image inside the canvas).
+ *
+ * @param newAOI whether {@link #sourceAOI} had a different value during previous rendering.
+ * @return whether this task needs to be run.
+ */
+ public boolean needsRun(final boolean newAOI) {
+ if (width <= 0 || height <= 0) {
+ return false;
+ }
+ if (!newAOI && view.getUserData() == source) {
+ final Image image = view.getImage();
+ final double dx = width - image.getWidth();
+ final double dy = height - image.getHeight();
+ /*
+ * Conditions: the image is not larger than the canvas (dx and dy >= 0)
+ * and at least one image side has the maximal size (dx or dy == 0).
+ */
+ if (dx >= 0 && dy >= 0 && (dx == 0 || dy == 0)) {
+ view.setX(dx / 2);
+ view.setY(dy / 2);
+ return false;
+ }
+ }
+ return true;
}
/**
* Prepares the ARGB values to be written in the JavaFX image.
* The task opportunistically returns statistics on all bands of the source image.
- * Those statistics were used for stretching the color ramp before to paint the JavaFX image.
+ * They are the statistics used for stretching the color ramp before to paint the JavaFX image.
+ *
+ * @return statistics on sample value, or {@code null} if not computed.
*/
@Override
protected Statistics[] call() {
- if (bounds == null) {
- bounds = ImageUtilities.getBounds(source);
+ if (sourceAOI == null) {
+ sourceAOI = ImageUtilities.getBounds(source);
}
- width = Math.min(bounds.width, MAX_SIZE);
- height = Math.min(bounds.height, MAX_SIZE);
- final double scale = Math.max(width / (double) bounds.width,
- height / (double) bounds.height);
/*
* Use a uniform scale. At least one of `width` or `height` will be unchanged.
+ * We favor showing fully the image instead of filling all the canvas space.
*/
- width = (int) Math.round(scale * bounds.width);
- height = (int) Math.round(scale * bounds.height);
+ final double scale = Math.min(width / (double) sourceAOI.width,
+ height / (double) sourceAOI.height);
+ xpos = (width - (width = Numerics.clamp(Math.round(scale * sourceAOI.width )))) / 2;
+ ypos = (height - (height = Numerics.clamp(Math.round(scale * sourceAOI.height)))) / 2;
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
final AffineTransform toCanvas = AffineTransform.getScaleInstance(scale, scale);
- toCanvas.translate(-bounds.x, -bounds.y);
+ toCanvas.translate(-sourceAOI.x, -sourceAOI.y);
/*
- * Stretch color ramp using statistics on the source image before to pain on JavaFX image.
+ * Stretch color ramp using statistics about source image before to paint on JavaFX image.
*/
final ImageProcessor processor = new ImageProcessor();
- final Statistics[] statistics = processor.valueOfStatistics(source, bounds, (DoubleUnaryOperator[]) null);
+ final Statistics[] statistics = processor.valueOfStatistics(source, sourceAOI, (DoubleUnaryOperator[]) null);
final RenderedImage image = processor.stretchColorRamp(source, JDK9.mapOf("multStdDev", 3, "statistics", statistics));
final RenderedImage mask = getMask(processor);
final BufferedImage buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
@@ -168,16 +220,20 @@ final class ImageConverter extends Task<Statistics[]> {
*/
@Override
protected void succeeded() {
- WritableImage destination = (WritableImage) canvas.getImage();
- if (destination == null || destination.getWidth() != width || destination.getHeight() != height) {
- destination = new WritableImage(width, height);
+ WritableImage destination = null;
+ if (data != null) {
+ destination = (WritableImage) view.getImage();
+ if (destination == null || destination.getWidth() != width || destination.getHeight() != height) {
+ destination = new WritableImage(width, height);
+ }
+ final PixelWriter writer = destination.getPixelWriter();
+ writer.setPixels(0, 0, width, height, PixelFormat.getIntArgbPreInstance(), data, 0, width);
+ data = null;
}
- final PixelWriter writer = destination.getPixelWriter();
- writer.setPixels(0, 0, width, height, PixelFormat.getIntArgbPreInstance(), data, 0, width);
- data = null;
- canvas.setImage(destination);
- canvas.setFitWidth (bounds.width);
- canvas.setFitHeight(bounds.height);
+ view.setImage(destination);
+ view.setUserData(source);
+ view.setX(xpos);
+ view.setY(ypos);
}
/**
@@ -186,6 +242,7 @@ final class ImageConverter extends Task<Statistics[]> {
@Override
protected void failed() {
data = null;
+ clear(view);
}
/**
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java
index 50879d1..a57ff74 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java
@@ -29,6 +29,8 @@ import java.text.ParsePosition;
import java.text.ParseException;
import java.awt.Rectangle;
import java.awt.image.RenderedImage;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
import javafx.beans.property.ObjectProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
@@ -52,7 +54,8 @@ import org.apache.sis.util.resources.Vocabulary;
* A viewer for property value. The property may be of various class (array, image, <i>etc</i>).
* If the type is unrecognized, the property is shown as text.
*
- * <p>This class extends {@link CompoundFormat} for implementation convenience only.</p>
+ * <p>This class extends {@link CompoundFormat} and implements {@code ChangeListener} for
+ * implementation convenience only. Users should not rely on this implementation details.</p>
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.2
@@ -60,7 +63,7 @@ import org.apache.sis.util.resources.Vocabulary;
* @module
*/
@SuppressWarnings({"serial","CloneableImplementsClone"}) // Not intended to be serialized.
-public final class PropertyView extends CompoundFormat<Object> {
+public final class PropertyView extends CompoundFormat<Object> implements ChangeListener<Number> {
/**
* The current property value. This is used for detecting changes.
*/
@@ -138,6 +141,8 @@ public final class PropertyView extends CompoundFormat<Object> {
if (background != null) {
imageCanvas.backgroundProperty().bind(background);
}
+ imageCanvas.widthProperty() .addListener(this);
+ imageCanvas.heightProperty().addListener(this);
}
/**
@@ -210,7 +215,8 @@ public final class PropertyView extends CompoundFormat<Object> {
* @param visibleBounds if the property is an image, currently visible region. Can be {@code null}.
*/
public void set(final Object newValue, final Rectangle visibleBounds) {
- if (newValue != value || !Objects.equals(visibleBounds, visibleImageBounds)) {
+ final boolean boundsChanged = !Objects.equals(visibleBounds, visibleImageBounds);
+ if (newValue != value || boundsChanged) {
if (runningTask != null) {
runningTask.cancel(BackgroundThreads.NO_INTERRUPT_DURING_IO);
runningTask = null;
@@ -220,7 +226,7 @@ public final class PropertyView extends CompoundFormat<Object> {
if (newValue == null) {
content = null;
} else if (newValue instanceof RenderedImage) {
- content = setImage((RenderedImage) newValue);
+ content = setImage((RenderedImage) newValue, boundsChanged);
} else if (newValue instanceof Throwable) {
content = setText((Throwable) newValue);
} else if (newValue instanceof Collection<?>) {
@@ -278,8 +284,11 @@ public final class PropertyView extends CompoundFormat<Object> {
/**
* Sets the property value to the given image.
+ *
+ * @param image the property value to set, or {@code null}.
+ * @param boundsChanged whether {@link #visibleImageBounds} changed since last call.
*/
- private Node setImage(final RenderedImage image) {
+ private Node setImage(final RenderedImage image, final boolean boundsChanged) {
ImageView node = imageView;
if (node == null) {
node = new ImageView();
@@ -310,14 +319,16 @@ public final class PropertyView extends CompoundFormat<Object> {
imagePane.setHgap(0);
imageView = node;
}
- final ImageConverter converter = new ImageConverter(image, visibleImageBounds, node);
- converter.setOnSucceeded((e) -> taskCompleted(converter.getValue()));
- converter.setOnFailed((e) -> {
- taskCompleted(null);
- view.set(setText(e.getSource().getException()));
- });
- runningTask = converter;
- BackgroundThreads.execute(converter);
+ final ImageConverter converter = new ImageConverter(image, visibleImageBounds, node, imageCanvas);
+ if (converter.needsRun(boundsChanged)) {
+ converter.setOnSucceeded((e) -> taskCompleted(converter.getValue()));
+ converter.setOnFailed((e) -> {
+ taskCompleted(null);
+ view.set(setText(e.getSource().getException()));
+ });
+ runningTask = converter;
+ BackgroundThreads.execute(converter);
+ }
return imagePane;
}
@@ -349,6 +360,23 @@ public final class PropertyView extends CompoundFormat<Object> {
}
sampleValueRange.setText(range);
meanValue.setText(mean);
+ changed(null, null, null); // For centering the image.
+ }
+
+ /**
+ * Invoked when the image canvas size changed. If the previous canvas size was 0, the image could not be rendered
+ * during the previous call to {@link #setImage(RenderedImage, boolean)}, so we need to call that method again now
+ * that the image size is known. In addition we also need to move the image to canvas center.
+ *
+ * @param property the image canvas property that changed (width or height).
+ * @param oldValue the old width or height value.
+ * @param newValue the new width or height value.
+ */
+ @Override
+ public void changed(final ObservableValue<? extends Number> property, final Number oldValue, final Number newValue) {
+ if (value instanceof RenderedImage) {
+ setImage((RenderedImage) value, false);
+ }
}
/**
@@ -357,7 +385,11 @@ public final class PropertyView extends CompoundFormat<Object> {
public void clear() {
value = null;
view.set(null);
- if (textView != null) textView .setText (null);
- if (imageView != null) imageView.setImage(null);
+ if (textView != null) {
+ textView .setText (null);
+ }
+ if (imageView != null) {
+ ImageConverter.clear(imageView);
+ }
}
}
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java
index a4bc23a..6cba9de 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java
@@ -93,6 +93,8 @@ import org.apache.sis.portrayal.PlanarCanvas; // For javadoc.
* in pixel coordinates, the translation terms in {@code resampledToDisplay} transform should stay integers.
*
* <p>Current version of this class does not perform a special case for {@link BufferedImage}.
+ * It may not be desirable because interpolations would not be applied in the same way, except
+ * when SIS {@link ImageProcessor} would have interpolated RGB color values anyway like Java2D.
* We wait to see if this class works well in the general case before doing special cases.</p>
*
* @author Martin Desruisseaux (Geomatys)
[sis] 01/03: Remove the 100 pixels margin which was added to the image rendered by `CoverageCanvas`. The margin intend was to have some content ready in advance when user translates the view. But it also consumes more memory, slows down the rendering (because of larger surface) and makes the `ImagePropertyExplorer` content a little bit more confusing.
Posted by de...@apache.org.
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 739b246cbf2c4372f4a31f7e4fec3fbf7ad71534
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Nov 28 12:02:56 2021 +0100
Remove the 100 pixels margin which was added to the image rendered by `CoverageCanvas`.
The margin intend was to have some content ready in advance when user translates the view.
But it also consumes more memory, slows down the rendering (because of larger surface)
and makes the `ImagePropertyExplorer` content a little bit more confusing.
The margin is still configurable programmatically by `CoverageCanvas` users.
A future version may add a widget in an option pane for letting user set this value,
or we could try to infer a value automatically based on direction and speed of pan.
---
.../src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java | 1 -
1 file changed, 1 deletion(-)
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 c6887ec..6e3da66 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
@@ -259,7 +259,6 @@ public class CoverageCanvas extends MapCanvasAWT {
coverageProperty .addListener((p,o,n) -> onPropertySpecified(null, n, resourceProperty));
sliceExtentProperty .addListener((p,o,n) -> onPropertySpecified(getResource(), getCoverage(), null));
interpolationProperty.addListener((p,o,n) -> onInterpolationSpecified(n));
- imageMargin.set(new Insets(100));
}
/**
[sis] 03/03: In `PlanarImage.verify()`, an unexpected value for "width" and "height" properties may be normal. So those "anomalies" should be reported only if everything else is okay.
Posted by de...@apache.org.
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 9edee116e148b738097cfeecc40cff1f75982300
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sun Nov 28 13:37:53 2021 +0100
In `PlanarImage.verify()`, an unexpected value for "width" and "height" properties may be normal.
So those "anomalies" should be reported only if everything else is okay.
---
.../sis/gui/coverage/ImagePropertyExplorer.java | 8 +++--
.../org/apache/sis/internal/gui/Resources.java | 2 +-
.../apache/sis/internal/gui/Resources.properties | 2 +-
.../sis/internal/gui/Resources_fr.properties | 2 +-
.../apache/sis/coverage/grid/ReshapedImage.java | 16 ++++-----
.../java/org/apache/sis/image/PlanarImage.java | 39 ++++++++++++++--------
.../java/org/apache/sis/image/ResampledImage.java | 10 +++---
.../sis/internal/coverage/j2d/TiledImage.java | 7 ++--
8 files changed, 51 insertions(+), 35 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
index eb1208d..4de7c8b 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
@@ -630,7 +630,9 @@ public class ImagePropertyExplorer extends Widget {
/**
* Invoked when a new image is shown in this cell node. This method also tests image consistency.
- * If an inconsistency is found, the line is shown in read with a warning message.
+ * If an inconsistency is found, the line is shown in red (except for "width" and "height") with
+ * a warning message. We do not use a red color for "width" and "height" because the mismatch may
+ * be normal.
*/
@Override protected void updateItem(final RenderedImage image, final boolean empty) {
super.updateItem(image, empty);
@@ -642,7 +644,9 @@ public class ImagePropertyExplorer extends Widget {
final String check = ((PlanarImage) image).verify();
if (check != null) {
text = Resources.format(Resources.Keys.InconsistencyIn_2, text, check);
- fill = Styles.ERROR_TEXT;
+ if (!(check.equals("width") || check.equals("height"))) {
+ fill = Styles.ERROR_TEXT;
+ }
}
}
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
index e9e4b79..b693899 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
@@ -257,7 +257,7 @@ public final class Resources extends IndexedResourceBundle {
public static final short ImageStart = 36;
/**
- * {0} – inconsistency in `{1}` property
+ * {0} – `{1}` has an unexpected value.
*/
public static final short InconsistencyIn_2 = 39;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
index 5fdf198..cbaefa2 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
@@ -60,7 +60,7 @@ GeodeticDataset_1 = {0} geodetic dataset
GeospatialFiles = Geospatial data files
Help = Help
ImageStart = Image start
-InconsistencyIn_2 = {0} \u2013 inconsistency in `{1}` property
+InconsistencyIn_2 = {0} \u2013 `{1}` has an unexpected value.
IsolinesInRange = Generate isolines at constant interval\nstarting from given minimum.
LicenseAgreement = Do you accept the license shown below?
Loading = Loading\u2026
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
index c2bf0d5..06e8ac6 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
@@ -65,7 +65,7 @@ GeodeticDataset_1 = Base de donn\u00e9es g\u00e9od\u00e9siques {0}
GeospatialFiles = Fichiers de donn\u00e9es g\u00e9ospatiales
Help = Aide
ImageStart = D\u00e9but de l\u2019image
-InconsistencyIn_2 = {0} \u2013 incoh\u00e9rence dans la propri\u00e9t\u00e9 `{1}`
+InconsistencyIn_2 = {0} \u2013 `{1}` a une valeur inattendue.
IsolinesInRange = G\u00e9n\u00e8re des isolignes \u00e0 intervalle constant en\ncommen\u00e7ant \u00e0 la valeur minimale sp\u00e9cifi\u00e9e.
LicenseAgreement = Acceptez-vous la licence ci-dessous?
Loading = Chargement\u2026
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ReshapedImage.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ReshapedImage.java
index ca98c7f..c0efa44 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ReshapedImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/ReshapedImage.java
@@ -288,16 +288,16 @@ final class ReshapedImage extends PlanarImage {
/**
* Verifies whether image layout information are consistent.
+ * This method first checks the properties modified by this class.
+ * If okay, then this method completes the check with all verifications
+ * {@linkplain ComputedImage#verify() documented in parent class}
*/
@Override
public String verify() {
- final String error = super.verify();
- if (error == null) {
- if (getMinX() != image.getMinX() + (minTileX - image.getMinTileX()) * getTileWidth() + offsetX) return "minX";
- if (getMinY() != image.getMinY() + (minTileY - image.getMinTileY()) * getTileHeight() + offsetY) return "minY";
- if (getTileGridXOffset() != super.getTileGridXOffset()) return "tileGridXOffset";
- if (getTileGridYOffset() != super.getTileGridYOffset()) return "tileGridYOffset";
- }
- return error;
+ if (getMinX() != image.getMinX() + (minTileX - image.getMinTileX()) * getTileWidth() + offsetX) return "minX";
+ if (getMinY() != image.getMinY() + (minTileY - image.getMinTileY()) * getTileHeight() + offsetY) return "minY";
+ if (getTileGridXOffset() != super.getTileGridXOffset()) return "tileGridXOffset";
+ if (getTileGridYOffset() != super.getTileGridYOffset()) return "tileGridYOffset";
+ return super.verify(); // "width" and "height" properties should be checked last.
}
}
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 56046c1..ff230d7 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
@@ -102,7 +102,7 @@ import org.apache.sis.coverage.grid.GridGeometry; // For javadoc
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
* @since 1.1
* @module
*/
@@ -517,20 +517,31 @@ public abstract class PlanarImage implements RenderedImage {
*
* <p>The default implementation may return the following identifiers, in that order
* (i.e. this method returns the identifier of the first test that fail):</p>
- * <ul>
- * <li>{@code "SampleModel"} — Sample model is incompatible with color model.</li>
- * <li>{@code "tileWidth"} — tile width is greater than sample model width.</li>
- * <li>{@code "tileHeight"} — tile height is greater than sample model height.</li>
- * <li>{@code "numXTiles"} — number of tiles on the X axis is inconsistent with image width.</li>
- * <li>{@code "width"} — image width is not an integer multiple of tile width.</li>
- * <li>{@code "numYTiles"} — number of tiles on the Y axis is inconsistent with image height.</li>
- * <li>{@code "height"} — image height is not an integer multiple of tile height.</li>
- * <li>{@code "tileX"} — {@code minTileX} and/or {@code tileGridXOffset} is inconsistent.</li>
- * <li>{@code "tileY"} — {@code minTileY} and/or {@code tileGridYOffset} is inconsistent.</li>
- * </ul>
*
- * Subclasses may perform additional checks. For example some subclasses also check specifically
- * for {@code "minX"}, {@code "minY"}, {@code "tileGridXOffset"} and {@code "tileGridYOffset"}.
+ * <table class="sis">
+ * <caption>Identifiers of inconsistent values</caption>
+ * <tr><th>Identifier</th> <th>Meaning</th></tr>
+ * <tr><td>{@code "SampleModel"}</td> <td>Sample model is incompatible with color model.</td></tr>
+ * <tr><td>{@code "tileWidth"}</td> <td>Tile width is greater than sample model width.</td></tr>
+ * <tr><td>{@code "tileHeight"}</td> <td>Tile height is greater than sample model height.</td></tr>
+ * <tr><td>{@code "numXTiles"}</td> <td>Number of tiles on the X axis is inconsistent with image width.</td></tr>
+ * <tr><td>{@code "numYTiles"}</td> <td>Number of tiles on the Y axis is inconsistent with image height.</td></tr>
+ * <tr><td>{@code "tileX"}</td> <td>{@code minTileX} and/or {@code tileGridXOffset} is inconsistent.</td></tr>
+ * <tr><td>{@code "tileY"}</td> <td>{@code minTileY} and/or {@code tileGridYOffset} is inconsistent.</td></tr>
+ * <tr><td>{@code "width"}</td> <td>image width is not an integer multiple of tile width.</td></tr>
+ * <tr><td>{@code "height"}</td> <td>Image height is not an integer multiple of tile height.</td></tr>
+ * </table>
+ *
+ * Subclasses may perform additional checks. For example some subclasses have specialized checks
+ * for {@code "minX"}, {@code "minY"}, {@code "tileGridXOffset"} and {@code "tileGridYOffset"}
+ * values before to fallback on the more generic {@code "tileX"} and {@code "tileY"} above checks.
+ *
+ * <h4>Ignorable inconsistency</h4>
+ * Inconsistency in {@code "width"} and {@code "height"} values may be acceptable
+ * if all other verifications pass (in particular the {@code "numXTiles"} and {@code "numYTiles"} checks).
+ * It happens when tiles in the last row or last column have some unused space compared to the image size.
+ * This is legal in TIFF format for example. For this reason, the {@code "width"} and {@code "height"}
+ * values should be checked last, after all other values have been verified consistent.
*
* @return {@code null} if image layout information are consistent,
* or the name of inconsistent attribute if a problem is found.
diff --git a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
index 54974f1..f348c61 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/image/ResampledImage.java
@@ -438,19 +438,19 @@ public class ResampledImage extends ComputedImage {
}
/**
- * Verifies whether image layout information are consistent. This method performs all verifications
- * {@linkplain ComputedImage#verify() documented in parent class}, then verifies that source coordinates
+ * Verifies whether image layout information are consistent. This method verifies that source coordinates
* required by this image (computed by converting {@linkplain #getBounds() this image bounds} using the
* {@link #toSource} transform) intersects the bounds of the source image. If this is not the case, then
* this method returns {@code "toSource"} for signaling that the transform may have a problem.
+ * Otherwise this method completes the check with all verifications
+ * {@linkplain ComputedImage#verify() documented in parent class}
*
* @return {@code null} if image layout information are consistent,
* or the name of inconsistent attribute if a problem is found.
*/
@Override
public String verify() {
- String error = super.verify();
- if (error == null && toSource instanceof MathTransform2D) try {
+ if (toSource instanceof MathTransform2D) try {
final Rectangle bounds = getBounds();
final Rectangle2D tb = Shapes2D.transform((MathTransform2D) toSource, bounds, bounds);
if (!ImageUtilities.getBounds(getSource()).intersects(tb)) {
@@ -460,7 +460,7 @@ public class ResampledImage extends ComputedImage {
recoverableException("verify", e);
return "toSource";
}
- return error;
+ return super.verify(); // "width" and "height" properties should be checked last.
}
/**
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 abecce8..19976bf 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
@@ -100,8 +100,9 @@ public class TiledImage extends PlanarImage {
/**
* Verifies whether image layout information and tile coordinates are consistent.
- * This method verifies the size and minimum pixel coordinates of all tiles,
- * in addition to the verifications documented in the super-class.
+ * This method verifies the size and minimum pixel coordinates of all tiles.
+ * If okay, then this method completes the check with all verifications
+ * {@linkplain ComputedImage#verify() documented in parent class}
*
* @return {@code null} if image layout information are consistent,
* or the name of inconsistent attribute if a problem is found.
@@ -122,7 +123,7 @@ public class TiledImage extends PlanarImage {
if (tile.getMinX() != tileWidth * tx + minX) return property(tx, ty, "x");
if (tile.getMinY() != tileHeight * ty + minY) return property(tx, ty, "y");
}
- return super.verify();
+ return super.verify(); // "width" and "height" properties should be checked last.
}
/**