/** Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************/package javax.swing;import java.awt.*;import java.awt.event.*;import java.awt.image.*;import java.lang.reflect.*;import java.lang.ref.WeakReference;import java.util.*;import com.sun.java.swing.SwingUtilities3;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 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;/*** 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.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);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, puntresetDoubleBufferPerWindow();}}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 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 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'edbufferInfos.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 implementsWindowListener {// 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.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 {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.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 {getCreateBufferStrategyMethod().invoke(root, 2, caps);bs = (BufferStrategy)getGetBufferStrategyMethod().invoke(root);} catch (InvocationTargetException ite) {// Type is not supportedif (LOGGER.isLoggable(PlatformLogger.Level.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 supportedif (LOGGER.isLoggable(PlatformLogger.Level.FINER)) {LOGGER.finer("createBufferStratety 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) {}}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。