|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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"); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private ArrayList<BufferInfo> bufferInfos; |
|
|
|
|
|
|
|
|
|
*/ |
|
private boolean painting; |
|
|
|
|
|
|
|
*/ |
|
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 |
|
// |
|
|
|
|
|
|
|
*/ |
|
private JComponent rootJ; |
|
|
|
|
|
*/ |
|
private int xOffset; |
|
|
|
|
|
*/ |
|
private int yOffset; |
|
|
|
|
|
*/ |
|
private Graphics bsg; |
|
|
|
|
|
*/ |
|
private BufferStrategy bufferStrategy; |
|
|
|
|
|
*/ |
|
private BufferInfo bufferInfo; |
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
// |
|
|
|
|
|
|
|
*/ |
|
protected void dispose() { |
|
// dipose can be invoked at any random time. To avoid |
|
// threading dependancies we do the actual diposing via an |
|
|
|
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(); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
bufferInfo.setInSync(false); |
|
// Fall through to old rendering. |
|
} |
|
} |
|
|
|
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, |
|
|
|
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 |
|
|
|
if (!flushAccumulatedRegion()) { |
|
// Flush failed, copyArea will be copying garbage, |
|
|
|
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 |
|
|
|
while(showing) { |
|
try { |
|
wait(); |
|
} catch (InterruptedException ie) { |
|
} |
|
} |
|
} |
|
if (LOGGER.isLoggable(PlatformLogger.Level.FINEST)) { |
|
LOGGER.finest("beginPaint"); |
|
} |
|
|
|
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 { |
|
|
|
resetDoubleBufferPerWindow(); |
|
|
|
rootJ.repaint(); |
|
} |
|
} |
|
} |
|
|
|
BufferInfo toDispose = null; |
|
synchronized(this) { |
|
painting = false; |
|
if (disposeBufferOnEnd) { |
|
disposeBufferOnEnd = false; |
|
toDispose = bufferInfo; |
|
bufferInfos.remove(toDispose); |
|
} |
|
} |
|
if (toDispose != null) { |
|
toDispose.dispose(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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"); |
|
} |
|
|
|
bufferInfo.setInSync(false); |
|
success = false; |
|
} |
|
} |
|
resetAccumulated(); |
|
return success; |
|
} |
|
|
|
private void resetAccumulated() { |
|
accumulatedX = Integer.MAX_VALUE; |
|
accumulatedY = Integer.MAX_VALUE; |
|
accumulatedMaxX = 0; |
|
accumulatedMaxY = 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void doubleBufferingChanged0(JRootPane rootPane) { |
|
|
|
BufferInfo info; |
|
synchronized(this) { |
|
// Make sure another thread isn't attempting to show from |
|
|
|
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 |
|
|
|
disposeBufferOnEnd = true; |
|
info = null; |
|
} else if (info != null) { |
|
bufferInfos.remove(info); |
|
} |
|
} |
|
if (info != null) { |
|
info.dispose(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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 |
|
|
|
bufferInfo.setInSync(false); |
|
if (!isRepaintingRoot()) { |
|
repaintRoot(rootJ); |
|
} |
|
else { |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
return root; |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void resetDoubleBufferPerWindow() { |
|
if (bufferInfos != null) { |
|
dispose(bufferInfos); |
|
bufferInfos = null; |
|
repaintManager.setPaintManager(null); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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) { |
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
private WeakReference<BufferStrategy> weakBS; |
|
private WeakReference<Container> root; |
|
// Indicates whether or not the backbuffer and display are in sync. |
|
|
|
private boolean inSync; |
|
|
|
private boolean contentsLostDuringExpose; |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isInSync() { |
|
return inSync; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Container getRoot() { |
|
return (root == null) ? null : root.get(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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) { |
|
|
|
if (ourBS != null) { |
|
ourBS.dispose(); |
|
} |
|
weakBS = null; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
bs = null; |
|
} |
|
return bs; |
|
} |
|
|
|
// Creates and returns a buffer strategy. If |
|
// there is a problem creating the buffer strategy this will |
|
|
|
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) { |
|
|
|
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) { |
|
|
|
if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) { |
|
LOGGER.finer("createBufferStratety failed", |
|
e); |
|
} |
|
} |
|
} |
|
return bs; |
|
} |
|
|
|
|
|
|
|
*/ |
|
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 |
|
|
|
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 |
|
|
|
root.repaint(); |
|
} |
|
else { |
|
setPaintAllOnExpose(true); |
|
} |
|
} |
|
|
|
public void windowIconified(WindowEvent e) { |
|
setPaintAllOnExpose(true); |
|
} |
|
|
|
|
|
public void windowClosed(WindowEvent e) { |
|
|
|
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) { |
|
} |
|
} |
|
} |