Path: blob/master/src/java.desktop/share/classes/sun/java2d/SurfaceDataProxy.java
41152 views
/*1* Copyright (c) 2007, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d;2627import java.awt.Color;28import java.awt.Rectangle;29import java.awt.AlphaComposite;30import java.awt.GraphicsEnvironment;3132import sun.awt.DisplayChangedListener;33import sun.java2d.StateTrackable.State;34import sun.java2d.loops.CompositeType;35import sun.java2d.loops.SurfaceType;36import sun.java2d.loops.Blit;37import sun.java2d.loops.BlitBg;38import sun.awt.image.SurfaceManager;39import sun.awt.image.SurfaceManager.FlushableCacheData;4041import java.security.AccessController;42import sun.security.action.GetPropertyAction;4344/**45* The proxy class encapsulates the logic for managing alternate46* SurfaceData representations of a primary SurfaceData.47* The main class will handle tracking the state changes of the48* primary SurfaceData and updating the associated SurfaceData49* proxy variants.50* <p>51* Subclasses have 2 main responsibilities:52* <ul>53* <li> Override the isSupportedOperation() method to determine if54* a given operation can be accelerated with a given source55* SurfaceData56* <li> Override the validateSurfaceData() method to create or update57* a given accelerated surface to hold the pixels for the indicated58* source SurfaceData59* </ul>60* If necessary, a subclass may also override the updateSurfaceData61* method to transfer the pixels to the accelerated surface.62* By default the parent class will transfer the pixels using a63* standard Blit operation between the two SurfaceData objects.64*/65public abstract class SurfaceDataProxy66implements DisplayChangedListener, SurfaceManager.FlushableCacheData67{68private static boolean cachingAllowed;69private static int defaultThreshold;7071static {72cachingAllowed = true;73@SuppressWarnings("removal")74String manimg = AccessController.doPrivileged(75new GetPropertyAction("sun.java2d.managedimages"));76if (manimg != null && manimg.equals("false")) {77cachingAllowed = false;78System.out.println("Disabling managed images");79}8081defaultThreshold = 1;82@SuppressWarnings("removal")83String num = AccessController.doPrivileged(84new GetPropertyAction("sun.java2d.accthreshold"));85if (num != null) {86try {87int parsed = Integer.parseInt(num);88if (parsed >= 0) {89defaultThreshold = parsed;90System.out.println("New Default Acceleration Threshold: " +91defaultThreshold);92}93} catch (NumberFormatException e) {94System.err.println("Error setting new threshold:" + e);95}96}97}9899public static boolean isCachingAllowed() {100return cachingAllowed;101}102103/**104* Determine if an alternate form for the srcData is needed105* and appropriate from the given operational parameters.106*/107public abstract boolean isSupportedOperation(SurfaceData srcData,108int txtype,109CompositeType comp,110Color bgColor);111112/**113* Construct an alternate form of the given SurfaceData.114* The contents of the returned SurfaceData may be undefined115* since the calling code will take care of updating the116* contents with a subsequent call to updateSurfaceData.117* <p>118* If the method returns null then there was a problem with119* allocating the accelerated surface. The getRetryTracker()120* method will be called to track when to attempt another121* revalidation.122*/123public abstract SurfaceData validateSurfaceData(SurfaceData srcData,124SurfaceData cachedData,125int w, int h);126127/**128* If the subclass is unable to validate or create a cached129* SurfaceData then this method will be used to get a130* StateTracker object that will indicate when to attempt131* to validate the surface again. Subclasses may return132* trackers which count down an ever increasing threshold133* to provide hysteresis on creating surfaces during low134* memory conditions. The default implementation just waits135* another "threshold" number of accesses before trying again.136*/137public StateTracker getRetryTracker(SurfaceData srcData) {138return new CountdownTracker(threshold);139}140141public static class CountdownTracker implements StateTracker {142private int countdown;143144public CountdownTracker(int threshold) {145this.countdown = threshold;146}147148public synchronized boolean isCurrent() {149return (--countdown >= 0);150}151}152153/**154* This instance is for cases where a caching implementation155* determines that a particular source image will never need156* to be cached - either the source SurfaceData was of an157* incompatible type, or it was in an UNTRACKABLE state or158* some other factor is discovered that permanently prevents159* acceleration or caching.160* This class optimally implements NOP variants of all necessary161* methods to avoid caching with a minimum of fuss.162*/163public static SurfaceDataProxy UNCACHED = new SurfaceDataProxy(0) {164@Override165public boolean isAccelerated() {166return false;167}168169@Override170public boolean isSupportedOperation(SurfaceData srcData,171int txtype,172CompositeType comp,173Color bgColor)174{175return false;176}177178@Override179public SurfaceData validateSurfaceData(SurfaceData srcData,180SurfaceData cachedData,181int w, int h)182{183throw new InternalError("UNCACHED should never validate SDs");184}185186@Override187public SurfaceData replaceData(SurfaceData srcData,188int txtype,189CompositeType comp,190Color bgColor)191{192// Not necessary to override this, but doing so is faster193return srcData;194}195};196197// The number of attempts to copy from a STABLE source before198// a cached copy is created or updated.199private int threshold;200201/*202* Source tracking data203*204* Every time that srcTracker is out of date we will reset numtries205* to threshold and set the cacheTracker to one that is non-current.206* numtries will then count down to 0 at which point the cacheTracker207* will remind us that we need to update the cachedSD before we can208* use it.209*210* Note that since these fields interrelate we should synchronize211* whenever we update them, but it should be OK to read them212* without synchronization.213*/214private StateTracker srcTracker;215private int numtries;216217/*218* Cached data219*220* We cache a SurfaceData created by the subclass in cachedSD and221* track its state (isValid and !surfaceLost) in cacheTracker.222*223* Also, when we want to note that cachedSD needs to be updated224* we replace the cacheTracker with a NEVER_CURRENT tracker which225* will cause us to try to revalidate and update the surface on226* next use.227*/228private SurfaceData cachedSD;229private StateTracker cacheTracker;230231/*232* Are we still the best object to control caching of data233* for the source image?234*/235private boolean valid;236237/**238* Create a SurfaceData proxy manager that attempts to create239* and cache a variant copy of the source SurfaceData after240* the default threshold number of attempts to copy from the241* STABLE source.242*/243public SurfaceDataProxy() {244this(defaultThreshold);245}246247/**248* Create a SurfaceData proxy manager that attempts to create249* and cache a variant copy of the source SurfaceData after250* the specified threshold number of attempts to copy from251* the STABLE source.252*/253public SurfaceDataProxy(int threshold) {254this.threshold = threshold;255256this.srcTracker = StateTracker.NEVER_CURRENT;257// numtries will be reset on first use258this.cacheTracker = StateTracker.NEVER_CURRENT;259260this.valid = true;261}262263/**264* Returns true iff this SurfaceData proxy is still the best265* way to control caching of the given source on the given266* destination.267*/268public boolean isValid() {269return valid;270}271272/**273* Sets the valid state to false so that the next time this274* proxy is fetched to generate a replacement SurfaceData,275* the code in SurfaceData knows to replace the proxy first.276*/277public void invalidate() {278this.valid = false;279}280281/**282* Flush all cached resources as per the FlushableCacheData interface.283* The deaccelerated parameter indicates if the flush is284* happening because the associated surface is no longer285* being accelerated (for instance the acceleration priority286* is set below the threshold needed for acceleration).287* Returns a boolean that indicates if the cached object is288* no longer needed and should be removed from the cache.289*/290public boolean flush(boolean deaccelerated) {291if (deaccelerated) {292invalidate();293}294flush();295return !isValid();296}297298/**299* Actively flushes (drops and invalidates) the cached surface300* so that it can be reclaimed quickly.301*/302public synchronized void flush() {303SurfaceData csd = this.cachedSD;304this.cachedSD = null;305this.cacheTracker = StateTracker.NEVER_CURRENT;306if (csd != null) {307csd.flush();308}309}310311/**312* Returns true iff this SurfaceData proxy is still valid313* and if it has a currently cached replacement that is also314* valid and current.315*/316public boolean isAccelerated() {317return (isValid() &&318srcTracker.isCurrent() &&319cacheTracker.isCurrent());320}321322/**323* This method should be called from subclasses which create324* cached SurfaceData objects that depend on the current325* properties of the display.326*/327protected void activateDisplayListener() {328GraphicsEnvironment ge =329GraphicsEnvironment.getLocalGraphicsEnvironment();330// We could have a HeadlessGE at this point, so double-check before331// assuming anything.332// Also, no point in listening to display change events if333// the image is never going to be accelerated.334if (ge instanceof SunGraphicsEnvironment) {335((SunGraphicsEnvironment)ge).addDisplayChangedListener(this);336}337}338339/**340* Invoked when the display mode has changed.341* This method will invalidate and drop the internal cachedSD object.342*/343public void displayChanged() {344flush();345}346347/**348* Invoked when the palette has changed.349*/350public void paletteChanged() {351// We could potentially get away with just resetting cacheTracker352// here but there is a small window of vulnerability in the353// replaceData method where we could be just finished with354// updating the cachedSD when this method is called and even355// though we set a non-current cacheTracker here it will then356// immediately get set to a current one by the thread that is357// updating the cachedSD. It is safer to just replace the358// srcTracker with a non-current version that will trigger a359// full update cycle the next time this proxy is used.360// The downside is having to go through a full threshold count361// before we can update and use our cache again, but palette362// changes should be relatively rare...363this.srcTracker = StateTracker.NEVER_CURRENT;364}365366/**367* This method attempts to replace the srcData with a cached version.368* It relies on the subclass to determine if the cached version will369* be useful given the operational parameters.370* This method checks any preexisting cached copy for being "up to date"371* and tries to update it if it is stale or non-existant and the372* appropriate number of accesses have occurred since it last was stale.373* <p>374* An outline of the process is as follows:375* <ol>376* <li> Check the operational parameters (txtype, comp, bgColor)377* to make sure that the operation is supported. Return the378* original SurfaceData if the operation cannot be accelerated.379* <li> Check the tracker for the source surface to see if it has380* remained stable since it was last cached. Update the state381* variables to cause both a threshold countdown and an update382* of the cached copy if it is not. (Setting cacheTracker to383* NEVER_CURRENT effectively marks it as "needing to be updated".)384* <li> Check the tracker for the cached copy to see if is still385* valid and up to date. Note that the cacheTracker may be386* non-current if either something happened to the cached copy387* (eg. surfaceLost) or if the source was out of date and the388* cacheTracker was set to NEVER_CURRENT to force an update.389* Decrement the countdown and copy the source to the cache390* as necessary and then update the variables to show that391* the cached copy is stable.392* </ol>393*/394public SurfaceData replaceData(SurfaceData srcData,395int txtype,396CompositeType comp,397Color bgColor)398{399if (isSupportedOperation(srcData, txtype, comp, bgColor)) {400// First deal with tracking the source.401if (!srcTracker.isCurrent()) {402synchronized (this) {403this.numtries = threshold;404this.srcTracker = srcData.getStateTracker();405this.cacheTracker = StateTracker.NEVER_CURRENT;406}407408if (!srcTracker.isCurrent()) {409// Dynamic or Untrackable (or a very recent modification)410if (srcData.getState() == State.UNTRACKABLE) {411// UNTRACKABLE means we can never cache again.412413// Invalidate so we get replaced next time we are used414// (presumably with an UNCACHED proxy).415invalidate();416417// Aggressively drop our reference to the cachedSD418// in case this proxy is not consulted again (and419// thus replaced) for a long time.420flush();421}422return srcData;423}424}425426// Then deal with checking the validity of the cached SurfaceData427SurfaceData csd = this.cachedSD;428if (!cacheTracker.isCurrent()) {429// Next make sure the dust has settled430synchronized (this) {431if (numtries > 0) {432--numtries;433return srcData;434}435}436437Rectangle r = srcData.getBounds();438int w = r.width;439int h = r.height;440441// Snapshot the tracker in case it changes while442// we are updating the cached SD...443StateTracker curTracker = srcTracker;444445csd = validateSurfaceData(srcData, csd, w, h);446if (csd == null) {447synchronized (this) {448if (curTracker == srcTracker) {449this.cacheTracker = getRetryTracker(srcData);450this.cachedSD = null;451}452}453return srcData;454}455456updateSurfaceData(srcData, csd, w, h);457if (!csd.isValid()) {458return srcData;459}460461synchronized (this) {462// We only reset these variables if the tracker from463// before the surface update is still in use and current464// Note that we must use a srcTracker that was fetched465// from before the update process to make sure that we466// do not lose some pixel changes in the shuffle.467if (curTracker == srcTracker && curTracker.isCurrent()) {468this.cacheTracker = csd.getStateTracker();469this.cachedSD = csd;470}471}472}473474if (csd != null) {475return csd;476}477}478479return srcData;480}481482/**483* This is the default implementation for updating the cached484* SurfaceData from the source (primary) SurfaceData.485* A simple Blit is used to copy the pixels from the source to486* the destination SurfaceData.487* A subclass can override this implementation if a more complex488* operation is required to update its cached copies.489*/490public void updateSurfaceData(SurfaceData srcData,491SurfaceData dstData,492int w, int h)493{494SurfaceType srcType = srcData.getSurfaceType();495SurfaceType dstType = dstData.getSurfaceType();496Blit blit = Blit.getFromCache(srcType,497CompositeType.SrcNoEa,498dstType);499blit.Blit(srcData, dstData,500AlphaComposite.Src, null,5010, 0, 0, 0, w, h);502dstData.markDirty();503}504505/**506* This is an alternate implementation for updating the cached507* SurfaceData from the source (primary) SurfaceData using a508* background color for transparent pixels.509* A simple BlitBg is used to copy the pixels from the source to510* the destination SurfaceData with the specified bgColor.511* A subclass can override the normal updateSurfaceData method512* and call this implementation instead if it wants to use color513* keying for bitmask images.514*/515public void updateSurfaceDataBg(SurfaceData srcData,516SurfaceData dstData,517int w, int h, Color bgColor)518{519SurfaceType srcType = srcData.getSurfaceType();520SurfaceType dstType = dstData.getSurfaceType();521BlitBg blitbg = BlitBg.getFromCache(srcType,522CompositeType.SrcNoEa,523dstType);524blitbg.BlitBg(srcData, dstData,525AlphaComposite.Src, null, bgColor.getRGB(),5260, 0, 0, 0, w, h);527dstData.markDirty();528}529}530531532