Hi Adrian,

    Isn't this fun?  Other than using a BufferedImage, your problem 
involved Swing more that Java 2D, so I am sending a copy of my 
response to the Swing list as well for comment and input.

-----Original Message-----
From: Adrian Barnett <[EMAIL PROTECTED]>
To: [EMAIL PROTECTED]
<[EMAIL PROTECTED]>
Date: Thursday, June 03, 1999 12:10 PM
Subject: [java2d] Problem using XOR mode

>I'm having a strange problem whilst trying to do something that
>should be fairly trivial.
>
>My applet hierarchy looks like this (somewhat simplified):
>JFrame  (main window)
>   |
>   - JPanel1  (main panel within the frame)
>        |
>        - JLabel1 (used to show current coordinates)
>        - JPanel2 (used as a drawing canvas)
>
>I have a complex drawing rendered in JPanel2. I then let the user
>select things inside it using a rubber-band rectangle. I draw the
> rubber-band in XOR painting mode, so that I do not have to keep
>redrawing the background - I can just overwrite the reactangle to
>erase it and then draw it in it's new position. This all works fine.
>
>The problems start when I attempt to update JLabel1 to show e.g.
>the current coordinates of the rectangle. What happens is that the
>label is correctly updated, but a copy of the label is drawn into the
>top left of the JPanel2 area, damaging the background image.
>This would be no  problem if I re-rendered the background all the
>time, but that would remove the need for using XOR painting to
>speed things up (and Swing is slow enough as it is).
>
>At first I couldn't work out where this copy of the label was coming
>from, but after a little investigation it seems that this is "normal"
>Swing behaviour.
>
>From I understand, each Swing window (like a JFrame) has a single
>image buffer that is used for all the GUI rendering. The components
>are all drawn into this buffer, and eventually blitted to the screen to
>show the complete frame (normal Swing double-buffering).
>
>I believe that what is happening is this: When I draw my complex
>background, it is actually drawn into the top left (0,0) of the
>Swing back-buffer, and then blitted to the correct place in the frame
>when the GUI is refreshed.  When I set the text in the label to reflect
>the size of the rubberband box, Swing draws the label into the top
>left of its background image buffer, which currently contains my
>background drawing. This damages the drawing.
>Then I want to move the rubberband box around. Because I do not
>redraw the scene, the Swing backbuffer still contains the original,
>damaged drawing. My rubberband box is drawn correctly, and the
>Swing double-buffering mechanism then shows JPanel2, complete
>with the copy of JLabel1 (that I obviously do not want).
>
>The object that controls the window has a method called
>setStatus(String text) which simply sets the text in JLabel1. The
>object that handles JPanel2 calls myparent.setStatus(text)
>to update the label. I've tried doing it in the paint() method, the
>mouse-event methods, and using a SwingWorker thread to call
>setStatus, but they all produce the same result.
>
>(I hope I've explained this well enough for others to make sense of it,
>because it confused the hell out of me until I worked out what's going
>on.)
>
>The workaround I have at the moment is to show the status in the the
>JFrames title, but that is not an ideal solution.
>
>Am I doing something wrong here? Surely it is fairly easy to draw an
>XOR rectangle around a panel and update a label at the same time,
>without having to completely repaint the background each time?
>
>Any hints would be much appreciated.
>Adrian Barnett
>


    I'd say you described the situation pretty accurately.  And in looking
around, XOR mode info in Swing is conspicuous in its absence.  Let
me say that, while in your case ( rubberbanding ) XOR mode is just
whay you want, many other drag/moving cases can be handled better
with other techniques, and Swing encourages that.

    First thing is that, after a little thought, double buffering doesn't
make much sense with XOR mode.  Kinda defeats the purpose.  So,
answer one is that, if any components are changed during the XOR
draw AND/OR if the draw is done in the paint, paintComponent, etc
routine, turn double buffering off.  The totally unobvious way to do that
is:

  RepaintManager rm = RepaintManager.currentManager( this );
  rm.setDoubleBufferingEnabled(false);

use
  rm.setDoubleBufferingEnabled(true);
when done to restore normal doublebuffer mode.

    While components have doublebuffering flags, it really chains up to
the topmost container and the RepaintManager controls it.  Note that,
if you're just doing XORs outside of paint routines AND you do not
change components, you can just draw as normal XOR mode.

    If you change components and you want to XOR over them, you
have to change them, paintImmediately() and clear RepaintManager's
dirty area ( it is still there, just not doublebuffering. )  If you don't,
the result won't be as expected and an unwanted redraw will occur.

      myComponent.setText(currentX + "," + currentY);
      Dimension dim = myComponent.getSize();
      myComponent.paintImmediately( 0, 0, dim.width, dim.height );
      rm.markCompletelyClean( myComponent );

    Even with this, funny things can happen ( like trash on the
screen ) if you move your XOR'ed image around a changed
component.  For one, think about a Layout Manager's effect on
location when the size of the component changes.

    Anyhow, I hope that and the attached code helps.  The code could
be optimized more, but I think it shows the point.  Good luck.


                                    Joe Sam Shirah
                                    Autumn Software


JXOR.java

Reply via email to