View Javadoc
1   /*
2    * Copyright (c) 2007, 2013, 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  
26  package sun.java2d;
27  
28  import java.awt.Color;
29  import java.awt.Rectangle;
30  import java.awt.AlphaComposite;
31  import java.awt.GraphicsEnvironment;
32  
33  import sun.awt.DisplayChangedListener;
34  import sun.java2d.StateTrackable.State;
35  import sun.java2d.loops.CompositeType;
36  import sun.java2d.loops.SurfaceType;
37  import sun.java2d.loops.Blit;
38  import sun.java2d.loops.BlitBg;
39  import sun.awt.image.SurfaceManager;
40  import sun.awt.image.SurfaceManager.FlushableCacheData;
41  
42  import java.security.AccessController;
43  import sun.security.action.GetPropertyAction;
44  
45  /**
46   * The proxy class encapsulates the logic for managing alternate
47   * SurfaceData representations of a primary SurfaceData.
48   * The main class will handle tracking the state changes of the
49   * primary SurfaceData and updating the associated SurfaceData
50   * proxy variants.
51   * <p>
52   * Subclasses have 2 main responsibilities:
53   * <ul>
54   * <li> Override the isSupportedOperation() method to determine if
55   *      a given operation can be accelerated with a given source
56   *      SurfaceData
57   * <li> Override the validateSurfaceData() method to create or update
58   *      a given accelerated surface to hold the pixels for the indicated
59   *      source SurfaceData
60   * </ul>
61   * If necessary, a subclass may also override the updateSurfaceData
62   * method to transfer the pixels to the accelerated surface.
63   * By default the parent class will transfer the pixels using a
64   * standard Blit operation between the two SurfaceData objects.
65   */
66  public abstract class SurfaceDataProxy
67      implements DisplayChangedListener, SurfaceManager.FlushableCacheData
68  {
69      private static boolean cachingAllowed;
70      private static int defaultThreshold;
71  
72      static {
73          cachingAllowed = true;
74          String manimg = AccessController.doPrivileged(
75              new GetPropertyAction("sun.java2d.managedimages"));
76          if (manimg != null && manimg.equals("false")) {
77              cachingAllowed = false;
78              System.out.println("Disabling managed images");
79          }
80  
81          defaultThreshold = 1;
82          String num = AccessController.doPrivileged(
83              new GetPropertyAction("sun.java2d.accthreshold"));
84          if (num != null) {
85              try {
86                  int parsed = Integer.parseInt(num);
87                  if (parsed >= 0) {
88                      defaultThreshold = parsed;
89                      System.out.println("New Default Acceleration Threshold: " +
90                                         defaultThreshold);
91                  }
92              } catch (NumberFormatException e) {
93                  System.err.println("Error setting new threshold:" + e);
94              }
95          }
96      }
97  
98      public static boolean isCachingAllowed() {
99          return cachingAllowed;
100     }
101 
102     /**
103      * Determine if an alternate form for the srcData is needed
104      * and appropriate from the given operational parameters.
105      */
106     public abstract boolean isSupportedOperation(SurfaceData srcData,
107                                                  int txtype,
108                                                  CompositeType comp,
109                                                  Color bgColor);
110 
111     /**
112      * Construct an alternate form of the given SurfaceData.
113      * The contents of the returned SurfaceData may be undefined
114      * since the calling code will take care of updating the
115      * contents with a subsequent call to updateSurfaceData.
116      * <p>
117      * If the method returns null then there was a problem with
118      * allocating the accelerated surface.  The getRetryTracker()
119      * method will be called to track when to attempt another
120      * revalidation.
121      */
122     public abstract SurfaceData validateSurfaceData(SurfaceData srcData,
123                                                     SurfaceData cachedData,
124                                                     int w, int h);
125 
126     /**
127      * If the subclass is unable to validate or create a cached
128      * SurfaceData then this method will be used to get a
129      * StateTracker object that will indicate when to attempt
130      * to validate the surface again.  Subclasses may return
131      * trackers which count down an ever increasing threshold
132      * to provide hysteresis on creating surfaces during low
133      * memory conditions.  The default implementation just waits
134      * another "threshold" number of accesses before trying again.
135      */
136     public StateTracker getRetryTracker(SurfaceData srcData) {
137         return new CountdownTracker(threshold);
138     }
139 
140     public static class CountdownTracker implements StateTracker {
141         private int countdown;
142 
143         public CountdownTracker(int threshold) {
144             this.countdown = threshold;
145         }
146 
147         public synchronized boolean isCurrent() {
148             return (--countdown >= 0);
149         }
150     }
151 
152     /**
153      * This instance is for cases where a caching implementation
154      * determines that a particular source image will never need
155      * to be cached - either the source SurfaceData was of an
156      * incompatible type, or it was in an UNTRACKABLE state or
157      * some other factor is discovered that permanently prevents
158      * acceleration or caching.
159      * This class optimally implements NOP variants of all necessary
160      * methods to avoid caching with a minimum of fuss.
161      */
162     public static SurfaceDataProxy UNCACHED = new SurfaceDataProxy(0) {
163         @Override
164         public boolean isAccelerated() {
165             return false;
166         }
167 
168         @Override
169         public boolean isSupportedOperation(SurfaceData srcData,
170                                             int txtype,
171                                             CompositeType comp,
172                                             Color bgColor)
173         {
174             return false;
175         }
176 
177         @Override
178         public SurfaceData validateSurfaceData(SurfaceData srcData,
179                                                SurfaceData cachedData,
180                                                int w, int h)
181         {
182             throw new InternalError("UNCACHED should never validate SDs");
183         }
184 
185         @Override
186         public SurfaceData replaceData(SurfaceData srcData,
187                                        int txtype,
188                                        CompositeType comp,
189                                        Color bgColor)
190         {
191             // Not necessary to override this, but doing so is faster
192             return srcData;
193         }
194     };
195 
196     // The number of attempts to copy from a STABLE source before
197     // a cached copy is created or updated.
198     private int threshold;
199 
200     /*
201      * Source tracking data
202      *
203      * Every time that srcTracker is out of date we will reset numtries
204      * to threshold and set the cacheTracker to one that is non-current.
205      * numtries will then count down to 0 at which point the cacheTracker
206      * will remind us that we need to update the cachedSD before we can
207      * use it.
208      *
209      * Note that since these fields interrelate we should synchronize
210      * whenever we update them, but it should be OK to read them
211      * without synchronization.
212      */
213     private StateTracker srcTracker;
214     private int numtries;
215 
216     /*
217      * Cached data
218      *
219      * We cache a SurfaceData created by the subclass in cachedSD and
220      * track its state (isValid and !surfaceLost) in cacheTracker.
221      *
222      * Also, when we want to note that cachedSD needs to be updated
223      * we replace the cacheTracker with a NEVER_CURRENT tracker which
224      * will cause us to try to revalidate and update the surface on
225      * next use.
226      */
227     private SurfaceData cachedSD;
228     private StateTracker cacheTracker;
229 
230     /*
231      * Are we still the best object to control caching of data
232      * for the source image?
233      */
234     private boolean valid;
235 
236     /**
237      * Create a SurfaceData proxy manager that attempts to create
238      * and cache a variant copy of the source SurfaceData after
239      * the default threshold number of attempts to copy from the
240      * STABLE source.
241      */
242     public SurfaceDataProxy() {
243         this(defaultThreshold);
244     }
245 
246     /**
247      * Create a SurfaceData proxy manager that attempts to create
248      * and cache a variant copy of the source SurfaceData after
249      * the specified threshold number of attempts to copy from
250      * the STABLE source.
251      */
252     public SurfaceDataProxy(int threshold) {
253         this.threshold = threshold;
254 
255         this.srcTracker = StateTracker.NEVER_CURRENT;
256         // numtries will be reset on first use
257         this.cacheTracker = StateTracker.NEVER_CURRENT;
258 
259         this.valid = true;
260     }
261 
262     /**
263      * Returns true iff this SurfaceData proxy is still the best
264      * way to control caching of the given source on the given
265      * destination.
266      */
267     public boolean isValid() {
268         return valid;
269     }
270 
271     /**
272      * Sets the valid state to false so that the next time this
273      * proxy is fetched to generate a replacement SurfaceData,
274      * the code in SurfaceData knows to replace the proxy first.
275      */
276     public void invalidate() {
277         this.valid = false;
278     }
279 
280     /**
281      * Flush all cached resources as per the FlushableCacheData interface.
282      * The deaccelerated parameter indicates if the flush is
283      * happening because the associated surface is no longer
284      * being accelerated (for instance the acceleration priority
285      * is set below the threshold needed for acceleration).
286      * Returns a boolean that indicates if the cached object is
287      * no longer needed and should be removed from the cache.
288      */
289     public boolean flush(boolean deaccelerated) {
290         if (deaccelerated) {
291             invalidate();
292         }
293         flush();
294         return !isValid();
295     }
296 
297     /**
298      * Actively flushes (drops and invalidates) the cached surface
299      * so that it can be reclaimed quickly.
300      */
301     public synchronized void flush() {
302         SurfaceData csd = this.cachedSD;
303         this.cachedSD = null;
304         this.cacheTracker = StateTracker.NEVER_CURRENT;
305         if (csd != null) {
306             csd.flush();
307         }
308     }
309 
310     /**
311      * Returns true iff this SurfaceData proxy is still valid
312      * and if it has a currently cached replacement that is also
313      * valid and current.
314      */
315     public boolean isAccelerated() {
316         return (isValid() &&
317                 srcTracker.isCurrent() &&
318                 cacheTracker.isCurrent());
319     }
320 
321     /**
322      * This method should be called from subclasses which create
323      * cached SurfaceData objects that depend on the current
324      * properties of the display.
325      */
326     protected void activateDisplayListener() {
327         GraphicsEnvironment ge =
328             GraphicsEnvironment.getLocalGraphicsEnvironment();
329         // We could have a HeadlessGE at this point, so double-check before
330         // assuming anything.
331         // Also, no point in listening to display change events if
332         // the image is never going to be accelerated.
333         if (ge instanceof SunGraphicsEnvironment) {
334             ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this);
335         }
336     }
337 
338     /**
339      * Invoked when the display mode has changed.
340      * This method will invalidate and drop the internal cachedSD object.
341      */
342     public void displayChanged() {
343         flush();
344     }
345 
346     /**
347      * Invoked when the palette has changed.
348      */
349     public void paletteChanged() {
350         // We could potentially get away with just resetting cacheTracker
351         // here but there is a small window of vulnerability in the
352         // replaceData method where we could be just finished with
353         // updating the cachedSD when this method is called and even
354         // though we set a non-current cacheTracker here it will then
355         // immediately get set to a current one by the thread that is
356         // updating the cachedSD.  It is safer to just replace the
357         // srcTracker with a non-current version that will trigger a
358         // full update cycle the next time this proxy is used.
359         // The downside is having to go through a full threshold count
360         // before we can update and use our cache again, but palette
361         // changes should be relatively rare...
362         this.srcTracker = StateTracker.NEVER_CURRENT;
363     }
364 
365     /**
366      * This method attempts to replace the srcData with a cached version.
367      * It relies on the subclass to determine if the cached version will
368      * be useful given the operational parameters.
369      * This method checks any preexisting cached copy for being "up to date"
370      * and tries to update it if it is stale or non-existant and the
371      * appropriate number of accesses have occurred since it last was stale.
372      * <p>
373      * An outline of the process is as follows:
374      * <ol>
375      * <li> Check the operational parameters (txtype, comp, bgColor)
376      *      to make sure that the operation is supported.  Return the
377      *      original SurfaceData if the operation cannot be accelerated.
378      * <li> Check the tracker for the source surface to see if it has
379      *      remained stable since it was last cached.  Update the state
380      *      variables to cause both a threshold countdown and an update
381      *      of the cached copy if it is not.  (Setting cacheTracker to
382      *      NEVER_CURRENT effectively marks it as "needing to be updated".)
383      * <li> Check the tracker for the cached copy to see if is still
384      *      valid and up to date.  Note that the cacheTracker may be
385      *      non-current if either something happened to the cached copy
386      *      (eg. surfaceLost) or if the source was out of date and the
387      *      cacheTracker was set to NEVER_CURRENT to force an update.
388      *      Decrement the countdown and copy the source to the cache
389      *      as necessary and then update the variables to show that
390      *      the cached copy is stable.
391      * </ol>
392      */
393     public SurfaceData replaceData(SurfaceData srcData,
394                                    int txtype,
395                                    CompositeType comp,
396                                    Color bgColor)
397     {
398         if (isSupportedOperation(srcData, txtype, comp, bgColor)) {
399             // First deal with tracking the source.
400             if (!srcTracker.isCurrent()) {
401                 synchronized (this) {
402                     this.numtries = threshold;
403                     this.srcTracker = srcData.getStateTracker();
404                     this.cacheTracker = StateTracker.NEVER_CURRENT;
405                 }
406 
407                 if (!srcTracker.isCurrent()) {
408                     // Dynamic or Untrackable (or a very recent modification)
409                     if (srcData.getState() == State.UNTRACKABLE) {
410                         // UNTRACKABLE means we can never cache again.
411 
412                         // Invalidate so we get replaced next time we are used
413                         // (presumably with an UNCACHED proxy).
414                         invalidate();
415 
416                         // Aggressively drop our reference to the cachedSD
417                         // in case this proxy is not consulted again (and
418                         // thus replaced) for a long time.
419                         flush();
420                     }
421                     return srcData;
422                 }
423             }
424 
425             // Then deal with checking the validity of the cached SurfaceData
426             SurfaceData csd = this.cachedSD;
427             if (!cacheTracker.isCurrent()) {
428                 // Next make sure the dust has settled
429                 synchronized (this) {
430                     if (numtries > 0) {
431                         --numtries;
432                         return srcData;
433                     }
434                 }
435 
436                 Rectangle r = srcData.getBounds();
437                 int w = r.width;
438                 int h = r.height;
439 
440                 // Snapshot the tracker in case it changes while
441                 // we are updating the cached SD...
442                 StateTracker curTracker = srcTracker;
443 
444                 csd = validateSurfaceData(srcData, csd, w, h);
445                 if (csd == null) {
446                     synchronized (this) {
447                         if (curTracker == srcTracker) {
448                             this.cacheTracker = getRetryTracker(srcData);
449                             this.cachedSD = null;
450                         }
451                     }
452                     return srcData;
453                 }
454 
455                 updateSurfaceData(srcData, csd, w, h);
456                 if (!csd.isValid()) {
457                     return srcData;
458                 }
459 
460                 synchronized (this) {
461                     // We only reset these variables if the tracker from
462                     // before the surface update is still in use and current
463                     // Note that we must use a srcTracker that was fetched
464                     // from before the update process to make sure that we
465                     // do not lose some pixel changes in the shuffle.
466                     if (curTracker == srcTracker && curTracker.isCurrent()) {
467                         this.cacheTracker = csd.getStateTracker();
468                         this.cachedSD = csd;
469                     }
470                 }
471             }
472 
473             if (csd != null) {
474                 return csd;
475             }
476         }
477 
478         return srcData;
479     }
480 
481     /**
482      * This is the default implementation for updating the cached
483      * SurfaceData from the source (primary) SurfaceData.
484      * A simple Blit is used to copy the pixels from the source to
485      * the destination SurfaceData.
486      * A subclass can override this implementation if a more complex
487      * operation is required to update its cached copies.
488      */
489     public void updateSurfaceData(SurfaceData srcData,
490                                   SurfaceData dstData,
491                                   int w, int h)
492     {
493         SurfaceType srcType = srcData.getSurfaceType();
494         SurfaceType dstType = dstData.getSurfaceType();
495         Blit blit = Blit.getFromCache(srcType,
496                                       CompositeType.SrcNoEa,
497                                       dstType);
498         blit.Blit(srcData, dstData,
499                   AlphaComposite.Src, null,
500                   0, 0, 0, 0, w, h);
501         dstData.markDirty();
502     }
503 
504     /**
505      * This is an alternate implementation for updating the cached
506      * SurfaceData from the source (primary) SurfaceData using a
507      * background color for transparent pixels.
508      * A simple BlitBg is used to copy the pixels from the source to
509      * the destination SurfaceData with the specified bgColor.
510      * A subclass can override the normal updateSurfaceData method
511      * and call this implementation instead if it wants to use color
512      * keying for bitmask images.
513      */
514     public void updateSurfaceDataBg(SurfaceData srcData,
515                                     SurfaceData dstData,
516                                     int w, int h, Color bgColor)
517     {
518         SurfaceType srcType = srcData.getSurfaceType();
519         SurfaceType dstType = dstData.getSurfaceType();
520         BlitBg blitbg = BlitBg.getFromCache(srcType,
521                                             CompositeType.SrcNoEa,
522                                             dstType);
523         blitbg.BlitBg(srcData, dstData,
524                       AlphaComposite.Src, null, bgColor.getRGB(),
525                       0, 0, 0, 0, w, h);
526         dstData.markDirty();
527     }
528 }