| /* |
| * Copyright (c) 2005, 2011, 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.applet.Applet; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.awt.image.*; |
| import java.lang.reflect.*; |
| import java.lang.ref.WeakReference; |
| import java.security.AccessController; |
| import java.util.*; |
| |
| import com.sun.java.swing.SwingUtilities3; |
| |
| import sun.awt.SubRegionShowable; |
| import sun.java2d.SunGraphics2D; |
| import sun.security.action.GetPropertyAction; |
| 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; |
| /** |
| * Parent Applet/Window for the current paint/copyArea |
| */ |
| private Container root; |
| /** |
| * 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.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) { |
| 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(PlatformLogger.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. |
| 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(PlatformLogger.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.FINEST)) { |
| LOGGER.finest("beginPaint"); |
| } |
| // Reset the area that needs to be painted. |
| resetAccumulated(); |
| } |
| |
| public void endPaint() { |
| if (LOGGER.isLoggable(PlatformLogger.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.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, boolean isPaint, int x, int y, |
| int w, int h) { |
| 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(PlatformLogger.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.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.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 boolean fetchRoot(JComponent c) { |
| boolean encounteredHW = false; |
| rootJ = c; |
| 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 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; |
| } |
| |
| /** |
| * 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.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.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.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.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.FINER)) { |
| LOGGER.finer("createBufferStratety failed", |
| e); |
| } |
| } |
| } |
| return bs; |
| } |
| |
| /** |
| * Cleans up and removes any references. |
| */ |
| public void dispose() { |
| Container root = getRoot(); |
| if (LOGGER.isLoggable(PlatformLogger.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) { |
| } |
| } |
| } |