956 lines · 35.5 KB · Java
RawDownload
/* * 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.     *

Lines 1–500 of 956.