| /* | |
| * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * 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. | |
| */ | |
| package javax.swing; | |
| import java.awt.*; | |
| import java.awt.event.*; | |
| import java.awt.image.*; | |
| import java.lang.ref.WeakReference; | |
| import java.util.*; | |
| import com.sun.java.swing.SwingUtilities3; | |
| import sun.awt.AWTAccessor; | |
| 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 overridden 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. | |
| // | |
| 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; | |
| BufferStrategyPaintManager() { | |
| bufferInfos = new ArrayList<BufferInfo>(1); | |
| } | |
| // | |
| // PaintManager methods | |
| // | |
| /** | |
| * Cleans up any created BufferStrategies. | |
| */ | |
| protected void dispose() { | |
| // dispose can be invoked at any random time. To avoid | |
| // threading dependencies we do the actual disposing 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); | |
| if (!bufferComponent.isOpaque()) { | |
| final SunGraphics2D g2d = (SunGraphics2D) bsg; | |
| final Color oldBg = g2d.getBackground(); | |
| g2d.setBackground(paintingComponent.getBackground()); | |
| g2d.clearRect(x, y, w, h); | |
| g2d.setBackground(oldBg); | |
| } | |
| 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 heavyweight (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 BufferStrategy. This will return null if | |
| * the BufferStrategy hasn't been created and <code>create</code> is | |
| * false, or if there is a problem in creating the | |
| * <code>BufferStrategy</code>. | |
| * | |
| * @param create If true and the BufferStrategy 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 { | |
| componentBS = AWTAccessor.getComponentAccessor().getBufferStrategy(root); | |
| } | |
| 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 { | |
| AWTAccessor.ComponentAccessor componentAccessor | |
| = AWTAccessor.getComponentAccessor(); | |
| componentAccessor.createBufferStrategy(root, 2, caps); | |
| bs = componentAccessor.getBufferStrategy(root); | |
| } catch (AWTException e) { | |
| // Type is not supported | |
| if (LOGGER.isLoggable(PlatformLogger.Level.FINER)) { | |
| LOGGER.finer("createBufferStrategy failed", | |
| e); | |
| } | |
| } | |
| } | |
| 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("createBufferStrategy 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) { | |
| } | |
| } | |
| } |