/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* A PaintManager implementation that uses a BufferStrategy for
* rendering.
*
* @author Scott Violet
*/
//
// 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.
//
"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.
*/
/**
* 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
// 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
//
/**
*/
/**
*/
/**
* 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.
*/
/**
* BufferStrategy currently being used.
*/
/**
* BufferInfo corresponding to root.
*/
/**
* Set to true if the bufferInfo needs to be disposed when current
* paint loop is done.
*/
private boolean disposeBufferOnEnd;
if (COMPONENT_GET_BUFFER_STRATEGY_METHOD == null) {
getMethods();
}
}
if (COMPONENT_CREATE_BUFFER_STRATEGY_METHOD == null) {
getMethods();
}
}
private static void getMethods() {
try {
getDeclaredMethod("createBufferStrategy",
new Class[] { int.class,
BufferCapabilities.class });
setAccessible(true);
getDeclaredMethod("getBufferStrategy");
} catch (SecurityException e) {
assert false;
} catch (NoSuchMethodException nsme) {
assert false;
}
return null;
}
});
}
}
//
// 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.
public void run() {
synchronized(BufferStrategyPaintManager.this) {
while (showing) {
try {
BufferStrategyPaintManager.this.wait();
} catch (InterruptedException ie) {
}
}
}
}
});
}
new RuntimeException());
}
if (bufferInfos != null) {
}
}
}
/**
* 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 {
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.
}
}
finally {
synchronized(this) {
showing = false;
notifyAll();
}
}
return false;
}
int x, int y, int w, int h) {
if (prepare(paintingComponent, true, x, y, w, h)) {
if ((g instanceof SunGraphics2D) &&
// 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.
}
x + w, y + h);
x + w, y + 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.
}
}
// 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 (clip) {
}
else {
deltaY);
}
} else {
}
// 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.
} else {
}
}
}
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) {
}
}
}
}
// Reset the area that needs to be painted.
}
public void endPaint() {
}
if (painting) {
if (!flushAccumulatedRegion()) {
if (!isRepaintingRoot()) {
}
else {
// Contents lost twice in a row, punt.
// In case we've left junk on the screen, force a repaint.
}
}
}
synchronized(this) {
painting = false;
if (disposeBufferOnEnd) {
disposeBufferOnEnd = false;
}
}
}
}
/**
* Renders the BufferStrategy to the screen.
*
* @return true if successful, false otherwise.
*/
private boolean flushAccumulatedRegion() {
boolean success = true;
if (!contentsLost) {
}
if (contentsLost) {
}
// Shown region was bogus, mark buffer as out of sync.
bufferInfo.setInSync(false);
success = false;
}
}
return success;
}
private void resetAccumulated() {
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.
*/
if ((!rootPane.isDoubleBuffered() ||
if (!SwingUtilities.isEventDispatchThread()) {
public void run() {
}
};
}
else {
}
}
}
/**
* Does the work for doubleBufferingChanged.
*/
// This will only happen on the EDT.
synchronized(this) {
// Make sure another thread isn't attempting to show from
// the back buffer.
while(showing) {
try {
wait();
} catch (InterruptedException ie) {
}
}
// 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;
}
}
}
}
/**
*
* @return true if should use buffering per window in painting.
*/
int w, int h) {
}
if (fetchRoot(c)) {
boolean contentsLost = false;
if (bufferInfo == null) {
contentsLost = true;
}
}
this.bufferInfo = bufferInfo;
if (!bufferInfo.hasBufferStrategyChanged()) {
if (bufferStrategy != null) {
if (bufferStrategy.contentsRestored()) {
contentsLost = true;
"prepare: contents restored in prepare");
}
}
}
else {
// Couldn't create BufferStrategy, fallback to normal
// painting.
return false;
}
if (bufferInfo.getContentsLostDuringExpose()) {
contentsLost = true;
}
}
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()) {
}
else {
// Contents lost twice in a row, punt
}
}
return (bufferInfos != null);
}
}
return false;
}
boolean encounteredHW = false;
rootJ = c;
root = c;
if (root instanceof JComponent) {
}
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) &&
// buffering if double buffering enabled on the JRootPane and
// the JRootPane wants true double buffering.
if (rootJ.isDoubleBuffered() &&
// 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;
}
/**
* Turns off double buffering per window.
*/
private void resetDoubleBufferPerWindow() {
if (bufferInfos != null) {
bufferInfos = null;
}
}
/**
* Returns the BufferInfo for the specified root or null if one
* hasn't been created yet.
*/
// Window gc'ed
}
}
return bufferInfo;
}
}
return null;
}
private void accumulate(int x, int y, int w, int 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
* completely repainted.
*/
// 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.
// 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;
root.addComponentListener(this);
}
}
this.paintAllOnExpose = paintAllOnExpose;
}
public boolean getPaintAllOnExpose() {
return paintAllOnExpose;
}
}
public boolean getContentsLostDuringExpose() {
return contentsLostDuringExpose;
}
}
/**
* Whether or not the contents of the buffer strategy
* is in sync with the window. This is set to true when the root
*/
public boolean isInSync() {
return inSync;
}
/**
* Returns the Root (Window or Applet) that this BufferInfo references.
*/
}
/**
* 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.
*/
bs = createBufferStrategy();
}
}
}
return bs;
}
/**
* Returns true if the buffer strategy of the component differs
* from current buffer strategy.
*/
public boolean hasBufferStrategyChanged() {
ourBS = getBufferStrategy(false);
}
else {
try {
} 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.
}
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.
*/
return null;
}
}
}
}
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.
}
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.
boolean isVsynced) {
if (isVsynced) {
caps = new ExtendedBufferCapabilities(
new ImageCapabilities(true), new ImageCapabilities(true),
} else {
caps = new BufferCapabilities(
new ImageCapabilities(true), new ImageCapabilities(true),
null);
}
try {
} catch (InvocationTargetException ite) {
// Type is not supported
ite);
}
} catch (IllegalArgumentException iae) {
assert false;
} catch (IllegalAccessException iae2) {
assert false;
}
}
else {
try {
} catch (AWTException e) {
// Type not supported
e);
}
}
}
return bs;
}
/**
* Cleans up and removes any references.
*/
public void dispose() {
}
root.removeComponentListener(this);
}
}
}
}
// 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.
// 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
// invocations we won't recognize we need to paint and
// the contents would be bogus. Calling repaint here
// fixs everything up.
}
else {
setPaintAllOnExpose(true);
}
}
setPaintAllOnExpose(true);
}
// On a dispose we chuck everything.
// Make sure we're not showing.
synchronized(BufferStrategyPaintManager.this) {
while (showing) {
try {
BufferStrategyPaintManager.this.wait();
} catch (InterruptedException ie) {
}
}
bufferInfos.remove(this);
}
dispose();
}
}
}
}
}
}
}
}