001: /*
002: * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.java2d;
027:
028: import java.awt.Color;
029: import java.awt.Rectangle;
030: import java.awt.AlphaComposite;
031: import java.awt.GraphicsEnvironment;
032:
033: import sun.awt.DisplayChangedListener;
034: import sun.java2d.StateTrackable.State;
035: import sun.java2d.loops.CompositeType;
036: import sun.java2d.loops.SurfaceType;
037: import sun.java2d.loops.Blit;
038: import sun.java2d.loops.BlitBg;
039: import sun.awt.image.SurfaceManager;
040: import sun.awt.image.SurfaceManager.FlushableCacheData;
041:
042: import java.security.AccessController;
043: import sun.security.action.GetPropertyAction;
044:
045: /**
046: * The proxy class encapsulates the logic for managing alternate
047: * SurfaceData representations of a primary SurfaceData.
048: * The main class will handle tracking the state changes of the
049: * primary SurfaceData and updating the associated SurfaceData
050: * proxy variants.
051: * <p>
052: * Subclasses have 2 main responsibilities:
053: * <ul>
054: * <li> Override the isSupportedOperation() method to determine if
055: * a given operation can be accelerated with a given source
056: * SurfaceData
057: * <li> Override the validateSurfaceData() method to create or update
058: * a given accelerated surface to hold the pixels for the indicated
059: * source SurfaceData
060: * </ul>
061: * If necessary, a subclass may also override the updateSurfaceData
062: * method to transfer the pixels to the accelerated surface.
063: * By default the parent class will transfer the pixels using a
064: * standard Blit operation between the two SurfaceData objects.
065: */
066: public abstract class SurfaceDataProxy implements
067: DisplayChangedListener, SurfaceManager.FlushableCacheData {
068: private static boolean cachingAllowed;
069: private static int defaultThreshold;
070:
071: static {
072: cachingAllowed = true;
073: String manimg = (String) AccessController
074: .doPrivileged(new GetPropertyAction(
075: "sun.java2d.managedimages"));
076: if (manimg != null && manimg.equals("false")) {
077: cachingAllowed = false;
078: System.out.println("Disabling managed images");
079: }
080:
081: defaultThreshold = 1;
082: String num = (String) AccessController
083: .doPrivileged(new GetPropertyAction(
084: "sun.java2d.accthreshold"));
085: if (num != null) {
086: try {
087: int parsed = Integer.parseInt(num);
088: if (parsed >= 0) {
089: defaultThreshold = parsed;
090: System.out
091: .println("New Default Acceleration Threshold: "
092: + defaultThreshold);
093: }
094: } catch (NumberFormatException e) {
095: System.err.println("Error setting new threshold:" + e);
096: }
097: }
098: }
099:
100: public static boolean isCachingAllowed() {
101: return cachingAllowed;
102: }
103:
104: /**
105: * Determine if an alternate form for the srcData is needed
106: * and appropriate from the given operational parameters.
107: */
108: public abstract boolean isSupportedOperation(SurfaceData srcData,
109: int txtype, CompositeType comp, 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(
123: SurfaceData srcData, SurfaceData cachedData, int w, int h);
124:
125: /**
126: * If the subclass is unable to validate or create a cached
127: * SurfaceData then this method will be used to get a
128: * StateTracker object that will indicate when to attempt
129: * to validate the surface again. Subclasses may return
130: * trackers which count down an ever increasing threshold
131: * to provide hysteresis on creating surfaces during low
132: * memory conditions. The default implementation just waits
133: * another "threshold" number of accesses before trying again.
134: */
135: public StateTracker getRetryTracker(SurfaceData srcData) {
136: return new CountdownTracker(threshold);
137: }
138:
139: public static class CountdownTracker implements StateTracker {
140: private int countdown;
141:
142: public CountdownTracker(int threshold) {
143: this .countdown = threshold;
144: }
145:
146: public synchronized boolean isCurrent() {
147: return (--countdown >= 0);
148: }
149: }
150:
151: /**
152: * This instance is for cases where a caching implementation
153: * determines that a particular source image will never need
154: * to be cached - either the source SurfaceData was of an
155: * incompatible type, or it was in an UNTRACKABLE state or
156: * some other factor is discovered that permanently prevents
157: * acceleration or caching.
158: * This class optimally implements NOP variants of all necessary
159: * methods to avoid caching with a minimum of fuss.
160: */
161: public static SurfaceDataProxy UNCACHED = new SurfaceDataProxy(0) {
162: @Override
163: public boolean isAccelerated() {
164: return false;
165: }
166:
167: @Override
168: public boolean isSupportedOperation(SurfaceData srcData,
169: int txtype, CompositeType comp, Color bgColor) {
170: return false;
171: }
172:
173: @Override
174: public SurfaceData validateSurfaceData(SurfaceData srcData,
175: SurfaceData cachedData, int w, int h) {
176: throw new InternalError(
177: "UNCACHED should never validate SDs");
178: }
179:
180: @Override
181: public SurfaceData replaceData(SurfaceData srcData, int txtype,
182: CompositeType comp, Color bgColor) {
183: // Not necessary to override this, but doing so is faster
184: return srcData;
185: }
186: };
187:
188: // The number of attempts to copy from a STABLE source before
189: // a cached copy is created or updated.
190: private int threshold;
191:
192: /*
193: * Source tracking data
194: *
195: * Every time that srcTracker is out of date we will reset numtries
196: * to threshold and set the cacheTracker to one that is non-current.
197: * numtries will then count down to 0 at which point the cacheTracker
198: * will remind us that we need to update the cachedSD before we can
199: * use it.
200: *
201: * Note that since these fields interrelate we should synchronize
202: * whenever we update them, but it should be OK to read them
203: * without synchronization.
204: */
205: private StateTracker srcTracker;
206: private int numtries;
207:
208: /*
209: * Cached data
210: *
211: * We cache a SurfaceData created by the subclass in cachedSD and
212: * track its state (isValid and !surfaceLost) in cacheTracker.
213: *
214: * Also, when we want to note that cachedSD needs to be updated
215: * we replace the cacheTracker with a NEVER_CURRENT tracker which
216: * will cause us to try to revalidate and update the surface on
217: * next use.
218: */
219: private SurfaceData cachedSD;
220: private StateTracker cacheTracker;
221:
222: /*
223: * Are we still the best object to control caching of data
224: * for the source image?
225: */
226: private boolean valid;
227:
228: /**
229: * Create a SurfaceData proxy manager that attempts to create
230: * and cache a variant copy of the source SurfaceData after
231: * the default threshold number of attempts to copy from the
232: * STABLE source.
233: */
234: public SurfaceDataProxy() {
235: this (defaultThreshold);
236: }
237:
238: /**
239: * Create a SurfaceData proxy manager that attempts to create
240: * and cache a variant copy of the source SurfaceData after
241: * the specified threshold number of attempts to copy from
242: * the STABLE source.
243: */
244: public SurfaceDataProxy(int threshold) {
245: this .threshold = threshold;
246:
247: this .srcTracker = StateTracker.NEVER_CURRENT;
248: // numtries will be reset on first use
249: this .cacheTracker = StateTracker.NEVER_CURRENT;
250:
251: this .valid = true;
252: }
253:
254: /**
255: * Returns true iff this SurfaceData proxy is still the best
256: * way to control caching of the given source on the given
257: * destination.
258: */
259: public boolean isValid() {
260: return valid;
261: }
262:
263: /**
264: * Sets the valid state to false so that the next time this
265: * proxy is fetched to generate a replacement SurfaceData,
266: * the code in SurfaceData knows to replace the proxy first.
267: */
268: public void invalidate() {
269: this .valid = false;
270: }
271:
272: /**
273: * Flush all cached resources as per the FlushableCacheData interface.
274: * The deaccelerated parameter indicates if the flush is
275: * happening because the associated surface is no longer
276: * being accelerated (for instance the acceleration priority
277: * is set below the threshold needed for acceleration).
278: * Returns a boolean that indicates if the cached object is
279: * no longer needed and should be removed from the cache.
280: */
281: public boolean flush(boolean deaccelerated) {
282: if (deaccelerated) {
283: invalidate();
284: }
285: flush();
286: return !isValid();
287: }
288:
289: /**
290: * Actively flushes (drops and invalidates) the cached surface
291: * so that it can be reclaimed quickly.
292: */
293: public synchronized void flush() {
294: SurfaceData csd = this .cachedSD;
295: this .cachedSD = null;
296: this .cacheTracker = StateTracker.NEVER_CURRENT;
297: if (csd != null) {
298: csd.flush();
299: }
300: }
301:
302: /**
303: * Returns true iff this SurfaceData proxy is still valid
304: * and if it has a currently cached replacement that is also
305: * valid and current.
306: */
307: public boolean isAccelerated() {
308: return (isValid() && srcTracker.isCurrent() && cacheTracker
309: .isCurrent());
310: }
311:
312: /**
313: * This method should be called from subclasses which create
314: * cached SurfaceData objects that depend on the current
315: * properties of the display.
316: */
317: protected void activateDisplayListener() {
318: GraphicsEnvironment ge = GraphicsEnvironment
319: .getLocalGraphicsEnvironment();
320: // We could have a HeadlessGE at this point, so double-check before
321: // assuming anything.
322: // Also, no point in listening to display change events if
323: // the image is never going to be accelerated.
324: if (ge instanceof SunGraphicsEnvironment) {
325: ((SunGraphicsEnvironment) ge)
326: .addDisplayChangedListener(this );
327: }
328: }
329:
330: /**
331: * Invoked when the display mode has changed.
332: * This method will invalidate and drop the internal cachedSD object.
333: */
334: public void displayChanged() {
335: flush();
336: }
337:
338: /**
339: * Invoked when the palette has changed.
340: */
341: public void paletteChanged() {
342: // We could potentially get away with just resetting cacheTracker
343: // here but there is a small window of vulnerability in the
344: // replaceData method where we could be just finished with
345: // updating the cachedSD when this method is called and even
346: // though we set a non-current cacheTracker here it will then
347: // immediately get set to a current one by the thread that is
348: // updating the cachedSD. It is safer to just replace the
349: // srcTracker with a non-current version that will trigger a
350: // full update cycle the next time this proxy is used.
351: // The downside is having to go through a full threshold count
352: // before we can update and use our cache again, but palette
353: // changes should be relatively rare...
354: this .srcTracker = StateTracker.NEVER_CURRENT;
355: }
356:
357: /**
358: * This method attempts to replace the srcData with a cached version.
359: * It relies on the subclass to determine if the cached version will
360: * be useful given the operational parameters.
361: * This method checks any preexisting cached copy for being "up to date"
362: * and tries to update it if it is stale or non-existant and the
363: * appropriate number of accesses have occured since it last was stale.
364: * <p>
365: * An outline of the process is as follows:
366: * <ol>
367: * <li> Check the operational parameters (txtype, comp, bgColor)
368: * to make sure that the operation is supported. Return the
369: * original SurfaceData if the operation cannot be accelerated.
370: * <li> Check the tracker for the source surface to see if it has
371: * remained stable since it was last cached. Update the state
372: * variables to cause both a threshold countdown and an update
373: * of the cached copy if it is not. (Setting cacheTracker to
374: * NEVER_CURRENT effectively marks it as "needing to be updated".)
375: * <li> Check the tracker for the cached copy to see if is still
376: * valid and up to date. Note that the cacheTracker may be
377: * non-current if either something happened to the cached copy
378: * (eg. surfaceLost) or if the source was out of date and the
379: * cacheTracker was set to NEVER_CURRENT to force an update.
380: * Decrement the countdown and copy the source to the cache
381: * as necessary and then update the variables to show that
382: * the cached copy is stable.
383: * </ol>
384: */
385: public SurfaceData replaceData(SurfaceData srcData, int txtype,
386: CompositeType comp, Color bgColor) {
387: if (isSupportedOperation(srcData, txtype, comp, bgColor)) {
388: // First deal with tracking the source.
389: if (!srcTracker.isCurrent()) {
390: synchronized (this ) {
391: this .numtries = threshold;
392: this .srcTracker = srcData.getStateTracker();
393: this .cacheTracker = StateTracker.NEVER_CURRENT;
394: }
395:
396: if (!srcTracker.isCurrent()) {
397: // Dynamic or Untrackable (or a very recent modification)
398: if (srcData.getState() == State.UNTRACKABLE) {
399: // UNTRACKABLE means we can never cache again.
400:
401: // Invalidate so we get replaced next time we are used
402: // (presumably with an UNCACHED proxy).
403: invalidate();
404:
405: // Aggressively drop our reference to the cachedSD
406: // in case this proxy is not consulted again (and
407: // thus replaced) for a long time.
408: flush();
409: }
410: return srcData;
411: }
412: }
413:
414: // Then deal with checking the validity of the cached SurfaceData
415: SurfaceData csd = this .cachedSD;
416: if (!cacheTracker.isCurrent()) {
417: // Next make sure the dust has settled
418: synchronized (this ) {
419: if (numtries > 0) {
420: --numtries;
421: return srcData;
422: }
423: }
424:
425: Rectangle r = srcData.getBounds();
426: int w = r.width;
427: int h = r.height;
428:
429: // Snapshot the tracker in case it changes while
430: // we are updating the cached SD...
431: StateTracker curTracker = srcTracker;
432:
433: csd = validateSurfaceData(srcData, csd, w, h);
434: if (csd == null) {
435: synchronized (this ) {
436: if (curTracker == srcTracker) {
437: this .cacheTracker = getRetryTracker(srcData);
438: this .cachedSD = null;
439: }
440: }
441: return srcData;
442: }
443:
444: updateSurfaceData(srcData, csd, w, h);
445: if (!csd.isValid()) {
446: return srcData;
447: }
448:
449: synchronized (this ) {
450: // We only reset these variables if the tracker from
451: // before the surface update is still in use and current
452: // Note that we must use a srcTracker that was fetched
453: // from before the update process to make sure that we
454: // do not lose some pixel changes in the shuffle.
455: if (curTracker == srcTracker
456: && curTracker.isCurrent()) {
457: this .cacheTracker = csd.getStateTracker();
458: this .cachedSD = csd;
459: }
460: }
461: }
462:
463: if (csd != null) {
464: return csd;
465: }
466: }
467:
468: return srcData;
469: }
470:
471: /**
472: * This is the default implementation for updating the cached
473: * SurfaceData from the source (primary) SurfaceData.
474: * A simple Blit is used to copy the pixels from the source to
475: * the destination SurfaceData.
476: * A subclass can override this implementation if a more complex
477: * operation is required to update its cached copies.
478: */
479: public void updateSurfaceData(SurfaceData srcData,
480: SurfaceData dstData, int w, int h) {
481: SurfaceType srcType = srcData.getSurfaceType();
482: SurfaceType dstType = dstData.getSurfaceType();
483: Blit blit = Blit.getFromCache(srcType, CompositeType.SrcNoEa,
484: dstType);
485: blit.Blit(srcData, dstData, AlphaComposite.Src, null, 0, 0, 0,
486: 0, w, h);
487: dstData.markDirty();
488: }
489:
490: /**
491: * This is an alternate implementation for updating the cached
492: * SurfaceData from the source (primary) SurfaceData using a
493: * background color for transparent pixels.
494: * A simple BlitBg is used to copy the pixels from the source to
495: * the destination SurfaceData with the specified bgColor.
496: * A subclass can override the normal updateSurfaceData method
497: * and call this implementation instead if it wants to use color
498: * keying for bitmask images.
499: */
500: public void updateSurfaceDataBg(SurfaceData srcData,
501: SurfaceData dstData, int w, int h, Color bgColor) {
502: SurfaceType srcType = srcData.getSurfaceType();
503: SurfaceType dstType = dstData.getSurfaceType();
504: BlitBg blitbg = BlitBg.getFromCache(srcType,
505: CompositeType.SrcNoEa, dstType);
506: blitbg.BlitBg(srcData, dstData, AlphaComposite.Src, null,
507: bgColor, 0, 0, 0, 0, w, h);
508: dstData.markDirty();
509: }
510: }
|