src/java.desktop/share/classes/javax/swing/RepaintManager.java
changeset 47216 71c04702a3d5
parent 43078 1d242b27f9e2
child 49086 96ea4dff640e
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package javax.swing;
       
    26 
       
    27 
       
    28 import java.awt.*;
       
    29 import java.awt.event.*;
       
    30 import java.awt.image.VolatileImage;
       
    31 import java.security.AccessControlContext;
       
    32 import java.security.AccessController;
       
    33 import java.security.PrivilegedAction;
       
    34 import java.util.*;
       
    35 import java.util.concurrent.atomic.AtomicInteger;
       
    36 import java.applet.*;
       
    37 
       
    38 import jdk.internal.misc.JavaSecurityAccess;
       
    39 import jdk.internal.misc.SharedSecrets;
       
    40 import sun.awt.AWTAccessor;
       
    41 import sun.awt.AppContext;
       
    42 import sun.awt.DisplayChangedListener;
       
    43 import sun.awt.SunToolkit;
       
    44 import sun.java2d.SunGraphicsEnvironment;
       
    45 import sun.security.action.GetPropertyAction;
       
    46 
       
    47 import com.sun.java.swing.SwingUtilities3;
       
    48 import java.awt.geom.AffineTransform;
       
    49 import sun.java2d.SunGraphics2D;
       
    50 import sun.java2d.pipe.Region;
       
    51 import sun.swing.SwingAccessor;
       
    52 import sun.swing.SwingUtilities2;
       
    53 import sun.swing.SwingUtilities2.RepaintListener;
       
    54 
       
    55 /**
       
    56  * This class manages repaint requests, allowing the number
       
    57  * of repaints to be minimized, for example by collapsing multiple
       
    58  * requests into a single repaint for members of a component tree.
       
    59  * <p>
       
    60  * As of 1.6 <code>RepaintManager</code> handles repaint requests
       
    61  * for Swing's top level components (<code>JApplet</code>,
       
    62  * <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>).
       
    63  * Any calls to <code>repaint</code> on one of these will call into the
       
    64  * appropriate <code>addDirtyRegion</code> method.
       
    65  *
       
    66  * @author Arnaud Weber
       
    67  * @since 1.2
       
    68  */
       
    69 public class RepaintManager
       
    70 {
       
    71     /**
       
    72      * Whether or not the RepaintManager should handle paint requests
       
    73      * for top levels.
       
    74      */
       
    75     static final boolean HANDLE_TOP_LEVEL_PAINT;
       
    76 
       
    77     private static final short BUFFER_STRATEGY_NOT_SPECIFIED = 0;
       
    78     private static final short BUFFER_STRATEGY_SPECIFIED_ON = 1;
       
    79     private static final short BUFFER_STRATEGY_SPECIFIED_OFF = 2;
       
    80 
       
    81     private static final short BUFFER_STRATEGY_TYPE;
       
    82 
       
    83     /**
       
    84      * Maps from GraphicsConfiguration to VolatileImage.
       
    85      */
       
    86     private Map<GraphicsConfiguration,VolatileImage> volatileMap = new
       
    87                         HashMap<GraphicsConfiguration,VolatileImage>(1);
       
    88 
       
    89     //
       
    90     // As of 1.6 Swing handles scheduling of paint events from native code.
       
    91     // That is, SwingPaintEventDispatcher is invoked on the toolkit thread,
       
    92     // which in turn invokes nativeAddDirtyRegion.  Because this is invoked
       
    93     // from the native thread we can not invoke any public methods and so
       
    94     // we introduce these added maps.  So, any time nativeAddDirtyRegion is
       
    95     // invoked the region is added to hwDirtyComponents and a work request
       
    96     // is scheduled.  When the work request is processed all entries in
       
    97     // this map are pushed to the real map (dirtyComponents) and then
       
    98     // painted with the rest of the components.
       
    99     //
       
   100     private Map<Container,Rectangle> hwDirtyComponents;
       
   101 
       
   102     private Map<Component,Rectangle> dirtyComponents;
       
   103     private Map<Component,Rectangle> tmpDirtyComponents;
       
   104     private java.util.List<Component> invalidComponents;
       
   105 
       
   106     // List of Runnables that need to be processed before painting from AWT.
       
   107     private java.util.List<Runnable> runnableList;
       
   108 
       
   109     boolean   doubleBufferingEnabled = true;
       
   110 
       
   111     private Dimension doubleBufferMaxSize;
       
   112 
       
   113     // Support for both the standard and volatile offscreen buffers exists to
       
   114     // provide backwards compatibility for the [rare] programs which may be
       
   115     // calling getOffScreenBuffer() and not expecting to get a VolatileImage.
       
   116     // Swing internally is migrating to use *only* the volatile image buffer.
       
   117 
       
   118     // Support for standard offscreen buffer
       
   119     //
       
   120     DoubleBufferInfo standardDoubleBuffer;
       
   121 
       
   122     /**
       
   123      * Object responsible for hanlding core paint functionality.
       
   124      */
       
   125     private PaintManager paintManager;
       
   126 
       
   127     private static final Object repaintManagerKey = RepaintManager.class;
       
   128 
       
   129     // Whether or not a VolatileImage should be used for double-buffered painting
       
   130     static boolean volatileImageBufferEnabled = true;
       
   131     /**
       
   132      * Type of VolatileImage which should be used for double-buffered
       
   133      * painting.
       
   134      */
       
   135     private static final int volatileBufferType;
       
   136     /**
       
   137      * Value of the system property awt.nativeDoubleBuffering.
       
   138      */
       
   139     private static boolean nativeDoubleBuffering;
       
   140 
       
   141     // The maximum number of times Swing will attempt to use the VolatileImage
       
   142     // buffer during a paint operation.
       
   143     private static final int VOLATILE_LOOP_MAX = 2;
       
   144 
       
   145     /**
       
   146      * Number of <code>beginPaint</code> that have been invoked.
       
   147      */
       
   148     private int paintDepth = 0;
       
   149 
       
   150     /**
       
   151      * Type of buffer strategy to use.  Will be one of the BUFFER_STRATEGY_
       
   152      * constants.
       
   153      */
       
   154     private short bufferStrategyType;
       
   155 
       
   156     //
       
   157     // BufferStrategyPaintManager has the unique characteristic that it
       
   158     // must deal with the buffer being lost while painting to it.  For
       
   159     // example, if we paint a component and show it and the buffer has
       
   160     // become lost we must repaint the whole window.  To deal with that
       
   161     // the PaintManager calls into repaintRoot, and if we're still in
       
   162     // the process of painting the repaintRoot field is set to the JRootPane
       
   163     // and after the current JComponent.paintImmediately call finishes
       
   164     // paintImmediately will be invoked on the repaintRoot.  In this
       
   165     // way we don't try to show garbage to the screen.
       
   166     //
       
   167     /**
       
   168      * True if we're in the process of painting the dirty regions.  This is
       
   169      * set to true in <code>paintDirtyRegions</code>.
       
   170      */
       
   171     private boolean painting;
       
   172     /**
       
   173      * If the PaintManager calls into repaintRoot during painting this field
       
   174      * will be set to the root.
       
   175      */
       
   176     private JComponent repaintRoot;
       
   177 
       
   178     /**
       
   179      * The Thread that has initiated painting.  If null it
       
   180      * indicates painting is not currently in progress.
       
   181      */
       
   182     private Thread paintThread;
       
   183 
       
   184     /**
       
   185      * Runnable used to process all repaint/revalidate requests.
       
   186      */
       
   187     private final ProcessingRunnable processingRunnable;
       
   188 
       
   189     private static final JavaSecurityAccess javaSecurityAccess =
       
   190             SharedSecrets.getJavaSecurityAccess();
       
   191 
       
   192     /**
       
   193      * Listener installed to detect display changes. When display changes,
       
   194      * schedules a callback to notify all RepaintManagers of the display
       
   195      * changes.
       
   196      */
       
   197     private static final DisplayChangedListener displayChangedHandler =
       
   198             new DisplayChangedHandler();
       
   199 
       
   200     static {
       
   201         SwingAccessor.setRepaintManagerAccessor(new SwingAccessor.RepaintManagerAccessor() {
       
   202             @Override
       
   203             public void addRepaintListener(RepaintManager rm, RepaintListener l) {
       
   204                 rm.addRepaintListener(l);
       
   205             }
       
   206             @Override
       
   207             public void removeRepaintListener(RepaintManager rm, RepaintListener l) {
       
   208                 rm.removeRepaintListener(l);
       
   209             }
       
   210         });
       
   211 
       
   212         volatileImageBufferEnabled = "true".equals(AccessController.
       
   213                 doPrivileged(new GetPropertyAction(
       
   214                 "swing.volatileImageBufferEnabled", "true")));
       
   215         boolean headless = GraphicsEnvironment.isHeadless();
       
   216         if (volatileImageBufferEnabled && headless) {
       
   217             volatileImageBufferEnabled = false;
       
   218         }
       
   219         nativeDoubleBuffering = "true".equals(AccessController.doPrivileged(
       
   220                     new GetPropertyAction("awt.nativeDoubleBuffering")));
       
   221         String bs = AccessController.doPrivileged(
       
   222                           new GetPropertyAction("swing.bufferPerWindow"));
       
   223         if (headless) {
       
   224             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
       
   225         }
       
   226         else if (bs == null) {
       
   227             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED;
       
   228         }
       
   229         else if ("true".equals(bs)) {
       
   230             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON;
       
   231         }
       
   232         else {
       
   233             BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
       
   234         }
       
   235         HANDLE_TOP_LEVEL_PAINT = "true".equals(AccessController.doPrivileged(
       
   236                new GetPropertyAction("swing.handleTopLevelPaint", "true")));
       
   237         GraphicsEnvironment ge = GraphicsEnvironment.
       
   238                 getLocalGraphicsEnvironment();
       
   239         if (ge instanceof SunGraphicsEnvironment) {
       
   240             ((SunGraphicsEnvironment) ge).addDisplayChangedListener(
       
   241                     displayChangedHandler);
       
   242         }
       
   243         Toolkit tk = Toolkit.getDefaultToolkit();
       
   244         if ((tk instanceof SunToolkit)
       
   245                 && ((SunToolkit) tk).isSwingBackbufferTranslucencySupported()) {
       
   246             volatileBufferType = Transparency.TRANSLUCENT;
       
   247         } else {
       
   248             volatileBufferType = Transparency.OPAQUE;
       
   249         }
       
   250     }
       
   251 
       
   252     /**
       
   253      * Return the RepaintManager for the calling thread given a Component.
       
   254      *
       
   255      * @param c a Component -- unused in the default implementation, but could
       
   256      *          be used by an overridden version to return a different RepaintManager
       
   257      *          depending on the Component
       
   258      * @return the RepaintManager object
       
   259      */
       
   260     public static RepaintManager currentManager(Component c) {
       
   261         // Note: DisplayChangedRunnable passes in null as the component, so if
       
   262         // component is ever used to determine the current
       
   263         // RepaintManager, DisplayChangedRunnable will need to be modified
       
   264         // accordingly.
       
   265         return currentManager(AppContext.getAppContext());
       
   266     }
       
   267 
       
   268     /**
       
   269      * Returns the RepaintManager for the specified AppContext.  If
       
   270      * a RepaintManager has not been created for the specified
       
   271      * AppContext this will return null.
       
   272      */
       
   273     static RepaintManager currentManager(AppContext appContext) {
       
   274         RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey);
       
   275         if (rm == null) {
       
   276             rm = new RepaintManager(BUFFER_STRATEGY_TYPE);
       
   277             appContext.put(repaintManagerKey, rm);
       
   278         }
       
   279         return rm;
       
   280     }
       
   281 
       
   282     /**
       
   283      * Return the RepaintManager for the calling thread given a JComponent.
       
   284      * <p>
       
   285     * Note: This method exists for backward binary compatibility with earlier
       
   286      * versions of the Swing library. It simply returns the result returned by
       
   287      * {@link #currentManager(Component)}.
       
   288      *
       
   289      * @param c a JComponent -- unused
       
   290      * @return the RepaintManager object
       
   291      */
       
   292     public static RepaintManager currentManager(JComponent c) {
       
   293         return currentManager((Component)c);
       
   294     }
       
   295 
       
   296 
       
   297     /**
       
   298      * Set the RepaintManager that should be used for the calling
       
   299      * thread. <b>aRepaintManager</b> will become the current RepaintManager
       
   300      * for the calling thread's thread group.
       
   301      * @param aRepaintManager  the RepaintManager object to use
       
   302      */
       
   303     public static void setCurrentManager(RepaintManager aRepaintManager) {
       
   304         if (aRepaintManager != null) {
       
   305             SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
       
   306         } else {
       
   307             SwingUtilities.appContextRemove(repaintManagerKey);
       
   308         }
       
   309     }
       
   310 
       
   311     /**
       
   312      * Create a new RepaintManager instance. You rarely call this constructor.
       
   313      * directly. To get the default RepaintManager, use
       
   314      * RepaintManager.currentManager(JComponent) (normally "this").
       
   315      */
       
   316     public RepaintManager() {
       
   317         // Because we can't know what a subclass is doing with the
       
   318         // volatile image we immediately punt in subclasses.  If this
       
   319         // poses a problem we'll need a more sophisticated detection algorithm,
       
   320         // or API.
       
   321         this(BUFFER_STRATEGY_SPECIFIED_OFF);
       
   322     }
       
   323 
       
   324     private RepaintManager(short bufferStrategyType) {
       
   325         // If native doublebuffering is being used, do NOT use
       
   326         // Swing doublebuffering.
       
   327         doubleBufferingEnabled = !nativeDoubleBuffering;
       
   328         synchronized(this) {
       
   329             dirtyComponents = new IdentityHashMap<Component,Rectangle>();
       
   330             tmpDirtyComponents = new IdentityHashMap<Component,Rectangle>();
       
   331             this.bufferStrategyType = bufferStrategyType;
       
   332             hwDirtyComponents = new IdentityHashMap<Container,Rectangle>();
       
   333         }
       
   334         processingRunnable = new ProcessingRunnable();
       
   335     }
       
   336 
       
   337     private void displayChanged() {
       
   338         clearImages();
       
   339     }
       
   340 
       
   341     /**
       
   342      * Mark the component as in need of layout and queue a runnable
       
   343      * for the event dispatching thread that will validate the components
       
   344      * first isValidateRoot() ancestor.
       
   345      *
       
   346      * @param invalidComponent a component
       
   347      * @see JComponent#isValidateRoot
       
   348      * @see #removeInvalidComponent
       
   349      */
       
   350     public synchronized void addInvalidComponent(JComponent invalidComponent)
       
   351     {
       
   352         RepaintManager delegate = getDelegate(invalidComponent);
       
   353         if (delegate != null) {
       
   354             delegate.addInvalidComponent(invalidComponent);
       
   355             return;
       
   356         }
       
   357         Component validateRoot =
       
   358             SwingUtilities.getValidateRoot(invalidComponent, true);
       
   359 
       
   360         if (validateRoot == null) {
       
   361             return;
       
   362         }
       
   363 
       
   364         /* Lazily create the invalidateComponents vector and add the
       
   365          * validateRoot if it's not there already.  If this validateRoot
       
   366          * is already in the vector, we're done.
       
   367          */
       
   368         if (invalidComponents == null) {
       
   369             invalidComponents = new ArrayList<Component>();
       
   370         }
       
   371         else {
       
   372             int n = invalidComponents.size();
       
   373             for(int i = 0; i < n; i++) {
       
   374                 if(validateRoot == invalidComponents.get(i)) {
       
   375                     return;
       
   376                 }
       
   377             }
       
   378         }
       
   379         invalidComponents.add(validateRoot);
       
   380 
       
   381         // Queue a Runnable to invoke paintDirtyRegions and
       
   382         // validateInvalidComponents.
       
   383         scheduleProcessingRunnable(SunToolkit.targetToAppContext(invalidComponent));
       
   384     }
       
   385 
       
   386 
       
   387     /**
       
   388      * Remove a component from the list of invalid components.
       
   389      *
       
   390      * @param component a component
       
   391      * @see #addInvalidComponent
       
   392      */
       
   393     public synchronized void removeInvalidComponent(JComponent component) {
       
   394         RepaintManager delegate = getDelegate(component);
       
   395         if (delegate != null) {
       
   396             delegate.removeInvalidComponent(component);
       
   397             return;
       
   398         }
       
   399         if(invalidComponents != null) {
       
   400             int index = invalidComponents.indexOf(component);
       
   401             if(index != -1) {
       
   402                 invalidComponents.remove(index);
       
   403             }
       
   404         }
       
   405     }
       
   406 
       
   407 
       
   408     /**
       
   409      * Add a component in the list of components that should be refreshed.
       
   410      * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
       
   411      * will be unioned with the region that should be redrawn.
       
   412      *
       
   413      * @see JComponent#repaint
       
   414      */
       
   415     @SuppressWarnings("deprecation")
       
   416     private void addDirtyRegion0(Container c, int x, int y, int w, int h) {
       
   417         /* Special cases we don't have to bother with.
       
   418          */
       
   419         if ((w <= 0) || (h <= 0) || (c == null)) {
       
   420             return;
       
   421         }
       
   422 
       
   423         if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
       
   424             return;
       
   425         }
       
   426 
       
   427         if (extendDirtyRegion(c, x, y, w, h)) {
       
   428             // Component was already marked as dirty, region has been
       
   429             // extended, no need to continue.
       
   430             return;
       
   431         }
       
   432 
       
   433         /* Make sure that c and all it ancestors (up to an Applet or
       
   434          * Window) are visible.  This loop has the same effect as
       
   435          * checking c.isShowing() (and note that it's still possible
       
   436          * that c is completely obscured by an opaque ancestor in
       
   437          * the specified rectangle).
       
   438          */
       
   439         Component root = null;
       
   440 
       
   441         // Note: We can't synchronize around this, Frame.getExtendedState
       
   442         // is synchronized so that if we were to synchronize around this
       
   443         // it could lead to the possibility of getting locks out
       
   444         // of order and deadlocking.
       
   445         for (Container p = c; p != null; p = p.getParent()) {
       
   446             if (!p.isVisible() || !p.isDisplayable()) {
       
   447                 return;
       
   448             }
       
   449             if ((p instanceof Window) || (p instanceof Applet)) {
       
   450                 // Iconified frames are still visible!
       
   451                 if (p instanceof Frame &&
       
   452                         (((Frame)p).getExtendedState() & Frame.ICONIFIED) ==
       
   453                                     Frame.ICONIFIED) {
       
   454                     return;
       
   455                 }
       
   456                 root = p;
       
   457                 break;
       
   458             }
       
   459         }
       
   460 
       
   461         if (root == null) return;
       
   462 
       
   463         synchronized(this) {
       
   464             if (extendDirtyRegion(c, x, y, w, h)) {
       
   465                 // In between last check and this check another thread
       
   466                 // queued up runnable, can bail here.
       
   467                 return;
       
   468             }
       
   469             dirtyComponents.put(c, new Rectangle(x, y, w, h));
       
   470         }
       
   471 
       
   472         // Queue a Runnable to invoke paintDirtyRegions and
       
   473         // validateInvalidComponents.
       
   474         scheduleProcessingRunnable(SunToolkit.targetToAppContext(c));
       
   475     }
       
   476 
       
   477     /**
       
   478      * Add a component in the list of components that should be refreshed.
       
   479      * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
       
   480      * will be unioned with the region that should be redrawn.
       
   481      *
       
   482      * @param c Component to repaint, null results in nothing happening.
       
   483      * @param x X coordinate of the region to repaint
       
   484      * @param y Y coordinate of the region to repaint
       
   485      * @param w Width of the region to repaint
       
   486      * @param h Height of the region to repaint
       
   487      * @see JComponent#repaint
       
   488      */
       
   489     public void addDirtyRegion(JComponent c, int x, int y, int w, int h)
       
   490     {
       
   491         RepaintManager delegate = getDelegate(c);
       
   492         if (delegate != null) {
       
   493             delegate.addDirtyRegion(c, x, y, w, h);
       
   494             return;
       
   495         }
       
   496         addDirtyRegion0(c, x, y, w, h);
       
   497     }
       
   498 
       
   499     /**
       
   500      * Adds <code>window</code> to the list of <code>Component</code>s that
       
   501      * need to be repainted.
       
   502      *
       
   503      * @param window Window to repaint, null results in nothing happening.
       
   504      * @param x X coordinate of the region to repaint
       
   505      * @param y Y coordinate of the region to repaint
       
   506      * @param w Width of the region to repaint
       
   507      * @param h Height of the region to repaint
       
   508      * @see JFrame#repaint
       
   509      * @see JWindow#repaint
       
   510      * @see JDialog#repaint
       
   511      * @since 1.6
       
   512      */
       
   513     public void addDirtyRegion(Window window, int x, int y, int w, int h) {
       
   514         addDirtyRegion0(window, x, y, w, h);
       
   515     }
       
   516 
       
   517     /**
       
   518      * Adds <code>applet</code> to the list of <code>Component</code>s that
       
   519      * need to be repainted.
       
   520      *
       
   521      * @param applet Applet to repaint, null results in nothing happening.
       
   522      * @param x X coordinate of the region to repaint
       
   523      * @param y Y coordinate of the region to repaint
       
   524      * @param w Width of the region to repaint
       
   525      * @param h Height of the region to repaint
       
   526      * @see JApplet#repaint
       
   527      * @since 1.6
       
   528      *
       
   529      * @deprecated The Applet API is deprecated. See the
       
   530      * <a href="../../java/applet/package-summary.html"> java.applet package
       
   531      * documentation</a> for further information.
       
   532      */
       
   533     @Deprecated(since = "9")
       
   534     public void addDirtyRegion(Applet applet, int x, int y, int w, int h) {
       
   535         addDirtyRegion0(applet, x, y, w, h);
       
   536     }
       
   537 
       
   538     @SuppressWarnings("deprecation")
       
   539     void scheduleHeavyWeightPaints() {
       
   540         Map<Container,Rectangle> hws;
       
   541 
       
   542         synchronized(this) {
       
   543             if (hwDirtyComponents.size() == 0) {
       
   544                 return;
       
   545             }
       
   546             hws = hwDirtyComponents;
       
   547             hwDirtyComponents =  new IdentityHashMap<Container,Rectangle>();
       
   548         }
       
   549         for (Container hw : hws.keySet()) {
       
   550             Rectangle dirty = hws.get(hw);
       
   551             if (hw instanceof Window) {
       
   552                 addDirtyRegion((Window)hw, dirty.x, dirty.y,
       
   553                                dirty.width, dirty.height);
       
   554             }
       
   555             else if (hw instanceof Applet) {
       
   556                 addDirtyRegion((Applet)hw, dirty.x, dirty.y,
       
   557                                dirty.width, dirty.height);
       
   558             }
       
   559             else { // SwingHeavyWeight
       
   560                 addDirtyRegion0(hw, dirty.x, dirty.y,
       
   561                                 dirty.width, dirty.height);
       
   562             }
       
   563         }
       
   564     }
       
   565 
       
   566     //
       
   567     // This is called from the toolkit thread when a native expose is
       
   568     // received.
       
   569     //
       
   570     void nativeAddDirtyRegion(AppContext appContext, Container c,
       
   571                               int x, int y, int w, int h) {
       
   572         if (w > 0 && h > 0) {
       
   573             synchronized(this) {
       
   574                 Rectangle dirty = hwDirtyComponents.get(c);
       
   575                 if (dirty == null) {
       
   576                     hwDirtyComponents.put(c, new Rectangle(x, y, w, h));
       
   577                 }
       
   578                 else {
       
   579                     hwDirtyComponents.put(c, SwingUtilities.computeUnion(
       
   580                                               x, y, w, h, dirty));
       
   581                 }
       
   582             }
       
   583             scheduleProcessingRunnable(appContext);
       
   584         }
       
   585     }
       
   586 
       
   587     //
       
   588     // This is called from the toolkit thread when awt needs to run a
       
   589     // Runnable before we paint.
       
   590     //
       
   591     void nativeQueueSurfaceDataRunnable(AppContext appContext,
       
   592                                         final Component c, final Runnable r)
       
   593     {
       
   594         synchronized(this) {
       
   595             if (runnableList == null) {
       
   596                 runnableList = new LinkedList<Runnable>();
       
   597             }
       
   598             runnableList.add(new Runnable() {
       
   599                 public void run() {
       
   600                     AccessControlContext stack = AccessController.getContext();
       
   601                     AccessControlContext acc =
       
   602                         AWTAccessor.getComponentAccessor().getAccessControlContext(c);
       
   603                     javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Void>() {
       
   604                         public Void run() {
       
   605                             r.run();
       
   606                             return null;
       
   607                         }
       
   608                     }, stack, acc);
       
   609                 }
       
   610             });
       
   611         }
       
   612         scheduleProcessingRunnable(appContext);
       
   613     }
       
   614 
       
   615     /**
       
   616      * Extends the dirty region for the specified component to include
       
   617      * the new region.
       
   618      *
       
   619      * @return false if <code>c</code> is not yet marked dirty.
       
   620      */
       
   621     private synchronized boolean extendDirtyRegion(
       
   622         Component c, int x, int y, int w, int h) {
       
   623         Rectangle r = dirtyComponents.get(c);
       
   624         if (r != null) {
       
   625             // A non-null r implies c is already marked as dirty,
       
   626             // and that the parent is valid. Therefore we can
       
   627             // just union the rect and bail.
       
   628             SwingUtilities.computeUnion(x, y, w, h, r);
       
   629             return true;
       
   630         }
       
   631         return false;
       
   632     }
       
   633 
       
   634     /**
       
   635      * Return the current dirty region for a component.
       
   636      * Return an empty rectangle if the component is not
       
   637      * dirty.
       
   638      *
       
   639      * @param aComponent a component
       
   640      * @return the region
       
   641      */
       
   642     public Rectangle getDirtyRegion(JComponent aComponent) {
       
   643         RepaintManager delegate = getDelegate(aComponent);
       
   644         if (delegate != null) {
       
   645             return delegate.getDirtyRegion(aComponent);
       
   646         }
       
   647         Rectangle r;
       
   648         synchronized(this) {
       
   649             r = dirtyComponents.get(aComponent);
       
   650         }
       
   651         if(r == null)
       
   652             return new Rectangle(0,0,0,0);
       
   653         else
       
   654             return new Rectangle(r);
       
   655     }
       
   656 
       
   657     /**
       
   658      * Mark a component completely dirty. <b>aComponent</b> will be
       
   659      * completely painted during the next paintDirtyRegions() call.
       
   660      *
       
   661      * @param aComponent a component
       
   662      */
       
   663     public void markCompletelyDirty(JComponent aComponent) {
       
   664         RepaintManager delegate = getDelegate(aComponent);
       
   665         if (delegate != null) {
       
   666             delegate.markCompletelyDirty(aComponent);
       
   667             return;
       
   668         }
       
   669         addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
       
   670     }
       
   671 
       
   672     /**
       
   673      * Mark a component completely clean. <b>aComponent</b> will not
       
   674      * get painted during the next paintDirtyRegions() call.
       
   675      *
       
   676      * @param aComponent a component
       
   677      */
       
   678     public void markCompletelyClean(JComponent aComponent) {
       
   679         RepaintManager delegate = getDelegate(aComponent);
       
   680         if (delegate != null) {
       
   681             delegate.markCompletelyClean(aComponent);
       
   682             return;
       
   683         }
       
   684         synchronized(this) {
       
   685                 dirtyComponents.remove(aComponent);
       
   686         }
       
   687     }
       
   688 
       
   689     /**
       
   690      * Convenience method that returns true if <b>aComponent</b> will be completely
       
   691      * painted during the next paintDirtyRegions(). If computing dirty regions is
       
   692      * expensive for your component, use this method and avoid computing dirty region
       
   693      * if it return true.
       
   694      *
       
   695      * @param aComponent a component
       
   696      * @return {@code true} if <b>aComponent</b> will be completely
       
   697      *         painted during the next paintDirtyRegions().
       
   698      */
       
   699     public boolean isCompletelyDirty(JComponent aComponent) {
       
   700         RepaintManager delegate = getDelegate(aComponent);
       
   701         if (delegate != null) {
       
   702             return delegate.isCompletelyDirty(aComponent);
       
   703         }
       
   704         Rectangle r;
       
   705 
       
   706         r = getDirtyRegion(aComponent);
       
   707         if(r.width == Integer.MAX_VALUE &&
       
   708            r.height == Integer.MAX_VALUE)
       
   709             return true;
       
   710         else
       
   711             return false;
       
   712     }
       
   713 
       
   714 
       
   715     /**
       
   716      * Validate all of the components that have been marked invalid.
       
   717      * @see #addInvalidComponent
       
   718      */
       
   719     public void validateInvalidComponents() {
       
   720         final java.util.List<Component> ic;
       
   721         synchronized(this) {
       
   722             if (invalidComponents == null) {
       
   723                 return;
       
   724             }
       
   725             ic = invalidComponents;
       
   726             invalidComponents = null;
       
   727         }
       
   728         int n = ic.size();
       
   729         for(int i = 0; i < n; i++) {
       
   730             final Component c = ic.get(i);
       
   731             AccessControlContext stack = AccessController.getContext();
       
   732             AccessControlContext acc =
       
   733                 AWTAccessor.getComponentAccessor().getAccessControlContext(c);
       
   734             javaSecurityAccess.doIntersectionPrivilege(
       
   735                 new PrivilegedAction<Void>() {
       
   736                     public Void run() {
       
   737                         c.validate();
       
   738                         return null;
       
   739                     }
       
   740                 }, stack, acc);
       
   741         }
       
   742     }
       
   743 
       
   744 
       
   745     /**
       
   746      * This is invoked to process paint requests.  It's needed
       
   747      * for backward compatibility in so far as RepaintManager would previously
       
   748      * not see paint requests for top levels, so, we have to make sure
       
   749      * a subclass correctly paints any dirty top levels.
       
   750      */
       
   751     private void prePaintDirtyRegions() {
       
   752         Map<Component,Rectangle> dirtyComponents;
       
   753         java.util.List<Runnable> runnableList;
       
   754         synchronized(this) {
       
   755             dirtyComponents = this.dirtyComponents;
       
   756             runnableList = this.runnableList;
       
   757             this.runnableList = null;
       
   758         }
       
   759         if (runnableList != null) {
       
   760             for (Runnable runnable : runnableList) {
       
   761                 runnable.run();
       
   762             }
       
   763         }
       
   764         paintDirtyRegions();
       
   765         if (dirtyComponents.size() > 0) {
       
   766             // This'll only happen if a subclass isn't correctly dealing
       
   767             // with toplevels.
       
   768             paintDirtyRegions(dirtyComponents);
       
   769         }
       
   770     }
       
   771 
       
   772     private void updateWindows(Map<Component,Rectangle> dirtyComponents) {
       
   773         Toolkit toolkit = Toolkit.getDefaultToolkit();
       
   774         if (!(toolkit instanceof SunToolkit &&
       
   775               ((SunToolkit)toolkit).needUpdateWindow()))
       
   776         {
       
   777             return;
       
   778         }
       
   779 
       
   780         Set<Window> windows = new HashSet<Window>();
       
   781         Set<Component> dirtyComps = dirtyComponents.keySet();
       
   782         for (Iterator<Component> it = dirtyComps.iterator(); it.hasNext();) {
       
   783             Component dirty = it.next();
       
   784             Window window = dirty instanceof Window ?
       
   785                 (Window)dirty :
       
   786                 SwingUtilities.getWindowAncestor(dirty);
       
   787             if (window != null &&
       
   788                 !window.isOpaque())
       
   789             {
       
   790                 windows.add(window);
       
   791             }
       
   792         }
       
   793 
       
   794         for (Window window : windows) {
       
   795             AWTAccessor.getWindowAccessor().updateWindow(window);
       
   796         }
       
   797     }
       
   798 
       
   799     boolean isPainting() {
       
   800         return painting;
       
   801     }
       
   802 
       
   803     /**
       
   804      * Paint all of the components that have been marked dirty.
       
   805      *
       
   806      * @see #addDirtyRegion
       
   807      */
       
   808     public void paintDirtyRegions() {
       
   809         synchronized(this) {  // swap for thread safety
       
   810             Map<Component,Rectangle> tmp = tmpDirtyComponents;
       
   811             tmpDirtyComponents = dirtyComponents;
       
   812             dirtyComponents = tmp;
       
   813             dirtyComponents.clear();
       
   814         }
       
   815         paintDirtyRegions(tmpDirtyComponents);
       
   816     }
       
   817 
       
   818     private void paintDirtyRegions(
       
   819         final Map<Component,Rectangle> tmpDirtyComponents)
       
   820     {
       
   821         if (tmpDirtyComponents.isEmpty()) {
       
   822             return;
       
   823         }
       
   824 
       
   825         final java.util.List<Component> roots =
       
   826             new ArrayList<Component>(tmpDirtyComponents.size());
       
   827         for (Component dirty : tmpDirtyComponents.keySet()) {
       
   828             collectDirtyComponents(tmpDirtyComponents, dirty, roots);
       
   829         }
       
   830 
       
   831         final AtomicInteger count = new AtomicInteger(roots.size());
       
   832         painting = true;
       
   833         try {
       
   834             for (int j=0 ; j < count.get(); j++) {
       
   835                 final int i = j;
       
   836                 final Component dirtyComponent = roots.get(j);
       
   837                 AccessControlContext stack = AccessController.getContext();
       
   838                 AccessControlContext acc =
       
   839                     AWTAccessor.getComponentAccessor().getAccessControlContext(dirtyComponent);
       
   840                 javaSecurityAccess.doIntersectionPrivilege(new PrivilegedAction<Void>() {
       
   841                     public Void run() {
       
   842                         Rectangle rect = tmpDirtyComponents.get(dirtyComponent);
       
   843                         // Sometimes when RepaintManager is changed during the painting
       
   844                         // we may get null here, see #6995769 for details
       
   845                         if (rect == null) {
       
   846                             return null;
       
   847                         }
       
   848 
       
   849                         int localBoundsH = dirtyComponent.getHeight();
       
   850                         int localBoundsW = dirtyComponent.getWidth();
       
   851                         SwingUtilities.computeIntersection(0,
       
   852                                                            0,
       
   853                                                            localBoundsW,
       
   854                                                            localBoundsH,
       
   855                                                            rect);
       
   856                         if (dirtyComponent instanceof JComponent) {
       
   857                             ((JComponent)dirtyComponent).paintImmediately(
       
   858                                 rect.x,rect.y,rect.width, rect.height);
       
   859                         }
       
   860                         else if (dirtyComponent.isShowing()) {
       
   861                             Graphics g = JComponent.safelyGetGraphics(
       
   862                                     dirtyComponent, dirtyComponent);
       
   863                             // If the Graphics goes away, it means someone disposed of
       
   864                             // the window, don't do anything.
       
   865                             if (g != null) {
       
   866                                 g.setClip(rect.x, rect.y, rect.width, rect.height);
       
   867                                 try {
       
   868                                     dirtyComponent.paint(g);
       
   869                                 } finally {
       
   870                                     g.dispose();
       
   871                                 }
       
   872                             }
       
   873                         }
       
   874                         // If the repaintRoot has been set, service it now and
       
   875                         // remove any components that are children of repaintRoot.
       
   876                         if (repaintRoot != null) {
       
   877                             adjustRoots(repaintRoot, roots, i + 1);
       
   878                             count.set(roots.size());
       
   879                             paintManager.isRepaintingRoot = true;
       
   880                             repaintRoot.paintImmediately(0, 0, repaintRoot.getWidth(),
       
   881                                                          repaintRoot.getHeight());
       
   882                             paintManager.isRepaintingRoot = false;
       
   883                             // Only service repaintRoot once.
       
   884                             repaintRoot = null;
       
   885                         }
       
   886 
       
   887                         return null;
       
   888                     }
       
   889                 }, stack, acc);
       
   890             }
       
   891         } finally {
       
   892             painting = false;
       
   893         }
       
   894 
       
   895         updateWindows(tmpDirtyComponents);
       
   896 
       
   897         tmpDirtyComponents.clear();
       
   898     }
       
   899 
       
   900 
       
   901     /**
       
   902      * Removes any components from roots that are children of
       
   903      * root.
       
   904      */
       
   905     private void adjustRoots(JComponent root,
       
   906                              java.util.List<Component> roots, int index) {
       
   907         for (int i = roots.size() - 1; i >= index; i--) {
       
   908             Component c = roots.get(i);
       
   909             for(;;) {
       
   910                 if (c == root || c == null || !(c instanceof JComponent)) {
       
   911                     break;
       
   912                 }
       
   913                 c = c.getParent();
       
   914             }
       
   915             if (c == root) {
       
   916                 roots.remove(i);
       
   917             }
       
   918         }
       
   919     }
       
   920 
       
   921     Rectangle tmp = new Rectangle();
       
   922 
       
   923     void collectDirtyComponents(Map<Component,Rectangle> dirtyComponents,
       
   924                                 Component dirtyComponent,
       
   925                                 java.util.List<Component> roots) {
       
   926         int dx, dy, rootDx, rootDy;
       
   927         Component component, rootDirtyComponent,parent;
       
   928         Rectangle cBounds;
       
   929 
       
   930         // Find the highest parent which is dirty.  When we get out of this
       
   931         // rootDx and rootDy will contain the translation from the
       
   932         // rootDirtyComponent's coordinate system to the coordinates of the
       
   933         // original dirty component.  The tmp Rect is also used to compute the
       
   934         // visible portion of the dirtyRect.
       
   935 
       
   936         component = rootDirtyComponent = dirtyComponent;
       
   937 
       
   938         int x = dirtyComponent.getX();
       
   939         int y = dirtyComponent.getY();
       
   940         int w = dirtyComponent.getWidth();
       
   941         int h = dirtyComponent.getHeight();
       
   942 
       
   943         dx = rootDx = 0;
       
   944         dy = rootDy = 0;
       
   945         tmp.setBounds(dirtyComponents.get(dirtyComponent));
       
   946 
       
   947         // System.out.println("Collect dirty component for bound " + tmp +
       
   948         //                                   "component bounds is " + cBounds);;
       
   949         SwingUtilities.computeIntersection(0,0,w,h,tmp);
       
   950 
       
   951         if (tmp.isEmpty()) {
       
   952             // System.out.println("Empty 1");
       
   953             return;
       
   954         }
       
   955 
       
   956         for(;;) {
       
   957             if(!(component instanceof JComponent))
       
   958                 break;
       
   959 
       
   960             parent = component.getParent();
       
   961             if(parent == null)
       
   962                 break;
       
   963 
       
   964             component = parent;
       
   965 
       
   966             dx += x;
       
   967             dy += y;
       
   968             tmp.setLocation(tmp.x + x, tmp.y + y);
       
   969 
       
   970             x = component.getX();
       
   971             y = component.getY();
       
   972             w = component.getWidth();
       
   973             h = component.getHeight();
       
   974             tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp);
       
   975 
       
   976             if (tmp.isEmpty()) {
       
   977                 // System.out.println("Empty 2");
       
   978                 return;
       
   979             }
       
   980 
       
   981             if (dirtyComponents.get(component) != null) {
       
   982                 rootDirtyComponent = component;
       
   983                 rootDx = dx;
       
   984                 rootDy = dy;
       
   985             }
       
   986         }
       
   987 
       
   988         if (dirtyComponent != rootDirtyComponent) {
       
   989             Rectangle r;
       
   990             tmp.setLocation(tmp.x + rootDx - dx,
       
   991                             tmp.y + rootDy - dy);
       
   992             r = dirtyComponents.get(rootDirtyComponent);
       
   993             SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
       
   994         }
       
   995 
       
   996         // If we haven't seen this root before, then we need to add it to the
       
   997         // list of root dirty Views.
       
   998 
       
   999         if (!roots.contains(rootDirtyComponent))
       
  1000             roots.add(rootDirtyComponent);
       
  1001     }
       
  1002 
       
  1003 
       
  1004     /**
       
  1005      * Returns a string that displays and identifies this
       
  1006      * object's properties.
       
  1007      *
       
  1008      * @return a String representation of this object
       
  1009      */
       
  1010     public synchronized String toString() {
       
  1011         StringBuilder sb = new StringBuilder();
       
  1012         if(dirtyComponents != null)
       
  1013             sb.append("" + dirtyComponents);
       
  1014         return sb.toString();
       
  1015     }
       
  1016 
       
  1017 
       
  1018     /**
       
  1019      * Return the offscreen buffer that should be used as a double buffer with
       
  1020      * the component <code>c</code>.
       
  1021      * By default there is a double buffer per RepaintManager.
       
  1022      * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
       
  1023      * This happens when the maximum double buffer size as been set for the receiving
       
  1024      * repaint manager.
       
  1025      *
       
  1026      * @param c the component
       
  1027      * @param proposedWidth the width of the buffer
       
  1028      * @param proposedHeight the height of the buffer
       
  1029      *
       
  1030      * @return the image
       
  1031      */
       
  1032     public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
       
  1033         RepaintManager delegate = getDelegate(c);
       
  1034         if (delegate != null) {
       
  1035             return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight);
       
  1036         }
       
  1037         return _getOffscreenBuffer(c, proposedWidth, proposedHeight);
       
  1038     }
       
  1039 
       
  1040     /**
       
  1041      * Return a volatile offscreen buffer that should be used as a
       
  1042      * double buffer with the specified component <code>c</code>.
       
  1043      * The image returned will be an instance of VolatileImage, or null
       
  1044      * if a VolatileImage object could not be instantiated.
       
  1045      * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>.
       
  1046      * This happens when the maximum double buffer size has been set for this
       
  1047      * repaint manager.
       
  1048      *
       
  1049      * @param c the component
       
  1050      * @param proposedWidth the width of the buffer
       
  1051      * @param proposedHeight the height of the buffer
       
  1052      *
       
  1053      * @return the volatile image
       
  1054      * @see java.awt.image.VolatileImage
       
  1055      * @since 1.4
       
  1056      */
       
  1057     public Image getVolatileOffscreenBuffer(Component c,
       
  1058                                             int proposedWidth,int proposedHeight) {
       
  1059         RepaintManager delegate = getDelegate(c);
       
  1060         if (delegate != null) {
       
  1061             return delegate.getVolatileOffscreenBuffer(c, proposedWidth,
       
  1062                                                         proposedHeight);
       
  1063         }
       
  1064 
       
  1065         // If the window is non-opaque, it's double-buffered at peer's level
       
  1066         Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
       
  1067         if (!w.isOpaque()) {
       
  1068             Toolkit tk = Toolkit.getDefaultToolkit();
       
  1069             if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
       
  1070                 return null;
       
  1071             }
       
  1072         }
       
  1073 
       
  1074         GraphicsConfiguration config = c.getGraphicsConfiguration();
       
  1075         if (config == null) {
       
  1076             config = GraphicsEnvironment.getLocalGraphicsEnvironment().
       
  1077                             getDefaultScreenDevice().getDefaultConfiguration();
       
  1078         }
       
  1079         Dimension maxSize = getDoubleBufferMaximumSize();
       
  1080         int width = proposedWidth < 1 ? 1 :
       
  1081             (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
       
  1082         int height = proposedHeight < 1 ? 1 :
       
  1083             (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
       
  1084         VolatileImage image = volatileMap.get(config);
       
  1085         if (image == null || image.getWidth() < width ||
       
  1086                              image.getHeight() < height) {
       
  1087             if (image != null) {
       
  1088                 image.flush();
       
  1089             }
       
  1090             image = config.createCompatibleVolatileImage(width, height,
       
  1091                                                          volatileBufferType);
       
  1092             volatileMap.put(config, image);
       
  1093         }
       
  1094         return image;
       
  1095     }
       
  1096 
       
  1097     private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
       
  1098         Dimension maxSize = getDoubleBufferMaximumSize();
       
  1099         DoubleBufferInfo doubleBuffer;
       
  1100         int width, height;
       
  1101 
       
  1102         // If the window is non-opaque, it's double-buffered at peer's level
       
  1103         Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
       
  1104         if (!w.isOpaque()) {
       
  1105             Toolkit tk = Toolkit.getDefaultToolkit();
       
  1106             if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
       
  1107                 return null;
       
  1108             }
       
  1109         }
       
  1110 
       
  1111         if (standardDoubleBuffer == null) {
       
  1112             standardDoubleBuffer = new DoubleBufferInfo();
       
  1113         }
       
  1114         doubleBuffer = standardDoubleBuffer;
       
  1115 
       
  1116         width = proposedWidth < 1? 1 :
       
  1117                   (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
       
  1118         height = proposedHeight < 1? 1 :
       
  1119                   (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
       
  1120 
       
  1121         if (doubleBuffer.needsReset || (doubleBuffer.image != null &&
       
  1122                                         (doubleBuffer.size.width < width ||
       
  1123                                          doubleBuffer.size.height < height))) {
       
  1124             doubleBuffer.needsReset = false;
       
  1125             if (doubleBuffer.image != null) {
       
  1126                 doubleBuffer.image.flush();
       
  1127                 doubleBuffer.image = null;
       
  1128             }
       
  1129             width = Math.max(doubleBuffer.size.width, width);
       
  1130             height = Math.max(doubleBuffer.size.height, height);
       
  1131         }
       
  1132 
       
  1133         Image result = doubleBuffer.image;
       
  1134 
       
  1135         if (doubleBuffer.image == null) {
       
  1136             result = c.createImage(width , height);
       
  1137             doubleBuffer.size = new Dimension(width, height);
       
  1138             if (c instanceof JComponent) {
       
  1139                 ((JComponent)c).setCreatedDoubleBuffer(true);
       
  1140                 doubleBuffer.image = result;
       
  1141             }
       
  1142             // JComponent will inform us when it is no longer valid
       
  1143             // (via removeNotify) we have no such hook to other components,
       
  1144             // therefore we don't keep a ref to the Component
       
  1145             // (indirectly through the Image) by stashing the image.
       
  1146         }
       
  1147         return result;
       
  1148     }
       
  1149 
       
  1150 
       
  1151     /**
       
  1152      * Set the maximum double buffer size.
       
  1153      *
       
  1154      * @param d the dimension
       
  1155      */
       
  1156     public void setDoubleBufferMaximumSize(Dimension d) {
       
  1157         doubleBufferMaxSize = d;
       
  1158         if (doubleBufferMaxSize == null) {
       
  1159             clearImages();
       
  1160         } else {
       
  1161             clearImages(d.width, d.height);
       
  1162         }
       
  1163     }
       
  1164 
       
  1165     private void clearImages() {
       
  1166         clearImages(0, 0);
       
  1167     }
       
  1168 
       
  1169     private void clearImages(int width, int height) {
       
  1170         if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) {
       
  1171             if (standardDoubleBuffer.image.getWidth(null) > width ||
       
  1172                 standardDoubleBuffer.image.getHeight(null) > height) {
       
  1173                 standardDoubleBuffer.image.flush();
       
  1174                 standardDoubleBuffer.image = null;
       
  1175             }
       
  1176         }
       
  1177         // Clear out the VolatileImages
       
  1178         Iterator<GraphicsConfiguration> gcs = volatileMap.keySet().iterator();
       
  1179         while (gcs.hasNext()) {
       
  1180             GraphicsConfiguration gc = gcs.next();
       
  1181             VolatileImage image = volatileMap.get(gc);
       
  1182             if (image.getWidth() > width || image.getHeight() > height) {
       
  1183                 image.flush();
       
  1184                 gcs.remove();
       
  1185             }
       
  1186         }
       
  1187     }
       
  1188 
       
  1189     /**
       
  1190      * Returns the maximum double buffer size.
       
  1191      *
       
  1192      * @return a Dimension object representing the maximum size
       
  1193      */
       
  1194     public Dimension getDoubleBufferMaximumSize() {
       
  1195         if (doubleBufferMaxSize == null) {
       
  1196             try {
       
  1197                 Rectangle virtualBounds = new Rectangle();
       
  1198                 GraphicsEnvironment ge = GraphicsEnvironment.
       
  1199                                                  getLocalGraphicsEnvironment();
       
  1200                 for (GraphicsDevice gd : ge.getScreenDevices()) {
       
  1201                     GraphicsConfiguration gc = gd.getDefaultConfiguration();
       
  1202                     virtualBounds = virtualBounds.union(gc.getBounds());
       
  1203                 }
       
  1204                 doubleBufferMaxSize = new Dimension(virtualBounds.width,
       
  1205                                                     virtualBounds.height);
       
  1206             } catch (HeadlessException e) {
       
  1207                 doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
       
  1208             }
       
  1209         }
       
  1210         return doubleBufferMaxSize;
       
  1211     }
       
  1212 
       
  1213     /**
       
  1214      * Enables or disables double buffering in this RepaintManager.
       
  1215      * CAUTION: The default value for this property is set for optimal
       
  1216      * paint performance on the given platform and it is not recommended
       
  1217      * that programs modify this property directly.
       
  1218      *
       
  1219      * @param aFlag  true to activate double buffering
       
  1220      * @see #isDoubleBufferingEnabled
       
  1221      */
       
  1222     public void setDoubleBufferingEnabled(boolean aFlag) {
       
  1223         doubleBufferingEnabled = aFlag;
       
  1224         PaintManager paintManager = getPaintManager();
       
  1225         if (!aFlag && paintManager.getClass() != PaintManager.class) {
       
  1226             setPaintManager(new PaintManager());
       
  1227         }
       
  1228     }
       
  1229 
       
  1230     /**
       
  1231      * Returns true if this RepaintManager is double buffered.
       
  1232      * The default value for this property may vary from platform
       
  1233      * to platform.  On platforms where native double buffering
       
  1234      * is supported in the AWT, the default value will be <code>false</code>
       
  1235      * to avoid unnecessary buffering in Swing.
       
  1236      * On platforms where native double buffering is not supported,
       
  1237      * the default value will be <code>true</code>.
       
  1238      *
       
  1239      * @return true if this object is double buffered
       
  1240      */
       
  1241     public boolean isDoubleBufferingEnabled() {
       
  1242         return doubleBufferingEnabled;
       
  1243     }
       
  1244 
       
  1245     /**
       
  1246      * This resets the double buffer. Actually, it marks the double buffer
       
  1247      * as invalid, the double buffer will then be recreated on the next
       
  1248      * invocation of getOffscreenBuffer.
       
  1249      */
       
  1250     void resetDoubleBuffer() {
       
  1251         if (standardDoubleBuffer != null) {
       
  1252             standardDoubleBuffer.needsReset = true;
       
  1253         }
       
  1254     }
       
  1255 
       
  1256     /**
       
  1257      * This resets the volatile double buffer.
       
  1258      */
       
  1259     void resetVolatileDoubleBuffer(GraphicsConfiguration gc) {
       
  1260         Image image = volatileMap.remove(gc);
       
  1261         if (image != null) {
       
  1262             image.flush();
       
  1263         }
       
  1264     }
       
  1265 
       
  1266     /**
       
  1267      * Returns true if we should use the <code>Image</code> returned
       
  1268      * from <code>getVolatileOffscreenBuffer</code> to do double buffering.
       
  1269      */
       
  1270     boolean useVolatileDoubleBuffer() {
       
  1271         return volatileImageBufferEnabled;
       
  1272     }
       
  1273 
       
  1274     /**
       
  1275      * Returns true if the current thread is the thread painting.  This
       
  1276      * will return false if no threads are painting.
       
  1277      */
       
  1278     private synchronized boolean isPaintingThread() {
       
  1279         return (Thread.currentThread() == paintThread);
       
  1280     }
       
  1281     //
       
  1282     // Paint methods.  You very, VERY rarely need to invoke these.
       
  1283     // They are invoked directly from JComponent's painting code and
       
  1284     // when painting happens outside the normal flow: DefaultDesktopManager
       
  1285     // and JViewport.  If you end up needing these methods in other places be
       
  1286     // careful that you don't get stuck in a paint loop.
       
  1287     //
       
  1288 
       
  1289     /**
       
  1290      * Paints a region of a component
       
  1291      *
       
  1292      * @param paintingComponent Component to paint
       
  1293      * @param bufferComponent Component to obtain buffer for
       
  1294      * @param g Graphics to paint to
       
  1295      * @param x X-coordinate
       
  1296      * @param y Y-coordinate
       
  1297      * @param w Width
       
  1298      * @param h Height
       
  1299      */
       
  1300     void paint(JComponent paintingComponent,
       
  1301                JComponent bufferComponent, Graphics g,
       
  1302                int x, int y, int w, int h) {
       
  1303         PaintManager paintManager = getPaintManager();
       
  1304         if (!isPaintingThread()) {
       
  1305             // We're painting to two threads at once.  PaintManager deals
       
  1306             // with this a bit better than BufferStrategyPaintManager, use
       
  1307             // it to avoid possible exceptions/corruption.
       
  1308             if (paintManager.getClass() != PaintManager.class) {
       
  1309                 paintManager = new PaintManager();
       
  1310                 paintManager.repaintManager = this;
       
  1311             }
       
  1312         }
       
  1313         if (!paintManager.paint(paintingComponent, bufferComponent, g,
       
  1314                                 x, y, w, h)) {
       
  1315             g.setClip(x, y, w, h);
       
  1316             paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h);
       
  1317         }
       
  1318     }
       
  1319 
       
  1320     /**
       
  1321      * Does a copy area on the specified region.
       
  1322      *
       
  1323      * @param clip Whether or not the copyArea needs to be clipped to the
       
  1324      *             Component's bounds.
       
  1325      */
       
  1326     void copyArea(JComponent c, Graphics g, int x, int y, int w, int h,
       
  1327                   int deltaX, int deltaY, boolean clip) {
       
  1328         getPaintManager().copyArea(c, g, x, y, w, h, deltaX, deltaY, clip);
       
  1329     }
       
  1330 
       
  1331     private java.util.List<RepaintListener> repaintListeners = new ArrayList<>(1);
       
  1332 
       
  1333     private void addRepaintListener(RepaintListener l) {
       
  1334         repaintListeners.add(l);
       
  1335     }
       
  1336 
       
  1337     private void removeRepaintListener(RepaintListener l) {
       
  1338         repaintListeners.remove(l);
       
  1339     }
       
  1340 
       
  1341     /**
       
  1342      * Notify the attached repaint listeners that an area of the {@code c} component
       
  1343      * has been immediately repainted, that is without scheduling a repaint runnable,
       
  1344      * due to performing a "blit" (via calling the {@code copyArea} method).
       
  1345      *
       
  1346      * @param c the component
       
  1347      * @param x the x coordinate of the area
       
  1348      * @param y the y coordinate of the area
       
  1349      * @param w the width of the area
       
  1350      * @param h the height of the area
       
  1351      */
       
  1352     void notifyRepaintPerformed(JComponent c, int x, int y, int w, int h) {
       
  1353         for (RepaintListener l : repaintListeners) {
       
  1354             l.repaintPerformed(c, x, y, w, h);
       
  1355         }
       
  1356     }
       
  1357 
       
  1358     /**
       
  1359      * Invoked prior to any paint/copyArea method calls.  This will
       
  1360      * be followed by an invocation of <code>endPaint</code>.
       
  1361      * <b>WARNING</b>: Callers of this method need to wrap the call
       
  1362      * in a <code>try/finally</code>, otherwise if an exception is thrown
       
  1363      * during the course of painting the RepaintManager may
       
  1364      * be left in a state in which the screen is not updated, eg:
       
  1365      * <pre>
       
  1366      * repaintManager.beginPaint();
       
  1367      * try {
       
  1368      *   repaintManager.paint(...);
       
  1369      * } finally {
       
  1370      *   repaintManager.endPaint();
       
  1371      * }
       
  1372      * </pre>
       
  1373      */
       
  1374     void beginPaint() {
       
  1375         boolean multiThreadedPaint = false;
       
  1376         int paintDepth;
       
  1377         Thread currentThread = Thread.currentThread();
       
  1378         synchronized(this) {
       
  1379             paintDepth = this.paintDepth;
       
  1380             if (paintThread == null || currentThread == paintThread) {
       
  1381                 paintThread = currentThread;
       
  1382                 this.paintDepth++;
       
  1383             } else {
       
  1384                 multiThreadedPaint = true;
       
  1385             }
       
  1386         }
       
  1387         if (!multiThreadedPaint && paintDepth == 0) {
       
  1388             getPaintManager().beginPaint();
       
  1389         }
       
  1390     }
       
  1391 
       
  1392     /**
       
  1393      * Invoked after <code>beginPaint</code> has been invoked.
       
  1394      */
       
  1395     void endPaint() {
       
  1396         if (isPaintingThread()) {
       
  1397             PaintManager paintManager = null;
       
  1398             synchronized(this) {
       
  1399                 if (--paintDepth == 0) {
       
  1400                     paintManager = getPaintManager();
       
  1401                 }
       
  1402             }
       
  1403             if (paintManager != null) {
       
  1404                 paintManager.endPaint();
       
  1405                 synchronized(this) {
       
  1406                     paintThread = null;
       
  1407                 }
       
  1408             }
       
  1409         }
       
  1410     }
       
  1411 
       
  1412     /**
       
  1413      * If possible this will show a previously rendered portion of
       
  1414      * a Component.  If successful, this will return true, otherwise false.
       
  1415      * <p>
       
  1416      * WARNING: This method is invoked from the native toolkit thread, be
       
  1417      * very careful as to what methods this invokes!
       
  1418      */
       
  1419     boolean show(Container c, int x, int y, int w, int h) {
       
  1420         return getPaintManager().show(c, x, y, w, h);
       
  1421     }
       
  1422 
       
  1423     /**
       
  1424      * Invoked when the doubleBuffered or useTrueDoubleBuffering
       
  1425      * properties of a JRootPane change.  This may come in on any thread.
       
  1426      */
       
  1427     void doubleBufferingChanged(JRootPane rootPane) {
       
  1428         getPaintManager().doubleBufferingChanged(rootPane);
       
  1429     }
       
  1430 
       
  1431     /**
       
  1432      * Sets the <code>PaintManager</code> that is used to handle all
       
  1433      * double buffered painting.
       
  1434      *
       
  1435      * @param paintManager The PaintManager to use.  Passing in null indicates
       
  1436      *        the fallback PaintManager should be used.
       
  1437      */
       
  1438     void setPaintManager(PaintManager paintManager) {
       
  1439         if (paintManager == null) {
       
  1440             paintManager = new PaintManager();
       
  1441         }
       
  1442         PaintManager oldPaintManager;
       
  1443         synchronized(this) {
       
  1444             oldPaintManager = this.paintManager;
       
  1445             this.paintManager = paintManager;
       
  1446             paintManager.repaintManager = this;
       
  1447         }
       
  1448         if (oldPaintManager != null) {
       
  1449             oldPaintManager.dispose();
       
  1450         }
       
  1451     }
       
  1452 
       
  1453     private synchronized PaintManager getPaintManager() {
       
  1454         if (paintManager == null) {
       
  1455             PaintManager paintManager = null;
       
  1456             if (doubleBufferingEnabled && !nativeDoubleBuffering) {
       
  1457                 switch (bufferStrategyType) {
       
  1458                 case BUFFER_STRATEGY_NOT_SPECIFIED:
       
  1459                     Toolkit tk = Toolkit.getDefaultToolkit();
       
  1460                     if (tk instanceof SunToolkit) {
       
  1461                         SunToolkit stk = (SunToolkit) tk;
       
  1462                         if (stk.useBufferPerWindow()) {
       
  1463                             paintManager = new BufferStrategyPaintManager();
       
  1464                         }
       
  1465                     }
       
  1466                     break;
       
  1467                 case BUFFER_STRATEGY_SPECIFIED_ON:
       
  1468                     paintManager = new BufferStrategyPaintManager();
       
  1469                     break;
       
  1470                 default:
       
  1471                     break;
       
  1472                 }
       
  1473             }
       
  1474             // null case handled in setPaintManager
       
  1475             setPaintManager(paintManager);
       
  1476         }
       
  1477         return paintManager;
       
  1478     }
       
  1479 
       
  1480     private void scheduleProcessingRunnable(AppContext context) {
       
  1481         if (processingRunnable.markPending()) {
       
  1482             Toolkit tk = Toolkit.getDefaultToolkit();
       
  1483             if (tk instanceof SunToolkit) {
       
  1484                 SunToolkit.getSystemEventQueueImplPP(context).
       
  1485                   postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
       
  1486                                                 processingRunnable));
       
  1487             } else {
       
  1488                 Toolkit.getDefaultToolkit().getSystemEventQueue().
       
  1489                       postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
       
  1490                                                     processingRunnable));
       
  1491             }
       
  1492         }
       
  1493     }
       
  1494 
       
  1495 
       
  1496     /**
       
  1497      * PaintManager is used to handle all double buffered painting for
       
  1498      * Swing.  Subclasses should call back into the JComponent method
       
  1499      * <code>paintToOffscreen</code> to handle the actual painting.
       
  1500      */
       
  1501     static class PaintManager {
       
  1502         /**
       
  1503          * RepaintManager the PaintManager has been installed on.
       
  1504          */
       
  1505         protected RepaintManager repaintManager;
       
  1506         boolean isRepaintingRoot;
       
  1507 
       
  1508         /**
       
  1509          * Paints a region of a component
       
  1510          *
       
  1511          * @param paintingComponent Component to paint
       
  1512          * @param bufferComponent Component to obtain buffer for
       
  1513          * @param g Graphics to paint to
       
  1514          * @param x X-coordinate
       
  1515          * @param y Y-coordinate
       
  1516          * @param w Width
       
  1517          * @param h Height
       
  1518          * @return true if painting was successful.
       
  1519          */
       
  1520         public boolean paint(JComponent paintingComponent,
       
  1521                              JComponent bufferComponent, Graphics g,
       
  1522                              int x, int y, int w, int h) {
       
  1523             // First attempt to use VolatileImage buffer for performance.
       
  1524             // If this fails (which should rarely occur), fallback to a
       
  1525             // standard Image buffer.
       
  1526             boolean paintCompleted = false;
       
  1527             Image offscreen;
       
  1528             int sw = w + 1;
       
  1529             int sh = h + 1;
       
  1530 
       
  1531             if (repaintManager.useVolatileDoubleBuffer() &&
       
  1532                 (offscreen = getValidImage(repaintManager.
       
  1533                 getVolatileOffscreenBuffer(bufferComponent, sw, sh))) != null) {
       
  1534                 VolatileImage vImage = (java.awt.image.VolatileImage)offscreen;
       
  1535                 GraphicsConfiguration gc = bufferComponent.
       
  1536                                             getGraphicsConfiguration();
       
  1537                 for (int i = 0; !paintCompleted &&
       
  1538                          i < RepaintManager.VOLATILE_LOOP_MAX; i++) {
       
  1539                     if (vImage.validate(gc) ==
       
  1540                                    VolatileImage.IMAGE_INCOMPATIBLE) {
       
  1541                         repaintManager.resetVolatileDoubleBuffer(gc);
       
  1542                         offscreen = repaintManager.getVolatileOffscreenBuffer(
       
  1543                             bufferComponent, sw, sh);
       
  1544                         vImage = (java.awt.image.VolatileImage)offscreen;
       
  1545                     }
       
  1546                     paintDoubleBuffered(paintingComponent, vImage, g, x, y,
       
  1547                                         w, h);
       
  1548                     paintCompleted = !vImage.contentsLost();
       
  1549                 }
       
  1550             }
       
  1551             // VolatileImage painting loop failed, fallback to regular
       
  1552             // offscreen buffer
       
  1553             if (!paintCompleted && (offscreen = getValidImage(
       
  1554                       repaintManager.getOffscreenBuffer(
       
  1555                       bufferComponent, w, h))) != null) {
       
  1556                 paintDoubleBuffered(paintingComponent, offscreen, g, x, y, w,
       
  1557                                     h);
       
  1558                 paintCompleted = true;
       
  1559             }
       
  1560             return paintCompleted;
       
  1561         }
       
  1562 
       
  1563         /**
       
  1564          * Does a copy area on the specified region.
       
  1565          */
       
  1566         public void copyArea(JComponent c, Graphics g, int x, int y, int w,
       
  1567                              int h, int deltaX, int deltaY, boolean clip) {
       
  1568             g.copyArea(x, y, w, h, deltaX, deltaY);
       
  1569         }
       
  1570 
       
  1571         /**
       
  1572          * Invoked prior to any calls to paint or copyArea.
       
  1573          */
       
  1574         public void beginPaint() {
       
  1575         }
       
  1576 
       
  1577         /**
       
  1578          * Invoked to indicate painting has been completed.
       
  1579          */
       
  1580         public void endPaint() {
       
  1581         }
       
  1582 
       
  1583         /**
       
  1584          * Shows a region of a previously rendered component.  This
       
  1585          * will return true if successful, false otherwise.  The default
       
  1586          * implementation returns false.
       
  1587          */
       
  1588         public boolean show(Container c, int x, int y, int w, int h) {
       
  1589             return false;
       
  1590         }
       
  1591 
       
  1592         /**
       
  1593          * Invoked when the doubleBuffered or useTrueDoubleBuffering
       
  1594          * properties of a JRootPane change.  This may come in on any thread.
       
  1595          */
       
  1596         public void doubleBufferingChanged(JRootPane rootPane) {
       
  1597         }
       
  1598 
       
  1599         /**
       
  1600          * Paints a portion of a component to an offscreen buffer.
       
  1601          */
       
  1602         protected void paintDoubleBuffered(JComponent c, Image image,
       
  1603                 Graphics g, int clipX, int clipY,
       
  1604                 int clipW, int clipH) {
       
  1605             if (image instanceof VolatileImage && isPixelsCopying(c, g)) {
       
  1606                 paintDoubleBufferedFPScales(c, image, g, clipX, clipY, clipW, clipH);
       
  1607             } else {
       
  1608                 paintDoubleBufferedImpl(c, image, g, clipX, clipY, clipW, clipH);
       
  1609             }
       
  1610         }
       
  1611 
       
  1612         private void paintDoubleBufferedImpl(JComponent c, Image image,
       
  1613                                              Graphics g, int clipX, int clipY,
       
  1614                                              int clipW, int clipH) {
       
  1615             Graphics osg = image.getGraphics();
       
  1616             int bw = Math.min(clipW, image.getWidth(null));
       
  1617             int bh = Math.min(clipH, image.getHeight(null));
       
  1618             int x,y,maxx,maxy;
       
  1619 
       
  1620             try {
       
  1621                 for(x = clipX, maxx = clipX+clipW; x < maxx ;  x += bw ) {
       
  1622                     for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) {
       
  1623                         osg.translate(-x, -y);
       
  1624                         osg.setClip(x,y,bw,bh);
       
  1625                         if (volatileBufferType != Transparency.OPAQUE
       
  1626                                 && osg instanceof Graphics2D) {
       
  1627                             final Graphics2D g2d = (Graphics2D) osg;
       
  1628                             final Color oldBg = g2d.getBackground();
       
  1629                             g2d.setBackground(c.getBackground());
       
  1630                             g2d.clearRect(x, y, bw, bh);
       
  1631                             g2d.setBackground(oldBg);
       
  1632                         }
       
  1633                         c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy);
       
  1634                         g.setClip(x, y, bw, bh);
       
  1635                         if (volatileBufferType != Transparency.OPAQUE
       
  1636                                 && g instanceof Graphics2D) {
       
  1637                             final Graphics2D g2d = (Graphics2D) g;
       
  1638                             final Composite oldComposite = g2d.getComposite();
       
  1639                             g2d.setComposite(AlphaComposite.Src);
       
  1640                             g2d.drawImage(image, x, y, c);
       
  1641                             g2d.setComposite(oldComposite);
       
  1642                         } else {
       
  1643                             g.drawImage(image, x, y, c);
       
  1644                         }
       
  1645                         osg.translate(x, y);
       
  1646                     }
       
  1647                 }
       
  1648             } finally {
       
  1649                 osg.dispose();
       
  1650             }
       
  1651         }
       
  1652 
       
  1653         private void paintDoubleBufferedFPScales(JComponent c, Image image,
       
  1654                                                  Graphics g, int clipX, int clipY,
       
  1655                                                  int clipW, int clipH) {
       
  1656             Graphics osg = image.getGraphics();
       
  1657             Graphics2D g2d = (Graphics2D) g;
       
  1658             Graphics2D osg2d = (Graphics2D) osg;
       
  1659 
       
  1660             AffineTransform identity = new AffineTransform();
       
  1661             int bw = Math.min(clipW, image.getWidth(null));
       
  1662             int bh = Math.min(clipH, image.getHeight(null));
       
  1663             int x, y, maxx, maxy;
       
  1664 
       
  1665             AffineTransform tx = g2d.getTransform();
       
  1666             double scaleX = tx.getScaleX();
       
  1667             double scaleY = tx.getScaleY();
       
  1668             double trX = tx.getTranslateX();
       
  1669             double trY = tx.getTranslateY();
       
  1670 
       
  1671             boolean translucent = volatileBufferType != Transparency.OPAQUE;
       
  1672             Composite oldComposite = g2d.getComposite();
       
  1673 
       
  1674             try {
       
  1675                 for (x = clipX, maxx = clipX + clipW; x < maxx; x += bw) {
       
  1676                     for (y = clipY, maxy = clipY + clipH; y < maxy; y += bh) {
       
  1677 
       
  1678                         // draw x, y, bw, bh
       
  1679                         int pixelx1 = Region.clipRound(x * scaleX + trX);
       
  1680                         int pixely1 = Region.clipRound(y * scaleY + trY);
       
  1681                         int pixelx2 = Region.clipRound((x + bw) * scaleX + trX);
       
  1682                         int pixely2 = Region.clipRound((y + bh) * scaleY + trY);
       
  1683                         int pixelw = pixelx2 - pixelx1;
       
  1684                         int pixelh = pixely2 - pixely1;
       
  1685 
       
  1686                         osg2d.setTransform(identity);
       
  1687                         if (translucent) {
       
  1688                             final Color oldBg = g2d.getBackground();
       
  1689                             g2d.setBackground(c.getBackground());
       
  1690                             g2d.clearRect(pixelx1, pixely1, pixelw, pixelh);
       
  1691                             g2d.setBackground(oldBg);
       
  1692                         }
       
  1693 
       
  1694                         osg2d.setClip(0, 0, pixelw, pixelh);
       
  1695                         osg2d.translate(trX - pixelx1, trY - pixely1);
       
  1696                         osg2d.scale(scaleX, scaleY);
       
  1697                         c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy);
       
  1698 
       
  1699                         g2d.setTransform(identity);
       
  1700                         g2d.setClip(pixelx1, pixely1, pixelw, pixelh);
       
  1701                         AffineTransform stx = new AffineTransform();
       
  1702                         stx.translate(pixelx1, pixely1);
       
  1703                         stx.scale(scaleX, scaleY);
       
  1704                         g2d.setTransform(stx);
       
  1705 
       
  1706                         if (translucent) {
       
  1707                             g2d.setComposite(AlphaComposite.Src);
       
  1708                         }
       
  1709 
       
  1710                         g2d.drawImage(image, 0, 0, c);
       
  1711 
       
  1712                         if (translucent) {
       
  1713                             g2d.setComposite(oldComposite);
       
  1714                         }
       
  1715                         g2d.setTransform(tx);
       
  1716                     }
       
  1717                 }
       
  1718             } finally {
       
  1719                 osg.dispose();
       
  1720             }
       
  1721         }
       
  1722 
       
  1723         /**
       
  1724          * If <code>image</code> is non-null with a positive size it
       
  1725          * is returned, otherwise null is returned.
       
  1726          */
       
  1727         private Image getValidImage(Image image) {
       
  1728             if (image != null && image.getWidth(null) > 0 &&
       
  1729                                  image.getHeight(null) > 0) {
       
  1730                 return image;
       
  1731             }
       
  1732             return null;
       
  1733         }
       
  1734 
       
  1735         /**
       
  1736          * Schedules a repaint for the specified component.  This differs
       
  1737          * from <code>root.repaint</code> in that if the RepaintManager is
       
  1738          * currently processing paint requests it'll process this request
       
  1739          * with the current set of requests.
       
  1740          */
       
  1741         protected void repaintRoot(JComponent root) {
       
  1742             assert (repaintManager.repaintRoot == null);
       
  1743             if (repaintManager.painting) {
       
  1744                 repaintManager.repaintRoot = root;
       
  1745             }
       
  1746             else {
       
  1747                 root.repaint();
       
  1748             }
       
  1749         }
       
  1750 
       
  1751         /**
       
  1752          * Returns true if the component being painted is the root component
       
  1753          * that was previously passed to <code>repaintRoot</code>.
       
  1754          */
       
  1755         protected boolean isRepaintingRoot() {
       
  1756             return isRepaintingRoot;
       
  1757         }
       
  1758 
       
  1759         /**
       
  1760          * Cleans up any state.  After invoked the PaintManager will no
       
  1761          * longer be used anymore.
       
  1762          */
       
  1763         protected void dispose() {
       
  1764         }
       
  1765 
       
  1766         private boolean isPixelsCopying(JComponent c, Graphics g) {
       
  1767 
       
  1768             AffineTransform tx = getTransform(g);
       
  1769             GraphicsConfiguration gc = c.getGraphicsConfiguration();
       
  1770 
       
  1771             if (tx == null || gc == null
       
  1772                     || !SwingUtilities2.isFloatingPointScale(tx)) {
       
  1773                 return false;
       
  1774             }
       
  1775 
       
  1776             AffineTransform gcTx = gc.getDefaultTransform();
       
  1777 
       
  1778             return gcTx.getScaleX() == tx.getScaleX()
       
  1779                     && gcTx.getScaleY() == tx.getScaleY();
       
  1780         }
       
  1781 
       
  1782         private static AffineTransform getTransform(Graphics g) {
       
  1783             if (g instanceof SunGraphics2D) {
       
  1784                 return ((SunGraphics2D) g).transform;
       
  1785             } else if (g instanceof Graphics2D) {
       
  1786                 return ((Graphics2D) g).getTransform();
       
  1787             }
       
  1788             return null;
       
  1789         }
       
  1790     }
       
  1791 
       
  1792     private class DoubleBufferInfo {
       
  1793         public Image image;
       
  1794         public Dimension size;
       
  1795         public boolean needsReset = false;
       
  1796     }
       
  1797 
       
  1798 
       
  1799     /**
       
  1800      * Listener installed to detect display changes. When display changes,
       
  1801      * schedules a callback to notify all RepaintManagers of the display
       
  1802      * changes. Only one DisplayChangedHandler is ever installed. The
       
  1803      * singleton instance will schedule notification for all AppContexts.
       
  1804      */
       
  1805     private static final class DisplayChangedHandler implements
       
  1806                                              DisplayChangedListener {
       
  1807         // Empty non private constructor was added because access to this
       
  1808         // class shouldn't be generated by the compiler using synthetic
       
  1809         // accessor method
       
  1810         DisplayChangedHandler() {
       
  1811         }
       
  1812 
       
  1813         public void displayChanged() {
       
  1814             scheduleDisplayChanges();
       
  1815         }
       
  1816 
       
  1817         public void paletteChanged() {
       
  1818         }
       
  1819 
       
  1820         private static void scheduleDisplayChanges() {
       
  1821             // To avoid threading problems, we notify each RepaintManager
       
  1822             // on the thread it was created on.
       
  1823             for (AppContext context : AppContext.getAppContexts()) {
       
  1824                 synchronized(context) {
       
  1825                     if (!context.isDisposed()) {
       
  1826                         EventQueue eventQueue = (EventQueue)context.get(
       
  1827                             AppContext.EVENT_QUEUE_KEY);
       
  1828                         if (eventQueue != null) {
       
  1829                             eventQueue.postEvent(new InvocationEvent(
       
  1830                                 Toolkit.getDefaultToolkit(),
       
  1831                                 new DisplayChangedRunnable()));
       
  1832                         }
       
  1833                     }
       
  1834                 }
       
  1835             }
       
  1836         }
       
  1837     }
       
  1838 
       
  1839 
       
  1840     private static final class DisplayChangedRunnable implements Runnable {
       
  1841         public void run() {
       
  1842             RepaintManager.currentManager((JComponent)null).displayChanged();
       
  1843         }
       
  1844     }
       
  1845 
       
  1846 
       
  1847     /**
       
  1848      * Runnable used to process all repaint/revalidate requests.
       
  1849      */
       
  1850     private final class ProcessingRunnable implements Runnable {
       
  1851         // If true, we're wainting on the EventQueue.
       
  1852         private boolean pending;
       
  1853 
       
  1854         /**
       
  1855          * Marks this processing runnable as pending. If this was not
       
  1856          * already marked as pending, true is returned.
       
  1857          */
       
  1858         public synchronized boolean markPending() {
       
  1859             if (!pending) {
       
  1860                 pending = true;
       
  1861                 return true;
       
  1862             }
       
  1863             return false;
       
  1864         }
       
  1865 
       
  1866         public void run() {
       
  1867             synchronized (this) {
       
  1868                 pending = false;
       
  1869             }
       
  1870             // First pass, flush any heavy paint events into real paint
       
  1871             // events.  If there are pending heavy weight requests this will
       
  1872             // result in q'ing this request up one more time.  As
       
  1873             // long as no other requests come in between now and the time
       
  1874             // the second one is processed nothing will happen.  This is not
       
  1875             // ideal, but the logic needed to suppress the second request is
       
  1876             // more headache than it's worth.
       
  1877             scheduleHeavyWeightPaints();
       
  1878             // Do the actual validation and painting.
       
  1879             validateInvalidComponents();
       
  1880             prePaintDirtyRegions();
       
  1881         }
       
  1882     }
       
  1883     private RepaintManager getDelegate(Component c) {
       
  1884         RepaintManager delegate = SwingUtilities3.getDelegateRepaintManager(c);
       
  1885         if (this == delegate) {
       
  1886             delegate = null;
       
  1887         }
       
  1888         return delegate;
       
  1889     }
       
  1890 }