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 0e402ff Provide a RenderingMode.DIRECT which avoid creation of a double buffer as
the cost of executing `paint(Graphics2D)` in the JavaFX thread. It is okay if that method
does only a `Graphics2D.drawRenderedImage(…)` call without scale or rotation, in which case
we are not blocking the JavaFX thread longer than in the RenderingMode.DOUBLE_BUFFERED case.
0e402ff is described below
commit 0e402ff336c6224e55351b4681fc7644fc4ee272
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Aug 6 16:12:15 2020 +0200
Provide a RenderingMode.DIRECT which avoid creation of a double buffer as the cost of
executing `paint(Graphics2D)` in the JavaFX thread.
It is okay if that method does only a `Graphics2D.drawRenderedImage(…)` call without
scale or rotation, in which case we are not blocking
the JavaFX thread longer than in the RenderingMode.DOUBLE_BUFFERED case.
---
.../apache/sis/gui/coverage/CoverageCanvas.java | 8 +-
.../java/org/apache/sis/gui/map/MapCanvas.java | 10 +-
.../java/org/apache/sis/gui/map/MapCanvasAWT.java | 216 +++++++++++++++++----
.../java/org/apache/sis/gui/map/RenderingMode.java | 51 +++++
4 files changed, 242 insertions(+), 43 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 1cd0ad1..cfafb5d 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
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Locale;
import java.awt.Graphics2D;
import java.awt.Rectangle;
+import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
@@ -55,6 +56,7 @@ import org.apache.sis.image.PlanarImage;
import org.apache.sis.image.Interpolation;
import org.apache.sis.gui.map.MapCanvas;
import org.apache.sis.gui.map.MapCanvasAWT;
+import org.apache.sis.gui.map.RenderingMode;
import org.apache.sis.gui.map.StatusBar;
import org.apache.sis.portrayal.RenderException;
import org.apache.sis.internal.gui.GUIUtilities;
@@ -193,6 +195,7 @@ public class CoverageCanvas extends MapCanvasAWT {
coverageProperty .addListener((p,o,n) -> onImageSpecified());
sliceExtentProperty .addListener((p,o,n) -> onImageSpecified());
interpolationProperty.addListener((p,o,n) -> onInterpolationSpecified(n));
+ super.setRenderingMode(RenderingMode.DIRECT);
}
/**
@@ -600,10 +603,13 @@ public class CoverageCanvas extends MapCanvasAWT {
}
/**
- * Draws the image in a background buffer after {@link #render()} finished to prepare
data.
+ * Draws the image after {@link #render()} finished to prepare data.
+ * This method may be invoked in a background thread or in JavaFX thread,
+ * depending on {@link RenderingMode}.
*/
@Override
protected void paint(final Graphics2D gr) {
+ gr.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
gr.drawRenderedImage(prefetchedImage, 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 a9c342e..a6f1e93 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
@@ -788,6 +788,9 @@ public abstract class MapCanvas extends PlanarCanvas {
/**
* Sets the width and height to the size of the given view,
* then returns {@code true} if the view is non-empty.
+ *
+ * <p>This method is invoked after {@link #createRenderer()}
+ * and before {@link #createWorker(Renderer)}.</p>
*/
final boolean initialize(final Pane view) {
width = Numerics.clamp(Math.round(view.getWidth()));
@@ -818,10 +821,9 @@ public abstract class MapCanvas extends PlanarCanvas {
* {@link MapCanvas} property; if some canvas properties are needed, they should
have been
* copied at construction time.
*
- * @throws TransformException if the rendering required coordinate transformation
and that
- * operation failed.
+ * @throws Exception if an error occurred while preparing data or rendering them.
*/
- protected abstract void render() throws TransformException;
+ protected abstract void render() throws Exception;
/**
* Invoked in JavaFX thread after {@link #render()} completion. This method can update
the
@@ -989,7 +991,7 @@ public abstract class MapCanvas extends PlanarCanvas {
Task<?> createWorker(final Renderer renderer) {
return new Task<Void>() {
/** Invoked in background thread. */
- @Override protected Void call() throws TransformException {
+ @Override protected Void call() throws Exception {
renderer.render();
return null;
}
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 42749a8..4770d0d 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
@@ -22,10 +22,12 @@ import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.GraphicsConfiguration;
-import java.awt.image.BufferedImage;
+import java.awt.RenderingHints;
import java.awt.image.DataBufferInt;
import java.awt.image.RenderedImage;
+import java.awt.image.BufferedImage;
import java.awt.image.VolatileImage;
+import javafx.application.Platform;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelBuffer;
@@ -33,7 +35,7 @@ import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritableImage;
import javafx.concurrent.Task;
import javafx.util.Callback;
-import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
@@ -60,6 +62,17 @@ public abstract class MapCanvasAWT extends MapCanvas {
private static final boolean NATIVE_ACCELERATION = false;
/**
+ * Whether {@link Renderer} will write directly into the {@link #buffer} in JavaFX thread,
+ * instead of writing into {@link #doubleBuffer} in a background thread and copy the
result
+ * in {@link #buffer} later. This flag should be set to {@code true} only when the painting
+ * is known to be fast.
+ *
+ * @see #getRenderingMode()
+ * @see #setRenderingMode(RenderingMode)
+ */
+ private boolean isDirect;
+
+ /**
* 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.
@@ -68,6 +81,11 @@ public abstract class MapCanvasAWT extends MapCanvas {
*
* <p>This buffered image contains the same data than the {@linkplain #image} of
this canvas.
* Those two images will share the same data array (no copy) and the same coordinate
system.</p>
+ *
+ * <h4>Restriction</h4>
+ * Type is restricted to {@link BufferedImage#TYPE_INT_ARGB_PRE} or {@link BufferedImage#TYPE_4BYTE_ABGR_PRE}
+ * because JavaFX {@link PixelBuffer} (stored in {@link #bufferWrapper}) accepts only
those types.
+ * We arbitrarily choose {@code TYPE_INT_ARGB_PRE}.
*/
private BufferedImage buffer;
@@ -75,16 +93,17 @@ public abstract class MapCanvasAWT extends MapCanvas {
* A temporary buffer where to draw the {@link RenderedImage} in a background thread.
* We use this double-buffering when the {@link #buffer} is already wrapped by JavaFX.
* After creating the image in background, its content is copied to {@link #buffer} in
- * JavaFX thread.
+ * JavaFX thread. This is used only with {@link RenderingMode#DOUBLE_BUFFERED}.
*/
private VolatileImage doubleBuffer;
/**
* The graphic configuration at the time {@link #buffer} has been rendered.
- * This will be used for creating compatible {@link VolatileImage} for updating.
+ * Used for creating compatible {@link #doubleBuffer} before updating image content.
* This configuration determines whether native acceleration will be enabled or not.
*
* @see #NATIVE_ACCELERATION
+ * @see RenderingMode#DOUBLE_BUFFERED
*/
private GraphicsConfiguration bufferConfiguration;
@@ -92,12 +111,17 @@ public abstract class MapCanvasAWT extends MapCanvas {
* Wraps {@link #buffer} data array for use by JavaFX images. This is the mechanism used
* by JavaFX 13+ for allowing {@link #image} to share the same data than {@link #buffer}.
* The same wrapper can be used for many {@link WritableImage} instances (e.g. thumbnails).
+ *
+ * <h4>Invariants</h4>
+ * <ul>
+ * <li>Shall be non-null if and only If {@link #buffer} is non-null.</li>
+ * </ul>
*/
private PixelBuffer<IntBuffer> bufferWrapper;
/**
* The node where the rendered map will be shown. Its content is prepared in a background
thread
- * by {@link Renderer#paint(Graphics2D)}. Subclasses should not set the image content
directly.
+ * by {@link Renderer}. Subclasses should not set the image content directly.
*/
protected final ImageView image;
@@ -114,6 +138,45 @@ public abstract class MapCanvasAWT extends MapCanvas {
}
/**
+ * Clears {@link #buffer} and all support fields.
+ */
+ private void clearBuffer() {
+ buffer = null;
+ doubleBuffer = null;
+ bufferWrapper = null;
+ bufferConfiguration = null;
+ }
+
+ /**
+ * Returns the current strategy (double buffering) for painting the map.
+ * The use of double buffering is related to the thread doing the painting.
+ * If double buffering is enabled (the default), then {@link Renderer#paint(Graphics2D)}
is invoked
+ * in a background thread and the result is copied into {@link #image} in JavaFX thread
when ready.
+ * If double buffering is disabled, then {@link Renderer#paint(Graphics2D)} is invoked
in JavaFX thread
+ * and modifies {@link #image} data in a more direct way.
+ *
+ * @return the current strategy (double buffering) for painting the map.
+ */
+ public RenderingMode getRenderingMode() {
+ return isDirect ? RenderingMode.DIRECT : RenderingMode.DOUBLE_BUFFERED;
+ }
+
+ /**
+ * Specifies whether to use double buffering when rendering the map.
+ * The default value is {@link RenderingMode#DOUBLE_BUFFERED}.
+ * That value should be changed to {@link RenderingMode#DIRECT} only if painting is known
to be fast
+ * because that mode causes {@link Renderer#paint(Graphics2D)} to be invoked in JavaFX
thread.
+ *
+ * @param mode the new rendering strategy.
+ */
+ public void setRenderingMode(final RenderingMode mode) {
+ ArgumentChecks.ensureNonNull("mode", mode);
+ if (isDirect != (isDirect = RenderingMode.DIRECT.equals(mode))) {
+ clearBuffer();
+ }
+ }
+
+ /**
* Invoked in JavaFX thread for creating a renderer to be executed in a background thread.
* Subclasses should copy in this method all {@code MapCanvas} properties that the background
thread
* will need for performing the rendering process.
@@ -127,16 +190,16 @@ public abstract class MapCanvasAWT extends MapCanvas {
/**
* A snapshot of {@link MapCanvasAWT} state to paint as an image.
* The snapshot is created in JavaFX thread by the {@link MapCanvasAWT#createRenderer()}
method,
- * then the rendering process is executed in a background thread by {@link #paint(Graphics2D)}.
+ * then the rendering process is executed in a background thread.
* Methods are invoked in the following order:
*
* <table class="sis">
* <caption>Methods invoked during a map rendering process</caption>
- * <tr><th>Method</th> <th>Thread</th>
<th>Remarks</th></tr>
- * <tr><td>{@link #createRenderer()}</td> <td>JavaFX thread</td>
<td></td></tr>
- * <tr><td>{@link #render()}</td> <td>Background thread</td>
<td></td></tr>
- * <tr><td>{@link #paint(Graphics2D)}</td> <td>Background thread</td>
<td>May be invoked many times.</td></tr>
- * <tr><td>{@link #commit(MapCanvas)}</td> <td>JavaFX thread</td>
<td></td></tr>
+ * <tr><th>Method</th> <th>Thread</th>
<th>Remarks</th></tr>
+ * <tr><td>{@link #createRenderer()}</td> <td>JavaFX thread</td>
<td>Collects all needed information.</td></tr>
+ * <tr><td>{@link #render()}</td> <td>Background thread</td>
<td>Computes what can be done in advance.</td></tr>
+ * <tr><td>{@link #paint(Graphics2D)}</td> <td>{@link #getRenderingMode()}</td>
<td>May be invoked many times.</td></tr>
+ * <tr><td>{@link #commit(MapCanvas)}</td> <td>JavaFX thread</td>
<td>Saves data to cache for reuse.</td></tr>
* </table>
*
* This class should not access any {@link MapCanvasAWT} property from a method invoked
in background thread
@@ -157,7 +220,10 @@ public abstract class MapCanvasAWT extends MapCanvas {
}
/**
- * Returns whether the given buffer is non-null and have the expected size.
+ * 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 buffer value of {@link #buffer}.
*/
final boolean isValid(final BufferedImage buffer) {
return (buffer != null)
@@ -172,18 +238,20 @@ public abstract class MapCanvasAWT extends MapCanvas {
*
* <p>The default implementation does nothing.</p>
*
- * @throws TransformException if the rendering required coordinate transformation
and that
- * operation failed.
+ * @throws Exception if an error occurred while preparing data.
*/
@Override
- protected void render() throws TransformException {
+ protected void render() throws Exception {
}
/**
- * Invoked in a background thread for rendering the map. This method should not access
any
- * {@link MapCanvas} property; if some canvas properties are needed, they should
have been
- * copied at construction time. This method may be invoked many times if the rendering
is
- * done in a {@link VolatileImage}.
+ * Invoked after {@link #render()} for doing the actual map painting.
+ * This method is invoked in the JavaFX thread if the rendering mode is {@link RenderingMode#DIRECT},
+ * or in a background thread otherwise. This method should not access any {@link
MapCanvas} property;
+ * if some canvas properties are needed, they should have been copied at construction
time.
+ *
+ * <p>This method may be invoked many times if the rendering is done in a {@link
VolatileImage}.
+ * It may happen only with {@link RenderingMode#DOUBLE_BUFFERED}.</p>
*
* @param gr the Java2D handler to use for rendering the map.
*/
@@ -213,34 +281,37 @@ public abstract class MapCanvasAWT extends MapCanvas {
* It may be because the map has new content, or because the viewed region moved or
* has been zoomed.
*
- * <p>There is two possible situations:</p>
+ * <p>There is three possible situations:</p>
* <ul class="verbose">
* <li>If the current buffers are not suitable, then we clear everything related
to Java2D buffered images.
- * Those resources will recreated from scratch in background thread. There is no
need for double-buffering
+ * Those resources will be recreated from scratch in background thread. There is
no need for double-buffering
* in such case because the new {@link BufferedImage} will not be shared with JavaFX
image before the end
* of this task.</li>
- * <li>If the current buffer are still valid, then we should not update {@link
BufferedImage} in background
+ * <li>Otherwise (current buffer it still valid), we should not update {@link
BufferedImage} in a background
* thread because the internal array of that image is shared with JavaFX image. That
image can be updated
- * only in JavaFX thread through the {@code PixelBuffer.update(…)} method. In this
case we will use a
- * {@link VolatileImage} as a temporary buffer.</li>
+ * only in JavaFX thread through the {@code PixelBuffer.update(…)} method. We do
that in two possible ways:
+ * <ul>
+ * <li>{@link RenderingMode#DIRECT}: paint directly in the JavaFX thread.</li>
+ * <li>{@link RenderingMode#DOUBLE_BUFFERED}: use a {@link VolatileImage}
as a temporary buffer.</li>
+ * </ul>
+ * </li>
* </ul>
*
- * In both cases we need to be careful to not use directly any {@link MapCanvas} field
from the {@code call()}
+ * In all cases we need to be careful to not use directly any {@link MapCanvas} field
from the {@code call()}
* methods. Information needed by {@code call()} must be copied first.
*
* @see #requestRepaint()
*/
@Override
final Task<?> createWorker(final MapCanvas.Renderer mc) {
+ assert Platform.isFxApplicationThread();
final Renderer context = (Renderer) mc;
if (!context.isValid(buffer)) {
- buffer = null;
- doubleBuffer = null;
- bufferWrapper = null;
- bufferConfiguration = null;
+ clearBuffer();
return new Creator(context);
} else {
- return new Updater(context);
+ return isDirect ? new Direct (context)
+ : new Updater(context);
}
}
@@ -252,7 +323,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
private final class Creator extends Task<WritableImage> {
/**
* The user-provided object which will perform the actual rendering.
- * Its {@link Renderer#paint(Graphics2D)} method will be invoked.
+ * Its {@link Renderer#paint(Graphics2D)} method will be invoked in background thread.
*/
private final Renderer renderer;
@@ -275,10 +346,16 @@ public abstract class MapCanvasAWT extends MapCanvas {
private GraphicsConfiguration configuration;
/**
+ * Value of {@link MapCanvasAWT#isDirect} at creation time.
+ */
+ private final boolean isDirect;
+
+ /**
* Creates a new task for painting without resource recycling.
*/
Creator(final Renderer context) {
renderer = context;
+ isDirect = MapCanvasAWT.this.isDirect;
}
/**
@@ -287,13 +364,16 @@ public abstract class MapCanvasAWT extends MapCanvas {
* background thread is executed; no direct reference to {@link MapCanvas} here.
*/
@Override
- protected WritableImage call() throws TransformException {
+ protected WritableImage call() throws Exception {
renderer.render();
final int width = renderer.getWidth();
final int height = renderer.getHeight();
drawTo = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
final Graphics2D gr = drawTo.createGraphics();
try {
+ gr.setRenderingHint(RenderingHints.KEY_RENDERING,
+ isDirect ? RenderingHints.VALUE_RENDER_SPEED
+ : RenderingHints.VALUE_RENDER_QUALITY);
configuration = gr.getDeviceConfiguration();
renderer.paint(gr);
} finally {
@@ -338,6 +418,68 @@ public abstract class MapCanvasAWT extends MapCanvas {
}
/**
+ * Tasks for updating {@link BufferedImage} directly in the JavaFX thread.
+ * This task is used if the rendering mode is {@link RenderingMode#DIRECT}.
+ */
+ private final class Direct extends Task<Void> implements Callback<PixelBuffer<IntBuffer>,
Rectangle2D> {
+ /**
+ * The user-provided object which will perform the actual rendering.
+ * Its {@link Renderer#paint(Graphics2D)} method will be invoked in JavaFX thread.
+ */
+ private final Renderer renderer;
+
+ /**
+ * Creates a new task for painting directly into the buffer shared by JavaFX image.
+ */
+ Direct(final Renderer context) {
+ renderer = context;
+ }
+
+ /**
+ * Invoked in background thread for preparing the image.
+ */
+ @Override
+ protected Void call() throws Exception {
+ renderer.render(); // Arbitrary user-specified
action.
+ return null;
+ }
+
+ /**
+ * Invoked by {@link PixelBuffer#updateBuffer(Callback)} for updating the {@link
#buffer} content.
+ */
+ @Override
+ public Rectangle2D call(final PixelBuffer<IntBuffer> wrapper) {
+ final BufferedImage drawTo = buffer;
+ final Graphics2D gr = drawTo.createGraphics();
+ try {
+ gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
+ gr.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
+ gr.clearRect(0, 0, drawTo.getWidth(), drawTo.getHeight());
+ renderer.paint(gr);
+ } finally {
+ gr.dispose();
+ }
+ return null;
+ }
+
+ /**
+ * Invoked in JavaFX thread on success.
+ */
+ @Override
+ protected void succeeded() {
+ bufferWrapper.updateBuffer(this); // This will invoke the `call(PixelBuffer)`
method above.
+ final boolean done = renderer.commit(MapCanvasAWT.this);
+ renderingCompleted(this);
+ if (!done || contentsChanged()) {
+ repaint();
+ }
+ }
+
+ @Override protected void failed() {renderingCompleted(this);}
+ @Override protected void cancelled() {renderingCompleted(this);}
+ }
+
+ /**
* Background tasks for painting in an existing {@link BufferedImage}. This task is invoked
* when previous resources (JavaFX image and Java2D volatile/buffered image) can be reused.
* The Java2D volatile image will be rendered in background thread, then its content
will be
@@ -346,7 +488,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
private final class Updater extends Task<VolatileImage> implements Callback<PixelBuffer<IntBuffer>,
Rectangle2D> {
/**
* The user-provided object which will perform the actual rendering.
- * Its {@link Renderer#paint(Graphics2D)} method will be invoked.
+ * Its {@link Renderer#paint(Graphics2D)} method will be invoked in background thread.
*/
private final Renderer renderer;
@@ -382,7 +524,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
* background thread is executed; no direct reference to {@link MapCanvas} here.
*/
@Override
- protected VolatileImage call() throws TransformException {
+ protected VolatileImage call() throws Exception {
renderer.render();
final int width = renderer.getWidth();
final int height = renderer.getHeight();
@@ -399,6 +541,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
}
final Graphics2D gr = drawTo.createGraphics();
try {
+ gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
gr.setBackground(ColorModelFactory.TRANSPARENT);
gr.clearRect(0, 0, drawTo.getWidth(), drawTo.getHeight());
renderer.paint(gr);
@@ -464,10 +607,7 @@ public abstract class MapCanvasAWT extends MapCanvas {
@Override
protected void clear() {
image.setImage(null);
- buffer = null;
- bufferWrapper = null;
- doubleBuffer = null;
- bufferConfiguration = null;
+ clearBuffer();
super.clear();
}
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/RenderingMode.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/RenderingMode.java
new file mode 100644
index 0000000..ee75679
--- /dev/null
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/RenderingMode.java
@@ -0,0 +1,51 @@
+/*
+ * 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.gui.map;
+
+import java.awt.Graphics2D;
+import java.awt.image.RenderedImage;
+import java.awt.geom.AffineTransform;
+
+
+/**
+ * Rendering strategies for {@link MapCanvasAWT}.
+ * This enumeration controls whether {@link MapCanvasAWT.Renderer#paint(Graphics2D)}
+ * should write directly into the {@linkplain MapCanvasAWT#image JavaFX image} buffer,
+ * or write in a temporary buffer to be copied later into the JavaFX image.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public enum RenderingMode {
+ /**
+ * {@link MapCanvasAWT.Renderer#paint(Graphics2D) Renderer.paint(…)} is invoked in
the JavaFX thread
+ * and paints directly into the JavaFX image buffer. This mode blocks the JavaFX thread
for the duration
+ * of the paint event. This mode should be used only when that painting is known to be
very fast,
+ * for example a single call to {@link Graphics2D#drawRenderedImage(RenderedImage, AffineTransform)}
+ * with an identity transform or a transform having only translation terms.
+ */
+ DIRECT,
+
+ /**
+ * {@link MapCanvasAWT.Renderer#paint(Graphics2D) Renderer.paint(…)} is invoked in
a background thread
+ * and paints into a temporary buffer. That temporary buffer is copied later into the
JavaFX image.
+ * This is the default rendering mode.
+ */
+ DOUBLE_BUFFERED
+}
|