sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 04/04: Prepare an image slightly larger than viewport area for more continuous translations (pans).
Date Wed, 13 Jan 2021 18:12:23 GMT
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 430647fbf0abbc88767486f804c8e88e6bdf7f2e
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Jan 13 19:11:24 2021 +0100

    Prepare an image slightly larger than viewport area for more continuous translations (pans).
---
 .../apache/sis/gui/coverage/CoverageCanvas.java    | 12 +++-
 .../org/apache/sis/gui/coverage/RenderingData.java |  2 +-
 .../java/org/apache/sis/gui/map/MapCanvas.java     |  4 +-
 .../java/org/apache/sis/gui/map/MapCanvasAWT.java  | 77 +++++++++++++++++++---
 4 files changed, 81 insertions(+), 14 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/CoverageCanvas.java
index 358ded5..8210001 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
@@ -40,6 +40,7 @@ import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.application.Platform;
 import javafx.concurrent.Task;
+import javafx.geometry.Insets;
 import javax.measure.Quantity;
 import javax.measure.quantity.Length;
 import org.opengis.geometry.Envelope;
@@ -528,9 +529,9 @@ public class CoverageCanvas extends MapCanvasAWT {
         private final LinearTransform objectiveToDisplay;
 
         /**
-         * Value of {@link CoverageCanvas#getDisplayBounds()} at the time this worker has
been initialized.
-         * This is the size and location of the display device, in pixel units.
-         * This value is usually constant when the widget is not resized.
+         * Value of {@link CoverageCanvas#getDisplayBounds()} at the time this worker has
been initialized,
+         * expanded by {@link CoverageCanvas#imageMargin}. This is the size and location
of the display device
+         * in pixel units, plus the margin. This value is usually constant when the widget
is not resized.
          */
         private final Envelope2D displayBounds;
 
@@ -594,6 +595,11 @@ public class CoverageCanvas extends MapCanvasAWT {
             if (data.validateCRS(objectiveCRS)) {
                 resampledImage = canvas.resampledImage;
             }
+            final Insets margin = canvas.imageMargin.get();
+            displayBounds.x      -= margin.getLeft();
+            displayBounds.width  += margin.getLeft() + margin.getRight();
+            displayBounds.y      -= margin.getTop();
+            displayBounds.height += margin.getTop() + margin.getBottom();
             if (canvas.isolines != null) {
                 isolines = canvas.isolines.prepare();
             }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
index 9eb2252..9ccd224 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/RenderingData.java
@@ -461,7 +461,7 @@ final class RenderingData implements Cloneable {
      *
      * @param  resampledImage      the image computed by {@link #resampleAndConvert resampleAndConvert(…)}.
      * @param  resampledToDisplay  the transform computed by {@link #getTransform(LinearTransform)}.
-     * @param  displayBounds       size and location of the display device, in pixel units.
+     * @param  displayBounds       size and location of the display device (plus margin),
in pixel units.
      * @return a temporary image with tiles intersecting the display region already computed.
      */
     final RenderedImage prefetch(final RenderedImage resampledImage, final AffineTransform
resampledToDisplay,
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
index e1e41c7..a33b22f 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvas.java
@@ -530,7 +530,7 @@ public abstract class MapCanvas extends PlanarCanvas {
      * the geographic location where the click occurred. This information is used for changing
the projection
      * while preserving approximately the location, scale and rotation of pixels around the
mouse cursor.
      */
-    @SuppressWarnings("serial")                                         // Not intended to
be serialized.
+    @SuppressWarnings({"serial","CloneableImplementsClone"})            // Not intended to
be serialized.
     final class MenuHandler extends DirectPosition2D
             implements EventHandler<MouseEvent>, ChangeListener<ReferenceSystem>,
PropertyChangeListener
     {
@@ -781,7 +781,7 @@ public abstract class MapCanvas extends PlanarCanvas {
          * <p>This method is invoked after {@link #createRenderer()}
          * and before {@link #createWorker(Renderer)}.</p>
          */
-        final boolean initialize(final Pane view) {
+        private boolean initialize(final Pane view) {
             width  = Numerics.clamp(Math.round(view.getWidth()));
             height = Numerics.clamp(Math.round(view.getHeight()));
             return width > 0 && height > 0;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvasAWT.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvasAWT.java
index 3cf9003..13f73ed 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvasAWT.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapCanvasAWT.java
@@ -27,13 +27,16 @@ import java.awt.image.RenderedImage;
 import java.awt.image.BufferedImage;
 import java.awt.image.VolatileImage;
 import javafx.application.Platform;
+import javafx.geometry.Insets;
 import javafx.geometry.Rectangle2D;
 import javafx.scene.image.ImageView;
 import javafx.scene.image.PixelBuffer;
 import javafx.scene.image.PixelFormat;
 import javafx.scene.image.WritableImage;
+import javafx.beans.property.ObjectProperty;
 import javafx.concurrent.Task;
 import javafx.util.Callback;
+import org.apache.sis.internal.gui.NonNullObjectProperty;
 import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
 
 
@@ -60,6 +63,14 @@ public abstract class MapCanvasAWT extends MapCanvas {
     private static final boolean NATIVE_ACCELERATION = false;
 
     /**
+     * Number of additional pixels to paint on each sides of the image, outside the viewing
area.
+     * Computing a larger image reduces the black borders that user sees during translations
or
+     * during zoom out before the new image is repainted.
+     * This property value can not be null but can be {@link Insets#EMPTY}.
+     */
+    public final ObjectProperty<Insets> imageMargin;
+
+    /**
      * A buffer where to draw the content of the map for the region to be displayed.
      * This buffer uses ARGB color model, contrarily to the {@link RenderedImage} of
      * {@link org.apache.sis.coverage.grid.GridCoverage} which may have any color model.
@@ -118,6 +129,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
      */
     public MapCanvasAWT(final Locale locale) {
         super(locale);
+        imageMargin = new NonNullObjectProperty<>(this, "imageMargin", new Insets(128));
         image = new ImageView();
         image.setPreserveRatio(true);
         floatingPane.getChildren().add(image);
@@ -170,6 +182,18 @@ public abstract class MapCanvasAWT extends MapCanvas {
      */
     protected abstract static class Renderer extends MapCanvas.Renderer {
         /**
+         * Values of the {@link MapCanvasAWT#imageMargin} property at construction time.
+         * Those values are initialized by {@link #isValid(Insets, BufferedImage)}.
+         */
+        private int left, top;
+
+        /**
+         * Image width and height, taking in account the margins.
+         * Those values are initialized by {@link #isValid(Insets, BufferedImage)}.
+         */
+        private int width, height;
+
+        /**
          * Creates a new renderer. The {@linkplain #getWidth() width} and {@linkplain #getHeight()
height}
          * are initially zero; they will get a non-zero values before {@link #paint(Graphics2D)}
is invoked.
          */
@@ -177,15 +201,48 @@ public abstract class MapCanvasAWT extends MapCanvas {
         }
 
         /**
+         * Rounds and clamp the given value. The upper limit is arbitrary.
+         */
+        private static int clamp(final double value) {
+            return (int) Math.max(0, Math.min(Short.MAX_VALUE, Math.round(value)));
+        }
+
+        /**
          * Returns whether the given buffer is non-null and has the expected size.
          * This verification shall be done only after {@link #initialize(Pane)} has been
invoked.
          *
+         * @param  margin  value of {@link #imageMargin}.
          * @param  buffer  value of {@link #buffer}.
          */
-        final boolean isValid(final BufferedImage buffer) {
+        private boolean isValid(final Insets margin, final BufferedImage buffer) {
+            final int right, bottom;
+            top    = clamp(margin.getTop());
+            right  = clamp(margin.getRight());
+            bottom = clamp(margin.getBottom());
+            left   = clamp(margin.getLeft());
+            width  = Math.addExact(getWidth(), left + right);
+            height = Math.addExact(getHeight(), top + bottom);
             return (buffer != null)
-                    && buffer.getWidth()  == super.getWidth()
-                    && buffer.getHeight() == super.getHeight();
+                    && buffer.getWidth()  == width
+                    && buffer.getHeight() == height;
+        }
+
+        /**
+         * Applies translation on the given graphics before {@link #paint(Graphics2D)}.
+         */
+        private void translate(final Graphics2D gr) {
+            gr.translate(left, top);
+        }
+
+        /**
+         * Compensates the translation applied by {@link #translate(Graphics2D)}.
+         * This method is invoked only if the image painting has been successful,
+         * otherwise we assume that old content is still present and require the
+         * old translations.
+         */
+        private void translate(final ImageView image) {
+            image.setTranslateX(-left);
+            image.setTranslateY(-top);
         }
 
         /**
@@ -256,7 +313,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
     final Task<?> createWorker(final MapCanvas.Renderer mc) {
         assert Platform.isFxApplicationThread();
         final Renderer context = (Renderer) mc;
-        if (!context.isValid(buffer)) {
+        if (!context.isValid(imageMargin.get(), buffer)) {
             clearBuffer();
             return new Creator(context);
         } else {
@@ -309,12 +366,13 @@ public abstract class MapCanvasAWT extends MapCanvas {
         @Override
         protected WritableImage call() throws Exception {
             renderer.render();
-            final int width  = renderer.getWidth();
-            final int height = renderer.getHeight();
+            final int width  = renderer.width;
+            final int height = renderer.height;
             drawTo = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
             final Graphics2D gr = drawTo.createGraphics();
             try {
                 configuration = gr.getDeviceConfiguration();
+                renderer.translate(gr);
                 renderer.paint(gr);
             } finally {
                 gr.dispose();
@@ -343,6 +401,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
         @Override
         protected void succeeded() {
             image.setImage(getValue());
+            renderer.translate(image);
             buffer              = drawTo;
             bufferWrapper       = wrapper;
             bufferConfiguration = configuration;
@@ -404,8 +463,8 @@ public abstract class MapCanvasAWT extends MapCanvas {
         @Override
         protected VolatileImage call() throws Exception {
             renderer.render();
-            final int width  = renderer.getWidth();
-            final int height = renderer.getHeight();
+            final int width  = renderer.width;
+            final int height = renderer.height;
             VolatileImage drawTo = previousBuffer;
             previousBuffer = null;                      // For letting GC do its work.
             if (drawTo == null) {
@@ -421,6 +480,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
                     try {
                         gr.setBackground(ColorModelFactory.TRANSPARENT);
                         gr.clearRect(0, 0, drawTo.getWidth(), drawTo.getHeight());
+                        renderer.translate(gr);
                         renderer.paint(gr);
                     } finally {
                         gr.dispose();
@@ -471,6 +531,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
             } finally {
                 drawTo.flush();                     // Release native resources.
             }
+            renderer.translate(image);
             final boolean done = renderer.commit(MapCanvasAWT.this);
             renderingCompleted(this);
             if (!done || contentsLost || contentsChanged()) {


Mime
View raw message