0N/A/*
2362N/A * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.java2d;
0N/A
0N/Aimport java.awt.Color;
0N/Aimport java.awt.Rectangle;
0N/Aimport java.awt.AlphaComposite;
0N/Aimport java.awt.GraphicsEnvironment;
0N/A
0N/Aimport sun.awt.DisplayChangedListener;
0N/Aimport sun.java2d.StateTrackable.State;
0N/Aimport sun.java2d.loops.CompositeType;
0N/Aimport sun.java2d.loops.SurfaceType;
0N/Aimport sun.java2d.loops.Blit;
0N/Aimport sun.java2d.loops.BlitBg;
0N/Aimport sun.awt.image.SurfaceManager;
0N/Aimport sun.awt.image.SurfaceManager.FlushableCacheData;
0N/A
0N/Aimport java.security.AccessController;
0N/Aimport sun.security.action.GetPropertyAction;
0N/A
0N/A/**
0N/A * The proxy class encapsulates the logic for managing alternate
0N/A * SurfaceData representations of a primary SurfaceData.
0N/A * The main class will handle tracking the state changes of the
0N/A * primary SurfaceData and updating the associated SurfaceData
0N/A * proxy variants.
0N/A * <p>
0N/A * Subclasses have 2 main responsibilities:
0N/A * <ul>
0N/A * <li> Override the isSupportedOperation() method to determine if
0N/A * a given operation can be accelerated with a given source
0N/A * SurfaceData
0N/A * <li> Override the validateSurfaceData() method to create or update
0N/A * a given accelerated surface to hold the pixels for the indicated
0N/A * source SurfaceData
0N/A * </ul>
0N/A * If necessary, a subclass may also override the updateSurfaceData
0N/A * method to transfer the pixels to the accelerated surface.
0N/A * By default the parent class will transfer the pixels using a
0N/A * standard Blit operation between the two SurfaceData objects.
0N/A */
0N/Apublic abstract class SurfaceDataProxy
0N/A implements DisplayChangedListener, SurfaceManager.FlushableCacheData
0N/A{
0N/A private static boolean cachingAllowed;
0N/A private static int defaultThreshold;
0N/A
0N/A static {
0N/A cachingAllowed = true;
0N/A String manimg = (String)AccessController.doPrivileged(
0N/A new GetPropertyAction("sun.java2d.managedimages"));
0N/A if (manimg != null && manimg.equals("false")) {
0N/A cachingAllowed = false;
0N/A System.out.println("Disabling managed images");
0N/A }
0N/A
0N/A defaultThreshold = 1;
0N/A String num = (String)AccessController.doPrivileged(
0N/A new GetPropertyAction("sun.java2d.accthreshold"));
0N/A if (num != null) {
0N/A try {
0N/A int parsed = Integer.parseInt(num);
0N/A if (parsed >= 0) {
0N/A defaultThreshold = parsed;
0N/A System.out.println("New Default Acceleration Threshold: " +
0N/A defaultThreshold);
0N/A }
0N/A } catch (NumberFormatException e) {
0N/A System.err.println("Error setting new threshold:" + e);
0N/A }
0N/A }
0N/A }
0N/A
0N/A public static boolean isCachingAllowed() {
0N/A return cachingAllowed;
0N/A }
0N/A
0N/A /**
0N/A * Determine if an alternate form for the srcData is needed
0N/A * and appropriate from the given operational parameters.
0N/A */
0N/A public abstract boolean isSupportedOperation(SurfaceData srcData,
0N/A int txtype,
0N/A CompositeType comp,
0N/A Color bgColor);
0N/A
0N/A /**
0N/A * Construct an alternate form of the given SurfaceData.
0N/A * The contents of the returned SurfaceData may be undefined
0N/A * since the calling code will take care of updating the
0N/A * contents with a subsequent call to updateSurfaceData.
0N/A * <p>
0N/A * If the method returns null then there was a problem with
0N/A * allocating the accelerated surface. The getRetryTracker()
0N/A * method will be called to track when to attempt another
0N/A * revalidation.
0N/A */
0N/A public abstract SurfaceData validateSurfaceData(SurfaceData srcData,
0N/A SurfaceData cachedData,
0N/A int w, int h);
0N/A
0N/A /**
0N/A * If the subclass is unable to validate or create a cached
0N/A * SurfaceData then this method will be used to get a
0N/A * StateTracker object that will indicate when to attempt
0N/A * to validate the surface again. Subclasses may return
0N/A * trackers which count down an ever increasing threshold
0N/A * to provide hysteresis on creating surfaces during low
0N/A * memory conditions. The default implementation just waits
0N/A * another "threshold" number of accesses before trying again.
0N/A */
0N/A public StateTracker getRetryTracker(SurfaceData srcData) {
0N/A return new CountdownTracker(threshold);
0N/A }
0N/A
0N/A public static class CountdownTracker implements StateTracker {
0N/A private int countdown;
0N/A
0N/A public CountdownTracker(int threshold) {
0N/A this.countdown = threshold;
0N/A }
0N/A
0N/A public synchronized boolean isCurrent() {
0N/A return (--countdown >= 0);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * This instance is for cases where a caching implementation
0N/A * determines that a particular source image will never need
0N/A * to be cached - either the source SurfaceData was of an
0N/A * incompatible type, or it was in an UNTRACKABLE state or
0N/A * some other factor is discovered that permanently prevents
0N/A * acceleration or caching.
0N/A * This class optimally implements NOP variants of all necessary
0N/A * methods to avoid caching with a minimum of fuss.
0N/A */
0N/A public static SurfaceDataProxy UNCACHED = new SurfaceDataProxy(0) {
0N/A @Override
0N/A public boolean isAccelerated() {
0N/A return false;
0N/A }
0N/A
0N/A @Override
0N/A public boolean isSupportedOperation(SurfaceData srcData,
0N/A int txtype,
0N/A CompositeType comp,
0N/A Color bgColor)
0N/A {
0N/A return false;
0N/A }
0N/A
0N/A @Override
0N/A public SurfaceData validateSurfaceData(SurfaceData srcData,
0N/A SurfaceData cachedData,
0N/A int w, int h)
0N/A {
0N/A throw new InternalError("UNCACHED should never validate SDs");
0N/A }
0N/A
0N/A @Override
0N/A public SurfaceData replaceData(SurfaceData srcData,
0N/A int txtype,
0N/A CompositeType comp,
0N/A Color bgColor)
0N/A {
0N/A // Not necessary to override this, but doing so is faster
0N/A return srcData;
0N/A }
0N/A };
0N/A
0N/A // The number of attempts to copy from a STABLE source before
0N/A // a cached copy is created or updated.
0N/A private int threshold;
0N/A
0N/A /*
0N/A * Source tracking data
0N/A *
0N/A * Every time that srcTracker is out of date we will reset numtries
0N/A * to threshold and set the cacheTracker to one that is non-current.
0N/A * numtries will then count down to 0 at which point the cacheTracker
0N/A * will remind us that we need to update the cachedSD before we can
0N/A * use it.
0N/A *
0N/A * Note that since these fields interrelate we should synchronize
0N/A * whenever we update them, but it should be OK to read them
0N/A * without synchronization.
0N/A */
0N/A private StateTracker srcTracker;
0N/A private int numtries;
0N/A
0N/A /*
0N/A * Cached data
0N/A *
0N/A * We cache a SurfaceData created by the subclass in cachedSD and
0N/A * track its state (isValid and !surfaceLost) in cacheTracker.
0N/A *
0N/A * Also, when we want to note that cachedSD needs to be updated
0N/A * we replace the cacheTracker with a NEVER_CURRENT tracker which
0N/A * will cause us to try to revalidate and update the surface on
0N/A * next use.
0N/A */
0N/A private SurfaceData cachedSD;
0N/A private StateTracker cacheTracker;
0N/A
0N/A /*
0N/A * Are we still the best object to control caching of data
0N/A * for the source image?
0N/A */
0N/A private boolean valid;
0N/A
0N/A /**
0N/A * Create a SurfaceData proxy manager that attempts to create
0N/A * and cache a variant copy of the source SurfaceData after
0N/A * the default threshold number of attempts to copy from the
0N/A * STABLE source.
0N/A */
0N/A public SurfaceDataProxy() {
0N/A this(defaultThreshold);
0N/A }
0N/A
0N/A /**
0N/A * Create a SurfaceData proxy manager that attempts to create
0N/A * and cache a variant copy of the source SurfaceData after
0N/A * the specified threshold number of attempts to copy from
0N/A * the STABLE source.
0N/A */
0N/A public SurfaceDataProxy(int threshold) {
0N/A this.threshold = threshold;
0N/A
0N/A this.srcTracker = StateTracker.NEVER_CURRENT;
0N/A // numtries will be reset on first use
0N/A this.cacheTracker = StateTracker.NEVER_CURRENT;
0N/A
0N/A this.valid = true;
0N/A }
0N/A
0N/A /**
0N/A * Returns true iff this SurfaceData proxy is still the best
0N/A * way to control caching of the given source on the given
0N/A * destination.
0N/A */
0N/A public boolean isValid() {
0N/A return valid;
0N/A }
0N/A
0N/A /**
0N/A * Sets the valid state to false so that the next time this
0N/A * proxy is fetched to generate a replacement SurfaceData,
0N/A * the code in SurfaceData knows to replace the proxy first.
0N/A */
0N/A public void invalidate() {
0N/A this.valid = false;
0N/A }
0N/A
0N/A /**
0N/A * Flush all cached resources as per the FlushableCacheData interface.
0N/A * The deaccelerated parameter indicates if the flush is
0N/A * happening because the associated surface is no longer
0N/A * being accelerated (for instance the acceleration priority
0N/A * is set below the threshold needed for acceleration).
0N/A * Returns a boolean that indicates if the cached object is
0N/A * no longer needed and should be removed from the cache.
0N/A */
0N/A public boolean flush(boolean deaccelerated) {
0N/A if (deaccelerated) {
0N/A invalidate();
0N/A }
0N/A flush();
0N/A return !isValid();
0N/A }
0N/A
0N/A /**
0N/A * Actively flushes (drops and invalidates) the cached surface
0N/A * so that it can be reclaimed quickly.
0N/A */
0N/A public synchronized void flush() {
0N/A SurfaceData csd = this.cachedSD;
0N/A this.cachedSD = null;
0N/A this.cacheTracker = StateTracker.NEVER_CURRENT;
0N/A if (csd != null) {
0N/A csd.flush();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns true iff this SurfaceData proxy is still valid
0N/A * and if it has a currently cached replacement that is also
0N/A * valid and current.
0N/A */
0N/A public boolean isAccelerated() {
0N/A return (isValid() &&
0N/A srcTracker.isCurrent() &&
0N/A cacheTracker.isCurrent());
0N/A }
0N/A
0N/A /**
0N/A * This method should be called from subclasses which create
0N/A * cached SurfaceData objects that depend on the current
0N/A * properties of the display.
0N/A */
0N/A protected void activateDisplayListener() {
0N/A GraphicsEnvironment ge =
0N/A GraphicsEnvironment.getLocalGraphicsEnvironment();
0N/A // We could have a HeadlessGE at this point, so double-check before
0N/A // assuming anything.
0N/A // Also, no point in listening to display change events if
0N/A // the image is never going to be accelerated.
0N/A if (ge instanceof SunGraphicsEnvironment) {
0N/A ((SunGraphicsEnvironment)ge).addDisplayChangedListener(this);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Invoked when the display mode has changed.
0N/A * This method will invalidate and drop the internal cachedSD object.
0N/A */
0N/A public void displayChanged() {
0N/A flush();
0N/A }
0N/A
0N/A /**
0N/A * Invoked when the palette has changed.
0N/A */
0N/A public void paletteChanged() {
0N/A // We could potentially get away with just resetting cacheTracker
0N/A // here but there is a small window of vulnerability in the
0N/A // replaceData method where we could be just finished with
0N/A // updating the cachedSD when this method is called and even
0N/A // though we set a non-current cacheTracker here it will then
0N/A // immediately get set to a current one by the thread that is
0N/A // updating the cachedSD. It is safer to just replace the
0N/A // srcTracker with a non-current version that will trigger a
0N/A // full update cycle the next time this proxy is used.
0N/A // The downside is having to go through a full threshold count
0N/A // before we can update and use our cache again, but palette
0N/A // changes should be relatively rare...
0N/A this.srcTracker = StateTracker.NEVER_CURRENT;
0N/A }
0N/A
0N/A /**
0N/A * This method attempts to replace the srcData with a cached version.
0N/A * It relies on the subclass to determine if the cached version will
0N/A * be useful given the operational parameters.
0N/A * This method checks any preexisting cached copy for being "up to date"
0N/A * and tries to update it if it is stale or non-existant and the
0N/A * appropriate number of accesses have occured since it last was stale.
0N/A * <p>
0N/A * An outline of the process is as follows:
0N/A * <ol>
0N/A * <li> Check the operational parameters (txtype, comp, bgColor)
0N/A * to make sure that the operation is supported. Return the
0N/A * original SurfaceData if the operation cannot be accelerated.
0N/A * <li> Check the tracker for the source surface to see if it has
0N/A * remained stable since it was last cached. Update the state
0N/A * variables to cause both a threshold countdown and an update
0N/A * of the cached copy if it is not. (Setting cacheTracker to
0N/A * NEVER_CURRENT effectively marks it as "needing to be updated".)
0N/A * <li> Check the tracker for the cached copy to see if is still
0N/A * valid and up to date. Note that the cacheTracker may be
0N/A * non-current if either something happened to the cached copy
0N/A * (eg. surfaceLost) or if the source was out of date and the
0N/A * cacheTracker was set to NEVER_CURRENT to force an update.
0N/A * Decrement the countdown and copy the source to the cache
0N/A * as necessary and then update the variables to show that
0N/A * the cached copy is stable.
0N/A * </ol>
0N/A */
0N/A public SurfaceData replaceData(SurfaceData srcData,
0N/A int txtype,
0N/A CompositeType comp,
0N/A Color bgColor)
0N/A {
0N/A if (isSupportedOperation(srcData, txtype, comp, bgColor)) {
0N/A // First deal with tracking the source.
0N/A if (!srcTracker.isCurrent()) {
0N/A synchronized (this) {
0N/A this.numtries = threshold;
0N/A this.srcTracker = srcData.getStateTracker();
0N/A this.cacheTracker = StateTracker.NEVER_CURRENT;
0N/A }
0N/A
0N/A if (!srcTracker.isCurrent()) {
0N/A // Dynamic or Untrackable (or a very recent modification)
0N/A if (srcData.getState() == State.UNTRACKABLE) {
0N/A // UNTRACKABLE means we can never cache again.
0N/A
0N/A // Invalidate so we get replaced next time we are used
0N/A // (presumably with an UNCACHED proxy).
0N/A invalidate();
0N/A
0N/A // Aggressively drop our reference to the cachedSD
0N/A // in case this proxy is not consulted again (and
0N/A // thus replaced) for a long time.
0N/A flush();
0N/A }
0N/A return srcData;
0N/A }
0N/A }
0N/A
0N/A // Then deal with checking the validity of the cached SurfaceData
0N/A SurfaceData csd = this.cachedSD;
0N/A if (!cacheTracker.isCurrent()) {
0N/A // Next make sure the dust has settled
0N/A synchronized (this) {
0N/A if (numtries > 0) {
0N/A --numtries;
0N/A return srcData;
0N/A }
0N/A }
0N/A
0N/A Rectangle r = srcData.getBounds();
0N/A int w = r.width;
0N/A int h = r.height;
0N/A
0N/A // Snapshot the tracker in case it changes while
0N/A // we are updating the cached SD...
0N/A StateTracker curTracker = srcTracker;
0N/A
0N/A csd = validateSurfaceData(srcData, csd, w, h);
0N/A if (csd == null) {
0N/A synchronized (this) {
0N/A if (curTracker == srcTracker) {
0N/A this.cacheTracker = getRetryTracker(srcData);
0N/A this.cachedSD = null;
0N/A }
0N/A }
0N/A return srcData;
0N/A }
0N/A
0N/A updateSurfaceData(srcData, csd, w, h);
0N/A if (!csd.isValid()) {
0N/A return srcData;
0N/A }
0N/A
0N/A synchronized (this) {
0N/A // We only reset these variables if the tracker from
0N/A // before the surface update is still in use and current
0N/A // Note that we must use a srcTracker that was fetched
0N/A // from before the update process to make sure that we
0N/A // do not lose some pixel changes in the shuffle.
0N/A if (curTracker == srcTracker && curTracker.isCurrent()) {
0N/A this.cacheTracker = csd.getStateTracker();
0N/A this.cachedSD = csd;
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (csd != null) {
0N/A return csd;
0N/A }
0N/A }
0N/A
0N/A return srcData;
0N/A }
0N/A
0N/A /**
0N/A * This is the default implementation for updating the cached
0N/A * SurfaceData from the source (primary) SurfaceData.
0N/A * A simple Blit is used to copy the pixels from the source to
0N/A * the destination SurfaceData.
0N/A * A subclass can override this implementation if a more complex
0N/A * operation is required to update its cached copies.
0N/A */
0N/A public void updateSurfaceData(SurfaceData srcData,
0N/A SurfaceData dstData,
0N/A int w, int h)
0N/A {
0N/A SurfaceType srcType = srcData.getSurfaceType();
0N/A SurfaceType dstType = dstData.getSurfaceType();
0N/A Blit blit = Blit.getFromCache(srcType,
0N/A CompositeType.SrcNoEa,
0N/A dstType);
0N/A blit.Blit(srcData, dstData,
0N/A AlphaComposite.Src, null,
0N/A 0, 0, 0, 0, w, h);
0N/A dstData.markDirty();
0N/A }
0N/A
0N/A /**
0N/A * This is an alternate implementation for updating the cached
0N/A * SurfaceData from the source (primary) SurfaceData using a
0N/A * background color for transparent pixels.
0N/A * A simple BlitBg is used to copy the pixels from the source to
0N/A * the destination SurfaceData with the specified bgColor.
0N/A * A subclass can override the normal updateSurfaceData method
0N/A * and call this implementation instead if it wants to use color
0N/A * keying for bitmask images.
0N/A */
0N/A public void updateSurfaceDataBg(SurfaceData srcData,
0N/A SurfaceData dstData,
0N/A int w, int h, Color bgColor)
0N/A {
0N/A SurfaceType srcType = srcData.getSurfaceType();
0N/A SurfaceType dstType = dstData.getSurfaceType();
0N/A BlitBg blitbg = BlitBg.getFromCache(srcType,
0N/A CompositeType.SrcNoEa,
0N/A dstType);
0N/A blitbg.BlitBg(srcData, dstData,
430N/A AlphaComposite.Src, null, bgColor.getRGB(),
0N/A 0, 0, 0, 0, w, h);
0N/A dstData.markDirty();
0N/A }
0N/A}