/*
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.swing;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.
VolatileImage;
import java.security.
AccessControlContext;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.*;
import java.util.concurrent.atomic.
AtomicInteger;
import java.applet.*;
import sun.awt.
AWTAccessor;
import sun.awt.
AppContext;
import sun.awt.
DisplayChangedListener;
import sun.awt.
SunToolkit;
import sun.java2d.
SunGraphicsEnvironment;
import sun.misc.
JavaSecurityAccess;
import sun.misc.
SharedSecrets;
import sun.security.action.
GetPropertyAction;
import com.sun.java.swing.
SwingUtilities3;
import sun.swing.
SwingAccessor;
import sun.swing.
SwingUtilities2.
RepaintListener;
/**
* This class manages repaint requests, allowing the number
* of repaints to be minimized, for example by collapsing multiple
* requests into a single repaint for members of a component tree.
* <p>
* As of 1.6 <code>RepaintManager</code> handles repaint requests
* for Swing's top level components (<code>JApplet</code>,
* <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>).
* Any calls to <code>repaint</code> on one of these will call into the
* appropriate <code>addDirtyRegion</code> method.
*
* @author Arnaud Weber
*/
public class
RepaintManager
{
/**
* Whether or not the RepaintManager should handle paint requests
* for top levels.
*/
static final boolean
HANDLE_TOP_LEVEL_PAINT;
private static final short
BUFFER_STRATEGY_NOT_SPECIFIED = 0;
private static final short
BUFFER_STRATEGY_SPECIFIED_ON = 1;
private static final short
BUFFER_STRATEGY_SPECIFIED_OFF = 2;
private static final short
BUFFER_STRATEGY_TYPE;
/**
* Maps from GraphicsConfiguration to VolatileImage.
*/
private
Map<
GraphicsConfiguration,
VolatileImage>
volatileMap = new
HashMap<
GraphicsConfiguration,
VolatileImage>(1);
//
// As of 1.6 Swing handles scheduling of paint events from native code.
// That is, SwingPaintEventDispatcher is invoked on the toolkit thread,
// which in turn invokes nativeAddDirtyRegion. Because this is invoked
// from the native thread we can not invoke any public methods and so
// we introduce these added maps. So, any time nativeAddDirtyRegion is
// invoked the region is added to hwDirtyComponents and a work request
// is scheduled. When the work request is processed all entries in
// this map are pushed to the real map (dirtyComponents) and then
// painted with the rest of the components.
//
private
Map<
Container,
Rectangle>
hwDirtyComponents;
private
Map<
Component,
Rectangle>
dirtyComponents;
private
Map<
Component,
Rectangle>
tmpDirtyComponents;
private java.util.
List<
Component>
invalidComponents;
// List of Runnables that need to be processed before painting from AWT.
private java.util.
List<
Runnable>
runnableList;
boolean
doubleBufferingEnabled = true;
private
Dimension doubleBufferMaxSize;
// Support for both the standard and volatile offscreen buffers exists to
// provide backwards compatibility for the [rare] programs which may be
// calling getOffScreenBuffer() and not expecting to get a VolatileImage.
// Swing internally is migrating to use *only* the volatile image buffer.
// Support for standard offscreen buffer
//
DoubleBufferInfo standardDoubleBuffer;
/**
* Object responsible for hanlding core paint functionality.
*/
private
PaintManager paintManager;
private static final
Object repaintManagerKey =
RepaintManager.class;
// Whether or not a VolatileImage should be used for double-buffered painting
static boolean
volatileImageBufferEnabled = true;
/**
* Type of VolatileImage which should be used for double-buffered
* painting.
*/
private static final int
volatileBufferType;
/**
* Value of the system property awt.nativeDoubleBuffering.
*/
private static boolean
nativeDoubleBuffering;
// The maximum number of times Swing will attempt to use the VolatileImage
// buffer during a paint operation.
private static final int
VOLATILE_LOOP_MAX = 2;
/**
* Number of <code>beginPaint</code> that have been invoked.
*/
private int
paintDepth = 0;
/**
* Type of buffer strategy to use. Will be one of the BUFFER_STRATEGY_
* constants.
*/
private short
bufferStrategyType;
//
// BufferStrategyPaintManager has the unique characteristic that it
// must deal with the buffer being lost while painting to it. For
// example, if we paint a component and show it and the buffer has
// become lost we must repaint the whole window. To deal with that
// the PaintManager calls into repaintRoot, and if we're still in
// the process of painting the repaintRoot field is set to the JRootPane
// and after the current JComponent.paintImmediately call finishes
// paintImmediately will be invoked on the repaintRoot. In this
// way we don't try to show garbage to the screen.
//
/**
* True if we're in the process of painting the dirty regions. This is
* set to true in <code>paintDirtyRegions</code>.
*/
private boolean
painting;
/**
* If the PaintManager calls into repaintRoot during painting this field
* will be set to the root.
*/
private
JComponent repaintRoot;
/**
* The Thread that has initiated painting. If null it
* indicates painting is not currently in progress.
*/
private
Thread paintThread;
/**
* Runnable used to process all repaint/revalidate requests.
*/
private final
ProcessingRunnable processingRunnable;
private static final
JavaSecurityAccess javaSecurityAccess =
SharedSecrets.
getJavaSecurityAccess();
/**
* Listener installed to detect display changes. When display changes,
* schedules a callback to notify all RepaintManagers of the display
* changes.
*/
private static final
DisplayChangedListener displayChangedHandler =
new
DisplayChangedHandler();
static {
SwingAccessor.
setRepaintManagerAccessor(new
SwingAccessor.
RepaintManagerAccessor() {
@
Override
public void
addRepaintListener(
RepaintManager rm,
RepaintListener l) {
rm.
addRepaintListener(
l);
}
@
Override
public void
removeRepaintListener(
RepaintManager rm,
RepaintListener l) {
rm.
removeRepaintListener(
l);
}
});
volatileImageBufferEnabled = "true".
equals(
AccessController.
doPrivileged(new
GetPropertyAction(
"swing.volatileImageBufferEnabled", "true")));
boolean
headless =
GraphicsEnvironment.
isHeadless();
if (
volatileImageBufferEnabled &&
headless) {
volatileImageBufferEnabled = false;
}
nativeDoubleBuffering = "true".
equals(
AccessController.
doPrivileged(
new
GetPropertyAction("awt.nativeDoubleBuffering")));
String bs =
AccessController.
doPrivileged(
new
GetPropertyAction("swing.bufferPerWindow"));
if (
headless) {
BUFFER_STRATEGY_TYPE =
BUFFER_STRATEGY_SPECIFIED_OFF;
}
else if (
bs == null) {
BUFFER_STRATEGY_TYPE =
BUFFER_STRATEGY_NOT_SPECIFIED;
}
else if ("true".
equals(
bs)) {
BUFFER_STRATEGY_TYPE =
BUFFER_STRATEGY_SPECIFIED_ON;
}
else {
BUFFER_STRATEGY_TYPE =
BUFFER_STRATEGY_SPECIFIED_OFF;
}
HANDLE_TOP_LEVEL_PAINT = "true".
equals(
AccessController.
doPrivileged(
new
GetPropertyAction("swing.handleTopLevelPaint", "true")));
GraphicsEnvironment ge =
GraphicsEnvironment.
getLocalGraphicsEnvironment();
if (
ge instanceof
SunGraphicsEnvironment) {
((
SunGraphicsEnvironment)
ge).
addDisplayChangedListener(
displayChangedHandler);
}
Toolkit tk =
Toolkit.
getDefaultToolkit();
if ((
tk instanceof
SunToolkit)
&& ((
SunToolkit)
tk).
isSwingBackbufferTranslucencySupported()) {
volatileBufferType =
Transparency.
TRANSLUCENT;
} else {
volatileBufferType =
Transparency.
OPAQUE;
}
}
/**
* Return the RepaintManager for the calling thread given a Component.
*
* @param c a Component -- unused in the default implementation, but could
* be used by an overridden version to return a different RepaintManager
* depending on the Component
* @return the RepaintManager object
*/
public static
RepaintManager currentManager(
Component c) {
// Note: DisplayChangedRunnable passes in null as the component, so if
// component is ever used to determine the current
// RepaintManager, DisplayChangedRunnable will need to be modified
// accordingly.
return
currentManager(
AppContext.
getAppContext());
}
/**
* Returns the RepaintManager for the specified AppContext. If
* a RepaintManager has not been created for the specified
* AppContext this will return null.
*/
static
RepaintManager currentManager(
AppContext appContext) {
RepaintManager rm = (
RepaintManager)
appContext.
get(
repaintManagerKey);
if (
rm == null) {
rm = new
RepaintManager(
BUFFER_STRATEGY_TYPE);
appContext.
put(
repaintManagerKey,
rm);
}
return
rm;
}
/**
* Return the RepaintManager for the calling thread given a JComponent.
* <p>
* Note: This method exists for backward binary compatibility with earlier
* versions of the Swing library. It simply returns the result returned by
* {@link #currentManager(Component)}.
*
* @param c a JComponent -- unused
* @return the RepaintManager object
*/
public static
RepaintManager currentManager(
JComponent c) {
return
currentManager((
Component)
c);
}
/**
* Set the RepaintManager that should be used for the calling
* thread. <b>aRepaintManager</b> will become the current RepaintManager
* for the calling thread's thread group.
* @param aRepaintManager the RepaintManager object to use
*/
public static void
setCurrentManager(
RepaintManager aRepaintManager) {
if (
aRepaintManager != null) {
SwingUtilities.
appContextPut(
repaintManagerKey,
aRepaintManager);
} else {
SwingUtilities.
appContextRemove(
repaintManagerKey);
}
}
/**
* Create a new RepaintManager instance. You rarely call this constructor.
* directly. To get the default RepaintManager, use
* RepaintManager.currentManager(JComponent) (normally "this").
*/
public
RepaintManager() {
// Because we can't know what a subclass is doing with the
// volatile image we immediately punt in subclasses. If this
// poses a problem we'll need a more sophisticated detection algorithm,
// or API.
this(
BUFFER_STRATEGY_SPECIFIED_OFF);
}
private
RepaintManager(short
bufferStrategyType) {
// If native doublebuffering is being used, do NOT use
// Swing doublebuffering.
doubleBufferingEnabled = !
nativeDoubleBuffering;
synchronized(this) {
dirtyComponents = new
IdentityHashMap<
Component,
Rectangle>();
tmpDirtyComponents = new
IdentityHashMap<
Component,
Rectangle>();
this.
bufferStrategyType =
bufferStrategyType;
hwDirtyComponents = new
IdentityHashMap<
Container,
Rectangle>();
}
processingRunnable = new
ProcessingRunnable();
}
private void
displayChanged() {
clearImages();
}
/**
* Mark the component as in need of layout and queue a runnable
* for the event dispatching thread that will validate the components
* first isValidateRoot() ancestor.
*
* @see JComponent#isValidateRoot
* @see #removeInvalidComponent
*/
public synchronized void
addInvalidComponent(
JComponent invalidComponent)
{
RepaintManager delegate =
getDelegate(
invalidComponent);
if (
delegate != null) {
delegate.
addInvalidComponent(
invalidComponent);
return;
}
Component validateRoot =
SwingUtilities.
getValidateRoot(
invalidComponent, true);
if (
validateRoot == null) {
return;
}
/* Lazily create the invalidateComponents vector and add the
* validateRoot if it's not there already. If this validateRoot
* is already in the vector, we're done.
*/
if (
invalidComponents == null) {
invalidComponents = new
ArrayList<
Component>();
}
else {
int
n =
invalidComponents.
size();
for(int
i = 0;
i <
n;
i++) {
if(
validateRoot ==
invalidComponents.
get(
i)) {
return;
}
}
}
invalidComponents.
add(
validateRoot);
// Queue a Runnable to invoke paintDirtyRegions and
// validateInvalidComponents.
scheduleProcessingRunnable(
SunToolkit.
targetToAppContext(
invalidComponent));
}
/**
* Remove a component from the list of invalid components.
*
* @see #addInvalidComponent
*/
public synchronized void
removeInvalidComponent(
JComponent component) {
RepaintManager delegate =
getDelegate(
component);
if (
delegate != null) {
delegate.
removeInvalidComponent(
component);
return;
}
if(
invalidComponents != null) {
int
index =
invalidComponents.
indexOf(
component);
if(
index != -1) {
invalidComponents.
remove(
index);
}
}
}
/**
* Add a component in the list of components that should be refreshed.
* If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
* will be unioned with the region that should be redrawn.
*
* @see JComponent#repaint
*/
private void
addDirtyRegion0(
Container c, int
x, int
y, int
w, int
h) {
/* Special cases we don't have to bother with.
*/
if ((
w <= 0) || (
h <= 0) || (
c == null)) {
return;
}
if ((
c.
getWidth() <= 0) || (
c.
getHeight() <= 0)) {
return;
}
if (
extendDirtyRegion(
c,
x,
y,
w,
h)) {
// Component was already marked as dirty, region has been
// extended, no need to continue.
return;
}
/* Make sure that c and all it ancestors (up to an Applet or
* Window) are visible. This loop has the same effect as
* checking c.isShowing() (and note that it's still possible
* that c is completely obscured by an opaque ancestor in
* the specified rectangle).
*/
Component root = null;
// Note: We can't synchronize around this, Frame.getExtendedState
// is synchronized so that if we were to synchronize around this
// it could lead to the possibility of getting locks out
// of order and deadlocking.
for (
Container p =
c;
p != null;
p =
p.
getParent()) {
if (!
p.
isVisible() || (
p.
getPeer() == null)) {
return;
}
if ((
p instanceof
Window) || (
p instanceof
Applet)) {
// Iconified frames are still visible!
if (
p instanceof
Frame &&
(((
Frame)
p).
getExtendedState() &
Frame.
ICONIFIED) ==
Frame.
ICONIFIED) {
return;
}
root =
p;
break;
}
}
if (
root == null) return;
synchronized(this) {
if (
extendDirtyRegion(
c,
x,
y,
w,
h)) {
// In between last check and this check another thread
// queued up runnable, can bail here.
return;
}
dirtyComponents.
put(
c, new
Rectangle(
x,
y,
w,
h));
}
// Queue a Runnable to invoke paintDirtyRegions and
// validateInvalidComponents.
scheduleProcessingRunnable(
SunToolkit.
targetToAppContext(
c));
}
/**
* Add a component in the list of components that should be refreshed.
* If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
* will be unioned with the region that should be redrawn.
*
* @param c Component to repaint, null results in nothing happening.
* @param x X coordinate of the region to repaint
* @param y Y coordinate of the region to repaint
* @param w Width of the region to repaint
* @param h Height of the region to repaint
* @see JComponent#repaint
*/
public void
addDirtyRegion(
JComponent c, int
x, int
y, int
w, int
h)
{
RepaintManager delegate =
getDelegate(
c);
if (
delegate != null) {
delegate.
addDirtyRegion(
c,
x,
y,
w,
h);
return;
}
addDirtyRegion0(
c,
x,
y,
w,
h);
}
/**
* Adds <code>window</code> to the list of <code>Component</code>s that
* need to be repainted.
*
* @param window Window to repaint, null results in nothing happening.
* @param x X coordinate of the region to repaint
* @param y Y coordinate of the region to repaint
* @param w Width of the region to repaint
* @param h Height of the region to repaint
* @see JFrame#repaint
* @see JWindow#repaint
* @see JDialog#repaint
* @since 1.6
*/
public void
addDirtyRegion(
Window window, int
x, int
y, int
w, int
h) {
addDirtyRegion0(
window,
x,
y,
w,
h);
}
/**
* Adds <code>applet</code> to the list of <code>Component</code>s that
* need to be repainted.
*
* @param applet Applet to repaint, null results in nothing happening.
* @param x X coordinate of the region to repaint
* @param y Y coordinate of the region to repaint
* @param w Width of the region to repaint
* @param h Height of the region to repaint
* @see JApplet#repaint
* @since 1.6
*/
public void
addDirtyRegion(
Applet applet, int
x, int
y, int
w, int
h) {
addDirtyRegion0(
applet,
x,
y,
w,
h);
}
void
scheduleHeavyWeightPaints() {
Map<
Container,
Rectangle>
hws;
synchronized(this) {
if (
hwDirtyComponents.
size() == 0) {
return;
}
hws =
hwDirtyComponents;
hwDirtyComponents = new
IdentityHashMap<
Container,
Rectangle>();
}
for (
Container hw :
hws.
keySet()) {
Rectangle dirty =
hws.
get(
hw);
if (
hw instanceof
Window) {
addDirtyRegion((
Window)
hw,
dirty.
x,
dirty.
y,
dirty.
width,
dirty.
height);
}
else if (
hw instanceof
Applet) {
addDirtyRegion((
Applet)
hw,
dirty.
x,
dirty.
y,
dirty.
width,
dirty.
height);
}
else { // SwingHeavyWeight
addDirtyRegion0(
hw,
dirty.
x,
dirty.
y,
dirty.
width,
dirty.
height);
}
}
}
//
// This is called from the toolkit thread when a native expose is
// received.
//
void
nativeAddDirtyRegion(
AppContext appContext,
Container c,
int
x, int
y, int
w, int
h) {
if (
w > 0 &&
h > 0) {
synchronized(this) {
Rectangle dirty =
hwDirtyComponents.
get(
c);
if (
dirty == null) {
hwDirtyComponents.
put(
c, new
Rectangle(
x,
y,
w,
h));
}
else {
hwDirtyComponents.
put(
c,
SwingUtilities.
computeUnion(
x,
y,
w,
h,
dirty));
}
}
scheduleProcessingRunnable(
appContext);
}
}
//
// This is called from the toolkit thread when awt needs to run a
// Runnable before we paint.
//
void
nativeQueueSurfaceDataRunnable(
AppContext appContext,
final
Component c, final
Runnable r)
{
synchronized(this) {
if (
runnableList == null) {
runnableList = new
LinkedList<
Runnable>();
}
runnableList.
add(new
Runnable() {
public void
run() {
AccessControlContext stack =
AccessController.
getContext();
AccessControlContext acc =
AWTAccessor.
getComponentAccessor().
getAccessControlContext(
c);
javaSecurityAccess.
doIntersectionPrivilege(new
PrivilegedAction<
Void>() {
public
Void run() {
r.
run();
return null;
}
},
stack,
acc);
}
});
}
scheduleProcessingRunnable(
appContext);
}
/**
* Extends the dirty region for the specified component to include
* the new region.
*
* @return false if <code>c</code> is not yet marked dirty.
*/
private synchronized boolean
extendDirtyRegion(
Component c, int
x, int
y, int
w, int
h) {
Rectangle r =
dirtyComponents.
get(
c);
if (
r != null) {
// A non-null r implies c is already marked as dirty,
// and that the parent is valid. Therefore we can
// just union the rect and bail.
SwingUtilities.
computeUnion(
x,
y,
w,
h,
r);
return true;
}
return false;
}
/** Return the current dirty region for a component.
* Return an empty rectangle if the component is not
* dirty.
*/
public
Rectangle getDirtyRegion(
JComponent aComponent) {
RepaintManager delegate =
getDelegate(
aComponent);
if (
delegate != null) {
return
delegate.
getDirtyRegion(
aComponent);
}
Rectangle r;
synchronized(this) {
r =
dirtyComponents.
get(
aComponent);
}
if(
r == null)
return new
Rectangle(0,0,0,0);
else
return new
Rectangle(
r);
}
/**
* Mark a component completely dirty. <b>aComponent</b> will be
* completely painted during the next paintDirtyRegions() call.
*/
public void
markCompletelyDirty(
JComponent aComponent) {
RepaintManager delegate =
getDelegate(
aComponent);
if (
delegate != null) {
delegate.
markCompletelyDirty(
aComponent);
return;
}
addDirtyRegion(
aComponent,0,0,
Integer.
MAX_VALUE,
Integer.
MAX_VALUE);
}
/**
* Mark a component completely clean. <b>aComponent</b> will not
* get painted during the next paintDirtyRegions() call.
*/
public void
markCompletelyClean(
JComponent aComponent) {
RepaintManager delegate =
getDelegate(
aComponent);
if (
delegate != null) {
delegate.
markCompletelyClean(
aComponent);
return;
}
synchronized(this) {
dirtyComponents.
remove(
aComponent);
}
}
/**
* Convenience method that returns true if <b>aComponent</b> will be completely
* painted during the next paintDirtyRegions(). If computing dirty regions is
* expensive for your component, use this method and avoid computing dirty region
* if it return true.
*/
public boolean
isCompletelyDirty(
JComponent aComponent) {
RepaintManager delegate =
getDelegate(
aComponent);
if (
delegate != null) {
return
delegate.
isCompletelyDirty(
aComponent);
}
Rectangle r;
r =
getDirtyRegion(
aComponent);
if(
r.
width ==
Integer.
MAX_VALUE &&
r.
height ==
Integer.
MAX_VALUE)
return true;
else
return false;
}
/**
* Validate all of the components that have been marked invalid.
* @see #addInvalidComponent
*/
public void
validateInvalidComponents() {
final java.util.
List<
Component>
ic;
synchronized(this) {
if (
invalidComponents == null) {
return;
}
ic =
invalidComponents;
invalidComponents = null;
}
int
n =
ic.
size();
for(int
i = 0;
i <
n;
i++) {
final
Component c =
ic.
get(
i);
AccessControlContext stack =
AccessController.
getContext();
AccessControlContext acc =
AWTAccessor.
getComponentAccessor().
getAccessControlContext(
c);
javaSecurityAccess.
doIntersectionPrivilege(
new
PrivilegedAction<
Void>() {
public
Void run() {
c.
validate();
return null;
}
},
stack,
acc);
}
}
/**
* This is invoked to process paint requests. It's needed
* for backward compatibility in so far as RepaintManager would previously
* not see paint requests for top levels, so, we have to make sure
* a subclass correctly paints any dirty top levels.
*/
private void
prePaintDirtyRegions() {
Map<
Component,
Rectangle>
dirtyComponents;
java.util.
List<
Runnable>
runnableList;
synchronized(this) {
dirtyComponents = this.
dirtyComponents;
runnableList = this.
runnableList;
this.
runnableList = null;
}
if (
runnableList != null) {
for (
Runnable runnable :
runnableList) {
runnable.
run();
}
}
paintDirtyRegions();
if (
dirtyComponents.
size() > 0) {
// This'll only happen if a subclass isn't correctly dealing
// with toplevels.
paintDirtyRegions(
dirtyComponents);
}
}
private void
updateWindows(
Map<
Component,
Rectangle>
dirtyComponents) {
Toolkit toolkit =
Toolkit.
getDefaultToolkit();
if (!(
toolkit instanceof
SunToolkit &&
((
SunToolkit)
toolkit).
needUpdateWindow()))
{
return;
}
Set<
Window>
windows = new
HashSet<
Window>();
Set<
Component>
dirtyComps =
dirtyComponents.
keySet();
for (
Iterator<
Component>
it =
dirtyComps.
iterator();
it.
hasNext();) {
Component dirty =
it.
next();
Window window =
dirty instanceof
Window ?
(
Window)
dirty :
SwingUtilities.
getWindowAncestor(
dirty);
if (
window != null &&
!
window.
isOpaque())
{
windows.
add(
window);
}
}
for (
Window window :
windows) {
AWTAccessor.
getWindowAccessor().
updateWindow(
window);
}
}
boolean
isPainting() {
return
painting;
}
/**
* Paint all of the components that have been marked dirty.
*
* @see #addDirtyRegion
*/
public void
paintDirtyRegions() {
synchronized(this) { // swap for thread safety
Map<
Component,
Rectangle>
tmp =
tmpDirtyComponents;
tmpDirtyComponents =
dirtyComponents;
dirtyComponents =
tmp;
dirtyComponents.
clear();
}
paintDirtyRegions(
tmpDirtyComponents);
}
private void
paintDirtyRegions(
final
Map<
Component,
Rectangle>
tmpDirtyComponents)
{
if (
tmpDirtyComponents.
isEmpty()) {
return;
}
final java.util.
List<
Component>
roots =
new
ArrayList<
Component>(
tmpDirtyComponents.
size());
for (
Component dirty :
tmpDirtyComponents.
keySet()) {
collectDirtyComponents(
tmpDirtyComponents,
dirty,
roots);
}
final
AtomicInteger count = new
AtomicInteger(
roots.
size());
painting = true;
try {
for (int
j=0 ;
j <
count.
get();
j++) {
final int
i =
j;
final
Component dirtyComponent =
roots.
get(
j);
AccessControlContext stack =
AccessController.
getContext();
AccessControlContext acc =
AWTAccessor.
getComponentAccessor().
getAccessControlContext(
dirtyComponent);
javaSecurityAccess.
doIntersectionPrivilege(new
PrivilegedAction<
Void>() {
public
Void run() {
Rectangle rect =
tmpDirtyComponents.
get(
dirtyComponent);
// Sometimes when RepaintManager is changed during the painting
// we may get null here, see #6995769 for details
if (
rect == null) {
return null;
}
int
localBoundsH =
dirtyComponent.
getHeight();
int
localBoundsW =
dirtyComponent.
getWidth();
SwingUtilities.
computeIntersection(0,
0,
localBoundsW,
localBoundsH,
rect);
if (
dirtyComponent instanceof
JComponent) {
((
JComponent)
dirtyComponent).
paintImmediately(
rect.
x,
rect.
y,
rect.
width,
rect.
height);
}
else if (
dirtyComponent.
isShowing()) {
Graphics g =
JComponent.
safelyGetGraphics(
dirtyComponent,
dirtyComponent);
// If the Graphics goes away, it means someone disposed of
// the window, don't do anything.
if (
g != null) {
g.
setClip(
rect.
x,
rect.
y,
rect.
width,
rect.
height);
try {
dirtyComponent.
paint(
g);
} finally {
g.
dispose();
}
}
}
// If the repaintRoot has been set, service it now and
// remove any components that are children of repaintRoot.
if (
repaintRoot != null) {
adjustRoots(
repaintRoot,
roots,
i + 1);
count.
set(
roots.
size());
paintManager.
isRepaintingRoot = true;
repaintRoot.
paintImmediately(0, 0,
repaintRoot.
getWidth(),
repaintRoot.
getHeight());
paintManager.
isRepaintingRoot = false;
// Only service repaintRoot once.
repaintRoot = null;
}
return null;
}
},
stack,
acc);
}
} finally {
painting = false;
}
updateWindows(
tmpDirtyComponents);
tmpDirtyComponents.
clear();
}
/**
* Removes any components from roots that are children of
* root.
*/
private void
adjustRoots(
JComponent root,
java.util.
List<
Component>
roots, int
index) {
for (int
i =
roots.
size() - 1;
i >=
index;
i--) {
Component c =
roots.
get(
i);
for(;;) {
if (
c ==
root ||
c == null || !(
c instanceof
JComponent)) {
break;
}
c =
c.
getParent();
}
if (
c ==
root) {
roots.
remove(
i);
}
}
}
Rectangle tmp = new
Rectangle();
void
collectDirtyComponents(
Map<
Component,
Rectangle>
dirtyComponents,
Component dirtyComponent,
java.util.
List<
Component>
roots) {
int
dx,
dy,
rootDx,
rootDy;
Component component,
rootDirtyComponent,
parent;
Rectangle cBounds;
// Find the highest parent which is dirty. When we get out of this
// rootDx and rootDy will contain the translation from the
// rootDirtyComponent's coordinate system to the coordinates of the
// original dirty component. The tmp Rect is also used to compute the
// visible portion of the dirtyRect.
component =
rootDirtyComponent =
dirtyComponent;
int
x =
dirtyComponent.
getX();
int
y =
dirtyComponent.
getY();
int
w =
dirtyComponent.
getWidth();
int
h =
dirtyComponent.
getHeight();
dx =
rootDx = 0;
dy =
rootDy = 0;
tmp.
setBounds(
dirtyComponents.
get(
dirtyComponent));
// System.out.println("Collect dirty component for bound " + tmp +
// "component bounds is " + cBounds);;
SwingUtilities.
computeIntersection(0,0,
w,
h,
tmp);
if (
tmp.
isEmpty()) {
// System.out.println("Empty 1");
return;
}
for(;;) {
if(!(
component instanceof
JComponent))
break;
parent =
component.
getParent();
if(
parent == null)
break;
component =
parent;
dx +=
x;
dy +=
y;
tmp.
setLocation(
tmp.
x +
x,
tmp.
y +
y);
x =
component.
getX();
y =
component.
getY();
w =
component.
getWidth();
h =
component.
getHeight();
tmp =
SwingUtilities.
computeIntersection(0,0,
w,
h,
tmp);
if (
tmp.
isEmpty()) {
// System.out.println("Empty 2");
return;
}
if (
dirtyComponents.
get(
component) != null) {
rootDirtyComponent =
component;
rootDx =
dx;
rootDy =
dy;
}
}
if (
dirtyComponent !=
rootDirtyComponent) {
Rectangle r;
tmp.
setLocation(
tmp.
x +
rootDx -
dx,
tmp.
y +
rootDy -
dy);
r =
dirtyComponents.
get(
rootDirtyComponent);
SwingUtilities.
computeUnion(
tmp.
x,
tmp.
y,
tmp.
width,
tmp.
height,
r);
}
// If we haven't seen this root before, then we need to add it to the
// list of root dirty Views.
if (!
roots.
contains(
rootDirtyComponent))
roots.
add(
rootDirtyComponent);
}
/**
* Returns a string that displays and identifies this
* object's properties.
*
* @return a String representation of this object
*/
public synchronized
String toString() {
StringBuffer sb = new
StringBuffer();
if(
dirtyComponents != null)
sb.
append("" +
dirtyComponents);
return
sb.
toString();
}
/**
* Return the offscreen buffer that should be used as a double buffer with
* the component <code>c</code>.
* By default there is a double buffer per RepaintManager.
* The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
* This happens when the maximum double buffer size as been set for the receiving
* repaint manager.
*/
public
Image getOffscreenBuffer(
Component c,int
proposedWidth,int
proposedHeight) {
RepaintManager delegate =
getDelegate(
c);
if (
delegate != null) {
return
delegate.
getOffscreenBuffer(
c,
proposedWidth,
proposedHeight);
}
return
_getOffscreenBuffer(
c,
proposedWidth,
proposedHeight);
}
/**
* Return a volatile offscreen buffer that should be used as a
* double buffer with the specified component <code>c</code>.
* The image returned will be an instance of VolatileImage, or null
* if a VolatileImage object could not be instantiated.
* This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>.
* This happens when the maximum double buffer size has been set for this
* repaint manager.
*
* @see java.awt.image.VolatileImage
* @since 1.4
*/
public
Image getVolatileOffscreenBuffer(
Component c,
int
proposedWidth,int
proposedHeight) {
RepaintManager delegate =
getDelegate(
c);
if (
delegate != null) {
return
delegate.
getVolatileOffscreenBuffer(
c,
proposedWidth,
proposedHeight);
}
// If the window is non-opaque, it's double-buffered at peer's level
Window w = (
c instanceof
Window) ? (
Window)
c :
SwingUtilities.
getWindowAncestor(
c);
if (!
w.
isOpaque()) {
Toolkit tk =
Toolkit.
getDefaultToolkit();
if ((
tk instanceof
SunToolkit) && (((
SunToolkit)
tk).
needUpdateWindow())) {
return null;
}
}
GraphicsConfiguration config =
c.
getGraphicsConfiguration();
if (
config == null) {
config =
GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice().
getDefaultConfiguration();
}
Dimension maxSize =
getDoubleBufferMaximumSize();
int
width =
proposedWidth < 1 ? 1 :
(
proposedWidth >
maxSize.
width?
maxSize.
width :
proposedWidth);
int
height =
proposedHeight < 1 ? 1 :
(
proposedHeight >
maxSize.
height?
maxSize.
height :
proposedHeight);
VolatileImage image =
volatileMap.
get(
config);
if (
image == null ||
image.
getWidth() <
width ||
image.
getHeight() <
height) {
if (
image != null) {
image.
flush();
}
image =
config.
createCompatibleVolatileImage(
width,
height,
volatileBufferType);
volatileMap.
put(
config,
image);
}
return
image;
}
private
Image _getOffscreenBuffer(
Component c, int
proposedWidth, int
proposedHeight) {
Dimension maxSize =
getDoubleBufferMaximumSize();
DoubleBufferInfo doubleBuffer;
int
width,
height;
// If the window is non-opaque, it's double-buffered at peer's level
Window w = (
c instanceof
Window) ? (
Window)
c :
SwingUtilities.
getWindowAncestor(
c);
if (!
w.
isOpaque()) {
Toolkit tk =
Toolkit.
getDefaultToolkit();
if ((
tk instanceof
SunToolkit) && (((
SunToolkit)
tk).
needUpdateWindow())) {
return null;
}
}
if (
standardDoubleBuffer == null) {
standardDoubleBuffer = new
DoubleBufferInfo();
}
doubleBuffer =
standardDoubleBuffer;
width =
proposedWidth < 1? 1 :
(
proposedWidth >
maxSize.
width?
maxSize.
width :
proposedWidth);
height =
proposedHeight < 1? 1 :
(
proposedHeight >
maxSize.
height?
maxSize.
height :
proposedHeight);
if (
doubleBuffer.
needsReset || (
doubleBuffer.
image != null &&
(
doubleBuffer.
size.
width <
width ||
doubleBuffer.
size.
height <
height))) {
doubleBuffer.
needsReset = false;
if (
doubleBuffer.
image != null) {
doubleBuffer.
image.
flush();
doubleBuffer.
image = null;
}
width =
Math.
max(
doubleBuffer.
size.
width,
width);
height =
Math.
max(
doubleBuffer.
size.
height,
height);
}
Image result =
doubleBuffer.
image;
if (
doubleBuffer.
image == null) {
result =
c.
createImage(
width ,
height);
doubleBuffer.
size = new
Dimension(
width,
height);
if (
c instanceof
JComponent) {
((
JComponent)
c).
setCreatedDoubleBuffer(true);
doubleBuffer.
image =
result;
}
// JComponent will inform us when it is no longer valid
// (via removeNotify) we have no such hook to other components,
// therefore we don't keep a ref to the Component
// (indirectly through the Image) by stashing the image.
}
return
result;
}
/** Set the maximum double buffer size. **/
public void
setDoubleBufferMaximumSize(
Dimension d) {
doubleBufferMaxSize =
d;
if (
doubleBufferMaxSize == null) {
clearImages();
} else {
clearImages(
d.
width,
d.
height);
}
}
private void
clearImages() {
clearImages(0, 0);
}
private void
clearImages(int
width, int
height) {
if (
standardDoubleBuffer != null &&
standardDoubleBuffer.
image != null) {
if (
standardDoubleBuffer.
image.
getWidth(null) >
width ||
standardDoubleBuffer.
image.
getHeight(null) >
height) {
standardDoubleBuffer.
image.
flush();
standardDoubleBuffer.
image = null;
}
}
// Clear out the VolatileImages
Iterator<
GraphicsConfiguration>
gcs =
volatileMap.
keySet().
iterator();
while (
gcs.
hasNext()) {
GraphicsConfiguration gc =
gcs.
next();
VolatileImage image =
volatileMap.
get(
gc);
if (
image.
getWidth() >
width ||
image.
getHeight() >
height) {
image.
flush();
gcs.
remove();
}
}
}
/**
* Returns the maximum double buffer size.
*
* @return a Dimension object representing the maximum size
*/
public
Dimension getDoubleBufferMaximumSize() {
if (
doubleBufferMaxSize == null) {
try {
Rectangle virtualBounds = new
Rectangle();
GraphicsEnvironment ge =
GraphicsEnvironment.
getLocalGraphicsEnvironment();
for (
GraphicsDevice gd :
ge.
getScreenDevices()) {
GraphicsConfiguration gc =
gd.
getDefaultConfiguration();
virtualBounds =
virtualBounds.
union(
gc.
getBounds());
}
doubleBufferMaxSize = new
Dimension(
virtualBounds.
width,
virtualBounds.
height);
} catch (
HeadlessException e) {
doubleBufferMaxSize = new
Dimension(
Integer.
MAX_VALUE,
Integer.
MAX_VALUE);
}
}
return
doubleBufferMaxSize;
}
/**
* Enables or disables double buffering in this RepaintManager.
* CAUTION: The default value for this property is set for optimal
* paint performance on the given platform and it is not recommended
* that programs modify this property directly.
*
* @param aFlag true to activate double buffering
* @see #isDoubleBufferingEnabled
*/
public void
setDoubleBufferingEnabled(boolean
aFlag) {
doubleBufferingEnabled =
aFlag;
PaintManager paintManager =
getPaintManager();
if (!
aFlag &&
paintManager.
getClass() !=
PaintManager.class) {
setPaintManager(new
PaintManager());
}
}
/**
* Returns true if this RepaintManager is double buffered.
* The default value for this property may vary from platform
* to platform. On platforms where native double buffering
* is supported in the AWT, the default value will be <code>false</code>
* to avoid unnecessary buffering in Swing.
* On platforms where native double buffering is not supported,
* the default value will be <code>true</code>.
*
* @return true if this object is double buffered
*/
public boolean
isDoubleBufferingEnabled() {
return
doubleBufferingEnabled;
}
/**
* This resets the double buffer. Actually, it marks the double buffer
* as invalid, the double buffer will then be recreated on the next
* invocation of getOffscreenBuffer.
*/
void
resetDoubleBuffer() {
if (
standardDoubleBuffer != null) {
standardDoubleBuffer.
needsReset = true;
}
}
/**
* This resets the volatile double buffer.
*/
void
resetVolatileDoubleBuffer(
GraphicsConfiguration gc) {
Image image =
volatileMap.
remove(
gc);
if (
image != null) {
image.
flush();
}
}
/**
* Returns true if we should use the <code>Image</code> returned
* from <code>getVolatileOffscreenBuffer</code> to do double buffering.
*/
boolean
useVolatileDoubleBuffer() {
return
volatileImageBufferEnabled;
}
/**
* Returns true if the current thread is the thread painting. This
* will return false if no threads are painting.
*/
private synchronized boolean
isPaintingThread() {
return (
Thread.
currentThread() ==
paintThread);
}
//
// Paint methods. You very, VERY rarely need to invoke these.
// They are invoked directly from JComponent's painting code and
// when painting happens outside the normal flow: DefaultDesktopManager
// and JViewport. If you end up needing these methods in other places be
// careful that you don't get stuck in a paint loop.
//
/**
* Paints a region of a component
*
* @param paintingComponent Component to paint
* @param bufferComponent Component to obtain buffer for
* @param g Graphics to paint to
* @param x X-coordinate
* @param y Y-coordinate
* @param w Width
* @param h Height
*/
void
paint(
JComponent paintingComponent,
JComponent bufferComponent,
Graphics g,
int
x, int
y, int
w, int
h) {
PaintManager paintManager =
getPaintManager();
if (!
isPaintingThread()) {
// We're painting to two threads at once. PaintManager deals
// with this a bit better than BufferStrategyPaintManager, use
// it to avoid possible exceptions/corruption.
if (
paintManager.
getClass() !=
PaintManager.class) {
paintManager = new
PaintManager();
paintManager.
repaintManager = this;
}
}
if (!
paintManager.
paint(
paintingComponent,
bufferComponent,
g,
x,
y,
w,
h)) {
g.
setClip(
x,
y,
w,
h);
paintingComponent.
paintToOffscreen(
g,
x,
y,
w,
h,
x +
w,
y +
h);
}
}
/**
* Does a copy area on the specified region.
*
* @param clip Whether or not the copyArea needs to be clipped to the
* Component's bounds.
*/
void
copyArea(
JComponent c,
Graphics g, int
x, int
y, int
w, int
h,
int
deltaX, int
deltaY, boolean
clip) {
getPaintManager().
copyArea(
c,
g,
x,
y,
w,
h,
deltaX,
deltaY,
clip);
}
private java.util.
List<
RepaintListener>
repaintListeners = new
ArrayList<>(1);
private void
addRepaintListener(
RepaintListener l) {
repaintListeners.
add(
l);
}
private void
removeRepaintListener(
RepaintListener l) {
repaintListeners.
remove(
l);
}
/**
* Notify the attached repaint listeners that an area of the {@code c} component
* has been immediately repainted, that is without scheduling a repaint runnable,
* due to performing a "blit" (via calling the {@code copyArea} method).
*
* @param c the component
* @param x the x coordinate of the area
* @param y the y coordinate of the area
* @param w the width of the area
* @param h the height of the area
*/
void
notifyRepaintPerformed(
JComponent c, int
x, int
y, int
w, int
h) {
for (
RepaintListener l :
repaintListeners) {
l.
repaintPerformed(
c,
x,
y,
w,
h);
}
}
/**
* Invoked prior to any paint/copyArea method calls. This will
* be followed by an invocation of <code>endPaint</code>.
* <b>WARNING</b>: Callers of this method need to wrap the call
* in a <code>try/finally</code>, otherwise if an exception is thrown
* during the course of painting the RepaintManager may
* be left in a state in which the screen is not updated, eg:
* <pre>
* repaintManager.beginPaint();
* try {
* repaintManager.paint(...);
* } finally {
* repaintManager.endPaint();
* }
* </pre>
*/
void
beginPaint() {
boolean
multiThreadedPaint = false;
int
paintDepth;
Thread currentThread =
Thread.
currentThread();
synchronized(this) {
paintDepth = this.
paintDepth;
if (
paintThread == null ||
currentThread ==
paintThread) {
paintThread =
currentThread;
this.
paintDepth++;
} else {
multiThreadedPaint = true;
}
}
if (!
multiThreadedPaint &&
paintDepth == 0) {
getPaintManager().
beginPaint();
}
}
/**
* Invoked after <code>beginPaint</code> has been invoked.
*/
void
endPaint() {
if (
isPaintingThread()) {
PaintManager paintManager = null;
synchronized(this) {
if (--
paintDepth == 0) {
paintManager =
getPaintManager();
}
}
if (
paintManager != null) {
paintManager.
endPaint();
synchronized(this) {
paintThread = null;
}
}
}
}
/**
* If possible this will show a previously rendered portion of
* a Component. If successful, this will return true, otherwise false.
* <p>
* WARNING: This method is invoked from the native toolkit thread, be
* very careful as to what methods this invokes!
*/
boolean
show(
Container c, int
x, int
y, int
w, int
h) {
return
getPaintManager().
show(
c,
x,
y,
w,
h);
}
/**
* Invoked when the doubleBuffered or useTrueDoubleBuffering
* properties of a JRootPane change. This may come in on any thread.
*/
void
doubleBufferingChanged(
JRootPane rootPane) {
getPaintManager().
doubleBufferingChanged(
rootPane);
}
/**
* Sets the <code>PaintManager</code> that is used to handle all
* double buffered painting.
*
* @param paintManager The PaintManager to use. Passing in null indicates
* the fallback PaintManager should be used.
*/
void
setPaintManager(
PaintManager paintManager) {
if (
paintManager == null) {
paintManager = new
PaintManager();
}
PaintManager oldPaintManager;
synchronized(this) {
oldPaintManager = this.
paintManager;
this.
paintManager =
paintManager;
paintManager.
repaintManager = this;
}
if (
oldPaintManager != null) {
oldPaintManager.
dispose();
}
}
private synchronized
PaintManager getPaintManager() {
if (
paintManager == null) {
PaintManager paintManager = null;
if (
doubleBufferingEnabled && !
nativeDoubleBuffering) {
switch (
bufferStrategyType) {
case
BUFFER_STRATEGY_NOT_SPECIFIED:
Toolkit tk =
Toolkit.
getDefaultToolkit();
if (
tk instanceof
SunToolkit) {
SunToolkit stk = (
SunToolkit)
tk;
if (
stk.
useBufferPerWindow()) {
paintManager = new
BufferStrategyPaintManager();
}
}
break;
case
BUFFER_STRATEGY_SPECIFIED_ON:
paintManager = new
BufferStrategyPaintManager();
break;
default:
break;
}
}
// null case handled in setPaintManager
setPaintManager(
paintManager);
}
return
paintManager;
}
private void
scheduleProcessingRunnable(
AppContext context) {
if (
processingRunnable.
markPending()) {
Toolkit tk =
Toolkit.
getDefaultToolkit();
if (
tk instanceof
SunToolkit) {
SunToolkit.
getSystemEventQueueImplPP(
context).
postEvent(new
InvocationEvent(
Toolkit.
getDefaultToolkit(),
processingRunnable));
} else {
Toolkit.
getDefaultToolkit().
getSystemEventQueue().
postEvent(new
InvocationEvent(
Toolkit.
getDefaultToolkit(),
processingRunnable));
}
}
}
/**
* PaintManager is used to handle all double buffered painting for
* Swing. Subclasses should call back into the JComponent method
* <code>paintToOffscreen</code> to handle the actual painting.
*/
static class
PaintManager {
/**
* RepaintManager the PaintManager has been installed on.
*/
protected
RepaintManager repaintManager;
boolean
isRepaintingRoot;
/**
* Paints a region of a component
*
* @param paintingComponent Component to paint
* @param bufferComponent Component to obtain buffer for
* @param g Graphics to paint to
* @param x X-coordinate
* @param y Y-coordinate
* @param w Width
* @param h Height
* @return true if painting was successful.
*/
public boolean
paint(
JComponent paintingComponent,
JComponent bufferComponent,
Graphics g,
int
x, int
y, int
w, int
h) {
// First attempt to use VolatileImage buffer for performance.
// If this fails (which should rarely occur), fallback to a
// standard Image buffer.
boolean
paintCompleted = false;
Image offscreen;
if (
repaintManager.
useVolatileDoubleBuffer() &&
(
offscreen =
getValidImage(
repaintManager.
getVolatileOffscreenBuffer(
bufferComponent,
w,
h))) != null) {
VolatileImage vImage = (java.awt.image.
VolatileImage)
offscreen;
GraphicsConfiguration gc =
bufferComponent.
getGraphicsConfiguration();
for (int
i = 0; !
paintCompleted &&
i <
RepaintManager.
VOLATILE_LOOP_MAX;
i++) {
if (
vImage.
validate(
gc) ==
VolatileImage.
IMAGE_INCOMPATIBLE) {
repaintManager.
resetVolatileDoubleBuffer(
gc);
offscreen =
repaintManager.
getVolatileOffscreenBuffer(
bufferComponent,
w,
h);
vImage = (java.awt.image.
VolatileImage)
offscreen;
}
paintDoubleBuffered(
paintingComponent,
vImage,
g,
x,
y,
w,
h);
paintCompleted = !
vImage.
contentsLost();
}
}
// VolatileImage painting loop failed, fallback to regular
// offscreen buffer
if (!
paintCompleted && (
offscreen =
getValidImage(
repaintManager.
getOffscreenBuffer(
bufferComponent,
w,
h))) != null) {
paintDoubleBuffered(
paintingComponent,
offscreen,
g,
x,
y,
w,
h);
paintCompleted = true;
}
return
paintCompleted;
}
/**
* Does a copy area on the specified region.
*/
public void
copyArea(
JComponent c,
Graphics g, int
x, int
y, int
w,
int
h, int
deltaX, int
deltaY, boolean
clip) {
g.
copyArea(
x,
y,
w,
h,
deltaX,
deltaY);
}
/**
* Invoked prior to any calls to paint or copyArea.
*/
public void
beginPaint() {
}
/**
* Invoked to indicate painting has been completed.
*/
public void
endPaint() {
}
/**
* Shows a region of a previously rendered component. This
* will return true if successful, false otherwise. The default
* implementation returns false.
*/
public boolean
show(
Container c, int
x, int
y, int
w, int
h) {
return false;
}
/**
* Invoked when the doubleBuffered or useTrueDoubleBuffering
* properties of a JRootPane change. This may come in on any thread.
*/
public void
doubleBufferingChanged(
JRootPane rootPane) {
}
/**
* Paints a portion of a component to an offscreen buffer.
*/
protected void
paintDoubleBuffered(
JComponent c,
Image image,
Graphics g, int
clipX, int
clipY,
int
clipW, int
clipH) {
Graphics osg =
image.
getGraphics();
int
bw =
Math.
min(
clipW,
image.
getWidth(null));
int
bh =
Math.
min(
clipH,
image.
getHeight(null));
int
x,
y,
maxx,
maxy;
try {
for(
x =
clipX,
maxx =
clipX+
clipW;
x <
maxx ;
x +=
bw ) {
for(
y=
clipY,
maxy =
clipY +
clipH;
y <
maxy ;
y +=
bh) {
osg.
translate(-
x, -
y);
osg.
setClip(
x,
y,
bw,
bh);
if (
volatileBufferType !=
Transparency.
OPAQUE
&&
osg instanceof
Graphics2D) {
final
Graphics2D g2d = (
Graphics2D)
osg;
final
Color oldBg =
g2d.
getBackground();
g2d.
setBackground(
c.
getBackground());
g2d.
clearRect(
x,
y,
bw,
bh);
g2d.
setBackground(
oldBg);
}
c.
paintToOffscreen(
osg,
x,
y,
bw,
bh,
maxx,
maxy);
g.
setClip(
x,
y,
bw,
bh);
if (
volatileBufferType !=
Transparency.
OPAQUE
&&
g instanceof
Graphics2D) {
final
Graphics2D g2d = (
Graphics2D)
g;
final
Composite oldComposite =
g2d.
getComposite();
g2d.
setComposite(
AlphaComposite.
Src);
g2d.
drawImage(
image,
x,
y,
c);
g2d.
setComposite(
oldComposite);
} else {
g.
drawImage(
image,
x,
y,
c);
}
osg.
translate(
x,
y);
}
}
} finally {
osg.
dispose();
}
}
/**
* If <code>image</code> is non-null with a positive size it
* is returned, otherwise null is returned.
*/
private
Image getValidImage(
Image image) {
if (
image != null &&
image.
getWidth(null) > 0 &&
image.
getHeight(null) > 0) {
return
image;
}
return null;
}
/**
* Schedules a repaint for the specified component. This differs
* from <code>root.repaint</code> in that if the RepaintManager is
* currently processing paint requests it'll process this request
* with the current set of requests.
*/
protected void
repaintRoot(
JComponent root) {
assert (
repaintManager.
repaintRoot == null);
if (
repaintManager.
painting) {
repaintManager.
repaintRoot =
root;
}
else {
root.
repaint();
}
}
/**
* Returns true if the component being painted is the root component
* that was previously passed to <code>repaintRoot</code>.
*/
protected boolean
isRepaintingRoot() {
return
isRepaintingRoot;
}
/**
* Cleans up any state. After invoked the PaintManager will no
* longer be used anymore.
*/
protected void
dispose() {
}
}
private class
DoubleBufferInfo {
public
Image image;
public
Dimension size;
public boolean
needsReset = false;
}
/**
* Listener installed to detect display changes. When display changes,
* schedules a callback to notify all RepaintManagers of the display
* changes. Only one DisplayChangedHandler is ever installed. The
* singleton instance will schedule notification for all AppContexts.
*/
private static final class
DisplayChangedHandler implements
DisplayChangedListener {
// Empty non private constructor was added because access to this
// class shouldn't be generated by the compiler using synthetic
// accessor method
DisplayChangedHandler() {
}
public void
displayChanged() {
scheduleDisplayChanges();
}
public void
paletteChanged() {
}
private static void
scheduleDisplayChanges() {
// To avoid threading problems, we notify each RepaintManager
// on the thread it was created on.
for (
AppContext context :
AppContext.
getAppContexts()) {
synchronized(
context) {
if (!
context.
isDisposed()) {
EventQueue eventQueue = (
EventQueue)
context.
get(
AppContext.
EVENT_QUEUE_KEY);
if (
eventQueue != null) {
eventQueue.
postEvent(new
InvocationEvent(
Toolkit.
getDefaultToolkit(),
new
DisplayChangedRunnable()));
}
}
}
}
}
}
private static final class
DisplayChangedRunnable implements
Runnable {
public void
run() {
RepaintManager.
currentManager((
JComponent)null).
displayChanged();
}
}
/**
* Runnable used to process all repaint/revalidate requests.
*/
private final class
ProcessingRunnable implements
Runnable {
// If true, we're wainting on the EventQueue.
private boolean
pending;
/**
* Marks this processing runnable as pending. If this was not
* already marked as pending, true is returned.
*/
public synchronized boolean
markPending() {
if (!
pending) {
pending = true;
return true;
}
return false;
}
public void
run() {
synchronized (this) {
pending = false;
}
// First pass, flush any heavy paint events into real paint
// events. If there are pending heavy weight requests this will
// result in q'ing this request up one more time. As
// long as no other requests come in between now and the time
// the second one is processed nothing will happen. This is not
// ideal, but the logic needed to suppress the second request is
// more headache than it's worth.
scheduleHeavyWeightPaints();
// Do the actual validation and painting.
validateInvalidComponents();
prePaintDirtyRegions();
}
}
private
RepaintManager getDelegate(
Component c) {
RepaintManager delegate =
SwingUtilities3.
getDelegateRepaintManager(
c);
if (this ==
delegate) {
delegate = null;
}
return
delegate;
}
}