/* * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.java2d; import java.awt.Color; import java.awt.Rectangle; import java.awt.AlphaComposite; import java.awt.GraphicsEnvironment; import sun.awt.DisplayChangedListener; import sun.java2d.StateTrackable.State; import sun.java2d.loops.CompositeType; import sun.java2d.loops.SurfaceType; import sun.java2d.loops.Blit; import sun.java2d.loops.BlitBg; import sun.awt.image.SurfaceManager; import sun.awt.image.SurfaceManager.FlushableCacheData; import java.security.AccessController; import sun.security.action.GetPropertyAction; /** * The proxy class encapsulates the logic for managing alternate * SurfaceData representations of a primary SurfaceData. * The main class will handle tracking the state changes of the * primary SurfaceData and updating the associated SurfaceData * proxy variants. *
* Subclasses have 2 main responsibilities: *
* If the method returns null then there was a problem with * allocating the accelerated surface. The getRetryTracker() * method will be called to track when to attempt another * revalidation. */ public abstract SurfaceData validateSurfaceData(SurfaceData srcData, SurfaceData cachedData, int w, int h); /** * If the subclass is unable to validate or create a cached * SurfaceData then this method will be used to get a * StateTracker object that will indicate when to attempt * to validate the surface again. Subclasses may return * trackers which count down an ever increasing threshold * to provide hysteresis on creating surfaces during low * memory conditions. The default implementation just waits * another "threshold" number of accesses before trying again. */ public StateTracker getRetryTracker(SurfaceData srcData) { return new CountdownTracker(threshold); } public static class CountdownTracker implements StateTracker { private int countdown; public CountdownTracker(int threshold) { this.countdown = threshold; } public synchronized boolean isCurrent() { return (--countdown >= 0); } } /** * This instance is for cases where a caching implementation * determines that a particular source image will never need * to be cached - either the source SurfaceData was of an * incompatible type, or it was in an UNTRACKABLE state or * some other factor is discovered that permanently prevents * acceleration or caching. * This class optimally implements NOP variants of all necessary * methods to avoid caching with a minimum of fuss. */ public static SurfaceDataProxy UNCACHED = new SurfaceDataProxy(0) { @Override public boolean isAccelerated() { return false; } @Override public boolean isSupportedOperation(SurfaceData srcData, int txtype, CompositeType comp, Color bgColor) { return false; } @Override public SurfaceData validateSurfaceData(SurfaceData srcData, SurfaceData cachedData, int w, int h) { throw new InternalError("UNCACHED should never validate SDs"); } @Override public SurfaceData replaceData(SurfaceData srcData, int txtype, CompositeType comp, Color bgColor) { // Not necessary to override this, but doing so is faster return srcData; } }; // The number of attempts to copy from a STABLE source before // a cached copy is created or updated. private int threshold; /* * Source tracking data * * Every time that srcTracker is out of date we will reset numtries * to threshold and set the cacheTracker to one that is non-current. * numtries will then count down to 0 at which point the cacheTracker * will remind us that we need to update the cachedSD before we can * use it. * * Note that since these fields interrelate we should synchronize * whenever we update them, but it should be OK to read them * without synchronization. */ private StateTracker srcTracker; private int numtries; /* * Cached data * * We cache a SurfaceData created by the subclass in cachedSD and * track its state (isValid and !surfaceLost) in cacheTracker. * * Also, when we want to note that cachedSD needs to be updated * we replace the cacheTracker with a NEVER_CURRENT tracker which * will cause us to try to revalidate and update the surface on * next use. */ private SurfaceData cachedSD; private StateTracker cacheTracker; /* * Are we still the best object to control caching of data * for the source image? */ private boolean valid; /** * Create a SurfaceData proxy manager that attempts to create * and cache a variant copy of the source SurfaceData after * the default threshold number of attempts to copy from the * STABLE source. */ public SurfaceDataProxy() { this(defaultThreshold); } /** * Create a SurfaceData proxy manager that attempts to create * and cache a variant copy of the source SurfaceData after * the specified threshold number of attempts to copy from * the STABLE source. */ public SurfaceDataProxy(int threshold) { this.threshold = threshold; this.srcTracker = StateTracker.NEVER_CURRENT; // numtries will be reset on first use this.cacheTracker = StateTracker.NEVER_CURRENT; this.valid = true; } /** * Returns true iff this SurfaceData proxy is still the best * way to control caching of the given source on the given * destination. */ public boolean isValid() { return valid; } /** * Sets the valid state to false so that the next time this * proxy is fetched to generate a replacement SurfaceData, * the code in SurfaceData knows to replace the proxy first. */ public void invalidate() { this.valid = false; } /** * Flush all cached resources as per the FlushableCacheData interface. * The deaccelerated parameter indicates if the flush is * happening because the associated surface is no longer * being accelerated (for instance the acceleration priority * is set below the threshold needed for acceleration). * Returns a boolean that indicates if the cached object is * no longer needed and should be removed from the cache. */ public boolean flush(boolean deaccelerated) { if (deaccelerated) { invalidate(); } flush(); return !isValid(); } /** * Actively flushes (drops and invalidates) the cached surface * so that it can be reclaimed quickly. */ public synchronized void flush() { SurfaceData csd = this.cachedSD; this.cachedSD = null; this.cacheTracker = StateTracker.NEVER_CURRENT; if (csd != null) { csd.flush(); } } /** * Returns true iff this SurfaceData proxy is still valid * and if it has a currently cached replacement that is also * valid and current. */ public boolean isAccelerated() { return (isValid() && srcTracker.isCurrent() && cacheTracker.isCurrent()); } /** * This method should be called from subclasses which create * cached SurfaceData objects that depend on the current * properties of the display. */ protected void activateDisplayListener() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); // We could have a HeadlessGE at this point, so double-check before // assuming anything. // Also, no point in listening to display change events if // the image is never going to be accelerated. if (ge instanceof SunGraphicsEnvironment) { ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this); } } /** * Invoked when the display mode has changed. * This method will invalidate and drop the internal cachedSD object. */ public void displayChanged() { flush(); } /** * Invoked when the palette has changed. */ public void paletteChanged() { // We could potentially get away with just resetting cacheTracker // here but there is a small window of vulnerability in the // replaceData method where we could be just finished with // updating the cachedSD when this method is called and even // though we set a non-current cacheTracker here it will then // immediately get set to a current one by the thread that is // updating the cachedSD. It is safer to just replace the // srcTracker with a non-current version that will trigger a // full update cycle the next time this proxy is used. // The downside is having to go through a full threshold count // before we can update and use our cache again, but palette // changes should be relatively rare... this.srcTracker = StateTracker.NEVER_CURRENT; } /** * This method attempts to replace the srcData with a cached version. * It relies on the subclass to determine if the cached version will * be useful given the operational parameters. * This method checks any preexisting cached copy for being "up to date" * and tries to update it if it is stale or non-existant and the * appropriate number of accesses have occured since it last was stale. *
* An outline of the process is as follows: *