View Javadoc

1   // Copyright (c) 2003-2004, FreeHEP
2   package org.freehep.graphics2d;
3   
4   import java.awt.Dimension;
5   import java.awt.Graphics;
6   import java.awt.Image;
7   import java.awt.image.BufferedImage;
8   import java.awt.print.PrinterGraphics;
9   
10  import javax.swing.JPanel;
11  
12  /**
13   * This class extends JPanel by adding double buffering. This is intended to be
14   * used in situations in which redrawing the contents of the panel is expensive.
15   * 
16   * @author Mark Donszelmann
17   * @version $Id: BufferedPanel.java 8584 2006-08-10 23:06:37Z duns $
18   */
19  public class BufferedPanel extends JPanel implements java.io.Serializable {
20  
21      private VectorGraphics offScreenGraphics;
22  
23      private Image offScreenImage;
24  
25      private Dimension oldDimension = new Dimension();
26  
27      private Dimension dim = new Dimension();
28  
29      // FREEHEP-503 Disabled, since in Linux this made the blitting very
30      // slow.     
31      // private Rectangle clip = new Rectangle();
32  
33      private boolean printing = false;
34  
35      private boolean exporting = false;
36  
37      private boolean repaint = false;
38  
39      public BufferedPanel() {
40          this(true);
41      }
42  
43      /**
44       * Creates a new BufferedPanel with a width and height set to zero.
45       * 
46       * @param opaque transparent panel
47       */
48      public BufferedPanel(boolean opaque) {
49  
50          // First turn off the Swing double buffering.
51          super(false);
52  
53          // Make this either opaque or transparent.
54          setOpaque(opaque);
55      }
56  
57      /**
58       * Triggers a full "user" repaint. If the "system" wants to repaint it will
59       * call the paint(Graphics) method directly, rather than scheduling a
60       * paint(Graphics) through a repaint().
61       */
62      public void repaint() {
63          super.repaint();
64          repaint = true;
65      }
66  
67      /**
68       * Triggers a full repaint, since the component is not valid anymore (size
69       * change, iconized, ...)
70       */
71      public void invalidate() {
72          super.invalidate();
73          repaint = true;
74      }
75  
76      /**
77       * Returns if true if paintComponent(VectorGraphics) should be called (was
78       * triggered by a repaint() or invalidate(), and resets the trigger.
79       */
80      private synchronized boolean shouldRepaint() {
81          boolean result = repaint;
82          repaint = false;
83          return result;
84      }
85  
86      /**
87       * Paint this panel by calling paintComponent(VectorGraphics) if necessary
88       * and flushing the buffered image to the screen. This method also handles
89       * printing and exporting separately.
90       * 
91       * @param g Graphics object
92       */
93      public void paintComponent(Graphics g) {
94          super.paintComponent(g);
95  
96          // do not paint if params are null
97          if ((g == null) || (offScreenImage == null))
98              return;
99  
100         // decide where we are painting
101         if (g instanceof PrinterGraphics)
102             printing = true;
103         if (g instanceof VectorGraphics)
104             exporting = true;
105 
106         if (!isDisplaying()) {
107             paintComponent(VectorGraphics.create(g));
108             return;
109         }
110 
111         if (shouldRepaint()) {
112             paintComponent(offScreenGraphics);
113         }
114 
115         // copy buffer to screen
116         //long t0 = System.currentTimeMillis();
117         
118         /*
119          * FREEHEP-503 Disabled, since in Linux this made the blitting very
120          * slow.
121          * 
122          * Despite what the API documentation says, getClipBounds()
123          * returns the current dirty region. This can then be used to speedup
124          * the drawing of the BufferedPanel.
125          */ 
126          
127         // clip = g.getClipBounds(clip);
128         // Make sure the clip is not larger than the bounds. 
129         // clip = clip.intersection(getBounds());
130         // BufferedImage bufferedImage = (BufferedImage)offScreenImage;
131         // BufferedImage subimage = bufferedImage.getSubimage(clip.x,clip.y,
132         // clip.width,clip.height); boolean done =
133         // g.drawImage(subimage,clip.x,clip.y,this);
134 
135 
136         /* boolean done = */ g.drawImage(offScreenImage, 0, 0, this);
137         // System.err.println("CopyImage "+done+" took:
138         // "+(System.currentTimeMillis()-t0)+" ms.");
139     }
140 
141     /**
142      * Returns a pointer to the graphics (VectorGraphics) context of the buffer.
143      * The user is NOT allowed to call dispose() on this graphics object.
144      * <P>
145      * NOTE: this method used to be called getGraphics, however, since the JVM
146      * paint thread may call getGraphics from paintImmediately and fails to work
147      * with our VectorGraphics context (the gc is not longer attached to the
148      * image), we decided to rename the method.
149      */
150     public Graphics getBufferedGraphics() {
151         return offScreenGraphics;
152     }
153 
154     /**
155      * Allows for custom graphics to be painted. Subclasses should implement
156      * real drawing here. They can ask isPrinting(), isExporting() or
157      * isDisplaying() to see where the output goes. If painting is done to a
158      * display it is done to a buffer which is kept and copied afterwards.
159      * 
160      * Note that the parameter here is of class VectorGraphics rather than
161      * Graphics.
162      */
163     public void paintComponent(VectorGraphics vg) {
164     }
165 
166     /**
167      * Resize and move a component.
168      */
169     public void setBounds(int x, int y, int w, int h) {
170         // Make sure that the parent's method is called first;
171         // otherwise, the resize never happens and new images are NOT
172         // made.
173         super.setBounds(x, y, w, h);
174 
175         // FREEHEP-503 Disabled, since in Linux this made the blitting very
176         // slow.     
177 //        clip = new Rectangle(x, y, w, h);
178 
179         // Make the backing image.
180         makeImage();
181     }
182 
183     /**
184      * Returns true if the drawing is made for a PrinterGraphics context.
185      */
186     public boolean isPrinting() {
187         return printing;
188     }
189 
190     /**
191      * Returns true if the drawing is made for a VectorGraphics context.
192      */
193     public boolean isExporting() {
194         return exporting;
195     }
196 
197     /**
198      * Returns true if the drawing is made for a PixelGraphics context, the
199      * display. True if not Printing and not Exporting.
200      */
201     public boolean isDisplaying() {
202         return ((!isExporting()) && (!isPrinting()));
203     }
204 
205     /**
206      * Make the buffered image for this panel. Check to see if the size has
207      * changed before doing anything.
208      */
209     private void makeImage() {
210 
211         // Get the full size of the panel.
212         dim = getSize(dim);
213 
214         // Check that the current size is positive and that the new
215         // dimension is not equal to the old one.
216         if (dim.width > 0 && dim.height > 0) {
217             if (!oldDimension.equals(dim)) {
218 
219                 // allocate an Image, which from before JDK 1.4 was always in
220                 // System Memory,
221                 // and from JDK 1.4 is a Managed Buffered Image in System memory
222                 // or VRAM (Windows)
223                 // or a BufferedImage (getClass always returns BufferedImage)
224                 // Real BufferedImages resides in System Memory.
225                 // Volatile Images reside in VRAM (Windows), or XServer memory.
226                 // Drawing more than ~1000 line segments to system memory (BI)
227                 // and
228                 // doing a copy to the display is faster than drawing them
229                 // to video ram (VI) and using the fast VRAM->display copy.
230                 // For us, System Memory is the choice for both Windows and
231                 // XWindows,
232                 // since we draw more lines that copying.
233 
234                 // However, it may be that the acceleration possible on VRAM my
235                 // up this
236                 // 1000 line limit.
237 
238                 // The image created here is a Managed Buffered Image, which can
239                 // be moved
240                 // from System Memory and copied to VRAM...
241                 // Use the flag -Dsun.java2d.ddoffscreen=false to keep the image
242                 // in
243                 // system memory, or -Dsun.java2d.ddforcevram=true to keep the
244                 // image
245                 // in VRAM. Using none, seems to affect performance in picking
246                 // mode.
247 
248                 // There is no programmatic (java) way of keeping the image in
249                 // system memory, not even if you create a BufferedImage
250                 // explicitly.
251                 if (isOpaque()) {
252                     offScreenImage = super.createImage(dim.width, dim.height);
253                 } else {
254                     offScreenImage = new BufferedImage(dim.width, dim.height,
255                             BufferedImage.TYPE_INT_ARGB);
256                 }
257                 offScreenGraphics = VectorGraphics.create(offScreenImage
258                         .getGraphics());
259 
260                 // Reset the old size of this panel.
261                 oldDimension.setSize(dim);
262             }
263         } else {
264             offScreenImage = null;
265             offScreenGraphics = null;
266         }
267     }
268 
269 }