diff --git a/src/java/com/threerings/cast/CharacterManager.java b/src/java/com/threerings/cast/CharacterManager.java index 3850b279..27b889e8 100644 --- a/src/java/com/threerings/cast/CharacterManager.java +++ b/src/java/com/threerings/cast/CharacterManager.java @@ -35,7 +35,7 @@ import com.samskivert.util.Throttle; import com.samskivert.util.Tuple; import com.threerings.media.image.Colorization; -import com.threerings.media.image.ImageManager; +import com.threerings.media.image.BaseImageManager; import com.threerings.util.DirectionCodes; import com.threerings.cast.CompositedActionFrames.ComponentFrames; @@ -52,7 +52,7 @@ public class CharacterManager /** * Constructs the character manager. */ - public CharacterManager (ImageManager imgr, ComponentRepository crepo) + public CharacterManager (BaseImageManager imgr, ComponentRepository crepo) { // keep these around _imgr = imgr; @@ -421,7 +421,7 @@ public class CharacterManager } /** The image manager with whom we interact. */ - protected ImageManager _imgr; + protected BaseImageManager _imgr; /** The component repository. */ protected ComponentRepository _crepo; diff --git a/src/java/com/threerings/cast/CompositedActionFrames.java b/src/java/com/threerings/cast/CompositedActionFrames.java index 4010b4b6..3e1c0c07 100644 --- a/src/java/com/threerings/cast/CompositedActionFrames.java +++ b/src/java/com/threerings/cast/CompositedActionFrames.java @@ -24,8 +24,8 @@ package com.threerings.cast; import com.samskivert.util.LRUHashMap; import com.samskivert.util.StringUtil; +import com.threerings.media.image.BaseImageManager; import com.threerings.media.image.Colorization; -import com.threerings.media.image.ImageManager; import com.threerings.media.util.MultiFrameImage; import com.threerings.cast.CharacterComponent; @@ -65,7 +65,7 @@ public class CompositedActionFrames * source frames and colorization configuration. The actual component * frame images will not be composited until they are requested. */ - public CompositedActionFrames (ImageManager imgr, LRUHashMap frameCache, + public CompositedActionFrames (BaseImageManager imgr, LRUHashMap frameCache, String action, ComponentFrames[] sources) { // sanity check @@ -177,7 +177,7 @@ public class CompositedActionFrames /** The image manager from whom we can obtain prepared volatile images * onto which to render our composited actions. */ - protected ImageManager _imgr; + protected BaseImageManager _imgr; /** Used to cache our composited action frame images. */ protected LRUHashMap _frameCache; diff --git a/src/java/com/threerings/cast/CompositedMaskedImage.java b/src/java/com/threerings/cast/CompositedMaskedImage.java index 3df352c4..a1acef81 100644 --- a/src/java/com/threerings/cast/CompositedMaskedImage.java +++ b/src/java/com/threerings/cast/CompositedMaskedImage.java @@ -28,7 +28,7 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Transparency; -import com.threerings.media.image.ImageManager; +import com.threerings.media.image.BaseImageManager; import com.threerings.media.image.Mirage; import com.threerings.media.image.VolatileMirage; @@ -40,7 +40,7 @@ import com.threerings.cast.CompositedActionFrames.ComponentFrames; public class CompositedMaskedImage extends CompositedMultiFrameImage { public CompositedMaskedImage ( - ImageManager imgr, ComponentFrames[] sources, String action, + BaseImageManager imgr, ComponentFrames[] sources, String action, int orient) { super(imgr, sources, action, orient); diff --git a/src/java/com/threerings/cast/CompositedMultiFrameImage.java b/src/java/com/threerings/cast/CompositedMultiFrameImage.java index c080f38e..794608c5 100644 --- a/src/java/com/threerings/cast/CompositedMultiFrameImage.java +++ b/src/java/com/threerings/cast/CompositedMultiFrameImage.java @@ -31,7 +31,7 @@ import java.util.Comparator; import com.threerings.cast.CompositedActionFrames.ComponentFrames; import com.threerings.cast.bundle.BundledComponentRepository.TileSetFrameImage; -import com.threerings.media.image.ImageManager; +import com.threerings.media.image.BaseImageManager; import com.threerings.media.image.Mirage; import com.threerings.media.image.VolatileMirage; @@ -43,7 +43,7 @@ public class CompositedMultiFrameImage implements TrimmedMultiFrameImage { public CompositedMultiFrameImage ( - ImageManager imgr, ComponentFrames[] sources, + BaseImageManager imgr, ComponentFrames[] sources, String action, int orient) { _imgr = imgr; @@ -190,7 +190,7 @@ public class CompositedMultiFrameImage } /** The image manager from whom we load our images. */ - protected ImageManager _imgr; + protected BaseImageManager _imgr; /** The action frames from which we obtain our source imagery. */ protected ComponentFrames[] _sources; diff --git a/src/java/com/threerings/cast/CompositedShadowImage.java b/src/java/com/threerings/cast/CompositedShadowImage.java index f7d34942..13f7174d 100644 --- a/src/java/com/threerings/cast/CompositedShadowImage.java +++ b/src/java/com/threerings/cast/CompositedShadowImage.java @@ -26,7 +26,7 @@ import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Rectangle; -import com.threerings.media.image.ImageManager; +import com.threerings.media.image.BaseImageManager; import com.threerings.media.image.Mirage; import com.threerings.media.image.VolatileMirage; @@ -38,7 +38,7 @@ import com.threerings.cast.CompositedActionFrames.ComponentFrames; */ public class CompositedShadowImage extends CompositedMultiFrameImage { - public CompositedShadowImage (ImageManager imgr, ComponentFrames[] sources, + public CompositedShadowImage (BaseImageManager imgr, ComponentFrames[] sources, String action, int orient, float shadowAlpha) { super(imgr, sources, action, orient); diff --git a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java index 22362d0c..1e219e23 100644 --- a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java +++ b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java @@ -40,10 +40,10 @@ import com.threerings.resource.FileResourceBundle; import com.threerings.resource.ResourceBundle; import com.threerings.resource.ResourceManager; +import com.threerings.media.image.BaseImageManager; import com.threerings.media.image.BufferedMirage; import com.threerings.media.image.Colorization; import com.threerings.media.image.ImageDataProvider; -import com.threerings.media.image.ImageManager; import com.threerings.media.image.ImageUtil; import com.threerings.media.image.Mirage; @@ -85,7 +85,7 @@ public class BundledComponentRepository * resource bundles. */ public BundledComponentRepository ( - ResourceManager rmgr, ImageManager imgr, String name) + ResourceManager rmgr, BaseImageManager imgr, String name) throws IOException { // keep this guy around @@ -261,7 +261,7 @@ public class BundledComponentRepository /** * Constructs an instance that will obtain image data from the specified resource bundle. */ - public ResourceBundleProvider (ImageManager imgr, ResourceBundle bundle) + public ResourceBundleProvider (BaseImageManager imgr, ResourceBundle bundle) { super(imgr, (String)null); _dprov = this; @@ -486,7 +486,7 @@ public class BundledComponentRepository return _set.getTile(getTileIndex(orient, index)); } - public Mirage getTileMirage(int orient, int index) { + public Mirage getTileMirage (int orient, int index) { return _set.getTileMirage(getTileIndex(orient, index)); } @@ -508,7 +508,7 @@ public class BundledComponentRepository } /** We use the image manager to decode and cache images. */ - protected ImageManager _imgr; + protected BaseImageManager _imgr; /** A table of action sequences. */ protected HashMap _actions; diff --git a/src/java/com/threerings/media/image/AWTImageCreator.java b/src/java/com/threerings/media/image/AWTImageCreator.java index ef28adfe..18c581f1 100644 --- a/src/java/com/threerings/media/image/AWTImageCreator.java +++ b/src/java/com/threerings/media/image/AWTImageCreator.java @@ -33,7 +33,7 @@ import java.awt.image.BufferedImage; * AWT to determine the optimal image format. */ public class AWTImageCreator - implements ImageManager.OptimalImageCreator + implements BaseImageManager.OptimalImageCreator { /** * Create an image creator that will rely on the AWT to determine the @@ -53,7 +53,7 @@ public class AWTImageCreator } } - // documentation inherited from interface ImageManager.OptimalImageCreator + // documentation inherited from interface BaseImageManager.OptimalImageCreator public BufferedImage createImage (int width, int height, int trans) { // DEBUG: override transparency for the moment on all images diff --git a/src/java/com/threerings/media/image/BackedVolatileMirage.java b/src/java/com/threerings/media/image/BackedVolatileMirage.java index b4dadf5b..c53f2166 100644 --- a/src/java/com/threerings/media/image/BackedVolatileMirage.java +++ b/src/java/com/threerings/media/image/BackedVolatileMirage.java @@ -39,7 +39,7 @@ public class BackedVolatileMirage extends VolatileMirage * Creates a mirage with the supplied regeneration informoation and * prepared image. */ - public BackedVolatileMirage (ImageManager imgr, BufferedImage source) + public BackedVolatileMirage (BaseImageManager imgr, BufferedImage source) { super(imgr, new Rectangle(0, 0, source.getWidth(), source.getHeight())); _source = source; diff --git a/src/java/com/threerings/media/image/BaseImageManager.java b/src/java/com/threerings/media/image/BaseImageManager.java new file mode 100644 index 00000000..364c8fb9 --- /dev/null +++ b/src/java/com/threerings/media/image/BaseImageManager.java @@ -0,0 +1,538 @@ +// $Id$ + +package com.threerings.media.image; + +import java.awt.Component; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.Transparency; +import java.awt.image.BufferedImage; + +import java.io.FileNotFoundException; +import java.io.IOException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import com.samskivert.util.LRUHashMap; +import com.samskivert.util.StringUtil; +import com.samskivert.util.Throttle; +import com.samskivert.util.Tuple; + +import com.threerings.media.Log; +import com.threerings.resource.ResourceManager; + +/** + * Provides a single point of access for image retrieval and caching. This does not include + * any tie-in to runtime adjustments to control caching and mirage creation. + */ +public class BaseImageManager + implements ImageUtil.ImageCreator +{ + /** + * Used to identify an image for caching and reconstruction. + */ + public static class ImageKey + { + /** The data provider from which this image's data is loaded. */ + public ImageDataProvider daprov; + + /** The path used to identify the image to the data provider. */ + public String path; + + protected ImageKey (ImageDataProvider daprov, String path) + { + this.daprov = daprov; + this.path = path; + } + + public int hashCode () + { + return path.hashCode() ^ daprov.getIdent().hashCode(); + } + + public boolean equals (Object other) + { + if (other == null || !(other instanceof ImageKey)) { + return false; + } + + ImageKey okey = (ImageKey)other; + return ((okey.daprov.getIdent().equals(daprov.getIdent())) && + (okey.path.equals(path))); + } + + public String toString () + { + return daprov.getIdent() + ":" + path; + } + } + + /** + * This interface allows the image manager to create images that are in a format optimal for + * rendering to the screen. + */ + public interface OptimalImageCreator + { + /** + * Requests that a blank image be created that is in a format and of a depth that are + * optimal for rendering to the screen. + */ + public BufferedImage createImage (int width, int height, int trans); + } + + /** + * Construct an image manager with the specified {@link ResourceManager} from which it will + * obtain its data. + */ + public BaseImageManager (ResourceManager rmgr, OptimalImageCreator icreator) + { + _rmgr = rmgr; + _icreator = icreator; + + // create our image cache + int icsize = getCacheSize(); + Log.debug("Creating image cache [size=" + icsize + "k]."); + _ccache = new LRUHashMap(icsize * 1024, new LRUHashMap.ItemSizer() { + public int computeSize (Object value) { + return (int)((CacheRecord)value).getEstimatedMemoryUsage(); + } + }); + _ccache.setTracking(true); + } + + /** + * A convenience constructor that creates an {@link AWTImageCreator} for use by the image + * manager. + */ + public BaseImageManager (ResourceManager rmgr, Component context) + { + this(rmgr, new AWTImageCreator(context)); + } + + /** + * Returns how much space we're willing to use for caching images. + */ + public int getCacheSize () + { + return DEFAULT_CACHE_SIZE; + } + + /** + * Clears all images out of the cache. + */ + public void clearCache () + { + Log.info("Clearing image manager cache."); + + _ccache.clear(); + } + + /** + * Creates a buffered image, optimized for display on our graphics device. + */ + public BufferedImage createImage (int width, int height, int transparency) + { + return _icreator.createImage(width, height, transparency); + } + + /** + * Loads (and caches) the specified image from the resource manager using the supplied path to + * identify the image. + */ + public BufferedImage getImage (String path) + { + return getImage(null, path, null); + } + + /** + * Like {@link #getImage(String)} but the specified colorizations are applied to the image + * before it is returned. + */ + public BufferedImage getImage (String path, Colorization[] zations) + { + return getImage(null, path, zations); + } + + /** + * Like {@link #getImage(String)} but the image is loaded from the specified resource set + * rathern than the default resource set. + */ + public BufferedImage getImage (String rset, String path) + { + return getImage(rset, path, null); + } + + /** + * Like {@link #getImage(String,String)} but the specified colorizations are applied to the + * image before it is returned. + */ + public BufferedImage getImage (String rset, String path, Colorization[] zations) + { + if (StringUtil.isBlank(path)) { + String errmsg = "Invalid image path [rset=" + rset + ", path=" + path + "]"; + throw new IllegalArgumentException(errmsg); + } + return getImage(getImageKey(rset, path), zations); + } + + /** + * Loads (and caches) the specified image from the resource manager using the supplied path to + * identify the image. + * + *
Additionally the image is optimized for display in the current graphics + * configuration. Consider using {@link #getMirage(ImageKey)} instead of prepared images as + * they (some day) will automatically use volatile images to increase performance. + */ + public BufferedImage getPreparedImage (String path) + { + return getPreparedImage(null, path, null); + } + + /** + * Loads (and caches) the specified image from the resource manager, obtaining the image from + * the supplied resource set. + * + *
Additionally the image is optimized for display in the current graphics + * configuration. Consider using {@link #getMirage(ImageKey)} instead of prepared images as + * they (some day) will automatically use volatile images to increase performance. + */ + public BufferedImage getPreparedImage (String rset, String path) + { + return getPreparedImage(rset, path, null); + } + + /** + * Loads (and caches) the specified image from the resource manager, obtaining the image from + * the supplied resource set and applying the using the supplied path to identify the image. + * + *
Additionally the image is optimized for display in the current graphics
+ * configuration. Consider using {@link #getMirage(ImageKey,Colorization[])} instead of
+ * prepared images as they (some day) will automatically use volatile images to increase
+ * performance.
+ */
+ public BufferedImage getPreparedImage (String rset, String path, Colorization[] zations)
+ {
+ BufferedImage image = getImage(rset, path, zations);
+ BufferedImage prepped = null;
+ if (image != null) {
+ prepped = createImage(image.getWidth(), image.getHeight(),
+ image.getColorModel().getTransparency());
+ Graphics2D pg = prepped.createGraphics();
+ pg.drawImage(image, 0, 0, null);
+ pg.dispose();
+ }
+ return prepped;
+ }
+
+ /**
+ * Returns an image key that can be used to fetch the image identified by the specified
+ * resource set and image path.
+ */
+ public ImageKey getImageKey (String rset, String path)
+ {
+ return getImageKey(getDataProvider(rset), path);
+ }
+
+ /**
+ * Returns an image key that can be used to fetch the image identified by the specified data
+ * provider and image path.
+ */
+ public ImageKey getImageKey (ImageDataProvider daprov, String path)
+ {
+ return new ImageKey(daprov, path);
+ }
+
+ /**
+ * Obtains the image identified by the specified key, caching if possible. The image will be
+ * recolored using the supplied colorizations if requested.
+ */
+ public BufferedImage getImage (ImageKey key, Colorization[] zations)
+ {
+ CacheRecord crec = null;
+ synchronized (_ccache) {
+ crec = (CacheRecord)_ccache.get(key);
+ }
+ if (crec != null) {
+// Log.info("Cache hit [key=" + key + ", crec=" + crec + "].");
+ return crec.getImage(zations, _ccache);
+ }
+// Log.info("Cache miss [key=" + key + ", crec=" + crec + "].");
+
+ // load up the raw image
+ BufferedImage image = loadImage(key);
+ if (image == null) {
+ Log.warning("Failed to load image " + key + ".");
+ // create a blank image instead
+ image = new BufferedImage(10, 10, BufferedImage.TYPE_BYTE_INDEXED);
+ }
+
+// Log.info("Loaded " + key.path + ", image=" + image +
+// ", size=" + ImageUtil.getEstimatedMemoryUsage(image));
+
+ // create a cache record
+ crec = new CacheRecord(key, image);
+ synchronized (_ccache) {
+ _ccache.put(key, crec);
+ }
+ _keySet.add(key);
+
+ // periodically report our image cache performance
+ reportCachePerformance();
+
+ return crec.getImage(zations, _ccache);
+ }
+
+ /**
+ * Creates a mirage which is an image optimized for display on our current display device and
+ * which will be stored into video memory if possible.
+ */
+ public Mirage getMirage (String rsrcPath)
+ {
+ return getMirage(getImageKey(_defaultProvider, rsrcPath), null, null);
+ }
+
+ /**
+ * Creates a mirage which is an image optimized for display on our current display device and
+ * which will be stored into video memory if possible.
+ */
+ public Mirage getMirage (ImageKey key)
+ {
+ return getMirage(key, null, null);
+ }
+
+ /**
+ * Like {@link #getMirage(ImageKey)} but that only the specified subimage of the source image
+ * is used to build the mirage.
+ */
+ public Mirage getMirage (ImageKey key, Rectangle bounds)
+ {
+ return getMirage(key, bounds, null);
+ }
+
+ /**
+ * Like {@link #getMirage(ImageKey)} but the supplied colorizations are applied to the source
+ * image before creating the mirage.
+ */
+ public Mirage getMirage (ImageKey key, Colorization[] zations)
+ {
+ return getMirage(key, null, zations);
+ }
+
+ /**
+ * Like {@link #getMirage(ImageKey,Colorization[])} except that the mirage is created using
+ * only the specified subset of the original image.
+ */
+ public Mirage getMirage (ImageKey key, Rectangle bounds, Colorization[] zations)
+ {
+ BufferedImage src = null;
+
+ float percentageOfDataBuffer = 1;
+ if (bounds == null) {
+ // if they specified no bounds, we need to load up the raw image and determine its
+ // bounds so that we can pass those along to the created mirage
+ src = getImage(key, zations);
+ bounds = new Rectangle(0, 0, src.getWidth(), src.getHeight());
+
+ }
+
+ return new CachedVolatileMirage(this, key, bounds, zations);
+
+ }
+
+ /**
+ * Returns the image creator that can be used to create buffered images optimized for rendering
+ * to the screen.
+ */
+ public OptimalImageCreator getImageCreator ()
+ {
+ return _icreator;
+ }
+
+ /**
+ * Returns the data provider configured to obtain image data from the specified resource set.
+ */
+ protected ImageDataProvider getDataProvider (final String rset)
+ {
+ if (rset == null) {
+ return _defaultProvider;
+ }
+
+ ImageDataProvider dprov = (ImageDataProvider)_providers.get(rset);
+ if (dprov == null) {
+ dprov = new ImageDataProvider() {
+ public BufferedImage loadImage (String path)
+ throws IOException {
+ // first attempt to load the image from the specified resource set
+ try {
+ return _rmgr.getImageResource(rset, path);
+ } catch (FileNotFoundException fnfe) {
+ // fall back to trying the classpath
+ return _rmgr.getImageResource(path);
+ }
+ }
+
+ public String getIdent () {
+ return "rmgr:" + rset;
+ }
+ };
+ _providers.put(rset, dprov);
+ }
+
+ return dprov;
+ }
+
+ /**
+ * Loads and returns the image with the specified key from the supplied data provider.
+ */
+ protected BufferedImage loadImage (ImageKey key)
+ {
+// if (EventQueue.isDispatchThread()) {
+// Log.info("Loading image on AWT thread " + key + ".");
+// }
+
+ BufferedImage image = null;
+ try {
+ Log.debug("Loading image " + key + ".");
+ image = key.daprov.loadImage(key.path);
+ if (image == null) {
+ Log.warning("ImageDataProvider.loadImage(" + key + ") returned null.");
+ }
+
+ } catch (Exception e) {
+ Log.warning("Unable to load image '" + key + "'.");
+ Log.logStackTrace(e);
+
+ // create a blank image in its stead
+ image = createImage(1, 1, Transparency.OPAQUE);
+ }
+
+ return image;
+ }
+
+ /**
+ * Reports statistics detailing the image manager cache performance and the current size of the
+ * cached images.
+ */
+ protected void reportCachePerformance ()
+ {
+ if (/* Log.getLevel() != Log.log.DEBUG || */
+ _cacheStatThrottle.throttleOp()) {
+ return;
+ }
+
+ // compute our estimated memory usage
+ long size = 0;
+
+ int[] eff = null;
+ synchronized (_ccache) {
+ Iterator iter = _ccache.values().iterator();
+ while (iter.hasNext()) {
+ size += ((CacheRecord)iter.next()).getEstimatedMemoryUsage();
+ }
+ eff = _ccache.getTrackedEffectiveness();
+ }
+ Log.info("ImageManager LRU [mem=" + (size / 1024) + "k, size=" + _ccache.size() +
+ ", hits=" + eff[0] + ", misses=" + eff[1] + ", totalKeys=" + _keySet.size() + "].");
+ }
+
+ /** Maintains a source image and a set of colorized versions in the image cache. */
+ protected static class CacheRecord
+ {
+ public CacheRecord (ImageKey key, BufferedImage source)
+ {
+ _key = key;
+ _source = source;
+ }
+
+ public BufferedImage getImage (Colorization[] zations, LRUHashMap cache)
+ {
+ if (zations == null) {
+ return _source;
+ }
+
+ if (_colorized == null) {
+ _colorized = new ArrayList These objects are never created directly, but always obtained from
- * the {@link ImageManager}.
+ * the {@link BaseImageManager}.
*/
public class CachedVolatileMirage extends VolatileMirage
{
@@ -44,7 +44,7 @@ public class CachedVolatileMirage extends VolatileMirage
* prepared image.
*/
protected CachedVolatileMirage (
- ImageManager imgr, ImageManager.ImageKey source,
+ BaseImageManager imgr, BaseImageManager.ImageKey source,
Rectangle bounds, Colorization[] zations)
{
super(imgr, bounds);
@@ -96,7 +96,7 @@ public class CachedVolatileMirage extends VolatileMirage
/** The key that identifies the image data used to create our volatile
* image. */
- protected ImageManager.ImageKey _source;
+ protected BaseImageManager.ImageKey _source;
/** Optional colorizations that are applied to our source image when
* creating our mirage. */
diff --git a/src/java/com/threerings/media/image/ImageManager.java b/src/java/com/threerings/media/image/ImageManager.java
index b75a1074..def0148f 100644
--- a/src/java/com/threerings/media/image/ImageManager.java
+++ b/src/java/com/threerings/media/image/ImageManager.java
@@ -22,324 +22,42 @@
package com.threerings.media.image;
import java.awt.Component;
-import java.awt.Graphics2D;
import java.awt.Rectangle;
-import java.awt.Transparency;
import java.awt.image.BufferedImage;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-
import com.samskivert.swing.RuntimeAdjust;
-import com.samskivert.util.LRUHashMap;
-import com.samskivert.util.StringUtil;
-import com.samskivert.util.Throttle;
-import com.samskivert.util.Tuple;
-
-import com.threerings.media.Log;
import com.threerings.media.MediaPrefs;
import com.threerings.resource.ResourceManager;
/**
- * Provides a single point of access for image retrieval and caching.
+ * Provides a single point of access for image retrieval and caching - just like the
+ * BaseImageManager but adds a tie in to the RuntimeAdjust system to control caching and
+ * mirage creation.
*/
-public class ImageManager
- implements ImageUtil.ImageCreator
+public class ImageManager extends BaseImageManager
{
- /**
- * Used to identify an image for caching and reconstruction.
- */
- public static class ImageKey
- {
- /** The data provider from which this image's data is loaded. */
- public ImageDataProvider daprov;
-
- /** The path used to identify the image to the data provider. */
- public String path;
-
- protected ImageKey (ImageDataProvider daprov, String path)
- {
- this.daprov = daprov;
- this.path = path;
- }
-
- public int hashCode ()
- {
- return path.hashCode() ^ daprov.getIdent().hashCode();
- }
-
- public boolean equals (Object other)
- {
- if (other == null || !(other instanceof ImageKey)) {
- return false;
- }
-
- ImageKey okey = (ImageKey)other;
- return ((okey.daprov.getIdent().equals(daprov.getIdent())) &&
- (okey.path.equals(path)));
- }
-
- public String toString ()
- {
- return daprov.getIdent() + ":" + path;
- }
- }
-
- /**
- * This interface allows the image manager to create images that are in a format optimal for
- * rendering to the screen.
- */
- public interface OptimalImageCreator
- {
- /**
- * Requests that a blank image be created that is in a format and of a depth that are
- * optimal for rendering to the screen.
- */
- public BufferedImage createImage (int width, int height, int trans);
- }
-
- /**
- * Construct an image manager with the specified {@link ResourceManager} from which it will
- * obtain its data.
- */
public ImageManager (ResourceManager rmgr, OptimalImageCreator icreator)
{
- _rmgr = rmgr;
- _icreator = icreator;
-
- // create our image cache
- int icsize = _cacheSize.getValue();
- Log.debug("Creating image cache [size=" + icsize + "k].");
- _ccache = new LRUHashMap(icsize * 1024, new LRUHashMap.ItemSizer() {
- public int computeSize (Object value) {
- return (int)((CacheRecord)value).getEstimatedMemoryUsage();
- }
- });
- _ccache.setTracking(true);
+ super(rmgr, icreator);
}
- /**
- * A convenience constructor that creates an {@link AWTImageCreator} for use by the image
- * manager.
- */
public ImageManager (ResourceManager rmgr, Component context)
{
- this(rmgr, new AWTImageCreator(context));
+ super(rmgr, context);
}
- /**
- * Clears all images out of the cache.
- */
- public void clearCache ()
+ @Override
+ public int getCacheSize ()
{
- Log.info("Clearing image manager cache.");
-
- _ccache.clear();
+ return _cacheSize.getValue();
}
- /**
- * Creates a buffered image, optimized for display on our graphics device.
- */
- public BufferedImage createImage (int width, int height, int transparency)
- {
- return _icreator.createImage(width, height, transparency);
- }
-
- /**
- * Loads (and caches) the specified image from the resource manager using the supplied path to
- * identify the image.
- */
- public BufferedImage getImage (String path)
- {
- return getImage(null, path, null);
- }
-
- /**
- * Like {@link #getImage(String)} but the specified colorizations are applied to the image
- * before it is returned.
- */
- public BufferedImage getImage (String path, Colorization[] zations)
- {
- return getImage(null, path, zations);
- }
-
- /**
- * Like {@link #getImage(String)} but the image is loaded from the specified resource set
- * rathern than the default resource set.
- */
- public BufferedImage getImage (String rset, String path)
- {
- return getImage(rset, path, null);
- }
-
- /**
- * Like {@link #getImage(String,String)} but the specified colorizations are applied to the
- * image before it is returned.
- */
- public BufferedImage getImage (String rset, String path, Colorization[] zations)
- {
- if (StringUtil.isBlank(path)) {
- String errmsg = "Invalid image path [rset=" + rset + ", path=" + path + "]";
- throw new IllegalArgumentException(errmsg);
- }
- return getImage(getImageKey(rset, path), zations);
- }
-
- /**
- * Loads (and caches) the specified image from the resource manager using the supplied path to
- * identify the image.
- *
- * Additionally the image is optimized for display in the current graphics
- * configuration. Consider using {@link #getMirage(ImageKey)} instead of prepared images as
- * they (some day) will automatically use volatile images to increase performance.
- */
- public BufferedImage getPreparedImage (String path)
- {
- return getPreparedImage(null, path, null);
- }
-
- /**
- * Loads (and caches) the specified image from the resource manager, obtaining the image from
- * the supplied resource set.
- *
- * Additionally the image is optimized for display in the current graphics
- * configuration. Consider using {@link #getMirage(ImageKey)} instead of prepared images as
- * they (some day) will automatically use volatile images to increase performance.
- */
- public BufferedImage getPreparedImage (String rset, String path)
- {
- return getPreparedImage(rset, path, null);
- }
-
- /**
- * Loads (and caches) the specified image from the resource manager, obtaining the image from
- * the supplied resource set and applying the using the supplied path to identify the image.
- *
- * Additionally the image is optimized for display in the current graphics
- * configuration. Consider using {@link #getMirage(ImageKey,Colorization[])} instead of
- * prepared images as they (some day) will automatically use volatile images to increase
- * performance.
- */
- public BufferedImage getPreparedImage (String rset, String path, Colorization[] zations)
- {
- BufferedImage image = getImage(rset, path, zations);
- BufferedImage prepped = null;
- if (image != null) {
- prepped = createImage(image.getWidth(), image.getHeight(),
- image.getColorModel().getTransparency());
- Graphics2D pg = prepped.createGraphics();
- pg.drawImage(image, 0, 0, null);
- pg.dispose();
- }
- return prepped;
- }
-
- /**
- * Returns an image key that can be used to fetch the image identified by the specified
- * resource set and image path.
- */
- public ImageKey getImageKey (String rset, String path)
- {
- return getImageKey(getDataProvider(rset), path);
- }
-
- /**
- * Returns an image key that can be used to fetch the image identified by the specified data
- * provider and image path.
- */
- public ImageKey getImageKey (ImageDataProvider daprov, String path)
- {
- return new ImageKey(daprov, path);
- }
-
- /**
- * Obtains the image identified by the specified key, caching if possible. The image will be
- * recolored using the supplied colorizations if requested.
- */
- public BufferedImage getImage (ImageKey key, Colorization[] zations)
- {
- CacheRecord crec = null;
- synchronized (_ccache) {
- crec = (CacheRecord)_ccache.get(key);
- }
- if (crec != null) {
-// Log.info("Cache hit [key=" + key + ", crec=" + crec + "].");
- return crec.getImage(zations, _ccache);
- }
-// Log.info("Cache miss [key=" + key + ", crec=" + crec + "].");
-
- // load up the raw image
- BufferedImage image = loadImage(key);
- if (image == null) {
- Log.warning("Failed to load image " + key + ".");
- // create a blank image instead
- image = new BufferedImage(10, 10, BufferedImage.TYPE_BYTE_INDEXED);
- }
-
-// Log.info("Loaded " + key.path + ", image=" + image +
-// ", size=" + ImageUtil.getEstimatedMemoryUsage(image));
-
- // create a cache record
- crec = new CacheRecord(key, image);
- synchronized (_ccache) {
- _ccache.put(key, crec);
- }
- _keySet.add(key);
-
- // periodically report our image cache performance
- reportCachePerformance();
-
- return crec.getImage(zations, _ccache);
- }
-
- /**
- * Creates a mirage which is an image optimized for display on our current display device and
- * which will be stored into video memory if possible.
- */
- public Mirage getMirage (String rsrcPath)
- {
- return getMirage(getImageKey(_defaultProvider, rsrcPath), null, null);
- }
-
- /**
- * Creates a mirage which is an image optimized for display on our current display device and
- * which will be stored into video memory if possible.
- */
- public Mirage getMirage (ImageKey key)
- {
- return getMirage(key, null, null);
- }
-
- /**
- * Like {@link #getMirage(ImageKey)} but that only the specified subimage of the source image
- * is used to build the mirage.
- */
- public Mirage getMirage (ImageKey key, Rectangle bounds)
- {
- return getMirage(key, bounds, null);
- }
-
- /**
- * Like {@link #getMirage(ImageKey)} but the supplied colorizations are applied to the source
- * image before creating the mirage.
- */
- public Mirage getMirage (ImageKey key, Colorization[] zations)
- {
- return getMirage(key, null, zations);
- }
-
- /**
- * Like {@link #getMirage(ImageKey,Colorization[])} except that the mirage is created using
- * only the specified subset of the original image.
- */
+ @Override
public Mirage getMirage (ImageKey key, Rectangle bounds, Colorization[] zations)
{
+ // We need to do something more complicated than the BaseImageManager because our
+ // runtime adjustments may affect how we create our mirages.
+
BufferedImage src = null;
float percentageOfDataBuffer = 1;
@@ -365,200 +83,10 @@ public class ImageManager
}
}
- /**
- * Returns the image creator that can be used to create buffered images optimized for rendering
- * to the screen.
- */
- public OptimalImageCreator getImageCreator ()
- {
- return _icreator;
- }
-
- /**
- * Returns the data provider configured to obtain image data from the specified resource set.
- */
- protected ImageDataProvider getDataProvider (final String rset)
- {
- if (rset == null) {
- return _defaultProvider;
- }
-
- ImageDataProvider dprov = (ImageDataProvider)_providers.get(rset);
- if (dprov == null) {
- dprov = new ImageDataProvider() {
- public BufferedImage loadImage (String path)
- throws IOException {
- // first attempt to load the image from the specified resource set
- try {
- return _rmgr.getImageResource(rset, path);
- } catch (FileNotFoundException fnfe) {
- // fall back to trying the classpath
- return _rmgr.getImageResource(path);
- }
- }
-
- public String getIdent () {
- return "rmgr:" + rset;
- }
- };
- _providers.put(rset, dprov);
- }
-
- return dprov;
- }
-
- /**
- * Loads and returns the image with the specified key from the supplied data provider.
- */
- protected BufferedImage loadImage (ImageKey key)
- {
-// if (EventQueue.isDispatchThread()) {
-// Log.info("Loading image on AWT thread " + key + ".");
-// }
-
- BufferedImage image = null;
- try {
- Log.debug("Loading image " + key + ".");
- image = key.daprov.loadImage(key.path);
- if (image == null) {
- Log.warning("ImageDataProvider.loadImage(" + key + ") returned null.");
- }
-
- } catch (Exception e) {
- Log.warning("Unable to load image '" + key + "'.");
- Log.logStackTrace(e);
-
- // create a blank image in its stead
- image = createImage(1, 1, Transparency.OPAQUE);
- }
-
- return image;
- }
-
- /**
- * Reports statistics detailing the image manager cache performance and the current size of the
- * cached images.
- */
- protected void reportCachePerformance ()
- {
- if (/* Log.getLevel() != Log.log.DEBUG || */
- _cacheStatThrottle.throttleOp()) {
- return;
- }
-
- // compute our estimated memory usage
- long size = 0;
-
- int[] eff = null;
- synchronized (_ccache) {
- Iterator iter = _ccache.values().iterator();
- while (iter.hasNext()) {
- size += ((CacheRecord)iter.next()).getEstimatedMemoryUsage();
- }
- eff = _ccache.getTrackedEffectiveness();
- }
- Log.info("ImageManager LRU [mem=" + (size / 1024) + "k, size=" + _ccache.size() +
- ", hits=" + eff[0] + ", misses=" + eff[1] + ", totalKeys=" + _keySet.size() + "].");
- }
-
- /** Maintains a source image and a set of colorized versions in the image cache. */
- protected static class CacheRecord
- {
- public CacheRecord (ImageKey key, BufferedImage source)
- {
- _key = key;
- _source = source;
- }
-
- public BufferedImage getImage (Colorization[] zations, LRUHashMap cache)
- {
- if (zations == null) {
- return _source;
- }
-
- if (_colorized == null) {
- _colorized = new ArrayList