BufferStrategyPaintManagerpublic class BufferStrategyPaintManager extends RepaintManager$PaintManager A PaintManager implementation that uses a BufferStrategy for
rendering. |
Fields Summary |
---|
private static Method | COMPONENT_CREATE_BUFFER_STRATEGY_METHOD | private static Method | COMPONENT_GET_BUFFER_STRATEGY_METHOD | private static boolean | TRY_FLIPIndicates whether or not we should try and get a flip buffer strategy
first, default is false. | private static final Logger | LOGGER | private ArrayList | bufferInfosList 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 boolean | paintingIndicates beginPaint has been invoked. This is
set to true for the life of beginPaint/endPaint pair. | private boolean | showingIndicates we're in the process of showing. All painting, on the EDT,
is blocked while this is true. | private int | accumulatedX | private int | accumulatedY | private int | accumulatedMaxX | private int | accumulatedMaxY | private JComponent | rootJFarthest JComponent ancestor for the current paint/copyArea. | private Container | rootParent Applet/Window for the current paint/copyArea | private int | xOffsetLocation of component being painted relative to root. | private int | yOffsetLocation of component being painted relative to root. | private Graphics | bsgGraphics from the BufferStrategy. | private BufferStrategy | bufferStrategyBufferStrategy currently being used. | private BufferInfo | bufferInfoBufferInfo corresponding to root. | private boolean | disposeBufferOnEndSet to true if the bufferInfo needs to be disposed when current
paint loop is done. |
Constructors Summary |
---|
BufferStrategyPaintManager()
bufferInfos = new ArrayList<BufferInfo>(1);
|
Methods Summary |
---|
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);
| 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(Level.FINEST)) {
LOGGER.finest("beginPaint");
}
// Reset the area that needs to be painted.
resetAccumulated();
| public void | copyArea(javax.swing.JComponent c, java.awt.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.
if (prepare(c, 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(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);
}
}
| protected void | dispose()Cleans up any created BufferStrategies.
// 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 {
wait();
} catch (InterruptedException ie) {
}
}
bufferInfos = BufferStrategyPaintManager.this.bufferInfos;
BufferStrategyPaintManager.this.bufferInfos = null;
}
dispose(bufferInfos);
}
});
| private void | dispose(java.util.List bufferInfos)
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.log(Level.FINER, "BufferStrategyPaintManager disposed",
new RuntimeException());
}
if (bufferInfos != null) {
for (BufferInfo bufferInfo : bufferInfos) {
bufferInfo.dispose();
}
}
| public void | doubleBufferingChanged(javax.swing.JRootPane rootPane)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.
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(javax.swing.JRootPane rootPane)Does the work for doubleBufferingChanged.
// 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();
}
| public void | endPaint()
if (LOGGER.isLoggable(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();
}
| private boolean | fetchRoot(javax.swing.JComponent c)
boolean encounteredHW = false;
rootJ = c;
root = c;
xOffset = yOffset = 0;
while (root != null && (!(root instanceof Window) &&
!(root instanceof 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 false;
}
}
}
}
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 true;
}
}
// Don't do true double buffering.
return false;
| private boolean | flushAccumulatedRegion()Renders the BufferStrategy to the screen.
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(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 javax.swing.BufferStrategyPaintManager$BufferInfo | getBufferInfo(java.awt.Container root)Returns the BufferInfo for the specified root or null if one
hasn't been created yet.
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(Level.FINER)) {
LOGGER.finer("BufferInfo pruned, root null");
}
}
else if (biRoot == root) {
return bufferInfo;
}
}
return null;
| private static java.lang.reflect.Method | getCreateBufferStrategyMethod()
if (COMPONENT_CREATE_BUFFER_STRATEGY_METHOD == null) {
getMethods();
}
return COMPONENT_CREATE_BUFFER_STRATEGY_METHOD;
| private static java.lang.reflect.Method | getGetBufferStrategyMethod()
TRY_FLIP = "true".equals(AccessController.doPrivileged(
new GetPropertyAction("swing.useFlipBufferStrategy", "false")));
if (COMPONENT_GET_BUFFER_STRATEGY_METHOD == null) {
getMethods();
}
return COMPONENT_GET_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;
}
});
| public boolean | paint(javax.swing.JComponent paintingComponent, javax.swing.JComponent bufferComponent, java.awt.Graphics g, int x, int y, int w, int h)
if (prepare(paintingComponent, 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(Level.FINER)) {
LOGGER.finer("prepare failed");
}
return super.paint(paintingComponent, bufferComponent, g, x, y, w, h);
| private boolean | prepare(javax.swing.JComponent c, boolean isPaint, int x, int y, int w, int h)Calculates information common to paint/copyArea.
if (bsg != null) {
bsg.dispose();
bsg = null;
}
bufferStrategy = null;
if (fetchRoot(c)) {
boolean contentsLost = false;
BufferInfo bufferInfo = getBufferInfo(root);
if (bufferInfo == null) {
contentsLost = true;
bufferInfo = new BufferInfo(root);
bufferInfos.add(bufferInfo);
if (LOGGER.isLoggable(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(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(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 void | resetAccumulated()
accumulatedX = Integer.MAX_VALUE;
accumulatedY = Integer.MAX_VALUE;
accumulatedMaxX = 0;
accumulatedMaxY = 0;
| private void | resetDoubleBufferPerWindow()Turns off double buffering per window.
if (bufferInfos != null) {
dispose(bufferInfos);
bufferInfos = null;
repaintManager.setPaintManager(null);
}
| public boolean | show(java.awt.Container c, int x, int y, int w, int h)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.
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.usingFlip && info.isInSync() &&
(bufferStrategy = info.getBufferStrategy(false)) != null) {
SubRegionShowable bsSubRegion =
(SubRegionShowable)bufferStrategy;
boolean paintAllOnExpose = info.getPaintAllOnExpose();
info.setPaintAllOnExpose(false);
if (bsSubRegion.validateAndShow(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;
|
|