Re: [OpenMap Users] OpenMap on JavaFX, getGraphics(), Thread.sleep() in EDT and external Thread

From: Don Dietrick <dietrick_at_bbn.com>
Date: Tue, 16 Jun 2009 18:04:49 -0400

The reason OpenMap uses active XOR rendering is to avoid having
paintComponent called. If paintComponent is called, then all the other
layers are being rendered as well, which, historically and unpredictably
(based on layer configurations), makes those activities seem sluggish.
OpenMap layers override the paint() method, which bypasses the built-in
double-buffering of the component.

To get the same kind of performance that this XOR mechanism provides
(non-withstanding this recent Windows Java 6 issue) the MapBean would
have to be modified to lock down and buffer its current state, and then
paint that buffer and whatever is being rendered. XOR rendering at that
point is moot, regular rendering will just work.

The BufferedMapBean and BufferedLayerMapBean already have a top level
image buffer, which gets rendered when the map isn't changing and layers
aren't calling for repaints(), for window reveals, for instance. Some
mechanism would have to be added to allow these drawing components to
render on top of that buffer while ignoring changes that may be
occurring underneath, at least until the drawing activity is complete.

- Don

Fabrice Bouye wrote:
> And it still has sluggish performances even with Java 1.6.0_14.
>
> However it is not the source of the issue: here a very small example of a Swing component shown in a Java Frame and in a JavaFX Stage.
> Component has a passive XOR render (by overriding its paintComponent() method) and an active XOR render (direct render on getGraphics() in mouseDragged()).
> Both renders show up in the Java program, only the passive render shows up in the JavaFX program.
>
> XORPanel.Java
> // Has two XOR rendering : one passive and one active.
> package xor;
>
> import java.awt.Color;
> import java.awt.Dimension;
> import java.awt.Graphics;
> import java.awt.Graphics2D;
> import java.awt.Insets;
> import java.awt.LinearGradientPaint;
> import java.awt.event.MouseEvent;
> import javax.swing.JPanel;
> import javax.swing.event.MouseInputListener;
>
> /**
> *
> * _at_author fabriceb
> */
> public class XORPanel extends JPanel {
>
> /**
> * Creates a new instance.
> */
> public XORPanel() {
> InnerListener innerListener = new InnerListener();
> addMouseListener(innerListener);
> addMouseMotionListener(innerListener);
> }
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> protected void paintComponent(Graphics g) {
> Insets insets = getInsets();
> Dimension size = getSize();
> int width = size.width - (insets.left + insets.right);
> int height = size.height - (insets.top + insets.bottom);
> Graphics2D g2d = (Graphics2D) g.create(insets.left, insets.top, width, height);
> try {
> LinearGradientPaint paint = new LinearGradientPaint(0, 0, 0, height, new float[]{0.0f, 1.0f}, new Color[]{Color.CYAN, Color.BLUE});
> g2d.setPaint(paint);
> g2d.fillRect(0, 0, width, height);
> g2d.setXORMode(Color.BLACK);
> double radius = (int) (0.9 * Math.min(width, height) / 2.0);
> g2d.drawOval((int) (width / 2.0 - radius), (int) (height / 2.0 - radius), (int) (2 * radius), (int) (2 * radius));
> } finally {
> g2d.dispose();
> }
> }
>
> private class InnerListener implements MouseInputListener {
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> public void mouseClicked(MouseEvent e) {
> }
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> public void mousePressed(MouseEvent e) {
> }
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> public void mouseReleased(MouseEvent e) {
> // Get rid of active rendering trash.
> repaint();
> }
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> public void mouseEntered(MouseEvent e) {
> }
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> public void mouseExited(MouseEvent e) {
> }
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> public void mouseDragged(MouseEvent e) {
> System.out.println("Mouse dragged!");
> // BAD!
> Graphics2D g2d = (Graphics2D) getGraphics();
> if (g2d == null) {
> return;
> }
> g2d.setXORMode(Color.WHITE);
> g2d.fillRect(e.getX() - 5, e.getY() - 5, 2 * 5, 2 * 5);
> g2d.setPaintMode();
> }
>
> /**
> * {_at_inheritDoc
> */
> _at_Override
> public void mouseMoved(MouseEvent e) {
> }
> }
> }
>
> Main.java
> // Both active and passive shows up.
> package xor;
>
> import java.awt.BorderLayout;
> import java.awt.Dimension;
> import javax.swing.JFrame;
> import javax.swing.SwingUtilities;
>
> public class Main {
>
> /**
> * _at_param args the command line arguments
> */
> public static void main(String[] args) {
> SwingUtilities.invokeLater(new Runnable() {
> /**
> * {_at_inheritDoc}
> */
> public void run() {
> XORPanel xorPanel = new XORPanel();
> JFrame frame = new JFrame("XOR test");
> frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
> frame.setLayout(new BorderLayout());
> frame.add(xorPanel, BorderLayout.CENTER);
> frame.setSize(new Dimension(600, 400));
> frame.setVisible(true);
> }
> });
> }
>
> }
>
> Main.fx
> // Only passive shows up.
> package xor;
>
> import javafx.stage.Stage;
> import javafx.scene.Scene;
> import javafx.ext.swing.SwingComponent;
>
> def xorPanel = new XORPanel();
> def xorNode = SwingComponent.wrap(xorPanel);
> xorNode.width = 600;
> xorNode.height = 400;
> def scene = Scene {
> content: [ xorNode ]
> }
> Stage {
> title: "XOR test"
> override var width on replace {
> xorNode.width = scene.width;
> }
> override var height on replace {
> xorNode.height = scene.height;
> }
> scene: scene;
> }
>
> ----
> Fabrice Bouyé (http://fabricebouye.cv.fm/)
> Fisheries IT Specialist
> Tel: +687 26 20 00 (Ext 411)
> Oceanic Fisheries, Pacific Community
> http://www.spc.int/
>
>
>> -----Original Message-----
>> From: dfdietrick_at_gmail.com [mailto:dfdietrick_at_gmail.com] On Behalf Of
>> Don Dietrick
>> Sent: Wednesday, June 17, 2009 6:31 AM
>> To: David Ward
>> Cc: Fabrice Bouye; openmap-users_at_bbn.com
>> Subject: Re: [OpenMap Users] OpenMap on JavaFX, getGraphics(),
>> Thread.sleep() in EDT and external Thread
>>
>> David,
>>
>> Thanks for posting that - I've heard from another user that ran into
>> the same issue and found that update worked for him as well.
>>
>> Cheers,
>>
>> Don
>>
>> On Tue, Jun 16, 2009 at 3:08 PM, David Ward<synriga_at_yahoo.com> wrote:
>>
>>> Don,
>>>
>>> Just a heads up.
>>>
>>> We ran it to an XOR rendering issue with later rendering for project
>>>
>> running
>>
>>> on Windows with DirectX/Direct3D. We would occasionally observe very
>>> sluggish performance on Windows.
>>>
>>> Java 6 Update 14 has a bug fix
>>> (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6635462) that
>>>
>> disables
>>
>>> the XOR rendering if detected. The bug report has work arounds for
>>>
>> earlier
>>
>>> Java 6 updates.
>>>
>>> Cheers,
>>> David
>>>
>>>
>>>
>>>
>>> On Jun 16, 2009, at 11:49 AM, Don Dietrick wrote:
>>>
>>>
>>>> Ahh, yes, the ZoomMouseMode, I forgot about that one, I don't use it
>>>>
>> that
>>
>>>> much. The internal workings of that mouse mode should be updated,
>>>>
>> I'm not
>>
>>>> sure at what point that will happen.
>>>>
>>>> So it looks like the real sin is the XOR drawing going on with the
>>>>
>> mouse
>>
>>>> modes, and now that I think about it, the drawing tool, by painting
>>>>
>> into the
>>
>>>> Graphics object. It'll be interesting to test and see if the XOR
>>>>
>> method is
>>
>>>> still needed, it was a performance necessity 10 years ago but might
>>>>
>> be
>>
>>>> averted now. I'll take a look at it at some point after 5.0 is
>>>>
>> released.
>>
>>>> I'm not seeing any weird behavior with the OverviewMapHandler, so if
>>>>
>> you
>>
>>>> find something please let me know.
>>>>
>>>> Thanks for the feedback,
>>>>
>>>> Don
>>>>
>>>> On Jun 15, 2009, at 5:53 PM, Fabrice Bouye wrote:
>>>>
>>>>
>>>>> Hi,
>>>>> It works good and as for most other Swing component that has no
>>>>>
>> JavaFX
>>
>>>>> wrapper (ie: JButton has a JavaFX wrapper named
>>>>> javafx.ext.swing.SwingButton), it is just a matter of doing
>>>>>
>> something
>>
>>>>> similar to (from memory):
>>>>>
>>>>> def mapBean = new BufferedLayerMapBean();
>>>>> // Setup map and everything as usual.
>>>>> [...]
>>>>> def mapNode = SwingComponent.wrap(mapBean);
>>>>> def scene = Scene {
>>>>> content: mapNode;
>>>>> }
>>>>> Stage {
>>>>> title: "OpenMap test"
>>>>> override var width = 800 on replace {
>>>>> mapNode.width = scene.width;
>>>>> }
>>>>> override var height = 600 on replace {
>>>>> mapNode.height = scene.height;
>>>>> }
>>>>> content: scene;
>>>>> }
>>>>>
>>>>> This should work on JavaFX 1.0, 1.1/1.1.1 or 1.2 (note: unlike
>>>>>
>> Java, so
>>
>>>>> far JavaFX binaries are not compatible between the different
>>>>>
>> releases so the
>>
>>>>> program would need to be recompiled when changing versions). And
>>>>>
>> the map
>>
>>>>> responds ok without any trouble to mouse movements such as clicks
>>>>>
>> or drags.
>>
>>>>> What is missing is the overlay for each mouse modes, that is the
>>>>>
>> visual
>>
>>>>> feedback of the mouse mode:
>>>>> - in NavMouseMoude, no drag rectangle or point except sometimes for
>>>>>
>> the
>>
>>>>> very first click.
>>>>> - in DistanceMouseMode, the circle and radius that shows the
>>>>>
>> distance
>>
>>>>> does not appear unless you resize the frame.
>>>>> - in ZoomMouseMode, partial redrawing of the map on screen (most of
>>>>>
>> the
>>
>>>>> time not the entire bean surface), may disrupts other nodes
>>>>>
>> rendering.
>>
>>>>> - in PanMouseMode, partial redrawing of the map on screen (most of
>>>>>
>> the
>>
>>>>> time not the entire bean surface), may disrupts other nodes
>>>>>
>> rendering.
>>
>>>>> Etc.
>>>>>
>>>>> This is mainly because the mouse modes use direct calls to
>>>>>
>> getGraphics(),
>>
>>>>> something that is not recommended (see books such as Filthy Rich
>>>>>
>> Clients
>>
>>>>> p46, 47, 48 -Romain and Chet were part of the Swing Team at that
>>>>>
>> time-, past
>>
>>>>> JavaOne BOFs, various blogs from members of the Swing Team). JavaFX
>>>>>
>> is more
>>
>>>>> conservative here and expects Swing component to follow best
>>>>>
>> practices so
>>
>>>>> they render Ok. It's not critical, and I was just wondering if this
>>>>>
>> would be
>>
>>>>> fixed later on since version 5.0 has been announced. At some point
>>>>>
>> I may end
>>
>>>>> up trying to implement the various mouse modes in JavaFX with node
>>>>>
>> rendering
>>
>>>>> instead of Java2D rendering in replacement if this is not feasible.
>>>>>
>>>>> After wandering through the sources to find out if getGraphics()
>>>>>
>> was
>>
>>>>> indeed is used, I've discovered that Thread.sleep() in the EDT and
>>>>>
>> using a
>>
>>>>> new Thread to set the projections out of EDT was used as well at
>>>>>
>> least in
>>
>>>>> ZoomMouseMode, two things that are not good either. Most recent
>>>>>
>> Swing best
>>
>>>>> practices involve using either a Swing Timer, not a
>>>>>
>> java.util.Timer, (job
>>
>>>>> occurs entirely in EDT with this one) or a SwingWorker (allows safe
>>>>> interaction between job executed in external Thread and the EDT)
>>>>>
>> both as
>>
>>>>> Thread replacements or SwingUtilities.invokeLater() or
>>>>> SwingUtilities.invokeAndWait() (for interaction with the external
>>>>>
>> Thread and
>>
>>>>> the EDT ; note: SwingUtilities.invokeAndWait() may be a source of
>>>>>
>> trouble
>>
>>>>> too).
>>>>>
>>>>> I am making API calls to change the bean projection and it work
>>>>>
>> good as
>>
>>>>> far as the map bean and it layers are concerned (they redraw and
>>>>>
>> show up
>>
>>>>> properly, etc.).
>>>>> Apart from the map beans and layers, the only other OpenMap
>>>>>
>> component I
>>
>>>>> am using so far is the OverviewMapHandler (integration in the
>>>>>
>> JavaFX UI is
>>
>>>>> done in a similar fashion through SwingComponent.wrap() too) but it
>>>>>
>> seems it
>>
>>>>> get lost* when I change the map's projection (not related to JavaFX
>>>>>
>> as it
>>
>>>>> does that in a pure Java test too), I am still investigating about
>>>>>
>> this one.
>>
>>>>> *That is once I change the projection's type (i.e.: from Mercator
>>>>>
>> to
>>
>>>>> LLXY, or any other), its status layer does not follow the
>>>>>
>> projection
>>
>>>>> anymore, though the handler itself still seems to react Ok to mouse
>>>>>
>> actions.
>>
>>>>> If I just pan and zoom without changing the projection type, the
>>>>>
>> status
>>
>>>>> layer works Ok and shows the proper area.
>>>>>
>>>>> ----
>>>>> Fabrice Bouyé (http://fabricebouye.cv.fm/)
>>>>> Fisheries IT Specialist
>>>>> Tel: +687 26 20 00 (Ext 411)
>>>>> Oceanic Fisheries, Pacific Community
>>>>> http://www.spc.int/
>>>>>
>>>>>
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Don Dietrick [mailto:dietrick_at_bbn.com]
>>>>>> Sent: Tuesday, June 16, 2009 2:43 AM
>>>>>> To: Fabrice Bouye
>>>>>> Cc: openmap-users_at_bbn.com
>>>>>> Subject: Re: [OpenMap Users] OpenMap on JavaFX, getGraphics(),
>>>>>> Thread.sleep() in EDT and external Thread
>>>>>>
>>>>>> Hi Fabrice,
>>>>>>
>>>>>> Thanks for the initial feedback on getting OpenMap wrapped in
>>>>>>
>> JavaFX,
>>
>>>>>> this is the first instance I've heard of someone trying this. I'm
>>>>>> interested in hearing how this works out.
>>>>>>
>>>>>> I'm also trying to understand your characterization of the mouse
>>>>>>
>> modes
>>
>>>>>> and how they contribute to making the map unresponsive. The mouse
>>>>>>
>> modes
>>
>>>>>> don't do anything to the EDT in particular, they act much like the
>>>>>> navigation panel buttons and any other swing widget controlling
>>>>>>
>> the
>>
>>>>>> map.
>>>>>> The MapBean has a support object, containing a thread, that is
>>>>>> responsible for telling the layers the map projection has changed,
>>>>>>
>> and
>>
>>>>>> the MapBean generally controls when that thread is woken up and
>>>>>>
>> tasked.
>>
>>>>>> Are you using any of the other Swing widgets, or making API calls,
>>>>>>
>> to
>>
>>>>>> change the MapBean's projection? Are they functioning, or does the
>>>>>> MapBean only render the initial projection?
>>>>>>
>>>>>> Cheers,
>>>>>>
>>>>>> - Don
>>>>>>
>>>>>> Fabrice Bouye wrote:
>>>>>>
>>>>>>> Hi,
>>>>>>>
>>>>>>> I've been developing a GIS GUI for a while for one of our
>>>>>>>
>> internal
>>
>>>>>>> projects using the OpenMap bean as our GIS component. While the
>>>>>>> project was initially in pure Java Swing, the project's GUI has
>>>>>>>
>> now
>>
>>>>>>> switched to JavaFX since it's been released last December
>>>>>>>
>> (recently
>>
>>>>>>> switched to JavaFX 1.2).
>>>>>>>
>>>>>>> As expected the map bean can be simply wrapped using the
>>>>>>> javafx.ext.swing.SwingComponent class and displayed as any other
>>>>>>>
>> kind
>>
>>>>>>> of Node in a Scene and it works good.
>>>>>>>
>>>>>>> But, there are still some issues in the way OpenMap's various
>>>>>>>
>> mouse
>>
>>>>>>> mode are done : when rendering their own overlay, they use direct
>>>>>>> access to the map bean getGraphics() method, they use
>>>>>>>
>> Thread.sleep()
>>
>>>>>>> in the EDT and they launch an external Thread to change the
>>>>>>>
>>>>>> projection
>>>>>>
>>>>>>> (see ZoomMouseMove, PanMouseMode and NavMouseMode source code for
>>>>>>> example), things which is usually not considered to be Swing good
>>>>>>> programming practices as they do get along with Swing's passive
>>>>>>> rendering architecture and interact poorly with the EDT itself.
>>>>>>>
>>>>>>> While the overlays show up Ok in Java when dragging the mouse
>>>>>>>
>> around,
>>
>>>>>>> they simply do not show up* at all when the bean is integrated in
>>>>>>>
>> a
>>
>>>>>>> JavaFX Scene.
>>>>>>>
>>>>>>> *to be true, sometimes it shows up on the initial click and never
>>>>>>> again after.
>>>>>>>
>>>>>>> I was wondering if there was any plan to change that for the next
>>>>>>> OpenMap major version?
>>>>>>>
>>>>>>> (i.e.: replace direct active rendering by some passive rendering
>>>>>>> architecture, replace Thread by SwingTimer or
>>>>>>> SwingUtilities.invokeLater(), access to component properties only
>>>>>>>
>> on
>>
>>>>>>> EDT, etc.)
>>>>>>>
>>>>>>> ----
>>>>>>> Fabrice Bouyé (http://fabricebouye.cv.fm/)
>>>>>>> Fisheries IT Specialist
>>>>>>> Tel: +687 26 20 00 (Ext 411)
>>>>>>> Oceanic Fisheries, Pacific Community
>>>>>>> http://www.spc.int/
>>>>>>>
>>>>>>>
>>>>> --
>>>>> [To unsubscribe to this list send an email to "majdart_at_bbn.com"
>>>>> with the following text in the BODY of the message "unsubscribe
>>>>> openmap-users"]
>>>>>
>>>> --
>>>> [To unsubscribe to this list send an email to "majdart_at_bbn.com"
>>>> with the following text in the BODY of the message "unsubscribe
>>>> openmap-users"]
>>>>
>>> --
>>> [To unsubscribe to this list send an email to "majdart_at_bbn.com"
>>> with the following text in the BODY of the message "unsubscribe
>>> openmap-users"]
>>>
>>>
>
> --
> [To unsubscribe to this list send an email to "majdart_at_bbn.com"
> with the following text in the BODY of the message "unsubscribe openmap-users"]
>

--
[To unsubscribe to this list send an email to "majdart_at_bbn.com"
with the following text in the BODY of the message "unsubscribe openmap-users"]
Received on Tue Jun 16 2009 - 18:05:12 EDT

This archive was generated by hypermail 2.3.0 : Tue Mar 28 2017 - 23:25:09 EDT