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 8713eccc5c3d5c015ca504621fdc837d3e99e62d Author: Martin Desruisseaux <[email protected]> AuthorDate: Wed Jan 13 15:39:01 2021 +0100 Remove RenderingMode.DIRECT because it causes twinkle at rendering time. --- .../apache/sis/gui/coverage/CoverageCanvas.java | 5 +- .../java/org/apache/sis/gui/map/MapCanvasAWT.java | 156 +++------------------ .../java/org/apache/sis/gui/map/RenderingMode.java | 51 ------- 3 files changed, 19 insertions(+), 193 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 b7d47fe..358ded5 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 @@ -61,7 +61,6 @@ import org.apache.sis.image.Interpolation; import org.apache.sis.coverage.Category; 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; @@ -211,7 +210,6 @@ 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); } /** @@ -677,8 +675,7 @@ public class CoverageCanvas extends MapCanvasAWT { /** * 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}. + * This method is invoked in a background thread. */ @Override protected void paint(final Graphics2D gr) { 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 8495411..3cf9003 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,7 +22,6 @@ import java.awt.AlphaComposite; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.GraphicsConfiguration; -import java.awt.RenderingHints; import java.awt.image.DataBufferInt; import java.awt.image.RenderedImage; import java.awt.image.BufferedImage; @@ -35,7 +34,6 @@ import javafx.scene.image.PixelFormat; import javafx.scene.image.WritableImage; import javafx.concurrent.Task; import javafx.util.Callback; -import org.apache.sis.util.ArgumentChecks; import org.apache.sis.internal.coverage.j2d.ColorModelFactory; @@ -62,17 +60,6 @@ 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. @@ -93,7 +80,7 @@ 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. This is used only with {@link RenderingMode#DOUBLE_BUFFERED}. + * JavaFX thread. */ private VolatileImage doubleBuffer; @@ -103,7 +90,6 @@ public abstract class MapCanvasAWT extends MapCanvas { * This configuration determines whether native acceleration will be enabled or not. * * @see #NATIVE_ACCELERATION - * @see RenderingMode#DOUBLE_BUFFERED */ private GraphicsConfiguration bufferConfiguration; @@ -148,35 +134,6 @@ public abstract class MapCanvasAWT extends MapCanvas { } /** - * 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. @@ -195,11 +152,11 @@ public abstract class MapCanvasAWT extends MapCanvas { * * <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>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> + * <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>Background thread</td> <td>Holds a {@link Graphics2D}.</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 @@ -246,12 +203,9 @@ public abstract class MapCanvasAWT extends MapCanvas { /** * 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> + * This method is invoked in a background thread, potentially many times if {@link VolatileImage} content + * is invalidated in the middle of rendering process. This method should not access any {@link MapCanvas} + * property; if some canvas properties are needed, they should have been copied at construction time. * * @param gr the Java2D handler to use for rendering the map. */ @@ -281,7 +235,7 @@ 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 three possible situations:</p> + * <p>There is two 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 be recreated from scratch in background thread. There is no need for double-buffering @@ -289,12 +243,8 @@ public abstract class MapCanvasAWT extends MapCanvas { * of this task.</li> * <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. 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> + * only in JavaFX thread through the {@code PixelBuffer.update(…)} method. A {@link VolatileImage} is used + * as a temporary buffer.</li> * </ul> * * In all cases we need to be careful to not use directly any {@link MapCanvas} field from the {@code call()} @@ -310,8 +260,7 @@ public abstract class MapCanvasAWT extends MapCanvas { clearBuffer(); return new Creator(context); } else { - return isDirect ? new Direct (context) - : new Updater(context); + return new Updater(context); } } @@ -346,16 +295,10 @@ 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; } /** @@ -371,9 +314,6 @@ public abstract class MapCanvasAWT extends MapCanvas { 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 { @@ -418,70 +358,6 @@ 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. - * This method shall be invoked in JavaFX thread. It is user responsibility to use this more only - * when the image is known to be very fast to draw. - */ - @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 @@ -543,7 +419,6 @@ 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); @@ -562,6 +437,11 @@ public abstract class MapCanvasAWT extends MapCanvas { /** * Invoked by {@link PixelBuffer#updateBuffer(Callback)} for updating the {@link #buffer} content. + * This method must be invoked in JavaFX thread. It copies the {@link VolatileImage} content to the + * {@link BufferedImage} shared with JavaFX in a single {@code Graphics2D.drawImage(…)} operation. + * The whole destination surface shall be written by {@code drawImage(…)}, so there is no need to invoke + * {@link Graphics2D#clearRect} first. It is important because the small delay between {@code clearRect(…)} + * and {@code drawImage(…)} can cause twinkle. */ @Override public Rectangle2D call(final PixelBuffer<IntBuffer> wrapper) { 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 deleted file mode 100644 index ee75679..0000000 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/RenderingMode.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 -}
