/*
* Copyright (c) 2005, 2013, 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.*;
import java.lang.reflect.*;
import java.lang.ref.
WeakReference;
import java.util.*;
import com.sun.java.swing.
SwingUtilities3;
import sun.awt.
SubRegionShowable;
import sun.java2d.
SunGraphics2D;
import sun.java2d.pipe.hw.
ExtendedBufferCapabilities;
import sun.awt.
SunToolkit;
import sun.util.logging.
PlatformLogger;
/**
* A PaintManager implementation that uses a BufferStrategy for
* rendering.
*
* @author Scott Violet
*/
class
BufferStrategyPaintManager extends
RepaintManager.
PaintManager {
//
// All drawing is done to a BufferStrategy. At the end of painting
// (endPaint) the region that was painted is flushed to the screen
// (using BufferStrategy.show).
//
// PaintManager.show is overriden to show directly from the
// BufferStrategy (when using blit), if successful true is
// returned and a paint event will not be generated. To avoid
// showing from the buffer while painting a locking scheme is
// implemented. When beginPaint is invoked the field painting is
// set to true. If painting is true and show is invoked we
// immediately return false. This is done to avoid blocking the
// toolkit thread while painting happens. In a similar way when
// show is invoked the field showing is set to true, beginPaint
// will then block until showing is true. This scheme ensures we
// only ever have one thread using the BufferStrategy and it also
// ensures the toolkit thread remains as responsive as possible.
//
// If we're using a flip strategy the contents of the backbuffer may
// have changed and so show only attempts to show from the backbuffer
// if we get a blit strategy.
//
//
// Methods used to create BufferStrategy for Applets.
//
private static
Method COMPONENT_CREATE_BUFFER_STRATEGY_METHOD;
private static
Method COMPONENT_GET_BUFFER_STRATEGY_METHOD;
private static final
PlatformLogger LOGGER =
PlatformLogger.
getLogger(
"javax.swing.BufferStrategyPaintManager");
/**
* List of BufferInfos. We don't use a Map primarily because
* there are typically only a handful of top level components making
* a Map overkill.
*/
private
ArrayList<
BufferInfo>
bufferInfos;
/**
* Indicates <code>beginPaint</code> has been invoked. This is
* set to true for the life of beginPaint/endPaint pair.
*/
private boolean
painting;
/**
* Indicates we're in the process of showing. All painting, on the EDT,
* is blocked while this is true.
*/
private boolean
showing;
//
// Region that we need to flush. When beginPaint is called these are
// reset and any subsequent calls to paint/copyArea then update these
// fields accordingly. When endPaint is called we then try and show
// the accumulated region.
// These fields are in the coordinate system of the root.
//
private int
accumulatedX;
private int
accumulatedY;
private int
accumulatedMaxX;
private int
accumulatedMaxY;
//
// The following fields are set by prepare
//
/**
* Farthest JComponent ancestor for the current paint/copyArea.
*/
private
JComponent rootJ;
/**
* Location of component being painted relative to root.
*/
private int
xOffset;
/**
* Location of component being painted relative to root.
*/
private int
yOffset;
/**
* Graphics from the BufferStrategy.
*/
private
Graphics bsg;
/**
* BufferStrategy currently being used.
*/
private
BufferStrategy bufferStrategy;
/**
* BufferInfo corresponding to root.
*/
private
BufferInfo bufferInfo;
/**
* Set to true if the bufferInfo needs to be disposed when current
* paint loop is done.
*/
private boolean
disposeBufferOnEnd;
private static
Method getGetBufferStrategyMethod() {
if (
COMPONENT_GET_BUFFER_STRATEGY_METHOD == null) {
getMethods();
}
return
COMPONENT_GET_BUFFER_STRATEGY_METHOD;
}
private static
Method getCreateBufferStrategyMethod() {
if (
COMPONENT_CREATE_BUFFER_STRATEGY_METHOD == null) {
getMethods();
}
return
COMPONENT_CREATE_BUFFER_STRATEGY_METHOD;
}
private static void
getMethods() {
java.security.
AccessController.
doPrivileged(
new java.security.
PrivilegedAction<
Object>() {
public
Object run() {
try {
COMPONENT_CREATE_BUFFER_STRATEGY_METHOD =
Component.class.
getDeclaredMethod("createBufferStrategy",
new
Class[] { int.class,
BufferCapabilities.class });
COMPONENT_CREATE_BUFFER_STRATEGY_METHOD.
setAccessible(true);
COMPONENT_GET_BUFFER_STRATEGY_METHOD =
Component.class.
getDeclaredMethod("getBufferStrategy");
COMPONENT_GET_BUFFER_STRATEGY_METHOD.
setAccessible(true);
} catch (
SecurityException e) {
assert false;
} catch (
NoSuchMethodException nsme) {
assert false;
}
return null;
}
});
}
BufferStrategyPaintManager() {
bufferInfos = new
ArrayList<
BufferInfo>(1);
}
//
// PaintManager methods
//
/**
* Cleans up any created BufferStrategies.
*/
protected void
dispose() {
// dipose can be invoked at any random time. To avoid
// threading dependancies we do the actual diposing via an
// invokeLater.
SwingUtilities.
invokeLater(new
Runnable() {
public void
run() {
java.util.
List<
BufferInfo>
bufferInfos;
synchronized(
BufferStrategyPaintManager.this) {
while (
showing) {
try {
BufferStrategyPaintManager.this.
wait();
} catch (
InterruptedException ie) {
}
}
bufferInfos =
BufferStrategyPaintManager.this.
bufferInfos;
BufferStrategyPaintManager.this.
bufferInfos = null;
}
dispose(
bufferInfos);
}
});
}
private void
dispose(java.util.
List<
BufferInfo>
bufferInfos) {
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("BufferStrategyPaintManager disposed",
new
RuntimeException());
}
if (
bufferInfos != null) {
for (
BufferInfo bufferInfo :
bufferInfos) {
bufferInfo.
dispose();
}
}
}
/**
* Shows the specified region of the back buffer. This will return
* true if successful, false otherwise. This is invoked on the
* toolkit thread in response to an expose event.
*/
public boolean
show(
Container c, int
x, int
y, int
w, int
h) {
synchronized(this) {
if (
painting) {
// Don't show from backbuffer while in the process of
// painting.
return false;
}
showing = true;
}
try {
BufferInfo info =
getBufferInfo(
c);
BufferStrategy bufferStrategy;
if (
info != null &&
info.
isInSync() &&
(
bufferStrategy =
info.
getBufferStrategy(false)) != null) {
SubRegionShowable bsSubRegion =
(
SubRegionShowable)
bufferStrategy;
boolean
paintAllOnExpose =
info.
getPaintAllOnExpose();
info.
setPaintAllOnExpose(false);
if (
bsSubRegion.
showIfNotLost(
x,
y, (
x +
w), (
y +
h))) {
return !
paintAllOnExpose;
}
// Mark the buffer as needing to be repainted. We don't
// immediately do a repaint as this method will return false
// indicating a PaintEvent should be generated which will
// trigger a complete repaint.
bufferInfo.
setContentsLostDuringExpose(true);
}
}
finally {
synchronized(this) {
showing = false;
notifyAll();
}
}
return false;
}
public boolean
paint(
JComponent paintingComponent,
JComponent bufferComponent,
Graphics g,
int
x, int
y, int
w, int
h) {
Container root =
fetchRoot(
paintingComponent);
if (
prepare(
paintingComponent,
root, true,
x,
y,
w,
h)) {
if ((
g instanceof
SunGraphics2D) &&
((
SunGraphics2D)
g).
getDestination() ==
root) {
// BufferStrategy may have already constrained the Graphics. To
// account for that we revert the constrain, then apply a
// constrain for Swing on top of that.
int
cx = ((
SunGraphics2D)
bsg).
constrainX;
int
cy = ((
SunGraphics2D)
bsg).
constrainY;
if (
cx != 0 ||
cy != 0) {
bsg.
translate(-
cx, -
cy);
}
((
SunGraphics2D)
bsg).
constrain(
xOffset +
cx,
yOffset +
cy,
x +
w,
y +
h);
bsg.
setClip(
x,
y,
w,
h);
paintingComponent.
paintToOffscreen(
bsg,
x,
y,
w,
h,
x +
w,
y +
h);
accumulate(
xOffset +
x,
yOffset +
y,
w,
h);
return true;
} else {
// Assume they are going to eventually render to the screen.
// This disables showing from backbuffer until a complete
// repaint occurs.
bufferInfo.
setInSync(false);
// Fall through to old rendering.
}
}
// Invalid root, do what Swing has always done.
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("prepare failed");
}
return super.paint(
paintingComponent,
bufferComponent,
g,
x,
y,
w,
h);
}
public void
copyArea(
JComponent c,
Graphics g, int
x, int
y, int
w, int
h,
int
deltaX, int
deltaY, boolean
clip) {
// Note: this method is only called internally and we know that
// g is from a heavyweight Component, so no check is necessary as
// it is in paint() above.
//
// If the buffer isn't in sync there is no point in doing a copyArea,
// it has garbage.
Container root =
fetchRoot(
c);
if (
prepare(
c,
root, false, 0, 0, 0, 0) &&
bufferInfo.
isInSync()) {
if (
clip) {
Rectangle cBounds =
c.
getVisibleRect();
int
relX =
xOffset +
x;
int
relY =
yOffset +
y;
bsg.
clipRect(
xOffset +
cBounds.
x,
yOffset +
cBounds.
y,
cBounds.
width,
cBounds.
height);
bsg.
copyArea(
relX,
relY,
w,
h,
deltaX,
deltaY);
}
else {
bsg.
copyArea(
xOffset +
x,
yOffset +
y,
w,
h,
deltaX,
deltaY);
}
accumulate(
x +
xOffset +
deltaX,
y +
yOffset +
deltaY,
w,
h);
} else {
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("copyArea: prepare failed or not in sync");
}
// Prepare failed, or not in sync. By calling super.copyArea
// we'll copy on screen. We need to flush any pending paint to
// the screen otherwise we'll do a copyArea on the wrong thing.
if (!
flushAccumulatedRegion()) {
// Flush failed, copyArea will be copying garbage,
// force repaint of all.
rootJ.
repaint();
} else {
super.copyArea(
c,
g,
x,
y,
w,
h,
deltaX,
deltaY,
clip);
}
}
}
public void
beginPaint() {
synchronized(this) {
painting = true;
// Make sure another thread isn't attempting to show from
// the back buffer.
while(
showing) {
try {
wait();
} catch (
InterruptedException ie) {
}
}
}
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINEST)) {
LOGGER.
finest("beginPaint");
}
// Reset the area that needs to be painted.
resetAccumulated();
}
public void
endPaint() {
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINEST)) {
LOGGER.
finest("endPaint: region " +
accumulatedX + " " +
accumulatedY + " " +
accumulatedMaxX + " " +
accumulatedMaxY);
}
if (
painting) {
if (!
flushAccumulatedRegion()) {
if (!
isRepaintingRoot()) {
repaintRoot(
rootJ);
}
else {
// Contents lost twice in a row, punt.
resetDoubleBufferPerWindow();
// In case we've left junk on the screen, force a repaint.
rootJ.
repaint();
}
}
}
BufferInfo toDispose = null;
synchronized(this) {
painting = false;
if (
disposeBufferOnEnd) {
disposeBufferOnEnd = false;
toDispose =
bufferInfo;
bufferInfos.
remove(
toDispose);
}
}
if (
toDispose != null) {
toDispose.
dispose();
}
}
/**
* Renders the BufferStrategy to the screen.
*
* @return true if successful, false otherwise.
*/
private boolean
flushAccumulatedRegion() {
boolean
success = true;
if (
accumulatedX !=
Integer.
MAX_VALUE) {
SubRegionShowable bsSubRegion = (
SubRegionShowable)
bufferStrategy;
boolean
contentsLost =
bufferStrategy.
contentsLost();
if (!
contentsLost) {
bsSubRegion.
show(
accumulatedX,
accumulatedY,
accumulatedMaxX,
accumulatedMaxY);
contentsLost =
bufferStrategy.
contentsLost();
}
if (
contentsLost) {
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("endPaint: contents lost");
}
// Shown region was bogus, mark buffer as out of sync.
bufferInfo.
setInSync(false);
success = false;
}
}
resetAccumulated();
return
success;
}
private void
resetAccumulated() {
accumulatedX =
Integer.
MAX_VALUE;
accumulatedY =
Integer.
MAX_VALUE;
accumulatedMaxX = 0;
accumulatedMaxY = 0;
}
/**
* Invoked when the double buffering or useTrueDoubleBuffering
* changes for a JRootPane. If the rootpane is not double
* buffered, or true double buffering changes we throw out any
* cache we may have.
*/
public void
doubleBufferingChanged(final
JRootPane rootPane) {
if ((!
rootPane.
isDoubleBuffered() ||
!
rootPane.
getUseTrueDoubleBuffering()) &&
rootPane.
getParent() != null) {
if (!
SwingUtilities.
isEventDispatchThread()) {
Runnable updater = new
Runnable() {
public void
run() {
doubleBufferingChanged0(
rootPane);
}
};
SwingUtilities.
invokeLater(
updater);
}
else {
doubleBufferingChanged0(
rootPane);
}
}
}
/**
* Does the work for doubleBufferingChanged.
*/
private void
doubleBufferingChanged0(
JRootPane rootPane) {
// This will only happen on the EDT.
BufferInfo info;
synchronized(this) {
// Make sure another thread isn't attempting to show from
// the back buffer.
while(
showing) {
try {
wait();
} catch (
InterruptedException ie) {
}
}
info =
getBufferInfo(
rootPane.
getParent());
if (
painting &&
bufferInfo ==
info) {
// We're in the process of painting and the user grabbed
// the Graphics. If we dispose now, endPaint will attempt
// to show a bogus BufferStrategy. Set a flag so that
// endPaint knows it needs to dispose this buffer.
disposeBufferOnEnd = true;
info = null;
} else if (
info != null) {
bufferInfos.
remove(
info);
}
}
if (
info != null) {
info.
dispose();
}
}
/**
* Calculates information common to paint/copyArea.
*
* @return true if should use buffering per window in painting.
*/
private boolean
prepare(
JComponent c,
Container root, boolean
isPaint, int
x, int
y,
int
w, int
h) {
if (
bsg != null) {
bsg.
dispose();
bsg = null;
}
bufferStrategy = null;
if (
root != null) {
boolean
contentsLost = false;
BufferInfo bufferInfo =
getBufferInfo(
root);
if (
bufferInfo == null) {
contentsLost = true;
bufferInfo = new
BufferInfo(
root);
bufferInfos.
add(
bufferInfo);
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("prepare: new BufferInfo: " +
root);
}
}
this.
bufferInfo =
bufferInfo;
if (!
bufferInfo.
hasBufferStrategyChanged()) {
bufferStrategy =
bufferInfo.
getBufferStrategy(true);
if (
bufferStrategy != null) {
bsg =
bufferStrategy.
getDrawGraphics();
if (
bufferStrategy.
contentsRestored()) {
contentsLost = true;
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("prepare: contents restored in prepare");
}
}
}
else {
// Couldn't create BufferStrategy, fallback to normal
// painting.
return false;
}
if (
bufferInfo.
getContentsLostDuringExpose()) {
contentsLost = true;
bufferInfo.
setContentsLostDuringExpose(false);
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("prepare: contents lost on expose");
}
}
if (
isPaint &&
c ==
rootJ &&
x == 0 &&
y == 0 &&
c.
getWidth() ==
w &&
c.
getHeight() ==
h) {
bufferInfo.
setInSync(true);
}
else if (
contentsLost) {
// We either recreated the BufferStrategy, or the contents
// of the buffer strategy were restored. We need to
// repaint the root pane so that the back buffer is in sync
// again.
bufferInfo.
setInSync(false);
if (!
isRepaintingRoot()) {
repaintRoot(
rootJ);
}
else {
// Contents lost twice in a row, punt
resetDoubleBufferPerWindow();
}
}
return (
bufferInfos != null);
}
}
return false;
}
private
Container fetchRoot(
JComponent c) {
boolean
encounteredHW = false;
rootJ =
c;
Container root =
c;
xOffset =
yOffset = 0;
while (
root != null &&
(!(
root instanceof
Window) &&
!
SunToolkit.
isInstanceOf(
root, "java.applet.Applet"))) {
xOffset +=
root.
getX();
yOffset +=
root.
getY();
root =
root.
getParent();
if (
root != null) {
if (
root instanceof
JComponent) {
rootJ = (
JComponent)
root;
}
else if (!
root.
isLightweight()) {
if (!
encounteredHW) {
encounteredHW = true;
}
else {
// We've encountered two hws now and may have
// a containment hierarchy with lightweights containing
// heavyweights containing other lightweights.
// Heavyweights poke holes in lightweight
// rendering so that if we call show on the BS
// (which is associated with the Window) you will
// not see the contents over any child
// heavyweights. If we didn't do this when we
// went to show the descendants of the nested hw
// you would see nothing, so, we bail out here.
return null;
}
}
}
}
if ((
root instanceof
RootPaneContainer) &&
(
rootJ instanceof
JRootPane)) {
// We're in a Swing heavyeight (JFrame/JWindow...), use double
// buffering if double buffering enabled on the JRootPane and
// the JRootPane wants true double buffering.
if (
rootJ.
isDoubleBuffered() &&
((
JRootPane)
rootJ).
getUseTrueDoubleBuffering()) {
// Whether or not a component is double buffered is a
// bit tricky with Swing. This gives a good approximation
// of the various ways to turn on double buffering for
// components.
return
root;
}
}
// Don't do true double buffering.
return null;
}
/**
* Turns off double buffering per window.
*/
private void
resetDoubleBufferPerWindow() {
if (
bufferInfos != null) {
dispose(
bufferInfos);
bufferInfos = null;
repaintManager.
setPaintManager(null);
}
}
/**
* Returns the BufferInfo for the specified root or null if one
* hasn't been created yet.
*/
private
BufferInfo getBufferInfo(
Container root) {
for (int
counter =
bufferInfos.
size() - 1;
counter >= 0;
counter--) {
BufferInfo bufferInfo =
bufferInfos.
get(
counter);
Container biRoot =
bufferInfo.
getRoot();
if (
biRoot == null) {
// Window gc'ed
bufferInfos.
remove(
counter);
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("BufferInfo pruned, root null");
}
}
else if (
biRoot ==
root) {
return
bufferInfo;
}
}
return null;
}
private void
accumulate(int
x, int
y, int
w, int
h) {
accumulatedX =
Math.
min(
x,
accumulatedX);
accumulatedY =
Math.
min(
y,
accumulatedY);
accumulatedMaxX =
Math.
max(
accumulatedMaxX,
x +
w);
accumulatedMaxY =
Math.
max(
accumulatedMaxY,
y +
h);
}
/**
* BufferInfo is used to track the BufferStrategy being used for
* a particular Component. In addition to tracking the BufferStrategy
* it will install a WindowListener and ComponentListener. When the
* component is hidden/iconified the buffer is marked as needing to be
* completely repainted.
*/
private class
BufferInfo extends
ComponentAdapter implements
WindowListener {
// NOTE: This class does NOT hold a direct reference to the root, if it
// did there would be a cycle between the BufferPerWindowPaintManager
// and the Window so that it could never be GC'ed
//
// Reference to BufferStrategy is referenced via WeakReference for
// same reason.
private
WeakReference<
BufferStrategy>
weakBS;
private
WeakReference<
Container>
root;
// Indicates whether or not the backbuffer and display are in sync.
// This is set to true when a full repaint on the rootpane is done.
private boolean
inSync;
// Indicates the contents were lost during and expose event.
private boolean
contentsLostDuringExpose;
// Indicates we need to generate a paint event on expose.
private boolean
paintAllOnExpose;
public
BufferInfo(
Container root) {
this.
root = new
WeakReference<
Container>(
root);
root.
addComponentListener(this);
if (
root instanceof
Window) {
((
Window)
root).
addWindowListener(this);
}
}
public void
setPaintAllOnExpose(boolean
paintAllOnExpose) {
this.
paintAllOnExpose =
paintAllOnExpose;
}
public boolean
getPaintAllOnExpose() {
return
paintAllOnExpose;
}
public void
setContentsLostDuringExpose(boolean
value) {
contentsLostDuringExpose =
value;
}
public boolean
getContentsLostDuringExpose() {
return
contentsLostDuringExpose;
}
public void
setInSync(boolean
inSync) {
this.
inSync =
inSync;
}
/**
* Whether or not the contents of the buffer strategy
* is in sync with the window. This is set to true when the root
* pane paints all, and false when contents are lost/restored.
*/
public boolean
isInSync() {
return
inSync;
}
/**
* Returns the Root (Window or Applet) that this BufferInfo references.
*/
public
Container getRoot() {
return (
root == null) ? null :
root.
get();
}
/**
* Returns the BufferStartegy. This will return null if
* the BufferStartegy hasn't been created and <code>create</code> is
* false, or if there is a problem in creating the
* <code>BufferStartegy</code>.
*
* @param create If true, and the BufferStartegy is currently null,
* one will be created.
*/
public
BufferStrategy getBufferStrategy(boolean
create) {
BufferStrategy bs = (
weakBS == null) ? null :
weakBS.
get();
if (
bs == null &&
create) {
bs =
createBufferStrategy();
if (
bs != null) {
weakBS = new
WeakReference<
BufferStrategy>(
bs);
}
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("getBufferStrategy: created bs: " +
bs);
}
}
return
bs;
}
/**
* Returns true if the buffer strategy of the component differs
* from current buffer strategy.
*/
public boolean
hasBufferStrategyChanged() {
Container root =
getRoot();
if (
root != null) {
BufferStrategy ourBS = null;
BufferStrategy componentBS = null;
ourBS =
getBufferStrategy(false);
if (
root instanceof
Window) {
componentBS = ((
Window)
root).
getBufferStrategy();
}
else {
try {
componentBS = (
BufferStrategy)
getGetBufferStrategyMethod().
invoke(
root);
} catch (
InvocationTargetException ite) {
assert false;
} catch (
IllegalArgumentException iae) {
assert false;
} catch (
IllegalAccessException iae2) {
assert false;
}
}
if (
componentBS !=
ourBS) {
// Component has a different BS, dispose ours.
if (
ourBS != null) {
ourBS.
dispose();
}
weakBS = null;
return true;
}
}
return false;
}
/**
* Creates the BufferStrategy. If the appropriate system property
* has been set we'll try for flip first and then we'll try for
* blit.
*/
private
BufferStrategy createBufferStrategy() {
Container root =
getRoot();
if (
root == null) {
return null;
}
BufferStrategy bs = null;
if (
SwingUtilities3.
isVsyncRequested(
root)) {
bs =
createBufferStrategy(
root, true);
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("createBufferStrategy: using vsynced strategy");
}
}
if (
bs == null) {
bs =
createBufferStrategy(
root, false);
}
if (!(
bs instanceof
SubRegionShowable)) {
// We do this for two reasons:
// 1. So that we know we can cast to SubRegionShowable and
// invoke show with the minimal region to update
// 2. To avoid the possibility of invoking client code
// on the toolkit thread.
bs = null;
}
return
bs;
}
// Creates and returns a buffer strategy. If
// there is a problem creating the buffer strategy this will
// eat the exception and return null.
private
BufferStrategy createBufferStrategy(
Container root,
boolean
isVsynced) {
BufferCapabilities caps;
if (
isVsynced) {
caps = new
ExtendedBufferCapabilities(
new
ImageCapabilities(true), new
ImageCapabilities(true),
BufferCapabilities.
FlipContents.
COPIED,
ExtendedBufferCapabilities.
VSyncType.
VSYNC_ON);
} else {
caps = new
BufferCapabilities(
new
ImageCapabilities(true), new
ImageCapabilities(true),
null);
}
BufferStrategy bs = null;
if (
SunToolkit.
isInstanceOf(
root, "java.applet.Applet")) {
try {
getCreateBufferStrategyMethod().
invoke(
root, 2,
caps);
bs = (
BufferStrategy)
getGetBufferStrategyMethod().
invoke(
root);
} catch (
InvocationTargetException ite) {
// Type is not supported
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("createBufferStratety failed",
ite);
}
} catch (
IllegalArgumentException iae) {
assert false;
} catch (
IllegalAccessException iae2) {
assert false;
}
}
else {
try {
((
Window)
root).
createBufferStrategy(2,
caps);
bs = ((
Window)
root).
getBufferStrategy();
} catch (
AWTException e) {
// Type not supported
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("createBufferStratety failed",
e);
}
}
}
return
bs;
}
/**
* Cleans up and removes any references.
*/
public void
dispose() {
Container root =
getRoot();
if (
LOGGER.
isLoggable(
PlatformLogger.
Level.
FINER)) {
LOGGER.
finer("disposed BufferInfo for: " +
root);
}
if (
root != null) {
root.
removeComponentListener(this);
if (
root instanceof
Window) {
((
Window)
root).
removeWindowListener(this);
}
BufferStrategy bs =
getBufferStrategy(false);
if (
bs != null) {
bs.
dispose();
}
}
this.
root = null;
weakBS = null;
}
// We mark the buffer as needing to be painted on a hide/iconify
// because the developer may have conditionalized painting based on
// visibility.
// Ideally we would also move to having the BufferStrategy being
// a SoftReference in Component here, but that requires changes to
// Component and the like.
public void
componentHidden(
ComponentEvent e) {
Container root =
getRoot();
if (
root != null &&
root.
isVisible()) {
// This case will only happen if a developer calls
// hide immediately followed by show. In this case
// the event is delivered after show and the window
// will still be visible. If a developer altered the
// contents of the window between the hide/show
// invocations we won't recognize we need to paint and
// the contents would be bogus. Calling repaint here
// fixs everything up.
root.
repaint();
}
else {
setPaintAllOnExpose(true);
}
}
public void
windowIconified(
WindowEvent e) {
setPaintAllOnExpose(true);
}
// On a dispose we chuck everything.
public void
windowClosed(
WindowEvent e) {
// Make sure we're not showing.
synchronized(
BufferStrategyPaintManager.this) {
while (
showing) {
try {
BufferStrategyPaintManager.this.
wait();
} catch (
InterruptedException ie) {
}
}
bufferInfos.
remove(this);
}
dispose();
}
public void
windowOpened(
WindowEvent e) {
}
public void
windowClosing(
WindowEvent e) {
}
public void
windowDeiconified(
WindowEvent e) {
}
public void
windowActivated(
WindowEvent e) {
}
public void
windowDeactivated(
WindowEvent e) {
}
}
}