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 }