* @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) { } }}Lines 501–956 of 956. Previous lines
* @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) { } } }