|
1 /* |
|
2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 package javax.swing; |
|
26 |
|
27 |
|
28 import java.awt.*; |
|
29 import java.awt.event.*; |
|
30 import java.awt.image.VolatileImage; |
|
31 import java.security.AccessControlContext; |
|
32 import java.security.AccessController; |
|
33 import java.security.PrivilegedAction; |
|
34 import java.util.*; |
|
35 import java.util.concurrent.atomic.AtomicInteger; |
|
36 import java.applet.*; |
|
37 |
|
38 import jdk.internal.misc.JavaSecurityAccess; |
|
39 import jdk.internal.misc.SharedSecrets; |
|
40 import sun.awt.AWTAccessor; |
|
41 import sun.awt.AppContext; |
|
42 import sun.awt.DisplayChangedListener; |
|
43 import sun.awt.SunToolkit; |
|
44 import sun.java2d.SunGraphicsEnvironment; |
|
45 import sun.security.action.GetPropertyAction; |
|
46 |
|
47 import com.sun.java.swing.SwingUtilities3; |
|
48 import java.awt.geom.AffineTransform; |
|
49 import sun.java2d.SunGraphics2D; |
|
50 import sun.java2d.pipe.Region; |
|
51 import sun.swing.SwingAccessor; |
|
52 import sun.swing.SwingUtilities2; |
|
53 import sun.swing.SwingUtilities2.RepaintListener; |
|
54 |
|
55 /** |
|
56 * This class manages repaint requests, allowing the number |
|
57 * of repaints to be minimized, for example by collapsing multiple |
|
58 * requests into a single repaint for members of a component tree. |
|
59 * <p> |
|
60 * As of 1.6 <code>RepaintManager</code> handles repaint requests |
|
61 * for Swing's top level components (<code>JApplet</code>, |
|
62 * <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>). |
|
63 * Any calls to <code>repaint</code> on one of these will call into the |
|
64 * appropriate <code>addDirtyRegion</code> method. |
|
65 * |
|
66 * @author Arnaud Weber |
|
67 * @since 1.2 |
|
68 */ |
|
69 public class RepaintManager |
|
70 { |
|
71 /** |
|
72 * Whether or not the RepaintManager should handle paint requests |
|
73 * for top levels. |
|
74 */ |
|
75 static final boolean HANDLE_TOP_LEVEL_PAINT; |
|
76 |
|
77 private static final short BUFFER_STRATEGY_NOT_SPECIFIED = 0; |
|
78 private static final short BUFFER_STRATEGY_SPECIFIED_ON = 1; |
|
79 private static final short BUFFER_STRATEGY_SPECIFIED_OFF = 2; |
|
80 |
|
81 private static final short BUFFER_STRATEGY_TYPE; |
|
82 |
|
83 /** |
|
84 * Maps from GraphicsConfiguration to VolatileImage. |
|
85 */ |
|
86 private Map<GraphicsConfiguration,VolatileImage> volatileMap = new |
|
87 HashMap<GraphicsConfiguration,VolatileImage>(1); |
|
88 |
|
89 // |
|
90 // As of 1.6 Swing handles scheduling of paint events from native code. |
|
91 // That is, SwingPaintEventDispatcher is invoked on the toolkit thread, |
|
92 // which in turn invokes nativeAddDirtyRegion. Because this is invoked |
|
93 // from the native thread we can not invoke any public methods and so |
|
94 // we introduce these added maps. So, any time nativeAddDirtyRegion is |
|
95 // invoked the region is added to hwDirtyComponents and a work request |
|
96 // is scheduled. When the work request is processed all entries in |
|
97 // this map are pushed to the real map (dirtyComponents) and then |
|
98 // painted with the rest of the components. |
|
99 // |
|
100 private Map<Container,Rectangle> hwDirtyComponents; |
|
101 |
|
102 private Map<Component,Rectangle> dirtyComponents; |
|
103 private Map<Component,Rectangle> tmpDirtyComponents; |
|
104 private java.util.List<Component> invalidComponents; |
|
105 |
|
106 // List of Runnables that need to be processed before painting from AWT. |
|
107 private java.util.List<Runnable> runnableList; |
|
108 |
|
109 boolean doubleBufferingEnabled = true; |
|
110 |
|
111 private Dimension doubleBufferMaxSize; |
|
112 |
|
113 // Support for both the standard and volatile offscreen buffers exists to |
|
114 // provide backwards compatibility for the [rare] programs which may be |
|
115 // calling getOffScreenBuffer() and not expecting to get a VolatileImage. |
|
116 // Swing internally is migrating to use *only* the volatile image buffer. |
|
117 |
|
118 // Support for standard offscreen buffer |
|
119 // |
|
120 DoubleBufferInfo standardDoubleBuffer; |
|
121 |
|
122 /** |
|
123 * Object responsible for hanlding core paint functionality. |
|
124 */ |
|
125 private PaintManager paintManager; |
|
126 |
|
127 private static final Object repaintManagerKey = RepaintManager.class; |
|
128 |
|
129 // Whether or not a VolatileImage should be used for double-buffered painting |
|
130 static boolean volatileImageBufferEnabled = true; |
|
131 /** |
|
132 * Type of VolatileImage which should be used for double-buffered |
|
133 * painting. |
|
134 */ |
|
135 private static final int volatileBufferType; |
|
136 /** |
|
137 * Value of the system property awt.nativeDoubleBuffering. |
|
138 */ |
|
139 private static boolean nativeDoubleBuffering; |
|
140 |
|
141 // The maximum number of times Swing will attempt to use the VolatileImage |
|
142 // buffer during a paint operation. |
|
143 private static final int VOLATILE_LOOP_MAX = 2; |
|
144 |
|
145 /** |
|
146 * Number of <code>beginPaint</code> that have been invoked. |
|
147 */ |
|
148 private int paintDepth = 0; |
|
149 |
|
150 /** |
|
151 * Type of buffer strategy to use. Will be one of the BUFFER_STRATEGY_ |
|
152 * constants. |
|
153 */ |
|
154 private short bufferStrategyType; |
|
155 |
|
156 // |
|
157 // BufferStrategyPaintManager has the unique characteristic that it |
|
158 // must deal with the buffer being lost while painting to it. For |
|
159 // example, if we paint a component and show it and the buffer has |
|
160 // become lost we must repaint the whole window. To deal with that |
|
161 // the PaintManager calls into repaintRoot, and if we're still in |
|
162 // the process of painting the repaintRoot field is set to the JRootPane |
|
163 // and after the current JComponent.paintImmediately call finishes |
|
164 // paintImmediately will be invoked on the repaintRoot. In this |
|
165 // way we don't try to show garbage to the screen. |
|
166 // |
|
167 /** |
|
168 * True if we're in the process of painting the dirty regions. This is |
|
169 * set to true in <code>paintDirtyRegions</code>. |
|
170 */ |
|
171 private boolean painting; |
|
172 /** |
|
173 * If the PaintManager calls into repaintRoot during painting this field |
|
174 * will be set to the root. |
|
175 */ |
|
176 private JComponent repaintRoot; |
|
177 |
|
178 /** |
|
179 * The Thread that has initiated painting. If null it |
|
180 * indicates painting is not currently in progress. |
|
181 */ |
|
182 private Thread paintThread; |
|
183 |
|
184 /** |
|
185 * Runnable used to process all repaint/revalidate requests. |
|
186 */ |
|
187 private final ProcessingRunnable processingRunnable; |
|
188 |
|
189 private static final JavaSecurityAccess javaSecurityAccess = |
|
190 SharedSecrets.getJavaSecurityAccess(); |
|
191 |
|
192 /** |
|
193 * Listener installed to detect display changes. When display changes, |
|
194 * schedules a callback to notify all RepaintManagers of the display |
|
195 * changes. |
|
196 */ |
|
197 private static final DisplayChangedListener displayChangedHandler = |
|
198 new DisplayChangedHandler(); |
|
199 |
|
200 static { |
|
201 SwingAccessor.setRepaintManagerAccessor(new SwingAccessor.RepaintManagerAccessor() { |
|
202 @Override |
|
203 public void addRepaintListener(RepaintManager rm, RepaintListener l) { |
|
204 rm.addRepaintListener(l); |
|
205 } |
|
206 @Override |
|
207 public void removeRepaintListener(RepaintManager rm, RepaintListener l) { |
|
208 rm.removeRepaintListener(l); |
|
209 } |
|
210 }); |
|
211 |
|
212 volatileImageBufferEnabled = "true".equals(AccessController. |
|
213 doPrivileged(new GetPropertyAction( |
|
214 "swing.volatileImageBufferEnabled", "true"))); |
|
215 boolean headless = GraphicsEnvironment.isHeadless(); |
|
216 if (volatileImageBufferEnabled && headless) { |
|
217 volatileImageBufferEnabled = false; |
|
218 } |
|
219 nativeDoubleBuffering = "true".equals(AccessController.doPrivileged( |
|
220 new GetPropertyAction("awt.nativeDoubleBuffering"))); |
|
221 String bs = AccessController.doPrivileged( |
|
222 new GetPropertyAction("swing.bufferPerWindow")); |
|
223 if (headless) { |
|
224 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF; |
|
225 } |
|
226 else if (bs == null) { |
|
227 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED; |
|
228 } |
|
229 else if ("true".equals(bs)) { |
|
230 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON; |
|
231 } |
|
232 else { |
|
233 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF; |
|
234 } |
|
235 HANDLE_TOP_LEVEL_PAINT = "true".equals(AccessController.doPrivileged( |
|
236 new GetPropertyAction("swing.handleTopLevelPaint", "true"))); |
|
237 GraphicsEnvironment ge = GraphicsEnvironment. |
|
238 getLocalGraphicsEnvironment(); |
|
239 if (ge instanceof SunGraphicsEnvironment) { |
|
240 ((SunGraphicsEnvironment) ge).addDisplayChangedListener( |
|
241 displayChangedHandler); |
|
242 } |
|
243 Toolkit tk = Toolkit.getDefaultToolkit(); |
|
244 if ((tk instanceof SunToolkit) |
|
245 && ((SunToolkit) tk).isSwingBackbufferTranslucencySupported()) { |
|
246 volatileBufferType = Transparency.TRANSLUCENT; |
|
247 } else { |
|
248 volatileBufferType = Transparency.OPAQUE; |
|
249 } |
|
250 } |
|
251 |
|
252 /** |
|
253 * Return the RepaintManager for the calling thread given a Component. |
|
254 * |
|
255 * @param c a Component -- unused in the default implementation, but could |
|
256 * be used by an overridden version to return a different RepaintManager |
|
257 * depending on the Component |
|
258 * @return the RepaintManager object |
|
259 */ |
|
260 public static RepaintManager currentManager(Component c) { |
|
261 // Note: DisplayChangedRunnable passes in null as the component, so if |
|
262 // component is ever used to determine the current |
|
263 // RepaintManager, DisplayChangedRunnable will need to be modified |
|
264 // accordingly. |
|
265 return currentManager(AppContext.getAppContext()); |
|
266 } |
|
267 |
|
268 /** |
|
269 * Returns the RepaintManager for the specified AppContext. If |
|
270 * a RepaintManager has not been created for the specified |
|
271 * AppContext this will return null. |
|
272 */ |
|
273 static RepaintManager currentManager(AppContext appContext) { |
|
274 RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey); |
|
275 if (rm == null) { |
|
276 rm = new RepaintManager(BUFFER_STRATEGY_TYPE); |
|
277 appContext.put(repaintManagerKey, rm); |
|
278 } |
|
279 return rm; |
|
280 } |
|
281 |
|
282 /** |
|
283 * Return the RepaintManager for the calling thread given a JComponent. |
|
284 * <p> |
|
285 * Note: This method exists for backward binary compatibility with earlier |
|
286 * versions of the Swing library. It simply returns the result returned by |
|
287 * {@link #currentManager(Component)}. |
|
288 * |
|
289 * @param c a JComponent -- unused |
|
290 * @return the RepaintManager object |
|
291 */ |
|
292 public static RepaintManager currentManager(JComponent c) { |
|
293 return currentManager((Component)c); |
|
294 } |
|
295 |
|
296 |
|
297 /** |
|
298 * Set the RepaintManager that should be used for the calling |
|
299 * thread. <b>aRepaintManager</b> will become the current RepaintManager |
|
300 * for the calling thread's thread group. |
|
301 * @param aRepaintManager the RepaintManager object to use |
|
302 */ |
|
303 public static void setCurrentManager(RepaintManager aRepaintManager) { |
|
304 if (aRepaintManager != null) { |
|
305 SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager); |
|
306 } else { |
|
307 SwingUtilities.appContextRemove(repaintManagerKey); |
|
308 } |
|
309 } |
|
310 |
|
311 /** |
|
312 * Create a new RepaintManager instance. You rarely call this constructor. |
|
313 * directly. To get the default RepaintManager, use |
|
314 * RepaintManager.currentManager(JComponent) (normally "this"). |
|
315 */ |
|
316 public RepaintManager() { |
|
317 // Because we can't know what a subclass is doing with the |
|
318 // volatile image we immediately punt in subclasses. If this |
|
319 // poses a problem we'll need a more sophisticated detection algorithm, |
|
320 // or API. |
|
321 this(BUFFER_STRATEGY_SPECIFIED_OFF); |
|
322 } |
|
323 |
|
324 private RepaintManager(short bufferStrategyType) { |
|
325 // If native doublebuffering is being used, do NOT use |
|
326 // Swing doublebuffering. |
|
327 doubleBufferingEnabled = !nativeDoubleBuffering; |
|
328 synchronized(this) { |
|
329 dirtyComponents = new IdentityHashMap<Component,Rectangle>(); |
|
330 tmpDirtyComponents = new IdentityHashMap<Component,Rectangle>(); |
|
331 this.bufferStrategyType = bufferStrategyType; |
|
332 hwDirtyComponents = new IdentityHashMap<Container,Rectangle>(); |
|
333 } |
|
334 processingRunnable = new ProcessingRunnable(); |
|
335 } |
|
336 |
|
337 private void displayChanged() { |
|
338 clearImages(); |
|
339 } |
|
340 |
|
341 /** |
|
342 * Mark the component as in need of layout and queue a runnable |
|
343 * for the event dispatching thread that will validate the components |
|
344 * first isValidateRoot() ancestor. |
|
345 * |
|
346 * @param invalidComponent a component |
|
347 * @see JComponent#isValidateRoot |
|
348 * @see #removeInvalidComponent |
|
349 */ |
|
350 public synchronized void addInvalidComponent(JComponent invalidComponent) |
|
351 { |
|
352 RepaintManager delegate = getDelegate(invalidComponent); |
|
353 if (delegate != null) { |
|
354 delegate.addInvalidComponent(invalidComponent); |
|
355 return; |
|
356 } |
|
357 Component validateRoot = |
|
358 SwingUtilities.getValidateRoot(invalidComponent, true); |
|
359 |
|
360 if (validateRoot == null) { |
|
361 return; |
|
362 } |
|
363 |
|
364 /* Lazily create the invalidateComponents vector and add the |
|
365 * validateRoot if it's not there already. If this validateRoot |
|
366 * is already in the vector, we're done. |
|
367 */ |
|
368 if (invalidComponents == null) { |
|
369 invalidComponents = new ArrayList<Component>(); |
|
370 } |
|
371 else { |
|
372 int n = invalidComponents.size(); |
|
373 for(int i = 0; i < n; i++) { |
|
374 if(validateRoot == invalidComponents.get(i)) { |
|
375 return; |
|
376 } |
|
377 } |
|
378 } |
|
379 invalidComponents.add(validateRoot); |
|
380 |
|
381 // Queue a Runnable to invoke paintDirtyRegions and |
|
382 // validateInvalidComponents. |
|
383 scheduleProcessingRunnable(SunToolkit.targetToAppContext(invalidComponent)); |
|
384 } |
|
385 |
|
386 |
|
387 /** |
|
388 * Remove a component from the list of invalid components. |
|
389 * |
|
390 * @param component a component |
|
391 * @see #addInvalidComponent |
|
392 */ |
|
393 public synchronized void removeInvalidComponent(JComponent component) { |
|
394 RepaintManager delegate = getDelegate(component); |
|
395 if (delegate != null) { |
|
396 delegate.removeInvalidComponent(component); |
|
397 return; |
|
398 } |
|
399 if(invalidComponents != null) { |
|
400 int index = invalidComponents.indexOf(component); |
|
401 if(index != -1) { |
|
402 invalidComponents.remove(index); |
|
403 } |
|
404 } |
|
405 } |
|
406 |
|
407 |
|
408 /** |
|
409 * Add a component in the list of components that should be refreshed. |
|
410 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i> |
|
411 * will be unioned with the region that should be redrawn. |
|
412 * |
|
413 * @see JComponent#repaint |
|
414 */ |
|
415 @SuppressWarnings("deprecation") |
|
416 private void addDirtyRegion0(Container c, int x, int y, int w, int h) { |
|
417 /* Special cases we don't have to bother with. |
|
418 */ |
|
419 if ((w <= 0) || (h <= 0) || (c == null)) { |
|
420 return; |
|
421 } |
|
422 |
|
423 if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) { |
|
424 return; |
|
425 } |
|
426 |
|
427 if (extendDirtyRegion(c, x, y, w, h)) { |
|
428 // Component was already marked as dirty, region has been |
|
429 // extended, no need to continue. |
|
430 return; |
|
431 } |
|
432 |
|
433 /* Make sure that c and all it ancestors (up to an Applet or |
|
434 * Window) are visible. This loop has the same effect as |
|
435 * checking c.isShowing() (and note that it's still possible |
|
436 * that c is completely obscured by an opaque ancestor in |
|
437 * the specified rectangle). |
|
438 */ |
|
439 Component root = null; |
|
440 |
|
441 // Note: We can't synchronize around this, Frame.getExtendedState |
|
442 // is synchronized so that if we were to synchronize around this |
|
443 // it could lead to the possibility of getting locks out |
|
444 // of order and deadlocking. |
|
445 for (Container p = c; p != null; p = p.getParent()) { |
|
446 if (!p.isVisible() || !p.isDisplayable()) { |
|
447 return; |
|
448 } |
|
449 if ((p instanceof Window) || (p instanceof Applet)) { |
|
450 // Iconified frames are still visible! |
|
451 if (p instanceof Frame && |
|
452 (((Frame)p).getExtendedState() & Frame.ICONIFIED) == |
|
453 Frame.ICONIFIED) { |
|
454 return; |
|
455 } |
|
456 root = p; |
|
457 break; |
|
458 } |
|
459 } |
|
460 |
|
461 if (root == null) return; |
|
462 |
|
463 synchronized(this) { |
|
464 if (extendDirtyRegion(c, x, y, w, h)) { |
|
465 // In between last check and this check another thread |
|
466 // queued up runnable, can bail here. |
|
467 return; |
|
468 } |
|
469 dirtyComponents.put(c, new Rectangle(x, y, w, h)); |
|
470 } |
|
471 |
|
472 // Queue a Runnable to invoke paintDirtyRegions and |
|
473 // validateInvalidComponents. |
|
474 scheduleProcessingRunnable(SunToolkit.targetToAppContext(c)); |
|
475 } |
|
476 |
|
477 /** |
|
478 * Add a component in the list of components that should be refreshed. |
|
479 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i> |
|
480 * will be unioned with the region that should be redrawn. |
|
481 * |
|
482 * @param c Component to repaint, null results in nothing happening. |
|
483 * @param x X coordinate of the region to repaint |
|
484 * @param y Y coordinate of the region to repaint |
|
485 * @param w Width of the region to repaint |
|
486 * @param h Height of the region to repaint |
|
487 * @see JComponent#repaint |
|
488 */ |
|
489 public void addDirtyRegion(JComponent c, int x, int y, int w, int h) |
|
490 { |
|
491 RepaintManager delegate = getDelegate(c); |
|
492 if (delegate != null) { |
|
493 delegate.addDirtyRegion(c, x, y, w, h); |
|
494 return; |
|
495 } |
|
496 addDirtyRegion0(c, x, y, w, h); |
|
497 } |
|
498 |
|
499 /** |
|
500 * Adds <code>window</code> to the list of <code>Component</code>s that |
|
501 * need to be repainted. |
|
502 * |
|
503 * @param window Window to repaint, null results in nothing happening. |
|
504 * @param x X coordinate of the region to repaint |
|
505 * @param y Y coordinate of the region to repaint |
|
506 * @param w Width of the region to repaint |
|
507 * @param h Height of the region to repaint |
|
508 * @see JFrame#repaint |
|
509 * @see JWindow#repaint |
|
510 * @see JDialog#repaint |
|
511 * @since 1.6 |
|
512 */ |
|
513 public void addDirtyRegion(Window window, int x, int y, int w, int h) { |
|
514 addDirtyRegion0(window, x, y, w, h); |
|
515 } |
|
516 |
|
517 /** |
|
518 * Adds <code>applet</code> to the list of <code>Component</code>s that |
|
519 * need to be repainted. |
|
520 * |
|
521 * @param applet Applet to repaint, null results in nothing happening. |
|
522 * @param x X coordinate of the region to repaint |
|
523 * @param y Y coordinate of the region to repaint |
|
524 * @param w Width of the region to repaint |
|
525 * @param h Height of the region to repaint |
|
526 * @see JApplet#repaint |
|
527 * @since 1.6 |
|
528 * |
|
529 * @deprecated The Applet API is deprecated. See the |
|
530 * <a href="../../java/applet/package-summary.html"> java.applet package |
|
531 * documentation</a> for further information. |
|
532 */ |
|
533 @Deprecated(since = "9") |
|
534 public void addDirtyRegion(Applet applet, int x, int y, int w, int h) { |
|
535 addDirtyRegion0(applet, x, y, w, h); |
|
536 } |
|
537 |
|
538 @SuppressWarnings("deprecation") |
|
539 void scheduleHeavyWeightPaints() { |
|
540 Map<Container,Rectangle> hws; |
|
541 |
|
542 synchronized(this) { |
|
543 if (hwDirtyComponents.size() == 0) { |
|
544 return; |
|
545 } |
|
546 hws = hwDirtyComponents; |
|
547 hwDirtyComponents = new IdentityHashMap<Container,Rectangle>(); |
|
548 } |
|
549 for (Container hw : hws.keySet()) { |
|
550 Rectangle dirty = hws.get(hw); |
|
551 if (hw instanceof Window) { |
|
552 addDirtyRegion((Window)hw, dirty.x, dirty.y, |
|
553 dirty.width, dirty.height); |
|
554 } |
|
555 else if (hw instanceof Applet) { |
|
556 addDirtyRegion((Applet)hw, dirty.x, dirty.y, |
|
557 dirty.width, dirty.height); |
|
558 } |
|
559 else { // SwingHeavyWeight |
|
560 addDirtyRegion0(hw, dirty.x, dirty.y, |
|
561 dirty.width, dirty.height); |
|
562 } |
|
563 } |
|
564 } |
|
565 |
|
566 // |
|
567 // This is called from the toolkit thread when a native expose is |
|
568 // received. |
|
569 // |
|
570 void nativeAddDirtyRegion(AppContext appContext, Container c, |
|
571 int x, int y, int w, int h) { |
|
572 if (w > 0 && h > 0) { |
|
573 synchronized(this) { |
|
574 Rectangle dirty = hwDirtyComponents.get(c); |
|
575 if (dirty == null) { |
|
576 hwDirtyComponents.put(c, new Rectangle(x, y, w, h)); |
|
577 } |
|
578 else { |
|
579 hwDirtyComponents.put(c, SwingUtilities.computeUnion( |
|
580 x, y, w, h, dirty)); |
|
581 } |
|
582 } |
|
583 scheduleProcessingRunnable(appContext); |
|
584 } |
|
585 } |
|
586 |
|
587 // |
|
588 // This is called from the toolkit thread when awt needs to run a |
|
589 // Runnable before we paint. |
|
590 // |
|
591 void nativeQueueSurfaceDataRunnable(AppContext appContext, |
|
592 final Component c, final Runnable r) |
|
593 { |
|
594 synchronized(this) { |
|
595 if (runnableList == null) { |
|
596 runnableList = new LinkedList<Runnable>(); |
|
597 } |
|
598 runnableList.add(new Runnable() { |
|
599 public void run() { |
|
600 AccessControlContext stack = AccessController.getContext(); |
|
601 AccessControlContext acc = |
|
602 AWTAccessor.getComponentAccessor().getAccessControlContext(c); |
|
603 javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Void>() { |
|
604 public Void run() { |
|
605 r.run(); |
|
606 return null; |
|
607 } |
|
608 }, stack, acc); |
|
609 } |
|
610 }); |
|
611 } |
|
612 scheduleProcessingRunnable(appContext); |
|
613 } |
|
614 |
|
615 /** |
|
616 * Extends the dirty region for the specified component to include |
|
617 * the new region. |
|
618 * |
|
619 * @return false if <code>c</code> is not yet marked dirty. |
|
620 */ |
|
621 private synchronized boolean extendDirtyRegion( |
|
622 Component c, int x, int y, int w, int h) { |
|
623 Rectangle r = dirtyComponents.get(c); |
|
624 if (r != null) { |
|
625 // A non-null r implies c is already marked as dirty, |
|
626 // and that the parent is valid. Therefore we can |
|
627 // just union the rect and bail. |
|
628 SwingUtilities.computeUnion(x, y, w, h, r); |
|
629 return true; |
|
630 } |
|
631 return false; |
|
632 } |
|
633 |
|
634 /** |
|
635 * Return the current dirty region for a component. |
|
636 * Return an empty rectangle if the component is not |
|
637 * dirty. |
|
638 * |
|
639 * @param aComponent a component |
|
640 * @return the region |
|
641 */ |
|
642 public Rectangle getDirtyRegion(JComponent aComponent) { |
|
643 RepaintManager delegate = getDelegate(aComponent); |
|
644 if (delegate != null) { |
|
645 return delegate.getDirtyRegion(aComponent); |
|
646 } |
|
647 Rectangle r; |
|
648 synchronized(this) { |
|
649 r = dirtyComponents.get(aComponent); |
|
650 } |
|
651 if(r == null) |
|
652 return new Rectangle(0,0,0,0); |
|
653 else |
|
654 return new Rectangle(r); |
|
655 } |
|
656 |
|
657 /** |
|
658 * Mark a component completely dirty. <b>aComponent</b> will be |
|
659 * completely painted during the next paintDirtyRegions() call. |
|
660 * |
|
661 * @param aComponent a component |
|
662 */ |
|
663 public void markCompletelyDirty(JComponent aComponent) { |
|
664 RepaintManager delegate = getDelegate(aComponent); |
|
665 if (delegate != null) { |
|
666 delegate.markCompletelyDirty(aComponent); |
|
667 return; |
|
668 } |
|
669 addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE); |
|
670 } |
|
671 |
|
672 /** |
|
673 * Mark a component completely clean. <b>aComponent</b> will not |
|
674 * get painted during the next paintDirtyRegions() call. |
|
675 * |
|
676 * @param aComponent a component |
|
677 */ |
|
678 public void markCompletelyClean(JComponent aComponent) { |
|
679 RepaintManager delegate = getDelegate(aComponent); |
|
680 if (delegate != null) { |
|
681 delegate.markCompletelyClean(aComponent); |
|
682 return; |
|
683 } |
|
684 synchronized(this) { |
|
685 dirtyComponents.remove(aComponent); |
|
686 } |
|
687 } |
|
688 |
|
689 /** |
|
690 * Convenience method that returns true if <b>aComponent</b> will be completely |
|
691 * painted during the next paintDirtyRegions(). If computing dirty regions is |
|
692 * expensive for your component, use this method and avoid computing dirty region |
|
693 * if it return true. |
|
694 * |
|
695 * @param aComponent a component |
|
696 * @return {@code true} if <b>aComponent</b> will be completely |
|
697 * painted during the next paintDirtyRegions(). |
|
698 */ |
|
699 public boolean isCompletelyDirty(JComponent aComponent) { |
|
700 RepaintManager delegate = getDelegate(aComponent); |
|
701 if (delegate != null) { |
|
702 return delegate.isCompletelyDirty(aComponent); |
|
703 } |
|
704 Rectangle r; |
|
705 |
|
706 r = getDirtyRegion(aComponent); |
|
707 if(r.width == Integer.MAX_VALUE && |
|
708 r.height == Integer.MAX_VALUE) |
|
709 return true; |
|
710 else |
|
711 return false; |
|
712 } |
|
713 |
|
714 |
|
715 /** |
|
716 * Validate all of the components that have been marked invalid. |
|
717 * @see #addInvalidComponent |
|
718 */ |
|
719 public void validateInvalidComponents() { |
|
720 final java.util.List<Component> ic; |
|
721 synchronized(this) { |
|
722 if (invalidComponents == null) { |
|
723 return; |
|
724 } |
|
725 ic = invalidComponents; |
|
726 invalidComponents = null; |
|
727 } |
|
728 int n = ic.size(); |
|
729 for(int i = 0; i < n; i++) { |
|
730 final Component c = ic.get(i); |
|
731 AccessControlContext stack = AccessController.getContext(); |
|
732 AccessControlContext acc = |
|
733 AWTAccessor.getComponentAccessor().getAccessControlContext(c); |
|
734 javaSecurityAccess.doIntersectionPrivilege( |
|
735 new PrivilegedAction<Void>() { |
|
736 public Void run() { |
|
737 c.validate(); |
|
738 return null; |
|
739 } |
|
740 }, stack, acc); |
|
741 } |
|
742 } |
|
743 |
|
744 |
|
745 /** |
|
746 * This is invoked to process paint requests. It's needed |
|
747 * for backward compatibility in so far as RepaintManager would previously |
|
748 * not see paint requests for top levels, so, we have to make sure |
|
749 * a subclass correctly paints any dirty top levels. |
|
750 */ |
|
751 private void prePaintDirtyRegions() { |
|
752 Map<Component,Rectangle> dirtyComponents; |
|
753 java.util.List<Runnable> runnableList; |
|
754 synchronized(this) { |
|
755 dirtyComponents = this.dirtyComponents; |
|
756 runnableList = this.runnableList; |
|
757 this.runnableList = null; |
|
758 } |
|
759 if (runnableList != null) { |
|
760 for (Runnable runnable : runnableList) { |
|
761 runnable.run(); |
|
762 } |
|
763 } |
|
764 paintDirtyRegions(); |
|
765 if (dirtyComponents.size() > 0) { |
|
766 // This'll only happen if a subclass isn't correctly dealing |
|
767 // with toplevels. |
|
768 paintDirtyRegions(dirtyComponents); |
|
769 } |
|
770 } |
|
771 |
|
772 private void updateWindows(Map<Component,Rectangle> dirtyComponents) { |
|
773 Toolkit toolkit = Toolkit.getDefaultToolkit(); |
|
774 if (!(toolkit instanceof SunToolkit && |
|
775 ((SunToolkit)toolkit).needUpdateWindow())) |
|
776 { |
|
777 return; |
|
778 } |
|
779 |
|
780 Set<Window> windows = new HashSet<Window>(); |
|
781 Set<Component> dirtyComps = dirtyComponents.keySet(); |
|
782 for (Iterator<Component> it = dirtyComps.iterator(); it.hasNext();) { |
|
783 Component dirty = it.next(); |
|
784 Window window = dirty instanceof Window ? |
|
785 (Window)dirty : |
|
786 SwingUtilities.getWindowAncestor(dirty); |
|
787 if (window != null && |
|
788 !window.isOpaque()) |
|
789 { |
|
790 windows.add(window); |
|
791 } |
|
792 } |
|
793 |
|
794 for (Window window : windows) { |
|
795 AWTAccessor.getWindowAccessor().updateWindow(window); |
|
796 } |
|
797 } |
|
798 |
|
799 boolean isPainting() { |
|
800 return painting; |
|
801 } |
|
802 |
|
803 /** |
|
804 * Paint all of the components that have been marked dirty. |
|
805 * |
|
806 * @see #addDirtyRegion |
|
807 */ |
|
808 public void paintDirtyRegions() { |
|
809 synchronized(this) { // swap for thread safety |
|
810 Map<Component,Rectangle> tmp = tmpDirtyComponents; |
|
811 tmpDirtyComponents = dirtyComponents; |
|
812 dirtyComponents = tmp; |
|
813 dirtyComponents.clear(); |
|
814 } |
|
815 paintDirtyRegions(tmpDirtyComponents); |
|
816 } |
|
817 |
|
818 private void paintDirtyRegions( |
|
819 final Map<Component,Rectangle> tmpDirtyComponents) |
|
820 { |
|
821 if (tmpDirtyComponents.isEmpty()) { |
|
822 return; |
|
823 } |
|
824 |
|
825 final java.util.List<Component> roots = |
|
826 new ArrayList<Component>(tmpDirtyComponents.size()); |
|
827 for (Component dirty : tmpDirtyComponents.keySet()) { |
|
828 collectDirtyComponents(tmpDirtyComponents, dirty, roots); |
|
829 } |
|
830 |
|
831 final AtomicInteger count = new AtomicInteger(roots.size()); |
|
832 painting = true; |
|
833 try { |
|
834 for (int j=0 ; j < count.get(); j++) { |
|
835 final int i = j; |
|
836 final Component dirtyComponent = roots.get(j); |
|
837 AccessControlContext stack = AccessController.getContext(); |
|
838 AccessControlContext acc = |
|
839 AWTAccessor.getComponentAccessor().getAccessControlContext(dirtyComponent); |
|
840 javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Void>() { |
|
841 public Void run() { |
|
842 Rectangle rect = tmpDirtyComponents.get(dirtyComponent); |
|
843 // Sometimes when RepaintManager is changed during the painting |
|
844 // we may get null here, see #6995769 for details |
|
845 if (rect == null) { |
|
846 return null; |
|
847 } |
|
848 |
|
849 int localBoundsH = dirtyComponent.getHeight(); |
|
850 int localBoundsW = dirtyComponent.getWidth(); |
|
851 SwingUtilities.computeIntersection(0, |
|
852 0, |
|
853 localBoundsW, |
|
854 localBoundsH, |
|
855 rect); |
|
856 if (dirtyComponent instanceof JComponent) { |
|
857 ((JComponent)dirtyComponent).paintImmediately( |
|
858 rect.x,rect.y,rect.width, rect.height); |
|
859 } |
|
860 else if (dirtyComponent.isShowing()) { |
|
861 Graphics g = JComponent.safelyGetGraphics( |
|
862 dirtyComponent, dirtyComponent); |
|
863 // If the Graphics goes away, it means someone disposed of |
|
864 // the window, don't do anything. |
|
865 if (g != null) { |
|
866 g.setClip(rect.x, rect.y, rect.width, rect.height); |
|
867 try { |
|
868 dirtyComponent.paint(g); |
|
869 } finally { |
|
870 g.dispose(); |
|
871 } |
|
872 } |
|
873 } |
|
874 // If the repaintRoot has been set, service it now and |
|
875 // remove any components that are children of repaintRoot. |
|
876 if (repaintRoot != null) { |
|
877 adjustRoots(repaintRoot, roots, i + 1); |
|
878 count.set(roots.size()); |
|
879 paintManager.isRepaintingRoot = true; |
|
880 repaintRoot.paintImmediately(0, 0, repaintRoot.getWidth(), |
|
881 repaintRoot.getHeight()); |
|
882 paintManager.isRepaintingRoot = false; |
|
883 // Only service repaintRoot once. |
|
884 repaintRoot = null; |
|
885 } |
|
886 |
|
887 return null; |
|
888 } |
|
889 }, stack, acc); |
|
890 } |
|
891 } finally { |
|
892 painting = false; |
|
893 } |
|
894 |
|
895 updateWindows(tmpDirtyComponents); |
|
896 |
|
897 tmpDirtyComponents.clear(); |
|
898 } |
|
899 |
|
900 |
|
901 /** |
|
902 * Removes any components from roots that are children of |
|
903 * root. |
|
904 */ |
|
905 private void adjustRoots(JComponent root, |
|
906 java.util.List<Component> roots, int index) { |
|
907 for (int i = roots.size() - 1; i >= index; i--) { |
|
908 Component c = roots.get(i); |
|
909 for(;;) { |
|
910 if (c == root || c == null || !(c instanceof JComponent)) { |
|
911 break; |
|
912 } |
|
913 c = c.getParent(); |
|
914 } |
|
915 if (c == root) { |
|
916 roots.remove(i); |
|
917 } |
|
918 } |
|
919 } |
|
920 |
|
921 Rectangle tmp = new Rectangle(); |
|
922 |
|
923 void collectDirtyComponents(Map<Component,Rectangle> dirtyComponents, |
|
924 Component dirtyComponent, |
|
925 java.util.List<Component> roots) { |
|
926 int dx, dy, rootDx, rootDy; |
|
927 Component component, rootDirtyComponent,parent; |
|
928 Rectangle cBounds; |
|
929 |
|
930 // Find the highest parent which is dirty. When we get out of this |
|
931 // rootDx and rootDy will contain the translation from the |
|
932 // rootDirtyComponent's coordinate system to the coordinates of the |
|
933 // original dirty component. The tmp Rect is also used to compute the |
|
934 // visible portion of the dirtyRect. |
|
935 |
|
936 component = rootDirtyComponent = dirtyComponent; |
|
937 |
|
938 int x = dirtyComponent.getX(); |
|
939 int y = dirtyComponent.getY(); |
|
940 int w = dirtyComponent.getWidth(); |
|
941 int h = dirtyComponent.getHeight(); |
|
942 |
|
943 dx = rootDx = 0; |
|
944 dy = rootDy = 0; |
|
945 tmp.setBounds(dirtyComponents.get(dirtyComponent)); |
|
946 |
|
947 // System.out.println("Collect dirty component for bound " + tmp + |
|
948 // "component bounds is " + cBounds);; |
|
949 SwingUtilities.computeIntersection(0,0,w,h,tmp); |
|
950 |
|
951 if (tmp.isEmpty()) { |
|
952 // System.out.println("Empty 1"); |
|
953 return; |
|
954 } |
|
955 |
|
956 for(;;) { |
|
957 if(!(component instanceof JComponent)) |
|
958 break; |
|
959 |
|
960 parent = component.getParent(); |
|
961 if(parent == null) |
|
962 break; |
|
963 |
|
964 component = parent; |
|
965 |
|
966 dx += x; |
|
967 dy += y; |
|
968 tmp.setLocation(tmp.x + x, tmp.y + y); |
|
969 |
|
970 x = component.getX(); |
|
971 y = component.getY(); |
|
972 w = component.getWidth(); |
|
973 h = component.getHeight(); |
|
974 tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp); |
|
975 |
|
976 if (tmp.isEmpty()) { |
|
977 // System.out.println("Empty 2"); |
|
978 return; |
|
979 } |
|
980 |
|
981 if (dirtyComponents.get(component) != null) { |
|
982 rootDirtyComponent = component; |
|
983 rootDx = dx; |
|
984 rootDy = dy; |
|
985 } |
|
986 } |
|
987 |
|
988 if (dirtyComponent != rootDirtyComponent) { |
|
989 Rectangle r; |
|
990 tmp.setLocation(tmp.x + rootDx - dx, |
|
991 tmp.y + rootDy - dy); |
|
992 r = dirtyComponents.get(rootDirtyComponent); |
|
993 SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r); |
|
994 } |
|
995 |
|
996 // If we haven't seen this root before, then we need to add it to the |
|
997 // list of root dirty Views. |
|
998 |
|
999 if (!roots.contains(rootDirtyComponent)) |
|
1000 roots.add(rootDirtyComponent); |
|
1001 } |
|
1002 |
|
1003 |
|
1004 /** |
|
1005 * Returns a string that displays and identifies this |
|
1006 * object's properties. |
|
1007 * |
|
1008 * @return a String representation of this object |
|
1009 */ |
|
1010 public synchronized String toString() { |
|
1011 StringBuilder sb = new StringBuilder(); |
|
1012 if(dirtyComponents != null) |
|
1013 sb.append("" + dirtyComponents); |
|
1014 return sb.toString(); |
|
1015 } |
|
1016 |
|
1017 |
|
1018 /** |
|
1019 * Return the offscreen buffer that should be used as a double buffer with |
|
1020 * the component <code>c</code>. |
|
1021 * By default there is a double buffer per RepaintManager. |
|
1022 * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code> |
|
1023 * This happens when the maximum double buffer size as been set for the receiving |
|
1024 * repaint manager. |
|
1025 * |
|
1026 * @param c the component |
|
1027 * @param proposedWidth the width of the buffer |
|
1028 * @param proposedHeight the height of the buffer |
|
1029 * |
|
1030 * @return the image |
|
1031 */ |
|
1032 public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) { |
|
1033 RepaintManager delegate = getDelegate(c); |
|
1034 if (delegate != null) { |
|
1035 return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight); |
|
1036 } |
|
1037 return _getOffscreenBuffer(c, proposedWidth, proposedHeight); |
|
1038 } |
|
1039 |
|
1040 /** |
|
1041 * Return a volatile offscreen buffer that should be used as a |
|
1042 * double buffer with the specified component <code>c</code>. |
|
1043 * The image returned will be an instance of VolatileImage, or null |
|
1044 * if a VolatileImage object could not be instantiated. |
|
1045 * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>. |
|
1046 * This happens when the maximum double buffer size has been set for this |
|
1047 * repaint manager. |
|
1048 * |
|
1049 * @param c the component |
|
1050 * @param proposedWidth the width of the buffer |
|
1051 * @param proposedHeight the height of the buffer |
|
1052 * |
|
1053 * @return the volatile image |
|
1054 * @see java.awt.image.VolatileImage |
|
1055 * @since 1.4 |
|
1056 */ |
|
1057 public Image getVolatileOffscreenBuffer(Component c, |
|
1058 int proposedWidth,int proposedHeight) { |
|
1059 RepaintManager delegate = getDelegate(c); |
|
1060 if (delegate != null) { |
|
1061 return delegate.getVolatileOffscreenBuffer(c, proposedWidth, |
|
1062 proposedHeight); |
|
1063 } |
|
1064 |
|
1065 // If the window is non-opaque, it's double-buffered at peer's level |
|
1066 Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c); |
|
1067 if (!w.isOpaque()) { |
|
1068 Toolkit tk = Toolkit.getDefaultToolkit(); |
|
1069 if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) { |
|
1070 return null; |
|
1071 } |
|
1072 } |
|
1073 |
|
1074 GraphicsConfiguration config = c.getGraphicsConfiguration(); |
|
1075 if (config == null) { |
|
1076 config = GraphicsEnvironment.getLocalGraphicsEnvironment(). |
|
1077 getDefaultScreenDevice().getDefaultConfiguration(); |
|
1078 } |
|
1079 Dimension maxSize = getDoubleBufferMaximumSize(); |
|
1080 int width = proposedWidth < 1 ? 1 : |
|
1081 (proposedWidth > maxSize.width? maxSize.width : proposedWidth); |
|
1082 int height = proposedHeight < 1 ? 1 : |
|
1083 (proposedHeight > maxSize.height? maxSize.height : proposedHeight); |
|
1084 VolatileImage image = volatileMap.get(config); |
|
1085 if (image == null || image.getWidth() < width || |
|
1086 image.getHeight() < height) { |
|
1087 if (image != null) { |
|
1088 image.flush(); |
|
1089 } |
|
1090 image = config.createCompatibleVolatileImage(width, height, |
|
1091 volatileBufferType); |
|
1092 volatileMap.put(config, image); |
|
1093 } |
|
1094 return image; |
|
1095 } |
|
1096 |
|
1097 private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) { |
|
1098 Dimension maxSize = getDoubleBufferMaximumSize(); |
|
1099 DoubleBufferInfo doubleBuffer; |
|
1100 int width, height; |
|
1101 |
|
1102 // If the window is non-opaque, it's double-buffered at peer's level |
|
1103 Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c); |
|
1104 if (!w.isOpaque()) { |
|
1105 Toolkit tk = Toolkit.getDefaultToolkit(); |
|
1106 if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) { |
|
1107 return null; |
|
1108 } |
|
1109 } |
|
1110 |
|
1111 if (standardDoubleBuffer == null) { |
|
1112 standardDoubleBuffer = new DoubleBufferInfo(); |
|
1113 } |
|
1114 doubleBuffer = standardDoubleBuffer; |
|
1115 |
|
1116 width = proposedWidth < 1? 1 : |
|
1117 (proposedWidth > maxSize.width? maxSize.width : proposedWidth); |
|
1118 height = proposedHeight < 1? 1 : |
|
1119 (proposedHeight > maxSize.height? maxSize.height : proposedHeight); |
|
1120 |
|
1121 if (doubleBuffer.needsReset || (doubleBuffer.image != null && |
|
1122 (doubleBuffer.size.width < width || |
|
1123 doubleBuffer.size.height < height))) { |
|
1124 doubleBuffer.needsReset = false; |
|
1125 if (doubleBuffer.image != null) { |
|
1126 doubleBuffer.image.flush(); |
|
1127 doubleBuffer.image = null; |
|
1128 } |
|
1129 width = Math.max(doubleBuffer.size.width, width); |
|
1130 height = Math.max(doubleBuffer.size.height, height); |
|
1131 } |
|
1132 |
|
1133 Image result = doubleBuffer.image; |
|
1134 |
|
1135 if (doubleBuffer.image == null) { |
|
1136 result = c.createImage(width , height); |
|
1137 doubleBuffer.size = new Dimension(width, height); |
|
1138 if (c instanceof JComponent) { |
|
1139 ((JComponent)c).setCreatedDoubleBuffer(true); |
|
1140 doubleBuffer.image = result; |
|
1141 } |
|
1142 // JComponent will inform us when it is no longer valid |
|
1143 // (via removeNotify) we have no such hook to other components, |
|
1144 // therefore we don't keep a ref to the Component |
|
1145 // (indirectly through the Image) by stashing the image. |
|
1146 } |
|
1147 return result; |
|
1148 } |
|
1149 |
|
1150 |
|
1151 /** |
|
1152 * Set the maximum double buffer size. |
|
1153 * |
|
1154 * @param d the dimension |
|
1155 */ |
|
1156 public void setDoubleBufferMaximumSize(Dimension d) { |
|
1157 doubleBufferMaxSize = d; |
|
1158 if (doubleBufferMaxSize == null) { |
|
1159 clearImages(); |
|
1160 } else { |
|
1161 clearImages(d.width, d.height); |
|
1162 } |
|
1163 } |
|
1164 |
|
1165 private void clearImages() { |
|
1166 clearImages(0, 0); |
|
1167 } |
|
1168 |
|
1169 private void clearImages(int width, int height) { |
|
1170 if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) { |
|
1171 if (standardDoubleBuffer.image.getWidth(null) > width || |
|
1172 standardDoubleBuffer.image.getHeight(null) > height) { |
|
1173 standardDoubleBuffer.image.flush(); |
|
1174 standardDoubleBuffer.image = null; |
|
1175 } |
|
1176 } |
|
1177 // Clear out the VolatileImages |
|
1178 Iterator<GraphicsConfiguration> gcs = volatileMap.keySet().iterator(); |
|
1179 while (gcs.hasNext()) { |
|
1180 GraphicsConfiguration gc = gcs.next(); |
|
1181 VolatileImage image = volatileMap.get(gc); |
|
1182 if (image.getWidth() > width || image.getHeight() > height) { |
|
1183 image.flush(); |
|
1184 gcs.remove(); |
|
1185 } |
|
1186 } |
|
1187 } |
|
1188 |
|
1189 /** |
|
1190 * Returns the maximum double buffer size. |
|
1191 * |
|
1192 * @return a Dimension object representing the maximum size |
|
1193 */ |
|
1194 public Dimension getDoubleBufferMaximumSize() { |
|
1195 if (doubleBufferMaxSize == null) { |
|
1196 try { |
|
1197 Rectangle virtualBounds = new Rectangle(); |
|
1198 GraphicsEnvironment ge = GraphicsEnvironment. |
|
1199 getLocalGraphicsEnvironment(); |
|
1200 for (GraphicsDevice gd : ge.getScreenDevices()) { |
|
1201 GraphicsConfiguration gc = gd.getDefaultConfiguration(); |
|
1202 virtualBounds = virtualBounds.union(gc.getBounds()); |
|
1203 } |
|
1204 doubleBufferMaxSize = new Dimension(virtualBounds.width, |
|
1205 virtualBounds.height); |
|
1206 } catch (HeadlessException e) { |
|
1207 doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); |
|
1208 } |
|
1209 } |
|
1210 return doubleBufferMaxSize; |
|
1211 } |
|
1212 |
|
1213 /** |
|
1214 * Enables or disables double buffering in this RepaintManager. |
|
1215 * CAUTION: The default value for this property is set for optimal |
|
1216 * paint performance on the given platform and it is not recommended |
|
1217 * that programs modify this property directly. |
|
1218 * |
|
1219 * @param aFlag true to activate double buffering |
|
1220 * @see #isDoubleBufferingEnabled |
|
1221 */ |
|
1222 public void setDoubleBufferingEnabled(boolean aFlag) { |
|
1223 doubleBufferingEnabled = aFlag; |
|
1224 PaintManager paintManager = getPaintManager(); |
|
1225 if (!aFlag && paintManager.getClass() != PaintManager.class) { |
|
1226 setPaintManager(new PaintManager()); |
|
1227 } |
|
1228 } |
|
1229 |
|
1230 /** |
|
1231 * Returns true if this RepaintManager is double buffered. |
|
1232 * The default value for this property may vary from platform |
|
1233 * to platform. On platforms where native double buffering |
|
1234 * is supported in the AWT, the default value will be <code>false</code> |
|
1235 * to avoid unnecessary buffering in Swing. |
|
1236 * On platforms where native double buffering is not supported, |
|
1237 * the default value will be <code>true</code>. |
|
1238 * |
|
1239 * @return true if this object is double buffered |
|
1240 */ |
|
1241 public boolean isDoubleBufferingEnabled() { |
|
1242 return doubleBufferingEnabled; |
|
1243 } |
|
1244 |
|
1245 /** |
|
1246 * This resets the double buffer. Actually, it marks the double buffer |
|
1247 * as invalid, the double buffer will then be recreated on the next |
|
1248 * invocation of getOffscreenBuffer. |
|
1249 */ |
|
1250 void resetDoubleBuffer() { |
|
1251 if (standardDoubleBuffer != null) { |
|
1252 standardDoubleBuffer.needsReset = true; |
|
1253 } |
|
1254 } |
|
1255 |
|
1256 /** |
|
1257 * This resets the volatile double buffer. |
|
1258 */ |
|
1259 void resetVolatileDoubleBuffer(GraphicsConfiguration gc) { |
|
1260 Image image = volatileMap.remove(gc); |
|
1261 if (image != null) { |
|
1262 image.flush(); |
|
1263 } |
|
1264 } |
|
1265 |
|
1266 /** |
|
1267 * Returns true if we should use the <code>Image</code> returned |
|
1268 * from <code>getVolatileOffscreenBuffer</code> to do double buffering. |
|
1269 */ |
|
1270 boolean useVolatileDoubleBuffer() { |
|
1271 return volatileImageBufferEnabled; |
|
1272 } |
|
1273 |
|
1274 /** |
|
1275 * Returns true if the current thread is the thread painting. This |
|
1276 * will return false if no threads are painting. |
|
1277 */ |
|
1278 private synchronized boolean isPaintingThread() { |
|
1279 return (Thread.currentThread() == paintThread); |
|
1280 } |
|
1281 // |
|
1282 // Paint methods. You very, VERY rarely need to invoke these. |
|
1283 // They are invoked directly from JComponent's painting code and |
|
1284 // when painting happens outside the normal flow: DefaultDesktopManager |
|
1285 // and JViewport. If you end up needing these methods in other places be |
|
1286 // careful that you don't get stuck in a paint loop. |
|
1287 // |
|
1288 |
|
1289 /** |
|
1290 * Paints a region of a component |
|
1291 * |
|
1292 * @param paintingComponent Component to paint |
|
1293 * @param bufferComponent Component to obtain buffer for |
|
1294 * @param g Graphics to paint to |
|
1295 * @param x X-coordinate |
|
1296 * @param y Y-coordinate |
|
1297 * @param w Width |
|
1298 * @param h Height |
|
1299 */ |
|
1300 void paint(JComponent paintingComponent, |
|
1301 JComponent bufferComponent, Graphics g, |
|
1302 int x, int y, int w, int h) { |
|
1303 PaintManager paintManager = getPaintManager(); |
|
1304 if (!isPaintingThread()) { |
|
1305 // We're painting to two threads at once. PaintManager deals |
|
1306 // with this a bit better than BufferStrategyPaintManager, use |
|
1307 // it to avoid possible exceptions/corruption. |
|
1308 if (paintManager.getClass() != PaintManager.class) { |
|
1309 paintManager = new PaintManager(); |
|
1310 paintManager.repaintManager = this; |
|
1311 } |
|
1312 } |
|
1313 if (!paintManager.paint(paintingComponent, bufferComponent, g, |
|
1314 x, y, w, h)) { |
|
1315 g.setClip(x, y, w, h); |
|
1316 paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h); |
|
1317 } |
|
1318 } |
|
1319 |
|
1320 /** |
|
1321 * Does a copy area on the specified region. |
|
1322 * |
|
1323 * @param clip Whether or not the copyArea needs to be clipped to the |
|
1324 * Component's bounds. |
|
1325 */ |
|
1326 void copyArea(JComponent c, Graphics g, int x, int y, int w, int h, |
|
1327 int deltaX, int deltaY, boolean clip) { |
|
1328 getPaintManager().copyArea(c, g, x, y, w, h, deltaX, deltaY, clip); |
|
1329 } |
|
1330 |
|
1331 private java.util.List<RepaintListener> repaintListeners = new ArrayList<>(1); |
|
1332 |
|
1333 private void addRepaintListener(RepaintListener l) { |
|
1334 repaintListeners.add(l); |
|
1335 } |
|
1336 |
|
1337 private void removeRepaintListener(RepaintListener l) { |
|
1338 repaintListeners.remove(l); |
|
1339 } |
|
1340 |
|
1341 /** |
|
1342 * Notify the attached repaint listeners that an area of the {@code c} component |
|
1343 * has been immediately repainted, that is without scheduling a repaint runnable, |
|
1344 * due to performing a "blit" (via calling the {@code copyArea} method). |
|
1345 * |
|
1346 * @param c the component |
|
1347 * @param x the x coordinate of the area |
|
1348 * @param y the y coordinate of the area |
|
1349 * @param w the width of the area |
|
1350 * @param h the height of the area |
|
1351 */ |
|
1352 void notifyRepaintPerformed(JComponent c, int x, int y, int w, int h) { |
|
1353 for (RepaintListener l : repaintListeners) { |
|
1354 l.repaintPerformed(c, x, y, w, h); |
|
1355 } |
|
1356 } |
|
1357 |
|
1358 /** |
|
1359 * Invoked prior to any paint/copyArea method calls. This will |
|
1360 * be followed by an invocation of <code>endPaint</code>. |
|
1361 * <b>WARNING</b>: Callers of this method need to wrap the call |
|
1362 * in a <code>try/finally</code>, otherwise if an exception is thrown |
|
1363 * during the course of painting the RepaintManager may |
|
1364 * be left in a state in which the screen is not updated, eg: |
|
1365 * <pre> |
|
1366 * repaintManager.beginPaint(); |
|
1367 * try { |
|
1368 * repaintManager.paint(...); |
|
1369 * } finally { |
|
1370 * repaintManager.endPaint(); |
|
1371 * } |
|
1372 * </pre> |
|
1373 */ |
|
1374 void beginPaint() { |
|
1375 boolean multiThreadedPaint = false; |
|
1376 int paintDepth; |
|
1377 Thread currentThread = Thread.currentThread(); |
|
1378 synchronized(this) { |
|
1379 paintDepth = this.paintDepth; |
|
1380 if (paintThread == null || currentThread == paintThread) { |
|
1381 paintThread = currentThread; |
|
1382 this.paintDepth++; |
|
1383 } else { |
|
1384 multiThreadedPaint = true; |
|
1385 } |
|
1386 } |
|
1387 if (!multiThreadedPaint && paintDepth == 0) { |
|
1388 getPaintManager().beginPaint(); |
|
1389 } |
|
1390 } |
|
1391 |
|
1392 /** |
|
1393 * Invoked after <code>beginPaint</code> has been invoked. |
|
1394 */ |
|
1395 void endPaint() { |
|
1396 if (isPaintingThread()) { |
|
1397 PaintManager paintManager = null; |
|
1398 synchronized(this) { |
|
1399 if (--paintDepth == 0) { |
|
1400 paintManager = getPaintManager(); |
|
1401 } |
|
1402 } |
|
1403 if (paintManager != null) { |
|
1404 paintManager.endPaint(); |
|
1405 synchronized(this) { |
|
1406 paintThread = null; |
|
1407 } |
|
1408 } |
|
1409 } |
|
1410 } |
|
1411 |
|
1412 /** |
|
1413 * If possible this will show a previously rendered portion of |
|
1414 * a Component. If successful, this will return true, otherwise false. |
|
1415 * <p> |
|
1416 * WARNING: This method is invoked from the native toolkit thread, be |
|
1417 * very careful as to what methods this invokes! |
|
1418 */ |
|
1419 boolean show(Container c, int x, int y, int w, int h) { |
|
1420 return getPaintManager().show(c, x, y, w, h); |
|
1421 } |
|
1422 |
|
1423 /** |
|
1424 * Invoked when the doubleBuffered or useTrueDoubleBuffering |
|
1425 * properties of a JRootPane change. This may come in on any thread. |
|
1426 */ |
|
1427 void doubleBufferingChanged(JRootPane rootPane) { |
|
1428 getPaintManager().doubleBufferingChanged(rootPane); |
|
1429 } |
|
1430 |
|
1431 /** |
|
1432 * Sets the <code>PaintManager</code> that is used to handle all |
|
1433 * double buffered painting. |
|
1434 * |
|
1435 * @param paintManager The PaintManager to use. Passing in null indicates |
|
1436 * the fallback PaintManager should be used. |
|
1437 */ |
|
1438 void setPaintManager(PaintManager paintManager) { |
|
1439 if (paintManager == null) { |
|
1440 paintManager = new PaintManager(); |
|
1441 } |
|
1442 PaintManager oldPaintManager; |
|
1443 synchronized(this) { |
|
1444 oldPaintManager = this.paintManager; |
|
1445 this.paintManager = paintManager; |
|
1446 paintManager.repaintManager = this; |
|
1447 } |
|
1448 if (oldPaintManager != null) { |
|
1449 oldPaintManager.dispose(); |
|
1450 } |
|
1451 } |
|
1452 |
|
1453 private synchronized PaintManager getPaintManager() { |
|
1454 if (paintManager == null) { |
|
1455 PaintManager paintManager = null; |
|
1456 if (doubleBufferingEnabled && !nativeDoubleBuffering) { |
|
1457 switch (bufferStrategyType) { |
|
1458 case BUFFER_STRATEGY_NOT_SPECIFIED: |
|
1459 Toolkit tk = Toolkit.getDefaultToolkit(); |
|
1460 if (tk instanceof SunToolkit) { |
|
1461 SunToolkit stk = (SunToolkit) tk; |
|
1462 if (stk.useBufferPerWindow()) { |
|
1463 paintManager = new BufferStrategyPaintManager(); |
|
1464 } |
|
1465 } |
|
1466 break; |
|
1467 case BUFFER_STRATEGY_SPECIFIED_ON: |
|
1468 paintManager = new BufferStrategyPaintManager(); |
|
1469 break; |
|
1470 default: |
|
1471 break; |
|
1472 } |
|
1473 } |
|
1474 // null case handled in setPaintManager |
|
1475 setPaintManager(paintManager); |
|
1476 } |
|
1477 return paintManager; |
|
1478 } |
|
1479 |
|
1480 private void scheduleProcessingRunnable(AppContext context) { |
|
1481 if (processingRunnable.markPending()) { |
|
1482 Toolkit tk = Toolkit.getDefaultToolkit(); |
|
1483 if (tk instanceof SunToolkit) { |
|
1484 SunToolkit.getSystemEventQueueImplPP(context). |
|
1485 postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), |
|
1486 processingRunnable)); |
|
1487 } else { |
|
1488 Toolkit.getDefaultToolkit().getSystemEventQueue(). |
|
1489 postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(), |
|
1490 processingRunnable)); |
|
1491 } |
|
1492 } |
|
1493 } |
|
1494 |
|
1495 |
|
1496 /** |
|
1497 * PaintManager is used to handle all double buffered painting for |
|
1498 * Swing. Subclasses should call back into the JComponent method |
|
1499 * <code>paintToOffscreen</code> to handle the actual painting. |
|
1500 */ |
|
1501 static class PaintManager { |
|
1502 /** |
|
1503 * RepaintManager the PaintManager has been installed on. |
|
1504 */ |
|
1505 protected RepaintManager repaintManager; |
|
1506 boolean isRepaintingRoot; |
|
1507 |
|
1508 /** |
|
1509 * Paints a region of a component |
|
1510 * |
|
1511 * @param paintingComponent Component to paint |
|
1512 * @param bufferComponent Component to obtain buffer for |
|
1513 * @param g Graphics to paint to |
|
1514 * @param x X-coordinate |
|
1515 * @param y Y-coordinate |
|
1516 * @param w Width |
|
1517 * @param h Height |
|
1518 * @return true if painting was successful. |
|
1519 */ |
|
1520 public boolean paint(JComponent paintingComponent, |
|
1521 JComponent bufferComponent, Graphics g, |
|
1522 int x, int y, int w, int h) { |
|
1523 // First attempt to use VolatileImage buffer for performance. |
|
1524 // If this fails (which should rarely occur), fallback to a |
|
1525 // standard Image buffer. |
|
1526 boolean paintCompleted = false; |
|
1527 Image offscreen; |
|
1528 int sw = w + 1; |
|
1529 int sh = h + 1; |
|
1530 |
|
1531 if (repaintManager.useVolatileDoubleBuffer() && |
|
1532 (offscreen = getValidImage(repaintManager. |
|
1533 getVolatileOffscreenBuffer(bufferComponent, sw, sh))) != null) { |
|
1534 VolatileImage vImage = (java.awt.image.VolatileImage)offscreen; |
|
1535 GraphicsConfiguration gc = bufferComponent. |
|
1536 getGraphicsConfiguration(); |
|
1537 for (int i = 0; !paintCompleted && |
|
1538 i < RepaintManager.VOLATILE_LOOP_MAX; i++) { |
|
1539 if (vImage.validate(gc) == |
|
1540 VolatileImage.IMAGE_INCOMPATIBLE) { |
|
1541 repaintManager.resetVolatileDoubleBuffer(gc); |
|
1542 offscreen = repaintManager.getVolatileOffscreenBuffer( |
|
1543 bufferComponent, sw, sh); |
|
1544 vImage = (java.awt.image.VolatileImage)offscreen; |
|
1545 } |
|
1546 paintDoubleBuffered(paintingComponent, vImage, g, x, y, |
|
1547 w, h); |
|
1548 paintCompleted = !vImage.contentsLost(); |
|
1549 } |
|
1550 } |
|
1551 // VolatileImage painting loop failed, fallback to regular |
|
1552 // offscreen buffer |
|
1553 if (!paintCompleted && (offscreen = getValidImage( |
|
1554 repaintManager.getOffscreenBuffer( |
|
1555 bufferComponent, w, h))) != null) { |
|
1556 paintDoubleBuffered(paintingComponent, offscreen, g, x, y, w, |
|
1557 h); |
|
1558 paintCompleted = true; |
|
1559 } |
|
1560 return paintCompleted; |
|
1561 } |
|
1562 |
|
1563 /** |
|
1564 * Does a copy area on the specified region. |
|
1565 */ |
|
1566 public void copyArea(JComponent c, Graphics g, int x, int y, int w, |
|
1567 int h, int deltaX, int deltaY, boolean clip) { |
|
1568 g.copyArea(x, y, w, h, deltaX, deltaY); |
|
1569 } |
|
1570 |
|
1571 /** |
|
1572 * Invoked prior to any calls to paint or copyArea. |
|
1573 */ |
|
1574 public void beginPaint() { |
|
1575 } |
|
1576 |
|
1577 /** |
|
1578 * Invoked to indicate painting has been completed. |
|
1579 */ |
|
1580 public void endPaint() { |
|
1581 } |
|
1582 |
|
1583 /** |
|
1584 * Shows a region of a previously rendered component. This |
|
1585 * will return true if successful, false otherwise. The default |
|
1586 * implementation returns false. |
|
1587 */ |
|
1588 public boolean show(Container c, int x, int y, int w, int h) { |
|
1589 return false; |
|
1590 } |
|
1591 |
|
1592 /** |
|
1593 * Invoked when the doubleBuffered or useTrueDoubleBuffering |
|
1594 * properties of a JRootPane change. This may come in on any thread. |
|
1595 */ |
|
1596 public void doubleBufferingChanged(JRootPane rootPane) { |
|
1597 } |
|
1598 |
|
1599 /** |
|
1600 * Paints a portion of a component to an offscreen buffer. |
|
1601 */ |
|
1602 protected void paintDoubleBuffered(JComponent c, Image image, |
|
1603 Graphics g, int clipX, int clipY, |
|
1604 int clipW, int clipH) { |
|
1605 if (image instanceof VolatileImage && isPixelsCopying(c, g)) { |
|
1606 paintDoubleBufferedFPScales(c, image, g, clipX, clipY, clipW, clipH); |
|
1607 } else { |
|
1608 paintDoubleBufferedImpl(c, image, g, clipX, clipY, clipW, clipH); |
|
1609 } |
|
1610 } |
|
1611 |
|
1612 private void paintDoubleBufferedImpl(JComponent c, Image image, |
|
1613 Graphics g, int clipX, int clipY, |
|
1614 int clipW, int clipH) { |
|
1615 Graphics osg = image.getGraphics(); |
|
1616 int bw = Math.min(clipW, image.getWidth(null)); |
|
1617 int bh = Math.min(clipH, image.getHeight(null)); |
|
1618 int x,y,maxx,maxy; |
|
1619 |
|
1620 try { |
|
1621 for(x = clipX, maxx = clipX+clipW; x < maxx ; x += bw ) { |
|
1622 for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) { |
|
1623 osg.translate(-x, -y); |
|
1624 osg.setClip(x,y,bw,bh); |
|
1625 if (volatileBufferType != Transparency.OPAQUE |
|
1626 && osg instanceof Graphics2D) { |
|
1627 final Graphics2D g2d = (Graphics2D) osg; |
|
1628 final Color oldBg = g2d.getBackground(); |
|
1629 g2d.setBackground(c.getBackground()); |
|
1630 g2d.clearRect(x, y, bw, bh); |
|
1631 g2d.setBackground(oldBg); |
|
1632 } |
|
1633 c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy); |
|
1634 g.setClip(x, y, bw, bh); |
|
1635 if (volatileBufferType != Transparency.OPAQUE |
|
1636 && g instanceof Graphics2D) { |
|
1637 final Graphics2D g2d = (Graphics2D) g; |
|
1638 final Composite oldComposite = g2d.getComposite(); |
|
1639 g2d.setComposite(AlphaComposite.Src); |
|
1640 g2d.drawImage(image, x, y, c); |
|
1641 g2d.setComposite(oldComposite); |
|
1642 } else { |
|
1643 g.drawImage(image, x, y, c); |
|
1644 } |
|
1645 osg.translate(x, y); |
|
1646 } |
|
1647 } |
|
1648 } finally { |
|
1649 osg.dispose(); |
|
1650 } |
|
1651 } |
|
1652 |
|
1653 private void paintDoubleBufferedFPScales(JComponent c, Image image, |
|
1654 Graphics g, int clipX, int clipY, |
|
1655 int clipW, int clipH) { |
|
1656 Graphics osg = image.getGraphics(); |
|
1657 Graphics2D g2d = (Graphics2D) g; |
|
1658 Graphics2D osg2d = (Graphics2D) osg; |
|
1659 |
|
1660 AffineTransform identity = new AffineTransform(); |
|
1661 int bw = Math.min(clipW, image.getWidth(null)); |
|
1662 int bh = Math.min(clipH, image.getHeight(null)); |
|
1663 int x, y, maxx, maxy; |
|
1664 |
|
1665 AffineTransform tx = g2d.getTransform(); |
|
1666 double scaleX = tx.getScaleX(); |
|
1667 double scaleY = tx.getScaleY(); |
|
1668 double trX = tx.getTranslateX(); |
|
1669 double trY = tx.getTranslateY(); |
|
1670 |
|
1671 boolean translucent = volatileBufferType != Transparency.OPAQUE; |
|
1672 Composite oldComposite = g2d.getComposite(); |
|
1673 |
|
1674 try { |
|
1675 for (x = clipX, maxx = clipX + clipW; x < maxx; x += bw) { |
|
1676 for (y = clipY, maxy = clipY + clipH; y < maxy; y += bh) { |
|
1677 |
|
1678 // draw x, y, bw, bh |
|
1679 int pixelx1 = Region.clipRound(x * scaleX + trX); |
|
1680 int pixely1 = Region.clipRound(y * scaleY + trY); |
|
1681 int pixelx2 = Region.clipRound((x + bw) * scaleX + trX); |
|
1682 int pixely2 = Region.clipRound((y + bh) * scaleY + trY); |
|
1683 int pixelw = pixelx2 - pixelx1; |
|
1684 int pixelh = pixely2 - pixely1; |
|
1685 |
|
1686 osg2d.setTransform(identity); |
|
1687 if (translucent) { |
|
1688 final Color oldBg = g2d.getBackground(); |
|
1689 g2d.setBackground(c.getBackground()); |
|
1690 g2d.clearRect(pixelx1, pixely1, pixelw, pixelh); |
|
1691 g2d.setBackground(oldBg); |
|
1692 } |
|
1693 |
|
1694 osg2d.setClip(0, 0, pixelw, pixelh); |
|
1695 osg2d.translate(trX - pixelx1, trY - pixely1); |
|
1696 osg2d.scale(scaleX, scaleY); |
|
1697 c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy); |
|
1698 |
|
1699 g2d.setTransform(identity); |
|
1700 g2d.setClip(pixelx1, pixely1, pixelw, pixelh); |
|
1701 AffineTransform stx = new AffineTransform(); |
|
1702 stx.translate(pixelx1, pixely1); |
|
1703 stx.scale(scaleX, scaleY); |
|
1704 g2d.setTransform(stx); |
|
1705 |
|
1706 if (translucent) { |
|
1707 g2d.setComposite(AlphaComposite.Src); |
|
1708 } |
|
1709 |
|
1710 g2d.drawImage(image, 0, 0, c); |
|
1711 |
|
1712 if (translucent) { |
|
1713 g2d.setComposite(oldComposite); |
|
1714 } |
|
1715 g2d.setTransform(tx); |
|
1716 } |
|
1717 } |
|
1718 } finally { |
|
1719 osg.dispose(); |
|
1720 } |
|
1721 } |
|
1722 |
|
1723 /** |
|
1724 * If <code>image</code> is non-null with a positive size it |
|
1725 * is returned, otherwise null is returned. |
|
1726 */ |
|
1727 private Image getValidImage(Image image) { |
|
1728 if (image != null && image.getWidth(null) > 0 && |
|
1729 image.getHeight(null) > 0) { |
|
1730 return image; |
|
1731 } |
|
1732 return null; |
|
1733 } |
|
1734 |
|
1735 /** |
|
1736 * Schedules a repaint for the specified component. This differs |
|
1737 * from <code>root.repaint</code> in that if the RepaintManager is |
|
1738 * currently processing paint requests it'll process this request |
|
1739 * with the current set of requests. |
|
1740 */ |
|
1741 protected void repaintRoot(JComponent root) { |
|
1742 assert (repaintManager.repaintRoot == null); |
|
1743 if (repaintManager.painting) { |
|
1744 repaintManager.repaintRoot = root; |
|
1745 } |
|
1746 else { |
|
1747 root.repaint(); |
|
1748 } |
|
1749 } |
|
1750 |
|
1751 /** |
|
1752 * Returns true if the component being painted is the root component |
|
1753 * that was previously passed to <code>repaintRoot</code>. |
|
1754 */ |
|
1755 protected boolean isRepaintingRoot() { |
|
1756 return isRepaintingRoot; |
|
1757 } |
|
1758 |
|
1759 /** |
|
1760 * Cleans up any state. After invoked the PaintManager will no |
|
1761 * longer be used anymore. |
|
1762 */ |
|
1763 protected void dispose() { |
|
1764 } |
|
1765 |
|
1766 private boolean isPixelsCopying(JComponent c, Graphics g) { |
|
1767 |
|
1768 AffineTransform tx = getTransform(g); |
|
1769 GraphicsConfiguration gc = c.getGraphicsConfiguration(); |
|
1770 |
|
1771 if (tx == null || gc == null |
|
1772 || !SwingUtilities2.isFloatingPointScale(tx)) { |
|
1773 return false; |
|
1774 } |
|
1775 |
|
1776 AffineTransform gcTx = gc.getDefaultTransform(); |
|
1777 |
|
1778 return gcTx.getScaleX() == tx.getScaleX() |
|
1779 && gcTx.getScaleY() == tx.getScaleY(); |
|
1780 } |
|
1781 |
|
1782 private static AffineTransform getTransform(Graphics g) { |
|
1783 if (g instanceof SunGraphics2D) { |
|
1784 return ((SunGraphics2D) g).transform; |
|
1785 } else if (g instanceof Graphics2D) { |
|
1786 return ((Graphics2D) g).getTransform(); |
|
1787 } |
|
1788 return null; |
|
1789 } |
|
1790 } |
|
1791 |
|
1792 private class DoubleBufferInfo { |
|
1793 public Image image; |
|
1794 public Dimension size; |
|
1795 public boolean needsReset = false; |
|
1796 } |
|
1797 |
|
1798 |
|
1799 /** |
|
1800 * Listener installed to detect display changes. When display changes, |
|
1801 * schedules a callback to notify all RepaintManagers of the display |
|
1802 * changes. Only one DisplayChangedHandler is ever installed. The |
|
1803 * singleton instance will schedule notification for all AppContexts. |
|
1804 */ |
|
1805 private static final class DisplayChangedHandler implements |
|
1806 DisplayChangedListener { |
|
1807 // Empty non private constructor was added because access to this |
|
1808 // class shouldn't be generated by the compiler using synthetic |
|
1809 // accessor method |
|
1810 DisplayChangedHandler() { |
|
1811 } |
|
1812 |
|
1813 public void displayChanged() { |
|
1814 scheduleDisplayChanges(); |
|
1815 } |
|
1816 |
|
1817 public void paletteChanged() { |
|
1818 } |
|
1819 |
|
1820 private static void scheduleDisplayChanges() { |
|
1821 // To avoid threading problems, we notify each RepaintManager |
|
1822 // on the thread it was created on. |
|
1823 for (AppContext context : AppContext.getAppContexts()) { |
|
1824 synchronized(context) { |
|
1825 if (!context.isDisposed()) { |
|
1826 EventQueue eventQueue = (EventQueue)context.get( |
|
1827 AppContext.EVENT_QUEUE_KEY); |
|
1828 if (eventQueue != null) { |
|
1829 eventQueue.postEvent(new InvocationEvent( |
|
1830 Toolkit.getDefaultToolkit(), |
|
1831 new DisplayChangedRunnable())); |
|
1832 } |
|
1833 } |
|
1834 } |
|
1835 } |
|
1836 } |
|
1837 } |
|
1838 |
|
1839 |
|
1840 private static final class DisplayChangedRunnable implements Runnable { |
|
1841 public void run() { |
|
1842 RepaintManager.currentManager((JComponent)null).displayChanged(); |
|
1843 } |
|
1844 } |
|
1845 |
|
1846 |
|
1847 /** |
|
1848 * Runnable used to process all repaint/revalidate requests. |
|
1849 */ |
|
1850 private final class ProcessingRunnable implements Runnable { |
|
1851 // If true, we're wainting on the EventQueue. |
|
1852 private boolean pending; |
|
1853 |
|
1854 /** |
|
1855 * Marks this processing runnable as pending. If this was not |
|
1856 * already marked as pending, true is returned. |
|
1857 */ |
|
1858 public synchronized boolean markPending() { |
|
1859 if (!pending) { |
|
1860 pending = true; |
|
1861 return true; |
|
1862 } |
|
1863 return false; |
|
1864 } |
|
1865 |
|
1866 public void run() { |
|
1867 synchronized (this) { |
|
1868 pending = false; |
|
1869 } |
|
1870 // First pass, flush any heavy paint events into real paint |
|
1871 // events. If there are pending heavy weight requests this will |
|
1872 // result in q'ing this request up one more time. As |
|
1873 // long as no other requests come in between now and the time |
|
1874 // the second one is processed nothing will happen. This is not |
|
1875 // ideal, but the logic needed to suppress the second request is |
|
1876 // more headache than it's worth. |
|
1877 scheduleHeavyWeightPaints(); |
|
1878 // Do the actual validation and painting. |
|
1879 validateInvalidComponents(); |
|
1880 prePaintDirtyRegions(); |
|
1881 } |
|
1882 } |
|
1883 private RepaintManager getDelegate(Component c) { |
|
1884 RepaintManager delegate = SwingUtilities3.getDelegateRepaintManager(c); |
|
1885 if (this == delegate) { |
|
1886 delegate = null; |
|
1887 } |
|
1888 return delegate; |
|
1889 } |
|
1890 } |