diff --git a/src/java/com/threerings/media/animation/BobbleAnimation.java b/src/java/com/threerings/media/animation/BobbleAnimation.java index cd8b3a986..84ac0adec 100644 --- a/src/java/com/threerings/media/animation/BobbleAnimation.java +++ b/src/java/com/threerings/media/animation/BobbleAnimation.java @@ -1,16 +1,16 @@ // -// $Id: BobbleAnimation.java,v 1.5 2002/11/20 02:18:49 mdb Exp $ +// $Id: BobbleAnimation.java,v 1.6 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.animation; import java.awt.Color; import java.awt.Graphics2D; -import java.awt.Image; import java.awt.Rectangle; import com.threerings.util.RandomUtil; import com.threerings.media.Log; +import com.threerings.media.image.Mirage; /** * An animation that bobbles an image around within a specific horizontal @@ -29,10 +29,10 @@ public class BobbleAnimation extends Animation * @param duration the time to animate in milliseconds. */ public BobbleAnimation ( - Image image, int sx, int sy, int rx, int ry, int duration) + Mirage image, int sx, int sy, int rx, int ry, int duration) { - super(new Rectangle(sx - rx, sy - ry, sx + image.getWidth(null) + rx, - sy + image.getHeight(null) + ry)); + super(new Rectangle(sx - rx, sy - ry, sx + image.getWidth() + rx, + sy + image.getHeight() + ry)); // save things off _image = image; @@ -72,7 +72,7 @@ public class BobbleAnimation extends Animation // documentation inherited public void paint (Graphics2D gfx) { - gfx.drawImage(_image, _x, _y, null); + _image.paint(gfx, _x, _y); } // documentation inherited @@ -98,7 +98,7 @@ public class BobbleAnimation extends Animation protected int _sx, _sy; /** The image to animate. */ - protected Image _image; + protected Mirage _image; /** Animation ending timing information. */ protected long _duration, _end; diff --git a/src/java/com/threerings/media/animation/ExplodeAnimation.java b/src/java/com/threerings/media/animation/ExplodeAnimation.java index 38dbc9f35..158788cc8 100644 --- a/src/java/com/threerings/media/animation/ExplodeAnimation.java +++ b/src/java/com/threerings/media/animation/ExplodeAnimation.java @@ -1,5 +1,5 @@ // -// $Id: ExplodeAnimation.java,v 1.13 2002/10/01 04:24:34 shaper Exp $ +// $Id: ExplodeAnimation.java,v 1.14 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.animation; @@ -7,14 +7,14 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; -import java.awt.Image; import java.awt.Rectangle; import java.awt.Shape; import com.samskivert.util.StringUtil; +import com.threerings.util.RandomUtil; import com.threerings.media.Log; -import com.threerings.util.RandomUtil; +import com.threerings.media.image.Mirage; /** * An animation that displays an object exploding into chunks, fading out @@ -88,7 +88,7 @@ public class ExplodeAnimation extends Animation * @param height the height of the object. */ public ExplodeAnimation ( - Image image, ExplodeInfo info, int x, int y, int width, int height) + Mirage image, ExplodeInfo info, int x, int y, int width, int height) { super(info.bounds); @@ -233,7 +233,7 @@ public class ExplodeAnimation extends Animation if (_image != null) { // draw the image chunk gfx.clipRect(-_hcwid, -_hchei, _cwid, _chei); - gfx.drawImage(_image, -_hcwid + xoff, -_hchei + yoff, null); + _image.paint(gfx, -_hcwid + xoff, -_hchei + yoff); } else { // draw the color chunk @@ -303,7 +303,7 @@ public class ExplodeAnimation extends Animation protected Color _color; /** The image to animate if we're using an image. */ - protected Image _image; + protected Mirage _image; /** The starting animation time. */ protected long _start; diff --git a/src/java/com/threerings/media/animation/FadeAnimation.java b/src/java/com/threerings/media/animation/FadeAnimation.java index 4a40aa2f7..7e837b828 100644 --- a/src/java/com/threerings/media/animation/FadeAnimation.java +++ b/src/java/com/threerings/media/animation/FadeAnimation.java @@ -1,5 +1,5 @@ // -// $Id: FadeAnimation.java,v 1.5 2002/10/01 04:24:34 shaper Exp $ +// $Id: FadeAnimation.java,v 1.6 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.animation; @@ -7,10 +7,10 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; -import java.awt.Image; import java.awt.Rectangle; import com.threerings.media.Log; +import com.threerings.media.image.Mirage; /** * An animation that displays an image fading from one alpha level to @@ -30,9 +30,9 @@ public class FadeAnimation extends Animation * @param target the target alpha level. */ public FadeAnimation ( - Image image, int x, int y, float alpha, float step, float target) + Mirage image, int x, int y, float alpha, float step, float target) { - super(new Rectangle(x, y, image.getWidth(null), image.getHeight(null))); + super(new Rectangle(x, y, image.getWidth(), image.getHeight())); // save things off _image = image; @@ -90,7 +90,7 @@ public class FadeAnimation extends Animation } else { gfx.setComposite(_comp); } - gfx.drawImage(_image, _x, _y, null); + _image.paint(gfx, _x, _y); gfx.setComposite(ocomp); } @@ -126,7 +126,7 @@ public class FadeAnimation extends Animation protected int _x, _y; /** The image to animate. */ - protected Image _image; + protected Mirage _image; /** The starting animation time. */ protected long _start; diff --git a/src/java/com/threerings/media/animation/SparkAnimation.java b/src/java/com/threerings/media/animation/SparkAnimation.java index 9f77a2a1f..76b082ff2 100644 --- a/src/java/com/threerings/media/animation/SparkAnimation.java +++ b/src/java/com/threerings/media/animation/SparkAnimation.java @@ -1,20 +1,20 @@ // -// $Id: SparkAnimation.java,v 1.1 2002/11/27 01:37:14 shaper Exp $ +// $Id: SparkAnimation.java,v 1.2 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.animation; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics2D; -import java.awt.Image; import java.awt.Rectangle; import java.util.Arrays; -import com.threerings.media.animation.Animation; - import com.threerings.util.RandomUtil; +import com.threerings.media.animation.Animation; +import com.threerings.media.image.Mirage; + /** * Displays a set of spark images originating from a specified position * and flying outward in random directions, fading out as they go, for a @@ -41,7 +41,7 @@ public class SparkAnimation extends Animation public SparkAnimation (Rectangle bounds, int x, int y, int jog, float minxvel, float minyvel, float xvel, float yvel, - float g, Image[] images, long delay) + float g, Mirage[] images, long delay) { super(bounds); @@ -141,7 +141,7 @@ public class SparkAnimation extends Animation gfx.setComposite(_comp); // draw all sparks for (int ii = 0; ii < _icount; ii++) { - gfx.drawImage(_images[ii], _xpos[ii], _ypos[ii], null); + _images[ii].paint(gfx, _xpos[ii], _ypos[ii]); } // restore the original composite gfx.setComposite(ocomp); @@ -158,7 +158,7 @@ public class SparkAnimation extends Animation } /** The spark images we're animating. */ - protected Image[] _images; + protected Mirage[] _images; /** The number of images we're animating. */ protected int _icount; diff --git a/src/java/com/threerings/media/image/BackedVolatileMirage.java b/src/java/com/threerings/media/image/BackedVolatileMirage.java new file mode 100644 index 000000000..91d32513e --- /dev/null +++ b/src/java/com/threerings/media/image/BackedVolatileMirage.java @@ -0,0 +1,58 @@ +// +// $Id: BackedVolatileMirage.java,v 1.1 2003/01/13 22:49:46 mdb Exp $ + +package com.threerings.media.image; + +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +import com.threerings.media.Log; + +/** + * Provides a volatile mirage that is backed by a buffered image that is + * not obtained from the image manager but is instead provided at + * construct time and completely circumvents the image manager's cache. As + * such, this should not be used unless you know what you're doing. + */ +public class BackedVolatileMirage extends VolatileMirage +{ + /** + * Creates a mirage with the supplied regeneration informoation and + * prepared image. + */ + public BackedVolatileMirage (ImageManager imgr, BufferedImage source) + { + super(imgr, new Rectangle(0, 0, source.getWidth(), source.getHeight())); + _source = source; + + // create our volatile image for the first time + createVolatileImage(); + } + + // documentation inherited + protected void refreshVolatileImage () + { + Graphics gfx = null; + try { + gfx = _image.getGraphics(); + gfx.drawImage(_source, -_bounds.x, -_bounds.y, null); + + } catch (Exception e) { + Log.warning("Failure refreshing mirage " + this + "."); + Log.logStackTrace(e); + + } finally { + gfx.dispose(); + } + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + super.toString(buf); + buf.append(", src=").append(_source); + } + + protected BufferedImage _source; +} diff --git a/src/java/com/threerings/media/image/BlankMirage.java b/src/java/com/threerings/media/image/BlankMirage.java new file mode 100644 index 000000000..89188c693 --- /dev/null +++ b/src/java/com/threerings/media/image/BlankMirage.java @@ -0,0 +1,53 @@ +// +// $Id: BlankMirage.java,v 1.1 2003/01/13 22:49:46 mdb Exp $ + +package com.threerings.media.image; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +/** + * A mirage implementation that contains no image data. Generally only + * useful for testing. + */ +public class BlankMirage implements Mirage +{ + public BlankMirage (int width, int height) + { + _width = width; + _height = height; + } + + // documentation inherited from interface + public void paint (Graphics2D gfx, int x, int y) + { + // nothing doing + } + + // documentation inherited from interface + public int getWidth () + { + return _width; + } + + // documentation inherited from interface + public int getHeight () + { + return _height; + } + + // documentation inherited from interface + public boolean hitTest (int x, int y) + { + return false; + } + + // documentation inherited from interface + public BufferedImage getSnapshot () + { + return null; + } + + protected int _width; + protected int _height; +} diff --git a/src/java/com/threerings/media/image/BufferedMirage.java b/src/java/com/threerings/media/image/BufferedMirage.java new file mode 100644 index 000000000..676c33462 --- /dev/null +++ b/src/java/com/threerings/media/image/BufferedMirage.java @@ -0,0 +1,50 @@ +// +// $Id: BufferedMirage.java,v 1.1 2003/01/13 22:49:46 mdb Exp $ + +package com.threerings.media.image; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +/** + * A simple mirage implementation that uses a buffered image. + */ +public class BufferedMirage implements Mirage +{ + public BufferedMirage (BufferedImage image) + { + _image = image; + } + + // documentation inherited from interface + public void paint (Graphics2D gfx, int x, int y) + { + gfx.drawImage(_image, x, y, null); + } + + // documentation inherited from interface + public int getWidth () + { + return _image.getWidth(); + } + + // documentation inherited from interface + public int getHeight () + { + return _image.getHeight(); + } + + // documentation inherited from interface + public boolean hitTest (int x, int y) + { + return ImageUtil.hitTest(_image, x, y); + } + + // documentation inherited from interface + public BufferedImage getSnapshot () + { + return _image; + } + + protected BufferedImage _image; +} diff --git a/src/java/com/threerings/media/image/CachedVolatileMirage.java b/src/java/com/threerings/media/image/CachedVolatileMirage.java new file mode 100644 index 000000000..18b174186 --- /dev/null +++ b/src/java/com/threerings/media/image/CachedVolatileMirage.java @@ -0,0 +1,78 @@ +// +// $Id: CachedVolatileMirage.java,v 1.1 2003/01/13 22:49:46 mdb Exp $ + +package com.threerings.media.image; + +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +import com.threerings.media.Log; +import com.samskivert.util.StringUtil; + +/** + * A mirage implementation which allows the image to be maintained in + * video memory and refetched from the image manager in the event that our + * target screen resolution changes or we are flushed from video memory + * for some other reason. + * + *
These objects are never created directly, but always obtained from
+ * the {@link ImageManager}.
+ */
+public class CachedVolatileMirage extends VolatileMirage
+{
+ /**
+ * Creates a mirage with the supplied regeneration informoation and
+ * prepared image.
+ */
+ protected CachedVolatileMirage (
+ ImageManager imgr, ImageManager.ImageKey source,
+ Rectangle bounds, Colorization[] zations)
+ {
+ super(imgr, bounds);
+
+ _source = source;
+ _zations = zations;
+
+ // create our volatile image for the first time
+ createVolatileImage();
+ }
+
+ /**
+ * Rerenders our volatile image from the its source image data.
+ */
+ protected void refreshVolatileImage ()
+ {
+ Graphics gfx = null;
+ try {
+ BufferedImage source = _imgr.getImage(_source, _zations);
+ gfx = _image.getGraphics();
+ gfx.drawImage(source, -_bounds.x, -_bounds.y, null);
+
+ } catch (Exception e) {
+ Log.warning("Failure refreshing mirage " + this + ".");
+ Log.logStackTrace(e);
+
+ } finally {
+ if (gfx != null) {
+ gfx.dispose();
+ }
+ }
+ }
+
+ // documentation inherited
+ protected void toString (StringBuffer buf)
+ {
+ super.toString(buf);
+ buf.append(", key=").append(_source);
+ buf.append(", zations=").append(_zations);
+ }
+
+ /** The key that identifies the image data used to create our volatile
+ * image. */
+ protected ImageManager.ImageKey _source;
+
+ /** Optional colorizations that are applied to our source image when
+ * creating our mirage. */
+ protected Colorization[] _zations;
+}
diff --git a/src/java/com/threerings/media/image/Colorization.java b/src/java/com/threerings/media/image/Colorization.java
index eca5cd0f7..e32ec2ac3 100644
--- a/src/java/com/threerings/media/image/Colorization.java
+++ b/src/java/com/threerings/media/image/Colorization.java
@@ -1,5 +1,5 @@
//
-// $Id: Colorization.java,v 1.6 2003/01/08 04:09:02 mdb Exp $
+// $Id: Colorization.java,v 1.7 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.image;
@@ -100,6 +100,12 @@ public class Colorization
return true;
}
+ // documentation inherited
+ public int hashCode ()
+ {
+ return colorizationId ^ rootColor.hashCode();
+ }
+
/**
* Compares this colorization to another based on id.
*/
diff --git a/src/java/com/threerings/media/image/ImageDataProvider.java b/src/java/com/threerings/media/image/ImageDataProvider.java
new file mode 100644
index 000000000..bca8b9930
--- /dev/null
+++ b/src/java/com/threerings/media/image/ImageDataProvider.java
@@ -0,0 +1,29 @@
+//
+// $Id: ImageDataProvider.java,v 1.1 2003/01/13 22:49:46 mdb Exp $
+
+package com.threerings.media.image;
+
+import java.io.IOException;
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ * Provides access to image data for the image with the specified
+ * path. Images loaded from different data providers (which are
+ * differentiated by reference equality) will be considered distinct
+ * images with respect to caching.
+ */
+public interface ImageDataProvider
+{
+ /**
+ * Returns a string identifier for this image data provider which wil
+ * be used to differentiate it from other providers and thus should be
+ * unique.
+ */
+ public String getIdent ();
+
+ /**
+ * Returns an input stream from which the image data for the specified
+ * image may be loaded.
+ */
+ public ImageInputStream loadImageData (String path) throws IOException;
+}
diff --git a/src/java/com/threerings/media/image/ImageIOLoader.java b/src/java/com/threerings/media/image/ImageIOLoader.java
index 408449e15..5636a1730 100644
--- a/src/java/com/threerings/media/image/ImageIOLoader.java
+++ b/src/java/com/threerings/media/image/ImageIOLoader.java
@@ -1,13 +1,22 @@
//
-// $Id: ImageIOLoader.java,v 1.5 2003/01/08 04:09:02 mdb Exp $
+// $Id: ImageIOLoader.java,v 1.6 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.image;
import java.awt.Image;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Iterator;
import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+import javax.imageio.stream.MemoryCacheImageInputStream;
+
+import com.samskivert.util.StringUtil;
+import java.io.RandomAccessFile;
+
+import com.threerings.media.Log;
/**
* Loads images using the ImageIO services provided by J2SE
@@ -20,6 +29,16 @@ public class ImageIOLoader implements ImageLoader
// we need to reference ImageIO in the constructor to force the
// classloader to attempt to load the ImageIO classes
ImageIO.setUseCache(true);
+
+// String[] fmts = ImageIO.getReaderFormatNames();
+// Log.info("Got formats " + StringUtil.toString(fmts) + ".");
+
+ Iterator iter = ImageIO.getImageReadersByFormatName("png");
+ if (iter.hasNext()) {
+ _reader = (ImageReader)iter.next();
+ } else {
+ Log.warning("Aiya! No reader.");
+ }
}
// documentation inherited
@@ -29,10 +48,18 @@ public class ImageIOLoader implements ImageLoader
// this seems to choke when decoding the compressed image data
// which may mean it's a JDK bug or something, but I'd like to see
// it resolved so that the image manager will work on applets
- //
- // ImageInputStream iis = new MemoryCacheImageInputStream(source);
- // return ImageIO.read(iis);
+// ImageInputStream iis = null;
+// // // iis = ImageIO.createImageInputStream(source);
+// iis = new MemoryCacheImageInputStream(source);
+
+// // // Log.info("Created stream " + iis + "/" + source);
+// _reader.setInput(iis, true, false);
+// return _reader.read(0);
+
+// return ImageIO.read(new MemoryCacheImageInputStream(source));
return ImageIO.read(source);
}
+
+ protected ImageReader _reader;
}
diff --git a/src/java/com/threerings/media/image/ImageManager.java b/src/java/com/threerings/media/image/ImageManager.java
index e4f44d749..c5dce6c96 100644
--- a/src/java/com/threerings/media/image/ImageManager.java
+++ b/src/java/com/threerings/media/image/ImageManager.java
@@ -1,21 +1,37 @@
//
-// $Id: ImageManager.java,v 1.34 2003/01/08 05:17:35 ray Exp $
+// $Id: ImageManager.java,v 1.35 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.image;
import java.awt.Component;
import java.awt.Graphics2D;
-import java.awt.Image;
+import java.awt.GraphicsConfiguration;
+import java.awt.Rectangle;
+import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import com.sun.imageio.plugins.png.PNGImageReaderSpi;
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageInputStream;
+
import com.samskivert.io.NestableIOException;
+import com.samskivert.util.ConfigUtil;
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;
@@ -25,6 +41,45 @@ import com.threerings.resource.ResourceManager;
*/
public class ImageManager
{
+ /**
+ * 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;
+ }
+ }
+
/**
* Construct an image manager with the specified {@link
* ResourceManager} from which it will obtain its data. A non-null
@@ -37,6 +92,12 @@ public class ImageManager
{
_rmgr = rmgr;
+ // create our image cache
+ int icsize = ConfigUtil.getSystemProperty(
+ "narya.media.image.cache_size", DEFAULT_IMAGE_CACHE_SIZE);
+ Log.debug("Creating image cache [size=" + icsize + "].");
+ _ccache = new LRUHashMap(icsize);
+
// try to figure out which image loader we'll be using
try {
_loader = (ImageLoader)Class.forName(IMAGEIO_LOADER).newInstance();
@@ -45,123 +106,253 @@ public class ImageManager
"Falling back to Toolkit [error=" + t + "].");
_loader = new ToolkitLoader(context);
}
+
+ // obtain our graphics configuration
+ if (context != null) {
+ _gc = context.getGraphicsConfiguration();
+ } else {
+ _gc = ImageUtil.getDefGC();
+ }
}
/**
- * Loads up the requested image from the specified resource set and
- * caches it for faster retrieval henceforth.
+ * Creates a buffered image, optimized for display on our graphics
+ * device.
*/
- public Image getImage (String rset, String path)
+ public BufferedImage createImage (int width, int height, int transparency)
{
- if (rset == null || path == null) {
- String errmsg = "Must supply non-null resource set and path " +
- "[rset=" + rset + ", path=" + path + "]";
+ return _gc.createCompatibleImage(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.blank(path)) {
+ String errmsg = "Invalid image path [rset=" + rset +
+ ", path=" + path + "]";
throw new IllegalArgumentException(errmsg);
}
- // periodically report our image cache performance
- reportCachePerformance();
-
- String key = rset + ":" + path;
- Image img = (Image)_imgs.get(key);
- if (img != null) {
- // Log.info("Retrieved image from cache [path=" + path + "].");
- return img;
- }
-
- InputStream imgin = null;
- try {
- // first attempt to load the image from the specified resource set
- try {
- imgin = _rmgr.getResource(rset, path);
- } catch (FileNotFoundException fnfe) {
- // fall through and try the classpath
- }
-
- // if that fails, attempt to load the image from the classpath
- if (imgin == null) {
- imgin = _rmgr.getResource(path);
-// Log.info("Fell back to classpath [rset=" + rset +
-// ", path=" + path + "].");
- }
-
- // now load up and optimize the image for display
- img = createImage(imgin);
-
- } catch (IOException ioe) {
- Log.warning("Failure loading image [rset=" + rset +
- ", path=" + path + ", error=" + ioe + "].");
-
- } finally {
- if (imgin != null) {
- try {
- imgin.close();
- } catch (IOException ioe) {
- Log.warning("Failure closing image input stream " +
- "[rset=" + rset + ", path=" + path +
- ", error=" + ioe + "].");
- }
- }
- }
-
- // Log.info("Loading image into cache [path=" + path + "].");
- if (img != null) {
- _imgs.put(key, img);
- } else {
- Log.warning("Unable to load image " +
- "[rset=" + rset + ", path=" + path + "].");
- Thread.dumpStack();
- // fake it so that we don't crap out
- img = ImageUtil.createImage(1, 1);
- }
- return img;
+ return getImage(getImageKey(rset, path), zations);
}
/**
- * Loads the image via the resource manager using the specified path
- * and caches it for faster retrieval henceforth.
+ * Returns an image key that can be used to fetch the image identified
+ * by the specified resource set and image path.
*/
- public Image getImage (String 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 = (CacheRecord)_ccache.get(key);
+ if (crec != null) {
+// Log.info("Cache hit [key=" + key + ", crec=" + crec + "].");
+ return crec.getImage(zations);
+ }
+// 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 cache record
+ crec = new CacheRecord(image);
+ _ccache.put(key, crec);
+ _keySet.add(key);
+
// periodically report our image cache performance
reportCachePerformance();
- Image img = (Image)_imgs.get(path);
- if (img != null) {
- // Log.info("Retrieved image from cache [path=" + path + "].");
- return img;
- }
+ return crec.getImage(zations);
+ }
- InputStream imgin = null;
- try {
- imgin = _rmgr.getResource(path);
- img = createImage(imgin);
+ /**
+ * 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;
+ }
- } catch (IOException ioe) {
- Log.warning("Failure loading image [path=" + path +
- ", error=" + ioe + "].");
-
- } finally {
- if (imgin != null) {
- try {
- imgin.close();
- } catch (IOException ioe) {
- Log.warning("Failure closing image input stream " +
- "[path=" + path + ", error=" + ioe + "].");
+ ImageDataProvider dprov = (ImageDataProvider)_providers.get(rset);
+ if (dprov == null) {
+ dprov = new ImageDataProvider() {
+ public ImageInputStream loadImageData (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)
+ {
+ BufferedImage image = null;
+ ImageInputStream imgin = null;
+ try {
+ Log.debug("Loading image " + key + ".");
+ imgin = key.daprov.loadImageData(key.path);
+ image = loadImage(imgin);
+
+ } 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);
+ }
+
+ if (imgin != null) {
+ try {
+ imgin.close();
+ } catch (IOException ioe) {
+ Log.warning("Failure closing image input '" + key + "'.");
+ Log.logStackTrace(ioe);
}
}
- // Log.info("Loading image into cache [path=" + path + "].");
- if (img != null) {
- _imgs.put(path, img);
- } else {
- Log.warning("Unable to load image [path=" + path + "].");
- Thread.dumpStack();
- // fake it so that we don't crap out
- img = ImageUtil.createImage(1, 1);
+ return image;
+ }
+
+ /**
+ * Called by {@link #loadImage(ImageKey)} to do the actual image
+ * loading.
+ */
+ protected BufferedImage loadImage (ImageInputStream imgin)
+ throws IOException
+ {
+ return ImageIO.read(imgin);
+// _reader.setInput(imgin, false, false);
+// return _reader.read(0, 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)
+ {
+ if (bounds == null) {
+ BufferedImage src = getImage(key, zations);
+ bounds = new Rectangle(0, 0, src.getWidth(), src.getHeight());
+// return new BufferedMirage(src);
+// } else {
+// BufferedImage src = getImage(key, zations);
+// src = src.getSubimage(bounds.x, bounds.y,
+// bounds.width, bounds.height);
+// return new BufferedMirage(src);
}
- return img;
+ return new CachedVolatileMirage(this, key, bounds, zations);
+// return new BlankMirage(bounds.width, bounds.height);
+ }
+
+ /**
+ * Returns the graphics configuration that should be used to optimize
+ * images for display.
+ */
+ public GraphicsConfiguration getGraphicsConfiguration ()
+ {
+ return _gc;
}
/**
@@ -170,125 +361,74 @@ public class ImageManager
*/
protected void reportCachePerformance ()
{
- if (!_cacheStatThrottle.throttleOp()) {
- long size = ImageUtil.getEstimatedMemoryUsage(
- _imgs.values().iterator());
- int[] eff = _imgs.getTrackedEffectiveness();
- Log.debug("ImageManager LRU [mem=" + (size / 1024) + "k" +
- ", size=" + _imgs.size() + ", hits=" + eff[0] +
- ", misses=" + eff[1] + "].");
+ if (/* Log.getLevel() != Log.log.DEBUG || */
+ _cacheStatThrottle.throttleOp()) {
+ return;
}
+
+ // compute our estimated memory usage
+ long size = 0;
+ Iterator iter = _ccache.values().iterator();
+ while (iter.hasNext()) {
+ size += ((CacheRecord)iter.next()).getEstimatedMemoryUsage();
+ }
+
+ int[] eff = _ccache.getTrackedEffectiveness();
+ Log.info("ImageManager LRU [mem=" + (size / 1024) + "k" +
+ ", size=" + _ccache.size() + ", hits=" + eff[0] +
+ ", misses=" + eff[1] + ", totalKeys=" + _keySet.size() + "].");
}
- /**
- * Loads the image via the resource manager using the specified
- * path. Does no caching and does not convert the image for optimized
- * display on the target graphics configuration. Instead the original
- * image as returned by the image loader is returned.
- */
- public Image loadImage (String path)
- throws IOException
+ /** Maintains a source image and a set of colorized versions in the
+ * image cache. */
+ protected static class CacheRecord
{
- InputStream imgin = null;
- try {
- imgin = _rmgr.getResource(path);
- return _loader.loadImage(new BufferedInputStream(imgin));
+ public CacheRecord (BufferedImage source)
+ {
+ _source = source;
+ }
- } catch (IllegalArgumentException iae) {
- String errmsg = "Error loading image [path=" + path + "]";
- throw new NestableIOException(errmsg, iae);
-
- } finally {
- if (imgin != null) {
- imgin.close();
+ public BufferedImage getImage (Colorization[] zations)
+ {
+ if (zations == null) {
+ return _source;
}
- }
- }
- /**
- * Loads the image from the given resource set using the specified
- * path. Does no caching and does not convert the image for optimized
- * display on the target graphics configuration. Instead the original
- * image as returned by the image loader is returned.
- */
- public Image loadImage (String rset, String path)
- throws IOException
- {
- InputStream imgin = null;
- try {
- imgin = _rmgr.getResource(rset, path);
- return _loader.loadImage(new BufferedInputStream(imgin));
-
- } catch (Throwable t) {
- String errmsg = "Error loading image " +
- "[rset=" + rset + ", path=" + path + "]";
- throw new NestableIOException(errmsg, t);
-
- } finally {
- if (imgin != null) {
- imgin.close();
+ if (_colorized == null) {
+ _colorized = new ArrayList();
}
- }
- }
- /**
- * Loads the image from the supplied input stream. Does no caching and
- * does not convert the image for optimized display on the target
- * graphics configuration. Instead the original image as returned by
- * the image loader is returned.
- */
- public Image loadImage (InputStream source)
- throws IOException
- {
- try {
- return _loader.loadImage(new BufferedInputStream(source));
- } catch (IllegalArgumentException iae) {
- String errmsg = "Error loading image";
- throw new NestableIOException(errmsg, iae);
- }
- }
+ // we search linearly through our list of colorized copies
+ // because it is not likely to be very long
+ int csize = _colorized.size();
+ for (int ii = 0; ii < csize; ii++) {
+ Tuple tup = (Tuple)_colorized.get(ii);
+ Colorization[] tzations = (Colorization[])tup.left;
+ if (Arrays.equals(zations, tzations)) {
+ return (BufferedImage)tup.right;
+ }
+ }
- /**
- * Creates an image that is optimized for rendering in the client's
- * environment and decodes the image data from the specified source
- * image into that target image. The resulting image is not cached.
- */
- public Image createImage (InputStream source)
- throws IOException
- {
- return createImage(loadImage(source));
- }
-
- /**
- * Creates an image that is optimized for rendering in the client's
- * environment and renders the supplied image into the optimized
- * image. The resulting image is not cached.
- */
- public Image createImage (Image src)
- {
- // not to freak out if we get this far and have no image
- if (src == null) {
- return null;
+ BufferedImage cimage = ImageUtil.recolorImage(_source, zations);
+ _colorized.add(new Tuple(zations, cimage));
+ return cimage;
}
- int swidth = src.getWidth(null);
- int sheight = src.getHeight(null);
- BufferedImage dest = null;
-
- // use the same kind of transparency as the source image if we can
- // when converting the image to a format optimized for display
- if (src instanceof BufferedImage) {
- int trans = ((BufferedImage)src).getColorModel().getTransparency();
- dest = ImageUtil.createImage(swidth, sheight, trans);
- } else {
- dest = ImageUtil.createImage(swidth, sheight);
+ public long getEstimatedMemoryUsage ()
+ {
+ return ImageUtil.getEstimatedMemoryUsage(_source);
}
- Graphics2D gfx = dest.createGraphics();
- gfx.drawImage(src, 0, 0, null);
- gfx.dispose();
+ public String toString ()
+ {
+ return "[wid=" + _source.getWidth() +
+ ", hei=" + _source.getHeight() +
+ ", ccount=" + ((_colorized == null) ? 0 : _colorized.size()) +
+ "]";
+ }
- return dest;
+ protected BufferedImage _source;
+ protected ArrayList _colorized;
}
/** A reference to the resource manager via which we load image data
@@ -300,11 +440,37 @@ public class ImageManager
protected ImageLoader _loader;
/** A cache of loaded images. */
- protected LRUHashMap _imgs = new LRUHashMap(IMAGE_CACHE_SIZE);
+ protected LRUHashMap _ccache;
+
+ /** The set of all keys we've ever seen. */
+ protected HashSet _keySet = new HashSet();
/** Throttle our cache status logging to once every 30 seconds. */
protected Throttle _cacheStatThrottle = new Throttle(1, 30000L);
+ /** The graphics configuration for the default screen device. */
+ protected GraphicsConfiguration _gc;
+
+ /** Our default data provider. */
+ protected ImageDataProvider _defaultProvider = new ImageDataProvider() {
+ public ImageInputStream loadImageData (String path)
+ throws IOException {
+ return _rmgr.getImageResource(path);
+ }
+
+ public String getIdent () {
+ return "rmgr:default";
+ }
+ };
+
+// /** Temporary hacked PNG reader that actually closes its
+// * InflaterInputStream when it's done with it. */
+// protected PNGImageReader _reader =
+// new PNGImageReader(new PNGImageReaderSpi());
+
+ /** Data providers for different resource sets. */
+ protected HashMap _providers = new HashMap();
+
/** The classname of the ImageIO-based image loader which we attempt
* to use but fallback from if we're not running a JVM that has
* ImageIO support. */
@@ -312,5 +478,5 @@ public class ImageManager
"com.threerings.media.image.ImageIOLoader";
/** The maximum number of images that may be cached at any one time. */
- protected static final int IMAGE_CACHE_SIZE = 30;
+ protected static final int DEFAULT_IMAGE_CACHE_SIZE = 100;
}
diff --git a/src/java/com/threerings/media/image/ImageUtil.java b/src/java/com/threerings/media/image/ImageUtil.java
index bfb30d2da..331e487aa 100644
--- a/src/java/com/threerings/media/image/ImageUtil.java
+++ b/src/java/com/threerings/media/image/ImageUtil.java
@@ -1,5 +1,5 @@
//
-// $Id: ImageUtil.java,v 1.21 2003/01/08 04:28:13 ray Exp $
+// $Id: ImageUtil.java,v 1.22 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.image;
@@ -426,8 +426,8 @@ public class ImageUtil
// copy the RGB from the base image
for (int ii=0; ii < 3; ii++) {
- int[] cdata = basedata.getSamples(0, 0, wid, hei,
- ii, (int[]) null);
+ int[] cdata = basedata.getSamples(
+ 0, 0, wid, hei, ii, (int[]) null);
target.setSamples(0, 0, wid, hei, ii, cdata);
}
@@ -490,22 +490,11 @@ public class ImageUtil
* Returns true if the supplied image contains a non-transparent pixel
* at the specified coordinates, false otherwise.
*/
- public static boolean hitTest (Image image, int x, int y)
+ public static boolean hitTest (BufferedImage image, int x, int y)
{
- if (image instanceof BufferedImage) {
- BufferedImage bimage = (BufferedImage)image;
- int argb = bimage.getRGB(x, y);
- // int alpha = argb >> 24;
- // Log.info("Checking [x=" + x + ", y=" + y + ", " + alpha);
-
- // it's only a hit if the pixel is non-transparent
- return (argb >> 24) != 0;
-
- } else {
- Log.warning("Can't check for transparent pixel " +
- "[image=" + image + "].");
- return true;
- }
+ // it's only a hit if the pixel is non-transparent
+ int argb = image.getRGB(x, y);
+ return (argb >> 24) != 0;
}
/**
diff --git a/src/java/com/threerings/media/image/Mirage.java b/src/java/com/threerings/media/image/Mirage.java
new file mode 100644
index 000000000..2eb033244
--- /dev/null
+++ b/src/java/com/threerings/media/image/Mirage.java
@@ -0,0 +1,43 @@
+//
+// $Id: Mirage.java,v 1.1 2003/01/13 22:49:46 mdb Exp $
+
+package com.threerings.media.image;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ * Provides an interface via which images can be accessed in a way that
+ * allows them to optionally be located in video memory where that affords
+ * performance improvements.
+ */
+public interface Mirage
+{
+ /**
+ * Renders this mirage at the specified position in the supplied
+ * graphics context.
+ */
+ public void paint (Graphics2D gfx, int x, int y);
+
+ /**
+ * Returns the width of this mirage.
+ */
+ public int getWidth ();
+
+ /**
+ * Returns the height of this mirage.
+ */
+ public int getHeight ();
+
+ /**
+ * Returns true if this mirage contains a non-transparent pixel at the
+ * specified coordinate.
+ */
+ public boolean hitTest (int x, int y);
+
+ /**
+ * Returns a snapshot of this mirage as a buffered image. The snapshot
+ * should not be modified by the caller.
+ */
+ public BufferedImage getSnapshot ();
+}
diff --git a/src/java/com/threerings/media/image/MirageIcon.java b/src/java/com/threerings/media/image/MirageIcon.java
new file mode 100644
index 000000000..6d386344c
--- /dev/null
+++ b/src/java/com/threerings/media/image/MirageIcon.java
@@ -0,0 +1,42 @@
+//
+// $Id: MirageIcon.java,v 1.1 2003/01/13 22:49:46 mdb Exp $
+
+package com.threerings.media.image;
+
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.Graphics;
+
+import javax.swing.Icon;
+
+/**
+ * Implements the Swing {@link Icon} interface with a mirage providing the
+ * image information.
+ */
+public class MirageIcon implements Icon
+{
+ public MirageIcon (Mirage mirage)
+ {
+ _mirage = mirage;
+ }
+
+ // documentation inherited from interface
+ public void paintIcon (Component c, Graphics g, int x, int y)
+ {
+ _mirage.paint((Graphics2D)g, x, y);
+ }
+
+ // documentation inherited from interface
+ public int getIconWidth()
+ {
+ return _mirage.getWidth();
+ }
+
+ // documentation inherited from interface
+ public int getIconHeight()
+ {
+ return _mirage.getHeight();
+ }
+
+ protected Mirage _mirage;
+}
diff --git a/src/java/com/threerings/media/image/VolatileMirage.java b/src/java/com/threerings/media/image/VolatileMirage.java
new file mode 100644
index 000000000..5a92f6564
--- /dev/null
+++ b/src/java/com/threerings/media/image/VolatileMirage.java
@@ -0,0 +1,144 @@
+//
+// $Id: VolatileMirage.java,v 1.1 2003/01/13 22:49:46 mdb Exp $
+
+package com.threerings.media.image;
+
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.Rectangle;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.awt.image.VolatileImage;
+
+import com.samskivert.util.StringUtil;
+
+/**
+ * A mirage implementation which allows the image to be maintained in
+ * video memory and rebuilt from some source image or images in the event
+ * that our target screen resolution changes or we are flushed from video
+ * memory for some other reason.
+ */
+public abstract class VolatileMirage implements Mirage
+{
+ /**
+ * Informs the base class of its image manager and image bounds.
+ */
+ protected VolatileMirage (ImageManager imgr, Rectangle bounds)
+ {
+ _imgr = imgr;
+ _bounds = bounds;
+ }
+
+ // documentation inherited from interface
+ public void paint (Graphics2D gfx, int x, int y)
+ {
+ // create our volatile image for the first time if necessary
+ if (_image == null) {
+ createVolatileImage();
+ }
+
+// int renders = 0;
+// do {
+// // validate that our image is compatible with the target GC
+// switch (_image.validate(_imgr.getGraphicsConfiguration())) {
+// case VolatileImage.IMAGE_RESTORED:
+// refreshVolatileImage(); // need to rerender it
+// break;
+// case VolatileImage.IMAGE_INCOMPATIBLE:
+// createVolatileImage(); // need to recreate it
+// break;
+// }
+
+ // now we can render it
+ gfx.drawImage(_image, x, y, null);
+// renders++;
+
+// // don't try forever
+// } while (_image.contentsLost() && (renders < 10));
+
+ // TODO: note number of attempted renders for performance
+ }
+
+ // documentation inherited from interface
+ public int getWidth ()
+ {
+ return _bounds.width;
+ }
+
+ // documentation inherited from interface
+ public int getHeight ()
+ {
+ return _bounds.height;
+ }
+
+ // documentation inherited from interface
+ public boolean hitTest (int x, int y)
+ {
+// return ImageUtil.hitTest(_image.getSnapshot(), x, y);
+ return ImageUtil.hitTest(_image, x, y);
+ }
+
+ // documentation inherited from interface
+ public BufferedImage getSnapshot ()
+ {
+// return _image.getSnapshot();
+ return _image;
+ }
+
+ /**
+ * Creates our volatile image from the information in our source
+ * image.
+ */
+ protected void createVolatileImage ()
+ {
+ // release any previous volatile image we might hold
+ if (_image != null) {
+ _image.flush();
+ }
+
+ // create a new, compatible, volatile image
+ GraphicsConfiguration gc = _imgr.getGraphicsConfiguration();
+// _image = gc.createCompatibleVolatileImage(
+// _bounds.width, _bounds.height);
+ _image = gc.createCompatibleImage(
+ _bounds.width, _bounds.height, Transparency.BITMASK);
+
+ // render our source image into the volatile image
+ refreshVolatileImage();
+ }
+
+ /**
+ * Rerenders our volatile image from the its source image data.
+ */
+ protected abstract void refreshVolatileImage ();
+
+ /**
+ * Generates a string representation of this instance.
+ */
+ public String toString ()
+ {
+ StringBuffer buf = new StringBuffer("[");
+ toString(buf);
+ return buf.append("]").toString();
+ }
+
+ /**
+ * Generates a string representation of this instance.
+ */
+ protected void toString (StringBuffer buf)
+ {
+ buf.append("bounds=").append(StringUtil.toString(_bounds));
+ }
+
+ /** The image manager with whom we interoperate. */
+ protected ImageManager _imgr;
+
+ /** The bounds of the region of our source image which we desire for
+ * this mirage (possibly the whole thing). */
+ protected Rectangle _bounds;
+
+ /** Our volatile image which lives in video memory and can go away at
+ * any time. */
+// protected VolatileImage _image;
+ protected BufferedImage _image;
+}
diff --git a/src/java/com/threerings/media/sprite/ImageSprite.java b/src/java/com/threerings/media/sprite/ImageSprite.java
index c7b45d64f..41b16820a 100644
--- a/src/java/com/threerings/media/sprite/ImageSprite.java
+++ b/src/java/com/threerings/media/sprite/ImageSprite.java
@@ -1,5 +1,5 @@
//
-// $Id: ImageSprite.java,v 1.16 2002/11/20 05:33:20 mdb Exp $
+// $Id: ImageSprite.java,v 1.17 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.sprite;
@@ -8,7 +8,6 @@ import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
-import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
@@ -16,6 +15,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import com.threerings.media.Log;
+import com.threerings.media.image.Mirage;
import com.threerings.media.util.MultiFrameImage;
import com.threerings.media.util.SingleFrameImageImpl;
@@ -75,7 +75,7 @@ public class ImageSprite extends Sprite
* Constructs an image sprite that will display the supplied single
* image when rendering itself.
*/
- public ImageSprite (Image image)
+ public ImageSprite (Mirage image)
{
this(new SingleFrameImageImpl(image));
}
@@ -85,8 +85,9 @@ public class ImageSprite extends Sprite
{
super.init();
- // now that we have our spritemanager, we can initialize our frames
- setFrameIndex(0, true);
+ // now that we have our sprite manager, we can lay ourselves out
+ // and initialize our frames
+ layout();
}
/**
@@ -157,7 +158,19 @@ public class ImageSprite extends Sprite
// set and init our frames
_frames = frames;
- setFrameIndex(0, true);
+ _frameIdx = 0;
+ layout();
+ }
+
+ /**
+ * Instructs this sprite to lay out its current frame and any
+ * accoutrements.
+ */
+ public void layout ()
+ {
+ if (_frames != null) {
+ setFrameIndex(_frameIdx, true);
+ }
}
/**
diff --git a/src/java/com/threerings/media/tile/IMImageProvider.java b/src/java/com/threerings/media/tile/IMImageProvider.java
new file mode 100644
index 000000000..e785a8eeb
--- /dev/null
+++ b/src/java/com/threerings/media/tile/IMImageProvider.java
@@ -0,0 +1,55 @@
+//
+// $Id: IMImageProvider.java,v 1.1 2003/01/13 22:49:46 mdb Exp $
+
+package com.threerings.media.tile;
+
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import com.threerings.media.image.Colorization;
+import com.threerings.media.image.ImageDataProvider;
+import com.threerings.media.image.ImageManager;
+import com.threerings.media.image.Mirage;
+
+/**
+ * Provides images to a tileset given a reference to the image manager and
+ * an image data provider.
+ */
+public class IMImageProvider implements ImageProvider
+{
+ public IMImageProvider (ImageManager imgr, ImageDataProvider dprov)
+ {
+ _imgr = imgr;
+ _dprov = dprov;
+ }
+
+ public IMImageProvider (ImageManager imgr, String rset)
+ {
+ _imgr = imgr;
+ _rset = rset;
+ }
+
+ // documentation inherited from interface
+ public BufferedImage getTileSetImage (String path, Colorization[] zations)
+ {
+ return _imgr.getImage(getImageKey(path), zations);
+ }
+
+ // documentation inherited from interface
+ public Mirage getTileImage (String path, Rectangle bounds,
+ Colorization[] zations)
+ {
+ return _imgr.getMirage(getImageKey(path), bounds, zations);
+ }
+
+ protected final ImageManager.ImageKey getImageKey (String path)
+ {
+ return (_dprov == null) ?
+ _imgr.getImageKey(_rset, path) :
+ _imgr.getImageKey(_dprov, path);
+ }
+
+ protected ImageManager _imgr;
+ protected ImageDataProvider _dprov;
+ protected String _rset;
+}
diff --git a/src/java/com/threerings/media/tile/ImageProvider.java b/src/java/com/threerings/media/tile/ImageProvider.java
index c772ef5ef..ac2b69eed 100644
--- a/src/java/com/threerings/media/tile/ImageProvider.java
+++ b/src/java/com/threerings/media/tile/ImageProvider.java
@@ -1,26 +1,45 @@
//
-// $Id: ImageProvider.java,v 1.2 2001/12/07 01:33:29 mdb Exp $
+// $Id: ImageProvider.java,v 1.3 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.tile;
-import java.awt.Image;
-import java.io.IOException;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import com.threerings.media.image.Colorization;
+import com.threerings.media.image.Mirage;
/**
- * A tileset needs to load its images from some location. That will
- * generally either be via the {@link TileManager} that constructed it or
- * the {@link TileSetRepository} that constructed it. The tile manager
- * loads images via the resource manager, whereas the tileset repository
- * will likely obtain images via its own resource bundles.
+ * Provides a generic interface via which tileset images may be loaded. In
+ * most cases, a running application will want to obtain images via the
+ * {@link ImageManager}, but in some circumstances a simpler image
+ * provider may be desirable to avoid the overhead of the image manager
+ * infrastructure when simple image loading is all that is desired.
*/
public interface ImageProvider
{
/**
- * Loads the image with the specified path.
+ * Returns the raw tileset image with the specified path.
*
- * @exception IOException thrown if an error occurs loading the image
- * data.
+ * @param path the path that identifies the desired image (corresponds
+ * to the image path from the tileset).
+ * @param zations if non-null, colorizations to apply to the source
+ * image before returning it.
*/
- public Image loadImage (String path)
- throws IOException;
+ public BufferedImage getTileSetImage (String path, Colorization[] zations);
+
+ /**
+ * Obtains the tile image with the specified path in the form of a
+ * {@link Mirage}. It should be cropped from the tileset image
+ * identified by the supplied path.
+ *
+ * @param path the path that identifies the desired image (corresponds
+ * to the image path from the tileset).
+ * @param bounds if non-null, the region of the image to be returned
+ * as a mirage. If null, the entire image should be returned.
+ * @param zations if non-null, colorizations to apply to the image
+ * before converting it into a mirage.
+ */
+ public Mirage getTileImage (String path, Rectangle bounds,
+ Colorization[] zations);
}
diff --git a/src/java/com/threerings/media/tile/NoSuchTileException.java b/src/java/com/threerings/media/tile/NoSuchTileException.java
index 36af390ec..7e00f7174 100644
--- a/src/java/com/threerings/media/tile/NoSuchTileException.java
+++ b/src/java/com/threerings/media/tile/NoSuchTileException.java
@@ -1,5 +1,5 @@
//
-// $Id: NoSuchTileException.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
+// $Id: NoSuchTileException.java,v 1.2 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.tile;
@@ -7,7 +7,7 @@ package com.threerings.media.tile;
* Thrown when an attempt is made to retrieve a non-existent tile from the
* tile manager.
*/
-public class NoSuchTileException extends TileException
+public class NoSuchTileException extends RuntimeException
{
public NoSuchTileException (int tid)
{
diff --git a/src/java/com/threerings/media/tile/NoSuchTileSetException.java b/src/java/com/threerings/media/tile/NoSuchTileSetException.java
index 3effeb536..222583501 100644
--- a/src/java/com/threerings/media/tile/NoSuchTileSetException.java
+++ b/src/java/com/threerings/media/tile/NoSuchTileSetException.java
@@ -1,24 +1,21 @@
//
-// $Id: NoSuchTileSetException.java,v 1.1 2001/10/11 00:41:26 shaper Exp $
+// $Id: NoSuchTileSetException.java,v 1.2 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.tile;
/**
- * Thrown when an attempt is made to retrieve a non-existent tile set
- * from the tile set manager.
+ * Thrown when an attempt is made to retrieve a non-existent tile set from
+ * the tile set manager.
*/
-public class NoSuchTileSetException extends TileException
+public class NoSuchTileSetException extends Exception
{
- public NoSuchTileSetException (int tsid)
+ public NoSuchTileSetException (String tileSetName)
{
- super("No such tile set [tsid=" + tsid + "]");
- _tsid = tsid;
+ super("No tile set named '" + tileSetName + "'");
}
- public int getTileSetId ()
+ public NoSuchTileSetException (int tileSetId)
{
- return _tsid;
+ super("No tile set with id '" + tileSetId + "'");
}
-
- protected int _tsid;
}
diff --git a/src/java/com/threerings/media/tile/ObjectTile.java b/src/java/com/threerings/media/tile/ObjectTile.java
index 057c5027c..f9b8a5fcc 100644
--- a/src/java/com/threerings/media/tile/ObjectTile.java
+++ b/src/java/com/threerings/media/tile/ObjectTile.java
@@ -1,15 +1,24 @@
//
-// $Id: ObjectTile.java,v 1.12 2003/01/08 04:09:02 mdb Exp $
+// $Id: ObjectTile.java,v 1.13 2003/01/13 22:49:46 mdb Exp $
package com.threerings.media.tile;
-import java.awt.Image;
-import java.awt.Rectangle;
+import com.threerings.media.image.Mirage;
/**
* An object tile extends the base tile to provide support for objects
- * whose image spans more than one unit tile. An object tile has
- * dimensions (in tile units) that represent its footprint or "shadow".
+ * whose image spans more than one unit tile.
+ *
+ *
An object tile is generally positioned based on its origin rather + * than the upper left of its image. Generally this origin is in the + * bottom center of the object image, but can be configured to be anywhere + * that the natural center point of "contact" is for the object. Note that + * this does not automatically adjust the semantics of {@link #paint}, it + * is just expected that the caller will account for the object tile's + * origin when painting, if appropriate. + * + *
An object tile has dimensions (in tile units) that represent its + * footprint or "shadow". */ public class ObjectTile extends Tile { @@ -17,21 +26,9 @@ public class ObjectTile extends Tile * Constructs a new object tile with the specified image. The base * width and height should be set before using this tile. */ - public ObjectTile (Image image, Rectangle bounds) + public ObjectTile (Mirage image) { - super(image, bounds); - } - - /** - * Constructs a new object tile with the specified base width and - * height. - */ - public ObjectTile (Image image, Rectangle bounds, - int baseWidth, int baseHeight) - { - super(image, bounds); - _baseWidth = baseWidth; - _baseHeight = baseHeight; + super(image); } /** @@ -53,7 +50,7 @@ public class ObjectTile extends Tile /** * Sets the object footprint in tile units. */ - public void setBase (int width, int height) + protected void setBase (int width, int height) { _baseWidth = width; _baseHeight = height; @@ -91,7 +88,7 @@ public class ObjectTile extends Tile * the origin tile and the left side of the image is aligned with the * left edge of the left-most base tile. */ - public void setOrigin (int x, int y) + protected void setOrigin (int x, int y) { _originX = x; _originY = y; diff --git a/src/java/com/threerings/media/tile/ObjectTileSet.java b/src/java/com/threerings/media/tile/ObjectTileSet.java index 281ae54c5..d04bdee45 100644 --- a/src/java/com/threerings/media/tile/ObjectTileSet.java +++ b/src/java/com/threerings/media/tile/ObjectTileSet.java @@ -1,14 +1,11 @@ // -// $Id: ObjectTileSet.java,v 1.8 2003/01/08 04:09:02 mdb Exp $ +// $Id: ObjectTileSet.java,v 1.9 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Image; -import java.awt.Rectangle; -import java.util.Arrays; - import com.samskivert.util.StringUtil; -import com.threerings.media.image.ImageManager; + +import com.threerings.media.image.Mirage; /** * The object tileset supports the specification of object information for @@ -69,24 +66,15 @@ public class ObjectTileSet extends SwissArmyTileSet * Creates instances of {@link ObjectTile}, which can span more than a * single tile's space in a display. */ - protected Tile createTile (int tileIndex, Image tilesetImage) + protected Tile createTile (int tileIndex, Mirage image) { - // default object dimensions to (1, 1) - int wid = 1, hei = 1; - - // retrieve object dimensions if known + ObjectTile tile = new ObjectTile(image); if (_owidths != null) { - wid = _owidths[tileIndex]; - hei = _oheights[tileIndex]; + tile.setBase(_owidths[tileIndex], _oheights[tileIndex]); } - - Rectangle bounds = computeTileBounds(tileIndex, tilesetImage); - ObjectTile tile = new ObjectTile(tilesetImage, bounds, wid, hei); - if (_xorigins != null) { tile.setOrigin(_xorigins[tileIndex], _yorigins[tileIndex]); } - return tile; } diff --git a/src/java/com/threerings/media/tile/SimpleCachingImageProvider.java b/src/java/com/threerings/media/tile/SimpleCachingImageProvider.java new file mode 100644 index 000000000..a8e0236fd --- /dev/null +++ b/src/java/com/threerings/media/tile/SimpleCachingImageProvider.java @@ -0,0 +1,56 @@ +// +// $Id: SimpleCachingImageProvider.java,v 1.1 2003/01/13 22:49:46 mdb Exp $ + +package com.threerings.media.tile; + +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.IOException; + +import com.samskivert.util.LRUHashMap; + +import com.threerings.media.Log; +import com.threerings.media.image.Colorization; +import com.threerings.media.image.Mirage; + +/** + * An image provider that can be used by command line tools to load images + * and provide them to tilesets when doing things like preprocessing + * tileset images. + */ +public abstract class SimpleCachingImageProvider implements ImageProvider +{ + // documentation inherited from interface + public BufferedImage getTileSetImage (String path, Colorization[] zations) + { + BufferedImage image = (BufferedImage)_cache.get(path); + if (image == null) { + try { + image = loadImage(path); + _cache.put(path, image); + } catch (IOException ioe) { + Log.warning("Failed to load image [path=" + path + + ", ioe=" + ioe + "]."); + } + } + return image; + } + + // documentation inherited from interface + public Mirage getTileImage (String path, Rectangle bounds, + Colorization[] zations) + { + // since we're just doing batch processing, we don't prepare + // images for the screen + throw new UnsupportedOperationException(); + } + + /** + * Derived classes must implement this method to actually load the raw + * source images. + */ + protected abstract BufferedImage loadImage (String path) + throws IOException; + + protected LRUHashMap _cache = new LRUHashMap(10); +} diff --git a/src/java/com/threerings/media/tile/SwissArmyTileSet.java b/src/java/com/threerings/media/tile/SwissArmyTileSet.java index 5e93bda73..005bcdd41 100644 --- a/src/java/com/threerings/media/tile/SwissArmyTileSet.java +++ b/src/java/com/threerings/media/tile/SwissArmyTileSet.java @@ -1,10 +1,9 @@ // -// $Id: SwissArmyTileSet.java,v 1.8 2002/05/06 18:08:32 mdb Exp $ +// $Id: SwissArmyTileSet.java,v 1.9 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; import java.awt.Dimension; -import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; @@ -128,7 +127,7 @@ public class SwissArmyTileSet extends TileSet } // documentation inherited - protected Rectangle computeTileBounds (int tileIndex, Image tilesetImage) + protected Rectangle computeTileBounds (int tileIndex) { // find the row number containing the sought-after tile int ridx, tcount, ty, tx; diff --git a/src/java/com/threerings/media/tile/Tile.java b/src/java/com/threerings/media/tile/Tile.java index 12a195c09..37b581c76 100644 --- a/src/java/com/threerings/media/tile/Tile.java +++ b/src/java/com/threerings/media/tile/Tile.java @@ -1,18 +1,11 @@ // -// $Id: Tile.java,v 1.25 2003/01/08 04:09:02 mdb Exp $ +// $Id: Tile.java,v 1.26 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.image.BufferedImage; +import java.awt.Graphics2D; -import com.samskivert.util.StringUtil; - -import com.threerings.media.Log; -import com.threerings.media.image.ImageUtil; +import com.threerings.media.image.Mirage; /** * A tile represents a single square in a single layer in a scene. @@ -20,40 +13,14 @@ import com.threerings.media.image.ImageUtil; public class Tile implements Cloneable { /** - * Constructs a tile with the specified tileset image. + * Constructs a tile with the specified image. * - * @param image the tileset image from which our tile image is - * extracted. - * @param bounds the bounds of the tile image within the greater - * tileset image. + * @param image our tile image in mirage form (which is optimized for + * screen rendering). */ - public Tile (Image image, Rectangle bounds) + public Tile (Mirage image) { - _image = image; - _bounds = bounds; - } - - /** - * Called to create our subimage. - */ - protected void createSubImage () - { - if (_image instanceof BufferedImage) { - try { - _subimage = ImageUtil.getSubimage( - _image, _bounds.x, _bounds.y, - _bounds.width, _bounds.height); - } catch (RuntimeException rte) { - Log.warning("Failure creating subimage [src=" + _image + - ", bounds=" + StringUtil.toString(_bounds) + - ", error=" + rte + "]."); - throw rte; - } - - } else { - String errmsg = "Can't obtain tile image [tile=" + this + "]."; - throw new RuntimeException(errmsg); - } + _mirage = image; } /** @@ -61,7 +28,7 @@ public class Tile implements Cloneable */ public int getWidth () { - return _bounds.width; + return _mirage.getWidth(); } /** @@ -69,16 +36,16 @@ public class Tile implements Cloneable */ public int getHeight () { - return _bounds.height; + return _mirage.getHeight(); } /** * Render the tile image at the specified position in the given * graphics context. */ - public void paint (Graphics gfx, int x, int y) + public void paint (Graphics2D gfx, int x, int y) { - gfx.drawImage(getImage(), x, y, null); + _mirage.paint(gfx, x, y); } /** @@ -87,22 +54,7 @@ public class Tile implements Cloneable */ public boolean hitTest (int x, int y) { - return ImageUtil.hitTest(_image, _bounds.x + x, _bounds.y + y); - } - - /** - * Returns the image used to render this tile, if access to that image - * can be obtained. The tile must have been created with a {@link - * BufferedImage} and derived classes may refuse to implement this - * function if they do special stuff that hampers their ability to - * return a single image describing the tile. - */ - public Image getImage () - { - if (_subimage == null) { - createSubImage(); - } - return _subimage; + return _mirage.hitTest(x, y); } /** @@ -135,18 +87,10 @@ public class Tile implements Cloneable */ protected void toString (StringBuffer buf) { - buf.append("srcimg=").append(_image.getClass().getName()); - buf.append("[").append(_image.getWidth(null)).append("x"); - buf.append(_image.getHeight(null)).append("]"); - buf.append(", bounds=").append(StringUtil.toString(_bounds)); + buf.append(_mirage.getWidth()).append("x"); + buf.append(_mirage.getHeight()); } /** Our tileset image. */ - protected Image _image; - - /** Our cropped tileset image. */ - protected Image _subimage; - - /** The bounds of the tile image within the tileset image. */ - protected Rectangle _bounds; + protected Mirage _mirage; } diff --git a/src/java/com/threerings/media/tile/TileException.java b/src/java/com/threerings/media/tile/TileException.java deleted file mode 100644 index e6fef0adc..000000000 --- a/src/java/com/threerings/media/tile/TileException.java +++ /dev/null @@ -1,15 +0,0 @@ -// -// $Id: TileException.java,v 1.1 2001/10/11 00:41:26 shaper Exp $ - -package com.threerings.media.tile; - -/** - * Thrown when an error occurs while working with tiles. - */ -public class TileException extends Exception -{ - public TileException (String message) - { - super(message); - } -} diff --git a/src/java/com/threerings/media/tile/TileIcon.java b/src/java/com/threerings/media/tile/TileIcon.java index 1f018bdc6..a56df2cd9 100644 --- a/src/java/com/threerings/media/tile/TileIcon.java +++ b/src/java/com/threerings/media/tile/TileIcon.java @@ -1,9 +1,10 @@ // -// $Id: TileIcon.java,v 1.1 2002/05/06 18:08:32 mdb Exp $ +// $Id: TileIcon.java,v 1.2 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; import java.awt.Component; +import java.awt.Graphics2D; import java.awt.Graphics; import javax.swing.Icon; @@ -25,7 +26,7 @@ public class TileIcon implements Icon // documentation inherited from interface public void paintIcon (Component c, Graphics g, int x, int y) { - _tile.paint(g, x, y); + _tile.paint((Graphics2D)g, x, y); } // documentation inherited from interface diff --git a/src/java/com/threerings/media/tile/TileManager.java b/src/java/com/threerings/media/tile/TileManager.java index 2c5e34a7c..4528baffe 100644 --- a/src/java/com/threerings/media/tile/TileManager.java +++ b/src/java/com/threerings/media/tile/TileManager.java @@ -1,16 +1,24 @@ // -// $Id: TileManager.java,v 1.26 2003/01/08 04:09:02 mdb Exp $ +// $Id: TileManager.java,v 1.27 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Image; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; import com.samskivert.io.PersistenceException; import com.samskivert.util.HashIntMap; +import com.samskivert.util.IntTuple; import com.threerings.media.Log; +import com.threerings.media.image.Colorization; +import com.threerings.media.image.ImageDataProvider; import com.threerings.media.image.ImageManager; +import com.threerings.media.image.Mirage; +import com.samskivert.util.LRUHashMap; /** * The tile manager provides a simplified interface for retrieving and @@ -27,17 +35,8 @@ import com.threerings.media.image.ImageManager; * Loading tilesets from a repository supports games with vast numbers of * tiles to which more tiles may be added on the fly (think the tiles for * an isometric-display graphical MUD). - * - *
When the tile manager is used to load tiles via the tileset - * repository, it caches the resulting tile instance so that they can be - * fetched again without rebuilding the tile image. Tilesets that are - * fetched by hand are not cached and it is assumed that the requesting - * application will cache the tile objects itself (probably by retaining - * references directly to the tile instances in which it is interested). - * The tile creation process is not hugely expensive, but does involve - * extracting the tile image from the larger tileset image. */ -public class TileManager implements ImageProvider +public class TileManager { /** * Creates a tile manager and provides it with a reference to the @@ -48,8 +47,8 @@ public class TileManager implements ImageProvider */ public TileManager (ImageManager imgr) { - // keep this guy around for later _imgr = imgr; + _defaultProvider = new IMImageProvider(_imgr, (String)null); } /** @@ -59,13 +58,7 @@ public class TileManager implements ImageProvider public UniformTileSet loadTileSet ( String imgPath, int count, int width, int height) { - UniformTileSet uts = new UniformTileSet(); - uts.setImageProvider(this); - uts.setImagePath(imgPath); - uts.setTileCount(count); - uts.setWidth(width); - uts.setHeight(height); - return uts; + return loadCachedTileSet("", imgPath, count, width, height); } /** @@ -73,15 +66,40 @@ public class TileManager implements ImageProvider * specified resource set) with the specified metadata parameters. */ public UniformTileSet loadTileSet ( - final String rset, String imgPath, int count, int width, int height) + String rset, String imgPath, int count, int width, int height) { - UniformTileSet uts = loadTileSet(imgPath, count, width, height); - // load up the image data from the image manager - uts.setImageProvider(new ImageProvider() { - public Image loadImage (String path) throws IOException { - return _imgr.getImage(rset, path); - } - }); + UniformTileSet uts = loadCachedTileSet( + rset, imgPath, count, width, height); + uts.setImageProvider(getImageProvider(rset)); + return uts; + } + + /** + * Returns an image provider that will load images from the specified + * resource set. + */ + public ImageProvider getImageProvider (String rset) + { + return new IMImageProvider(_imgr, rset); + } + + /** + * Used to load and cache tilesets loaded via {@link #loadTileSet}. + */ + protected UniformTileSet loadCachedTileSet ( + String bundle, String imgPath, int count, int width, int height) + { + String key = bundle + "::" + imgPath; + UniformTileSet uts = (UniformTileSet)_handcache.get(key); + if (uts == null) { + uts = new UniformTileSet(); + uts.setImageProvider(_defaultProvider); + uts.setImagePath(imgPath); + uts.setTileCount(count); + uts.setWidth(width); + uts.setHeight(height); + _handcache.put(key, uts); + } return uts; } @@ -122,20 +140,49 @@ public class TileManager implements ImageProvider } try { - TileSet set = (TileSet)_cache.get(tileSetId); + TileSet set = (TileSet)_setcache.get(tileSetId); if (set == null) { set = _setrep.getTileSet(tileSetId); - _cache.put(tileSetId, set); + _setcache.put(tileSetId, set); } return set; } catch (PersistenceException pe) { - Log.warning("Unable to load tileset [id=" + tileSetId + + Log.warning("Failure loading tileset [id=" + tileSetId + ", error=" + pe + "]."); throw new NoSuchTileSetException(tileSetId); } } + /** + * Returns the tileset with the specified name. + * + * @throws NoSuchTileSetException if no tileset with the specified + * name is available via our configured tile set repository. + */ + public TileSet getTileSet (String name) + throws NoSuchTileSetException + { + // make sure we have a repository configured + if (_setrep == null) { + throw new NoSuchTileSetException(name); + } + + try { + TileSet set = (TileSet)_byname.get(name); + if (set == null) { + set = _setrep.getTileSet(name); + _byname.put(name, set); + } + return set; + + } catch (PersistenceException pe) { + Log.warning("Failure loading tileset [name=" + name + + ", error=" + pe + "]."); + throw new NoSuchTileSetException(name); + } + } + /** * Returns the {@link Tile} object from the specified tileset at the * specified index. @@ -152,20 +199,21 @@ public class TileManager implements ImageProvider return set.getTile(tileIndex); } - // documentation inherited - public Image loadImage (String path) - throws IOException - { - // load up the image data from the resource manager - return _imgr.getImage(path); - } - /** The entity through which we decode and cache images. */ protected ImageManager _imgr; /** Cache of tilesets that have been requested thus far. */ - protected HashIntMap _cache = new HashIntMap(); + protected HashIntMap _setcache = new HashIntMap(); + + /** A cache of tilesets that have been loaded by hand. */ + protected HashMap _handcache = new HashMap(); + + /** A mapping from tileset name to tileset. */ + protected HashMap _byname = new HashMap(); /** The tile set repository. */ protected TileSetRepository _setrep; + + /** Used to load tileset images from the default resource source. */ + protected ImageProvider _defaultProvider; } diff --git a/src/java/com/threerings/media/tile/TileMultiFrameImage.java b/src/java/com/threerings/media/tile/TileMultiFrameImage.java index 5c59e3ebb..7a93fb439 100644 --- a/src/java/com/threerings/media/tile/TileMultiFrameImage.java +++ b/src/java/com/threerings/media/tile/TileMultiFrameImage.java @@ -1,9 +1,9 @@ // -// $Id: TileMultiFrameImage.java,v 1.1 2002/09/17 20:39:03 mdb Exp $ +// $Id: TileMultiFrameImage.java,v 1.2 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Graphics; +import java.awt.Graphics2D; import com.threerings.media.Log; import com.threerings.media.util.MultiFrameImage; @@ -32,52 +32,25 @@ public class TileMultiFrameImage implements MultiFrameImage // documentation inherited from interface public int getWidth (int index) { - try { - return _source.getTile(index).getWidth(); - } catch (NoSuchTileException nste) { - Log.warning("Eh? Tile set reported 'no such tile' " + - "[tcount=" + _source.getTileCount() + - ", tindex=" + index + "]."); - return -1; - } + return _source.getTile(index).getWidth(); } // documentation inherited from interface public int getHeight (int index) { - try { - return _source.getTile(index).getHeight(); - } catch (NoSuchTileException nste) { - Log.warning("Eh? Tile set reported 'no such tile' " + - "[tcount=" + _source.getTileCount() + - ", tindex=" + index + "]."); - return -1; - } + return _source.getTile(index).getHeight(); } // documentation inherited from interface - public void paintFrame (Graphics g, int index, int x, int y) + public void paintFrame (Graphics2D g, int index, int x, int y) { - try { - _source.getTile(index).paint(g, x, y); - } catch (NoSuchTileException nste) { - Log.warning("Eh? Tile set reported 'no such tile' " + - "[tcount=" + _source.getTileCount() + - ", tindex=" + index + "]."); - } + _source.getTile(index).paint(g, x, y); } // documentation inherited from interface public boolean hitTest (int index, int x, int y) { - try { - return _source.getTile(index).hitTest(x, y); - } catch (NoSuchTileException nste) { - Log.warning("Eh? Tile set reported 'no such tile' " + - "[tcount=" + _source.getTileCount() + - ", tindex=" + index + "]."); - return false; - } + return _source.getTile(index).hitTest(x, y); } protected TileSet _source; diff --git a/src/java/com/threerings/media/tile/TileSet.java b/src/java/com/threerings/media/tile/TileSet.java index 0b187fef3..86aa9179a 100644 --- a/src/java/com/threerings/media/tile/TileSet.java +++ b/src/java/com/threerings/media/tile/TileSet.java @@ -1,22 +1,21 @@ // -// $Id: TileSet.java,v 1.35 2003/01/08 04:09:02 mdb Exp $ +// $Id: TileSet.java,v 1.36 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; import java.awt.Color; -import java.awt.Image; import java.awt.Rectangle; -import java.awt.Transparency; import java.awt.image.BufferedImage; - -import java.io.IOException; import java.io.Serializable; +import com.samskivert.util.ConfigUtil; +import com.samskivert.util.LRUHashMap; import com.samskivert.util.StringUtil; +import com.samskivert.util.Tuple; import com.threerings.media.Log; import com.threerings.media.image.Colorization; -import com.threerings.media.image.ImageUtil; +import com.threerings.media.image.Mirage; /** * A tileset stores information on a single logical set of tiles. It @@ -70,10 +69,6 @@ public abstract class TileSet public void setImagePath (String imagePath) { _imagePath = imagePath; - - // clear out any reference to a loaded image and cached tiles - _tilesetImg = null; - _tiles = null; } /** @@ -90,14 +85,16 @@ public abstract class TileSet public abstract int getTileCount (); /** - * Creates a copy of this tileset with the supplied colorizations - * applied to its source image. + * Creates a copy of this tileset which will apply the supplied + * colorizations to its tileset image when creating tiles. */ - public TileSet cloneColorized (Colorization[] zations) + public TileSet clone (Colorization[] zations) { - TileSet tset = null; try { - tset = (TileSet)clone(); + TileSet tset = (TileSet)clone(); + tset._zations = zations; + return tset; + } catch (CloneNotSupportedException cnse) { Log.warning("Unable to clone tileset prior to colorization " + "[tset=" + this + @@ -105,37 +102,6 @@ public abstract class TileSet ", error=" + cnse + "]."); return null; } - - // make sure the tileset was able to load its image - Image timg = tset.getTileSetImage(); - if (timg == null) { - Log.warning("Failed to load tileset image in preparation " + - "for colorization [tset=" + tset + "]."); - // return the uncolorized tileset since it has no freaking - // source image anyway - return tset; - - } else if (!(timg instanceof BufferedImage)) { - Log.warning("Can't recolor tileset with non-buffered " + - "image source [source=" + timg + "]."); - return tset; - } - - // create the recolored image and update the tileset - tset._tilesetImg = - ImageUtil.recolorImage((BufferedImage)timg, zations); - - return tset; - } - - // documentation inherited - public Object clone () - throws CloneNotSupportedException - { - TileSet dup = (TileSet)super.clone(); - // clear out the tileset cache - dup._tiles = null; - return dup; } /** @@ -170,35 +136,81 @@ public abstract class TileSet public Tile getTile (int tileIndex) throws NoSuchTileException { - int tcount = getTileCount(); + checkTileIndex(tileIndex); + // create our tile cache if necessary + if (_tiles == null) { + int tcsize = ConfigUtil.getSystemProperty( + "narya.media.tile.cache_size", DEFAULT_TILE_CACHE_SIZE); + Log.debug("Creating tile cache [size=" + tcsize + "]."); + _tiles = new LRUHashMap(tcsize); + } + + // fetch and cache the tile + Tuple key = new Tuple(this, new Integer(tileIndex)); + Tile tile = (Tile)_tiles.get(key); + if (tile == null) { + // create and initialize the tile object + tile = createTile(tileIndex, getTileMirage(tileIndex)); + initTile(tile); + _tiles.put(key, tile); + } + + return tile; + } + + /** + * Returns the entire, raw, uncut, unprepared tileset source image. + */ + public BufferedImage getTileSetImage () + { + return _improv.getTileSetImage(_imagePath, _zations); + } + + /** + * Returns the raw (unprepared) image that would be used by the tile + * at the specified index. + */ + public BufferedImage getTileImage (int tileIndex) + throws NoSuchTileException + { + checkTileIndex(tileIndex); + Rectangle bounds = computeTileBounds(tileIndex); + BufferedImage timg = getTileSetImage(); + return timg.getSubimage(bounds.x, bounds.y, + bounds.width, bounds.height); + } + + /** + * Returns a prepared version of the image that would be used by the + * tile at the specified index. Because tilesets are often used simply + * to provide access to a collection of uniform images, this method is + * provided to bypass the creation of a {@link Tile} object when all + * that is desired is access to the underlying image. + */ + public Mirage getTileMirage (int tileIndex) + throws NoSuchTileException + { + checkTileIndex(tileIndex); + Rectangle bounds = computeTileBounds(tileIndex); + if (_improv == null) { + Log.warning("Aiya! Tile set missing image provider " + + "[path=" + _imagePath + "]."); + } + return _improv.getTileImage(_imagePath, bounds, _zations); + } + + /** + * Used to ensure that the specified tile index is valid. + */ + protected void checkTileIndex (int tileIndex) + throws NoSuchTileException + { // bail if there's no such tile + int tcount = getTileCount(); if (tileIndex < 0 || tileIndex >= tcount) { throw new NoSuchTileException(tileIndex); } - - // create our tile cache array if necessary - if (_tiles == null) { - _tiles = new Tile[tcount]; - } - - // fill in the cache if necessary - if (_tiles[tileIndex] == null) { - // get our tileset image - Image tsimg = getTileSetImage(); - if (tsimg == null) { - // we already logged an error, so we can just freak out - throw new NoSuchTileException(tileIndex); - } - - // create, initialize and cache the tile object - Tile tile = createTile(tileIndex, tsimg); - initTile(tile); -// _tiles[tileIndex] = tile; - return tile; - } - - return _tiles[tileIndex]; } /** @@ -210,25 +222,20 @@ public abstract class TileSet * * @param tileIndex the index of the tile whose bounds are to be * computed. - * @param tilesetImage the tileset image that contains the imagery for - * the tile in question. */ - protected abstract Rectangle computeTileBounds ( - int tileIndex, Image tilesetImage); + protected abstract Rectangle computeTileBounds (int tileIndex); /** * Creates a tile for the specified tile index. * * @param tileIndex the index of the tile to be created. - * @param tilesetImage the tileset image that contains the imagery for - * the tile to be created. + * @param image the tile image in the form of a {@link Mirage}. * * @return a configured tile. */ - protected Tile createTile (int tileIndex, Image tilesetImage) + protected Tile createTile (int tileIndex, Mirage image) { - return new Tile(tilesetImage, - computeTileBounds(tileIndex, tilesetImage)); + return new Tile(image); } /** @@ -243,42 +250,6 @@ public abstract class TileSet // nothing for now } - /** - * Returns the tileset image (which is loaded if it has not yet been - * loaded). Generally this is not called by external entities, rather - * {@link #getTile} is used and the image rendered by rendering the - * tile. - * - * @return the tileset image or null if an error occurred loading the - * image. - */ - public Image getTileSetImage () - { - // return it straight away if it's already loaded - if (_tilesetImg != null) { - return _tilesetImg; - } - - // load up the tileset image via the image provider - try { - _tilesetImg = _improv.loadImage(_imagePath); - - // HACKOLA: if we're a vessel tileset, colorize ourselves - if (_name != null && (this instanceof ObjectTileSet) && - _name.indexOf("Vessel") != -1) { - _tilesetImg = ImageUtil.recolorImage( - (BufferedImage)_tilesetImg, ZATIONS); - } - - } catch (IOException ioe) { - Log.warning("Failed to retrieve tileset image " + - "[name=" + _name + ", path=" + _imagePath + - ", error=" + ioe + "]."); - } - - return _tilesetImg; - } - /** * Generates a string representation of the tileset information. */ @@ -307,18 +278,12 @@ public abstract class TileSet /** The tileset name. */ protected String _name; - /** A sparse array containing the tiles that have been requested from - * this tileset. */ - protected transient Tile[] _tiles; + /** Colorizations to be applied to tiles created from this tileset. */ + protected transient Colorization[] _zations; /** The entity from which we obtain our tile image. */ protected transient ImageProvider _improv; - /** The image containing all tile images for this set. This is private - * because it should be accessed via {@link #getTileSetImage} even by - * derived classes. */ - private transient Image _tilesetImg; - /** Increase this value when object's serialized state is impacted by * a class change (modification of fields, inheritance). */ private static final long serialVersionUID = 1; @@ -332,4 +297,10 @@ public abstract class TileSet new float[] { .1f, .3f, 0.6f }, new float[] { .71f, 0f, .07f }), }; + + /** A weak cache of our tiles. */ + protected static LRUHashMap _tiles; + + /** The default tile cache size. */ + protected static final int DEFAULT_TILE_CACHE_SIZE = 500; } diff --git a/src/java/com/threerings/media/tile/TileSetRepository.java b/src/java/com/threerings/media/tile/TileSetRepository.java index 003d227e0..b49250d50 100644 --- a/src/java/com/threerings/media/tile/TileSetRepository.java +++ b/src/java/com/threerings/media/tile/TileSetRepository.java @@ -1,11 +1,13 @@ // -// $Id: TileSetRepository.java,v 1.3 2001/11/29 00:12:11 mdb Exp $ +// $Id: TileSetRepository.java,v 1.4 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; import java.util.Iterator; import com.samskivert.io.PersistenceException; +import com.threerings.media.image.ImageDataProvider; + /** * The tileset repository interface should be implemented by classes that * provide access to tilesets keyed on a unique tileset identifier. The @@ -28,7 +30,9 @@ public interface TileSetRepository throws PersistenceException; /** - * Returns the {@link TileSet} with the specified tile set identifier. + * Returns the {@link TileSet} with the specified tile set + * identifier. The repository is responsible for configuring the tile + * set with an image provider. * * @exception NoSuchTileSetException thrown if no tileset exists with * the specified identifier. @@ -37,4 +41,17 @@ public interface TileSetRepository */ public TileSet getTileSet (int tileSetId) throws NoSuchTileSetException, PersistenceException; + + /** + * Returns the {@link TileSet} with the specified tile set name. The + * repository is responsible for configuring the tile set with an + * image provider. + * + * @exception NoSuchTileSetException thrown if no tileset exists with + * the specified name. + * @exception PersistenceException thrown if an error occurs + * communicating with the underlying persistence mechanism. + */ + public TileSet getTileSet (String setName) + throws NoSuchTileSetException, PersistenceException; } diff --git a/src/java/com/threerings/media/tile/TileUtil.java b/src/java/com/threerings/media/tile/TileUtil.java index e00111f3d..570f98eaf 100644 --- a/src/java/com/threerings/media/tile/TileUtil.java +++ b/src/java/com/threerings/media/tile/TileUtil.java @@ -1,40 +1,13 @@ // -// $Id: TileUtil.java,v 1.3 2002/05/06 18:08:32 mdb Exp $ +// $Id: TileUtil.java,v 1.4 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import com.threerings.media.Log; - /** * Miscellaneous utility routines for working with tiles. */ public class TileUtil { - /** - * Returns the image associated with the given tile from the given - * tile manager, or null if an error occurred. Any exceptions that - * occur are logged. - * - * @param tilemgr the tile manager via which the tile should be - * fetched. - * @param tileSetId the id of the tileset from which to fetch the - * tile. - * @param tileIndex the index of the tile to be fetched. - */ - public static Tile getTile ( - TileManager tilemgr, int tileSetId, int tileIndex) - { - try { - TileSet set = tilemgr.getTileSet(tileSetId); - return set.getTile(tileIndex); - - } catch (TileException te) { - Log.warning("Error retrieving tile [tsid=" + tileSetId + - ", tidx=" + tileIndex + ", error=" + te + "]."); - return null; - } - } - /** * Generates a fully-qualified tileid given the supplied tileset id * and tile index. This fully-qualified id can be used to fetch the diff --git a/src/java/com/threerings/media/tile/TrimmedObjectTileSet.java b/src/java/com/threerings/media/tile/TrimmedObjectTileSet.java index b38a139c3..10d72fdee 100644 --- a/src/java/com/threerings/media/tile/TrimmedObjectTileSet.java +++ b/src/java/com/threerings/media/tile/TrimmedObjectTileSet.java @@ -1,9 +1,8 @@ // -// $Id: TrimmedObjectTileSet.java,v 1.2 2003/01/12 01:19:58 shaper Exp $ +// $Id: TrimmedObjectTileSet.java,v 1.3 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Image; import java.awt.Rectangle; import java.io.IOException; @@ -12,6 +11,7 @@ import java.io.OutputStream; import com.samskivert.util.StringUtil; import com.threerings.media.Log; +import com.threerings.media.image.Mirage; import com.threerings.media.tile.util.TileSetTrimmer; /** @@ -30,16 +30,15 @@ public class TrimmedObjectTileSet extends TileSet } // documentation inherited - protected Rectangle computeTileBounds (int tileIndex, Image tilesetImage) + protected Rectangle computeTileBounds (int tileIndex) { - // N/A - return null; + return _bounds[tileIndex]; } // documentation inherited - protected Tile createTile (int tileIndex, Image tilesetImage) + protected Tile createTile (int tileIndex, Mirage tileImage) { - ObjectTile tile = new ObjectTile(tilesetImage, _bounds[tileIndex]); + ObjectTile tile = new ObjectTile(tileImage); tile.setBase(_ometrics[tileIndex].width, _ometrics[tileIndex].height); tile.setOrigin(_ometrics[tileIndex].x, _ometrics[tileIndex].y); return tile; diff --git a/src/java/com/threerings/media/tile/TrimmedTile.java b/src/java/com/threerings/media/tile/TrimmedTile.java index e11d091a0..9fb102f7c 100644 --- a/src/java/com/threerings/media/tile/TrimmedTile.java +++ b/src/java/com/threerings/media/tile/TrimmedTile.java @@ -1,18 +1,14 @@ // -// $Id: TrimmedTile.java,v 1.4 2003/01/08 04:09:02 mdb Exp $ +// $Id: TrimmedTile.java,v 1.5 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Graphics; -import java.awt.Image; +import java.awt.Graphics2D; import java.awt.Rectangle; -import java.awt.Shape; - -import java.awt.image.BufferedImage; import com.samskivert.util.StringUtil; -import com.threerings.media.image.ImageUtil; +import com.threerings.media.image.Mirage; /** * Behaves just like a regular tile, but contains a "trimmed" image which @@ -28,61 +24,48 @@ public class TrimmedTile extends Tile * * @param tilesetSource the tileset image that contains our trimmed * tile image. - * @param bounds contains the width and height of the + * @param tbounds contains the width and height of the * untrimmed tile, but the x and y offset of the - * trimmed tile image in the supplied tileset source image. - * @param tbounds the bounds of the trimmed image in the coordinate - * system defined by the untrimmed image. + * trimmed tile image in the original untrimmed tile image. */ - public TrimmedTile (Image image, Rectangle bounds, Rectangle tbounds) + public TrimmedTile (Mirage image, Rectangle tbounds) { - super(image, bounds); + super(image); _tbounds = tbounds; } // documentation inherited - public void paint (Graphics gfx, int x, int y) + public int getWidth () { - if (_subimage == null) { - createSubImage(); - } - gfx.drawImage(_subimage, x + _tbounds.x, y + _tbounds.y, null); - } - - /** - * Returns the bounds of the trimmed image within the coordinate - * system defined by the complete virtual tile. The returned rectangle - * should not be modified. - */ - public Rectangle getTrimmedBounds () - { - return _tbounds; + return _tbounds.width; } // documentation inherited - public Image getImage () + public int getHeight () { - String errmsg = "Can't convert trimmed tile to image " + - "[tile=" + this + "]."; - throw new RuntimeException(errmsg); + return _tbounds.height; + } + + // documentation inherited + public void paint (Graphics2D gfx, int x, int y) + { + _mirage.paint(gfx, x + _tbounds.x, y + _tbounds.y); + } + + /** + * Fills in the bounds of the trimmed image within the coordinate + * system defined by the complete virtual tile. + */ + public void getTrimmedBounds (Rectangle tbounds) + { + tbounds.setBounds(_tbounds.x, _tbounds.y, + _mirage.getWidth(), _mirage.getHeight()); } // documentation inherited public boolean hitTest (int x, int y) { - return ImageUtil.hitTest(_image, _bounds.x + x, _bounds.y + y); - } - - // documentation inherited - protected void createSubImage () - { - if (_image instanceof BufferedImage) { - _subimage = ImageUtil.getSubimage(_image, _bounds.x, _bounds.y, - _tbounds.width, _tbounds.height); - } else { - String errmsg = "Can't obtain tile image [tile=" + this + "]."; - throw new RuntimeException(errmsg); - } + return super.hitTest(x - _tbounds.x, y - _tbounds.y); } // documentation inherited @@ -91,7 +74,6 @@ public class TrimmedTile extends Tile buf.append(", tbounds=").append(StringUtil.toString(_tbounds)); } - /** The dimensions of the trimmed image in the coordinate space - * defined by the untrimmed image. */ + /** Our extra trimmed image dimension information. */ protected Rectangle _tbounds; } diff --git a/src/java/com/threerings/media/tile/TrimmedTileSet.java b/src/java/com/threerings/media/tile/TrimmedTileSet.java index 62c6562df..9fbef6c1d 100644 --- a/src/java/com/threerings/media/tile/TrimmedTileSet.java +++ b/src/java/com/threerings/media/tile/TrimmedTileSet.java @@ -1,14 +1,14 @@ // -// $Id: TrimmedTileSet.java,v 1.5 2002/08/19 22:58:15 mdb Exp $ +// $Id: TrimmedTileSet.java,v 1.6 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Image; import java.awt.Rectangle; import java.io.IOException; import java.io.OutputStream; +import com.threerings.media.image.Mirage; import com.threerings.media.tile.util.TileSetTrimmer; /** @@ -24,17 +24,15 @@ public class TrimmedTileSet extends TileSet } // documentation inherited - protected Rectangle computeTileBounds (int tileIndex, Image tilesetImage) + protected Rectangle computeTileBounds (int tileIndex) { - // N/A - return null; + return _obounds[tileIndex]; } // documentation inherited - protected Tile createTile (int tileIndex, Image tilesetImage) + protected Tile createTile (int tileIndex, Mirage image) { - return new TrimmedTile( - tilesetImage, _obounds[tileIndex], _tbounds[tileIndex]); + return new TrimmedTile(image, _tbounds[tileIndex]); } /** @@ -51,23 +49,13 @@ public class TrimmedTileSet extends TileSet final TrimmedTileSet tset = new TrimmedTileSet(); tset.setName(source.getName()); int tcount = source.getTileCount(); + tset._tbounds = new Rectangle[tcount]; + tset._obounds = new Rectangle[tcount]; // grab the dimensions of the original tiles - tset._obounds = new Rectangle[tcount]; for (int ii = 0; ii < tcount; ii++) { - try { - Tile tile = source.getTile(ii); - tset._obounds[ii] = new Rectangle(); - tset._obounds[ii].width = tile.getWidth(); - tset._obounds[ii].height = tile.getHeight(); - } catch (NoSuchTileException nste) { - String errmsg = "Urk! TileSet is ill-behaved. " + - "Claimed to have " + tcount + " tiles, but choked when " + - "we asked for tile " + ii + " [tset=" + source + "]."; - throw new RuntimeException(errmsg); - } + tset._tbounds[ii] = source.computeTileBounds(ii); } - tset._tbounds = new Rectangle[tcount]; // create the trimmed tileset image TileSetTrimmer.TrimMetricsReceiver tmr = @@ -75,10 +63,10 @@ public class TrimmedTileSet extends TileSet public void trimmedTile (int tileIndex, int imageX, int imageY, int trimX, int trimY, int trimWidth, int trimHeight) { - tset._obounds[tileIndex].x = imageX; - tset._obounds[tileIndex].y = imageY; - tset._tbounds[tileIndex] = - new Rectangle(trimX, trimY, trimWidth, trimHeight); + tset._tbounds[tileIndex].x = trimX; + tset._tbounds[tileIndex].y = trimY; + tset._obounds[tileIndex] = + new Rectangle(imageX, imageY, trimWidth, trimHeight); } }; TileSetTrimmer.trimTileSet(source, destImage, tmr); @@ -86,11 +74,11 @@ public class TrimmedTileSet extends TileSet return tset; } - /** The width and height of the untrimmed tile, and the x and y offset + /** The width and height of the trimmed tile, and the x and y offset * of the trimmed image within our tileset image. */ protected Rectangle[] _obounds; - /** The width and height of the trimmed image and the x and y offset + /** The width and height of the untrimmed image and the x and y offset * within the untrimmed image at which the trimmed image should be * rendered. */ protected Rectangle[] _tbounds; diff --git a/src/java/com/threerings/media/tile/UniformTileSet.java b/src/java/com/threerings/media/tile/UniformTileSet.java index 5d28da9e3..8c999c3a0 100644 --- a/src/java/com/threerings/media/tile/UniformTileSet.java +++ b/src/java/com/threerings/media/tile/UniformTileSet.java @@ -1,13 +1,10 @@ // -// $Id: UniformTileSet.java,v 1.10 2003/01/08 04:09:02 mdb Exp $ +// $Id: UniformTileSet.java,v 1.11 2003/01/13 22:49:46 mdb Exp $ package com.threerings.media.tile; -import java.awt.Image; import java.awt.Rectangle; - -import com.threerings.media.Log; -import com.threerings.media.image.ImageUtil; +import java.awt.image.BufferedImage; /** * A uniform tileset is one that is composed of tiles that are all the @@ -65,28 +62,13 @@ public class UniformTileSet extends TileSet return _height; } - /** - * Returns the image that would be used by the tile at the specified - * index. Because the uniform tileset is often used simply to provide - * access to a collection of uniform images, this method is provided - * to bypass the creation of a {@link Tile} object when all that is - * desired is access to the underlying image. - */ - public Image getTileImage (int tileIndex) - { - Image tsimg = getTileSetImage(); - if (tsimg == null) { - return null; - } - Rectangle tb = computeTileBounds(tileIndex, tsimg); - return ImageUtil.getSubimage(tsimg, tb.x, tb.y, tb.width, tb.height); - } - // documentation inherited - protected Rectangle computeTileBounds (int tileIndex, Image tilesetImage) + protected Rectangle computeTileBounds (int tileIndex) { + BufferedImage tsimg = getTileSetImage(); + // figure out from whence to crop the tile - int tilesPerRow = tilesetImage.getWidth(null) / _width; + int tilesPerRow = tsimg.getWidth() / _width; int row = tileIndex / tilesPerRow; int col = tileIndex % tilesPerRow; diff --git a/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java b/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java index eeb771a55..e65b7d58a 100644 --- a/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java +++ b/src/java/com/threerings/media/tile/bundle/BundledTileSetRepository.java @@ -1,5 +1,5 @@ // -// $Id: BundledTileSetRepository.java,v 1.8 2003/01/08 04:09:02 mdb Exp $ +// $Id: BundledTileSetRepository.java,v 1.9 2003/01/13 22:49:47 mdb Exp $ package com.threerings.media.tile.bundle; @@ -16,7 +16,10 @@ import com.threerings.resource.ResourceBundle; import com.threerings.resource.ResourceManager; import com.threerings.media.Log; +import com.threerings.media.image.ImageDataProvider; import com.threerings.media.image.ImageManager; + +import com.threerings.media.tile.IMImageProvider; import com.threerings.media.tile.NoSuchTileSetException; import com.threerings.media.tile.TileSet; import com.threerings.media.tile.TileSetRepository; @@ -35,13 +38,13 @@ public class BundledTileSetRepository * * @param rmgr the resource manager from which to obtain our resource * set. - * @param imgr the image manager that we'll use to decode and cache - * images. + * @param imgr the image manager through which we will configure the + * tile sets to load their images. * @param name the name of the resource set from which we will be * loading our tile data. */ - public BundledTileSetRepository ( - ResourceManager rmgr, ImageManager imgr, String name) + public BundledTileSetRepository (ResourceManager rmgr, ImageManager imgr, + String name) { // first we obtain the resource set from which we will load up our // tileset bundles @@ -64,7 +67,7 @@ public class BundledTileSetRepository // unserialize our tileset bundle TileSetBundle tsb = BundleUtil.extractBundle(rbundles[i]); // initialize it and add it to the list - tsb.init(rbundles[i], imgr); + tsb.init(rbundles[i]); tbundles.add(tsb); } catch (Exception e) { @@ -78,43 +81,15 @@ public class BundledTileSetRepository // finally create one big fat array of all of the tileset bundles _bundles = new TileSetBundle[tbundles.size()]; tbundles.toArray(_bundles); - } - /** - * Searches for the tileset with the specified name, which must reside - * in a tileset bundle identified by the supplied bundle identifying - * string. - * - * @param bundleId a string that will be substring matched against the - * names of all known tileset bundles. Only bundles whose bundle file - * path contains this string will be searched. - * @param setName the name of the tileset to be located. - * - * @return The first tileset matching the supplied parameters or null - * if no matching tileset could be found. - */ - public TileSet locateTileSet (String bundleId, String setName) - { - int bcount = _bundles.length; - for (int ii = 0; ii < bcount; ii++) { - TileSetBundle tsb = _bundles[ii]; - // skip non-matching bundles - if (tsb.getSource().getPath().indexOf(bundleId) == -1) { - continue; - } - // search for the tileset in this bundle - Iterator tsiter = tsb.enumerateTileSets(); - while (tsiter.hasNext()) { - TileSet set = (TileSet)tsiter.next(); - if (set.getName().equals(setName)) { - return set; - } - } + // create image providers for our bundles + _improvs = new IMImageProvider[_bundles.length]; + for (int ii = 0; ii < _bundles.length; ii++) { + _improvs[ii] = new IMImageProvider(imgr, _bundles[ii]); } - return null; } - // documentation inherited + // documentation inherited from interface public Iterator enumerateTileSetIds () throws PersistenceException { @@ -130,7 +105,7 @@ public class BundledTileSetRepository }); } - // documentation inherited + // documentation inherited from interface public Iterator enumerateTileSets () throws PersistenceException { @@ -146,7 +121,7 @@ public class BundledTileSetRepository }); } - // documentation inherited + // documentation inherited from interface public TileSet getTileSet (int tileSetId) throws NoSuchTileSetException, PersistenceException { @@ -155,12 +130,36 @@ public class BundledTileSetRepository for (int i = 0; i < blength; i++) { tset = _bundles[i].getTileSet(tileSetId); if (tset != null) { + tset.setImageProvider(_improvs[i]); return tset; } } throw new NoSuchTileSetException(tileSetId); } + // documentation inherited from interface + public TileSet getTileSet (String setName) + throws NoSuchTileSetException, PersistenceException + { + int bcount = _bundles.length; + for (int ii = 0; ii < bcount; ii++) { + TileSetBundle tsb = _bundles[ii]; + // search for the tileset in this bundle + Iterator tsiter = tsb.enumerateTileSets(); + while (tsiter.hasNext()) { + TileSet set = (TileSet)tsiter.next(); + if (set.getName().equals(setName)) { + set.setImageProvider(_improvs[ii]); + return set; + } + } + } + return null; + } + /** An array of tileset bundles from which we obtain tilesets. */ protected TileSetBundle[] _bundles; + + /** Image providers for each of our tile set bundles. */ + protected IMImageProvider[] _improvs; } diff --git a/src/java/com/threerings/media/tile/bundle/TileSetBundle.java b/src/java/com/threerings/media/tile/bundle/TileSetBundle.java index 86b195ba6..1a2049e82 100644 --- a/src/java/com/threerings/media/tile/bundle/TileSetBundle.java +++ b/src/java/com/threerings/media/tile/bundle/TileSetBundle.java @@ -1,10 +1,12 @@ // -// $Id: TileSetBundle.java,v 1.12 2003/01/08 04:09:02 mdb Exp $ +// $Id: TileSetBundle.java,v 1.13 2003/01/13 22:49:47 mdb Exp $ package com.threerings.media.tile.bundle; import java.awt.Image; import javax.imageio.ImageIO; +import javax.imageio.stream.FileImageInputStream; +import javax.imageio.stream.ImageInputStream; import java.io.File; import java.io.FileNotFoundException; @@ -20,8 +22,7 @@ import com.samskivert.util.HashIntMap; import com.threerings.resource.ResourceBundle; -import com.threerings.media.image.ImageManager; -import com.threerings.media.tile.ImageProvider; +import com.threerings.media.image.ImageDataProvider; import com.threerings.media.tile.TileSet; /** @@ -29,17 +30,16 @@ import com.threerings.media.tile.TileSet; * bundle of tilesets stored on the local filesystem. */ public class TileSetBundle extends HashIntMap - implements Serializable, ImageProvider + implements Serializable, ImageDataProvider { /** * Initializes this resource bundle with a reference to the jarfile * from which it was loaded and from which it can load image data. The * image manager will be used to decode the images. */ - public void init (ResourceBundle bundle, ImageManager imgr) + public void init (ResourceBundle bundle) { _bundle = bundle; - _imgr = imgr; } /** @@ -82,19 +82,17 @@ public class TileSetBundle extends HashIntMap return values().iterator(); } - // documentation inherited - public Image loadImage (String path) + // documentation inherited from interface + public String getIdent () + { + return "tsb:" + _bundle.getSource(); + } + + // documentation inherited from interface + public ImageInputStream loadImageData (String path) throws IOException { - // obtain the image data from our jarfile - InputStream imgin = _bundle.getResource(path); - if (imgin == null) { - String errmsg = "No such image in resource bundle " + - "[bundle=" + _bundle + ", path=" + path + "]."; - throw new FileNotFoundException(errmsg); - } - // return _imgr.createImage(imgin); - return ImageIO.read(imgin); + return new FileImageInputStream(_bundle.getResourceFile(path)); } // custom serialization process @@ -120,7 +118,6 @@ public class TileSetBundle extends HashIntMap for (int i = 0; i < count; i++) { int tileSetId = in.readInt(); TileSet set = (TileSet)in.readObject(); - set.setImageProvider(this); put(tileSetId, set); } } @@ -128,10 +125,7 @@ public class TileSetBundle extends HashIntMap /** That from which we load our tile images. */ protected transient ResourceBundle _bundle; - /** We use the image manager to decode our images. */ - protected transient ImageManager _imgr; - /** Increase this value when object's serialized state is impacted by * a class change (modification of fields, inheritance). */ - private static final long serialVersionUID = 1; + private static final long serialVersionUID = 2; } diff --git a/src/java/com/threerings/media/tile/bundle/tools/DumpBundle.java b/src/java/com/threerings/media/tile/bundle/tools/DumpBundle.java index f2b0a1e6f..4e7ed6c9a 100644 --- a/src/java/com/threerings/media/tile/bundle/tools/DumpBundle.java +++ b/src/java/com/threerings/media/tile/bundle/tools/DumpBundle.java @@ -1,5 +1,5 @@ // -// $Id: DumpBundle.java,v 1.8 2003/01/08 04:09:02 mdb Exp $ +// $Id: DumpBundle.java,v 1.9 2003/01/13 22:49:47 mdb Exp $ package com.threerings.media.tile.bundle.tools; @@ -9,8 +9,6 @@ import java.util.Iterator; import com.threerings.resource.ResourceManager; import com.threerings.resource.ResourceBundle; -import com.threerings.media.image.ImageManager; - import com.threerings.media.tile.TileSet; import com.threerings.media.tile.bundle.BundleUtil; import com.threerings.media.tile.bundle.TileSetBundle; @@ -34,7 +32,6 @@ public class DumpBundle // create a resource and image manager in case they want to dump // the tiles ResourceManager rmgr = new ResourceManager("rsrc"); - ImageManager imgr = new ImageManager(rmgr, null); for (int i = 0; i < args.length; i++) { // oh the hackery @@ -47,7 +44,7 @@ public class DumpBundle try { ResourceBundle bundle = new ResourceBundle(file); TileSetBundle tsb = BundleUtil.extractBundle(bundle); - tsb.init(bundle, imgr); + tsb.init(bundle); Iterator tsids = tsb.enumerateTileSetIds(); while (tsids.hasNext()) { diff --git a/src/java/com/threerings/media/tile/bundle/tools/TileSetBundler.java b/src/java/com/threerings/media/tile/bundle/tools/TileSetBundler.java index ebe4c70f8..6e251b1eb 100644 --- a/src/java/com/threerings/media/tile/bundle/tools/TileSetBundler.java +++ b/src/java/com/threerings/media/tile/bundle/tools/TileSetBundler.java @@ -1,9 +1,10 @@ // -// $Id: TileSetBundler.java,v 1.9 2003/01/12 01:19:33 shaper Exp $ +// $Id: TileSetBundler.java,v 1.10 2003/01/13 22:49:47 mdb Exp $ package com.threerings.media.tile.bundle.tools; -import java.awt.Image; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; import javax.imageio.ImageIO; import java.io.File; @@ -31,9 +32,11 @@ import com.samskivert.io.NestableIOException; import com.samskivert.io.PersistenceException; import com.threerings.media.Log; +import com.threerings.media.image.Colorization; +import com.threerings.media.image.Mirage; -import com.threerings.media.tile.ImageProvider; import com.threerings.media.tile.ObjectTileSet; +import com.threerings.media.tile.SimpleCachingImageProvider; import com.threerings.media.tile.TileSet; import com.threerings.media.tile.TileSetIDBroker; import com.threerings.media.tile.TrimmedObjectTileSet; @@ -54,39 +57,39 @@ import com.threerings.media.tile.tools.xml.TileSetRuleSet; * parsers. An example configuration follows: * *
- ** * This configuration would be used to parse a bundle description that * looked something like the following: * *- * + * </ruleset> + * </mapping> + * </bundler-config> *- * - *bundle.tilesets.uniform - *+ * <bundler-config> + * <mapping> + * <path>bundle.tilesets.uniform</path> + * <ruleset> * com.threerings.media.tile.tools.xml.UniformTileSetRuleSet - * - *- * - *bundle.tilesets.object - *+ * </ruleset> + * </mapping> + * <mapping> + * <path>bundle.tilesets.object</path> + * <ruleset> * com.threerings.media.tile.tools.xml.ObjectTileSetRuleSet - * - *
- ** * The class specified in the- * - * + * <bundle> + * <tilesets> + * <uniform> + * <tileset> + * <!-- ... --> + * </tileset> + * </uniform> + * <object> + * <tileset> + * <!-- ... --> + * </tileset> + * </object> + * </tilesets> *- * - * - *- * - * - *
ruleset element must derive
@@ -277,6 +280,14 @@ public class TileSetBundler
Manifest manifest = new Manifest();
JarOutputStream jar = new JarOutputStream(fout, manifest);
+ // create an image provider for loading our tileset images
+ SimpleCachingImageProvider improv = new SimpleCachingImageProvider() {
+ protected BufferedImage loadImage (String path)
+ throws IOException {
+ return ImageIO.read(new File(bundleDesc.getParent(), path));
+ }
+ };
+
try {
// write all of the image files to the bundle, converting the
// tilesets to trimmed tilesets in the process
@@ -300,14 +311,8 @@ public class TileSetBundler
if (set instanceof ObjectTileSet) {
// set the tileset up with an image provider; we need to
// do this so that we can trim it!
- set.setImageProvider(new ImageProvider() {
- public Image loadImage (String path)
- throws IOException {
- File source = new File(bundleDesc.getParent(),
- path);
- return ImageIO.read(source);
- }
- });
+ set.setImageProvider(improv);
+
// create a trimmed object tileset, which will write
// the trimmed tileset image to the jar output stream
TrimmedObjectTileSet tset =
diff --git a/src/java/com/threerings/media/tile/util/TileSetTrimmer.java b/src/java/com/threerings/media/tile/util/TileSetTrimmer.java
index 138cc4656..0c3765485 100644
--- a/src/java/com/threerings/media/tile/util/TileSetTrimmer.java
+++ b/src/java/com/threerings/media/tile/util/TileSetTrimmer.java
@@ -1,5 +1,5 @@
//
-// $Id: TileSetTrimmer.java,v 1.5 2003/01/08 04:09:03 mdb Exp $
+// $Id: TileSetTrimmer.java,v 1.6 2003/01/13 22:49:47 mdb Exp $
package com.threerings.media.tile.util;
@@ -20,7 +20,6 @@ import com.samskivert.util.StringUtil;
import com.threerings.media.Log;
import com.threerings.media.image.ImageUtil;
-import com.threerings.media.tile.NoSuchTileException;
import com.threerings.media.tile.Tile;
import com.threerings.media.tile.TileSet;
@@ -88,17 +87,11 @@ public class TileSetTrimmer
for (int ii = 0; ii < tcount; ii++) {
// extract the image from the original tileset
try {
- Tile tile = source.getTile(ii);
- timgs[ii] = (BufferedImage)tile.getImage();
-
+ timgs[ii] = source.getTileImage(ii);
} catch (RasterFormatException rfe) {
throw new IOException("Failed to get tile image " +
"[tidx=" + ii + ", tset=" + source +
", rfe=" + rfe + "].");
-
- } catch (NoSuchTileException nste) {
- throw new RuntimeException("WTF? No such tile [tset=" + source +
- ", tidx=" + ii + "]");
}
// figure out how tightly we can trim it
diff --git a/src/java/com/threerings/media/util/MultiFrameImage.java b/src/java/com/threerings/media/util/MultiFrameImage.java
index 3d8cc7ff1..da5288719 100644
--- a/src/java/com/threerings/media/util/MultiFrameImage.java
+++ b/src/java/com/threerings/media/util/MultiFrameImage.java
@@ -1,10 +1,9 @@
//
-// $Id: MultiFrameImage.java,v 1.3 2002/09/17 19:11:13 mdb Exp $
+// $Id: MultiFrameImage.java,v 1.4 2003/01/13 22:49:47 mdb Exp $
package com.threerings.media.util;
-import java.awt.Graphics;
-import java.awt.Image;
+import java.awt.Graphics2D;
/**
* The multi-frame image interface provides encapsulated access to a set
@@ -31,7 +30,7 @@ public interface MultiFrameImage
* Renders the specified frame into the specified graphics object at
* the specified coordinates.
*/
- public void paintFrame (Graphics g, int index, int x, int y);
+ public void paintFrame (Graphics2D g, int index, int x, int y);
/**
* Returns true if the specified frame contains a non-transparent
diff --git a/src/java/com/threerings/media/util/MultiFrameImageImpl.java b/src/java/com/threerings/media/util/MultiFrameImageImpl.java
index d6dec84c5..026410316 100644
--- a/src/java/com/threerings/media/util/MultiFrameImageImpl.java
+++ b/src/java/com/threerings/media/util/MultiFrameImageImpl.java
@@ -1,12 +1,11 @@
//
-// $Id: MultiFrameImageImpl.java,v 1.4 2003/01/08 04:09:03 mdb Exp $
+// $Id: MultiFrameImageImpl.java,v 1.5 2003/01/13 22:49:47 mdb Exp $
package com.threerings.media.util;
-import java.awt.Graphics;
-import java.awt.Image;
+import java.awt.Graphics2D;
-import com.threerings.media.image.ImageUtil;
+import com.threerings.media.image.Mirage;
/**
* A basic implementation of the {@link MultiFrameImage} interface
@@ -18,41 +17,41 @@ public class MultiFrameImageImpl implements MultiFrameImage
/**
* Constructs a multiple frame image object.
*/
- public MultiFrameImageImpl (Image[] imgs)
+ public MultiFrameImageImpl (Mirage[] mirages)
{
- _imgs = imgs;
+ _mirages = mirages;
}
// documentation inherited
public int getFrameCount ()
{
- return _imgs.length;
+ return _mirages.length;
}
// documentation inherited from interface
public int getWidth (int index)
{
- return _imgs[index].getWidth(null);
+ return _mirages[index].getWidth();
}
// documentation inherited from interface
public int getHeight (int index)
{
- return _imgs[index].getHeight(null);
+ return _mirages[index].getHeight();
}
// documentation inherited from interface
- public void paintFrame (Graphics g, int index, int x, int y)
+ public void paintFrame (Graphics2D g, int index, int x, int y)
{
- g.drawImage(_imgs[index], x, y, null);
+ _mirages[index].paint(g, x, y);
}
// documentation inherited from interface
public boolean hitTest (int index, int x, int y)
{
- return ImageUtil.hitTest(_imgs[index], x, y);
+ return _mirages[index].hitTest(x, y);
}
/** The frame images. */
- protected Image _imgs[];
+ protected Mirage[] _mirages;
}
diff --git a/src/java/com/threerings/media/util/SingleFrameImageImpl.java b/src/java/com/threerings/media/util/SingleFrameImageImpl.java
index e1e3e8193..172ceda8d 100644
--- a/src/java/com/threerings/media/util/SingleFrameImageImpl.java
+++ b/src/java/com/threerings/media/util/SingleFrameImageImpl.java
@@ -1,12 +1,11 @@
//
-// $Id: SingleFrameImageImpl.java,v 1.4 2003/01/08 04:09:03 mdb Exp $
+// $Id: SingleFrameImageImpl.java,v 1.5 2003/01/13 22:49:47 mdb Exp $
package com.threerings.media.util;
-import java.awt.Graphics;
-import java.awt.Image;
+import java.awt.Graphics2D;
-import com.threerings.media.image.ImageUtil;
+import com.threerings.media.image.Mirage;
/**
* The single frame image class is a basic implementation of the {@link
@@ -18,9 +17,9 @@ public class SingleFrameImageImpl implements MultiFrameImage
/**
* Constructs a single frame image object.
*/
- public SingleFrameImageImpl (Image img)
+ public SingleFrameImageImpl (Mirage mirage)
{
- _img = img;
+ _mirage = mirage;
}
// documentation inherited
@@ -32,27 +31,27 @@ public class SingleFrameImageImpl implements MultiFrameImage
// documentation inherited from interface
public int getWidth (int index)
{
- return _img.getWidth(null);
+ return _mirage.getWidth();
}
// documentation inherited from interface
public int getHeight (int index)
{
- return _img.getHeight(null);
+ return _mirage.getHeight();
}
// documentation inherited from interface
- public void paintFrame (Graphics g, int index, int x, int y)
+ public void paintFrame (Graphics2D g, int index, int x, int y)
{
- g.drawImage(_img, x, y, null);
+ _mirage.paint(g, x, y);
}
// documentation inherited from interface
public boolean hitTest (int index, int x, int y)
{
- return ImageUtil.hitTest(_img, x, y);
+ return _mirage.hitTest(x, y);
}
/** The frame image. */
- protected Image _img;
+ protected Mirage _mirage;
}
diff --git a/src/java/com/threerings/media/util/SingleTileImageImpl.java b/src/java/com/threerings/media/util/SingleTileImageImpl.java
new file mode 100644
index 000000000..9e289b7bb
--- /dev/null
+++ b/src/java/com/threerings/media/util/SingleTileImageImpl.java
@@ -0,0 +1,57 @@
+//
+// $Id: SingleTileImageImpl.java,v 1.1 2003/01/13 22:49:47 mdb Exp $
+
+package com.threerings.media.util;
+
+import java.awt.Graphics2D;
+
+import com.threerings.media.tile.Tile;
+
+/**
+ * The single frame image class is a basic implementation of the {@link
+ * MultiFrameImage} interface intended to facilitate the creation of MFIs
+ * whose display frames consist of only a single tile image.
+ */
+public class SingleTileImageImpl implements MultiFrameImage
+{
+ /**
+ * Constructs a single frame image object.
+ */
+ public SingleTileImageImpl (Tile tile)
+ {
+ _tile = tile;
+ }
+
+ // documentation inherited
+ public int getFrameCount ()
+ {
+ return 1;
+ }
+
+ // documentation inherited from interface
+ public int getWidth (int index)
+ {
+ return _tile.getWidth();
+ }
+
+ // documentation inherited from interface
+ public int getHeight (int index)
+ {
+ return _tile.getHeight();
+ }
+
+ // documentation inherited from interface
+ public void paintFrame (Graphics2D g, int index, int x, int y)
+ {
+ _tile.paint(g, x, y);
+ }
+
+ // documentation inherited from interface
+ public boolean hitTest (int index, int x, int y)
+ {
+ return _tile.hitTest(x, y);
+ }
+
+ /** The frame image. */
+ protected Tile _tile;
+}