diff --git a/src/java/com/threerings/cast/CharacterManager.java b/src/java/com/threerings/cast/CharacterManager.java
index 5d322f863..3f9041866 100644
--- a/src/java/com/threerings/cast/CharacterManager.java
+++ b/src/java/com/threerings/cast/CharacterManager.java
@@ -1,5 +1,5 @@
//
-// $Id: CharacterManager.java,v 1.17 2002/04/15 23:06:49 mdb Exp $
+// $Id: CharacterManager.java,v 1.18 2002/05/04 06:57:24 mdb Exp $
package com.threerings.cast;
@@ -8,6 +8,7 @@ import java.util.Iterator;
import java.util.HashMap;
import com.samskivert.util.Tuple;
+import org.apache.commons.collections.LRUMap;
import com.threerings.media.sprite.MultiFrameImage;
@@ -168,6 +169,8 @@ public class CharacterManager
// if that failed, we'll just have to generate the danged things
if (frames == null) {
+ Log.info("Creating frames [descrip=" + descrip +
+ ", action=" + action + "].");
// do the compositing
frames = createCompositeFrames(descrip, action);
// cache the result on disk if we've got such a cache
@@ -194,8 +197,6 @@ public class CharacterManager
CharacterDescriptor descrip, String action)
throws NoSuchComponentException
{
- MultiFrameImage[] frames = new MultiFrameImage[DIRECTION_COUNT];
-
int[] cids = descrip.getComponentIds();
int ccount = cids.length;
Colorization[][] zations = descrip.getColorizations();
@@ -214,12 +215,20 @@ public class CharacterManager
// sort them into the proper rendering order
Arrays.sort(components);
- // now composite the component frames, one atop the next; using
- // the colorizations if we have them
- for (int i = 0; i < ccount; i++) {
- ColorizedComponent cc = components[i];
- TileUtil.compositeFrames(
- frames, cc.component.getFrames(action), cc.zations);
+ // create the multiframe image and colorization arrays that we'll
+ // supply to the lazy compositing result multiframe image
+ CompositedFrameImage[] frames =
+ new CompositedFrameImage[DIRECTION_COUNT];
+ zations = new Colorization[ccount][];
+ for (int orient = 0; orient < DIRECTION_COUNT; orient++) {
+ MultiFrameImage[] sources = new MultiFrameImage[ccount];
+ for (int cc = 0; cc < ccount; cc++) {
+ ColorizedComponent ccomp = components[cc];
+ sources[cc] = ccomp.component.getFrames(action)[orient];
+ zations[cc] = ccomp.zations;
+ }
+ frames[orient] = new CompositedFrameImage(
+ action + orient, sources, zations);
}
return frames;
@@ -250,11 +259,15 @@ public class CharacterManager
protected HashMap _actions = new HashMap();
/** A cache of composited animation frames. */
- protected HashMap _frames = new HashMap();
+ protected LRUMap _frames = new LRUMap(ACTION_CACHE_SIZE);
/** The character class to be created. */
protected Class _charClass = CharacterSprite.class;
/** The action animation cache, if we have one. */
protected ActionCache _acache;
+
+ /** The number of actions to cache before we start clearing them
+ * out. */
+ protected static final int ACTION_CACHE_SIZE = 5;
}
diff --git a/src/java/com/threerings/cast/CompositedActionFrames.java b/src/java/com/threerings/cast/CompositedActionFrames.java
new file mode 100644
index 000000000..0602d39f2
--- /dev/null
+++ b/src/java/com/threerings/cast/CompositedActionFrames.java
@@ -0,0 +1,64 @@
+//
+// $Id: CompositedActionFrames.java,v 1.1 2002/05/04 06:57:24 mdb Exp $
+
+package com.threerings.cast;
+
+import java.awt.Image;
+import com.threerings.media.sprite.MultiFrameImage;
+import com.threerings.cast.util.TileUtil;
+
+/**
+ * An implementation of the {@link MultiFrameImage} interface that is used
+ * to lazily create composited character frames when they are requested.
+ */
+public class CompositedFrameImage implements MultiFrameImage
+{
+ /**
+ * Constructs a composited frame image with the supplied source
+ * component images and colorization configuration. The actual
+ * component frame images will not be composited until they are
+ * requested.
+ */
+ public CompositedFrameImage (
+ String action, MultiFrameImage[] sources, Colorization[][] zations)
+ {
+ _action = action;
+ _sources = sources;
+ _zations = zations;
+
+ // the sources must all have the same frame count, so we just use
+ // the count from the first one
+ int frameCount = _sources[0].getFrameCount();
+ _images = new Image[frameCount];
+ }
+
+ // documentation inherited
+ public int getFrameCount ()
+ {
+ return _images.length;
+ }
+
+ // documentation inherited
+ public Image getFrame (int index)
+ {
+ // create the frame if we don't already have it
+ if (_images[index] == null) {
+ Log.info("Compositing [action=" + _action +
+ ", index=" + index + "].");
+ _images[index] =
+ TileUtil.compositeFrames(index, _sources, _zations);
+ }
+ return _images[index];
+ }
+
+ protected String _action;
+
+ /** Our source frame images. */
+ protected MultiFrameImage[] _sources;
+
+ /** The colorizations for our source components. */
+ protected Colorization[][] _zations;
+
+ /** The frame images. */
+ protected Image[] _images;
+}
diff --git a/src/java/com/threerings/cast/util/TileUtil.java b/src/java/com/threerings/cast/util/TileUtil.java
index 6fe5d6eab..eab56a77f 100644
--- a/src/java/com/threerings/cast/util/TileUtil.java
+++ b/src/java/com/threerings/cast/util/TileUtil.java
@@ -1,5 +1,5 @@
//
-// $Id: TileUtil.java,v 1.12 2002/03/16 03:15:05 shaper Exp $
+// $Id: TileUtil.java,v 1.13 2002/05/04 06:57:24 mdb Exp $
package com.threerings.cast.util;
@@ -21,98 +21,112 @@ import com.threerings.cast.Colorization;
public class TileUtil
implements DirectionCodes
{
+// /**
+// * Renders each of the given src component frames into
+// * the corresponding frames of dest, allocating blank
+// * image frames for dest if none yet exist. If
+// * zations is not null, the provided list of
+// * colorizations will be applied as well.
+// *
+// * @throws IllegalArgumentException if the frame count of the source
+// * and destination images don't match.
+// */
+// public static void compositeFrames (
+// MultiFrameImage[] dest, MultiFrameImage[] src,
+// Colorization[] zations)
+// {
+// for (int orient = 0; orient < DIRECTION_COUNT; orient++) {
+// MultiFrameImage sframes = src[orient];
+// MultiFrameImage dframes = dest[orient];
+
+// // create blank destination frames if needed
+// if (dframes == null) {
+// dframes = (dest[orient] = new BlankFrameImage(sframes));
+// }
+
+// // sanity check
+// int dsize = dframes.getFrameCount();
+// int ssize = sframes.getFrameCount();
+// if (dsize != ssize) {
+// String errmsg = "Can't composite images with inequal " +
+// "frame counts [dest=" + dsize + ", src=" + ssize + "].";
+// throw new IllegalArgumentException(errmsg);
+// }
+
+// // slap the images together
+// for (int ii = 0; ii < dsize; ii++) {
+// Image dimg = dframes.getFrame(ii);
+// BufferedImage simg = (BufferedImage)sframes.getFrame(ii);
+
+// // recolor the source image
+// if (zations != null) {
+// for (int i = 0; i < zations.length; i++) {
+// Colorization cz = zations[i];
+// if (zations[i] != null) {
+// simg = ImageUtil.recolorImage(
+// simg, cz.rootColor, cz.range, cz.offsets);
+// }
+// }
+// }
+
+// // now splat the recolored image onto the target
+// Graphics g = dimg.getGraphics();
+// g.drawImage(simg, 0, 0, null);
+// g.dispose();
+// }
+// }
+// }
+
/**
- * Renders each of the given src component frames into
- * the corresponding frames of dest, allocating blank
- * image frames for dest if none yet exist. If
- * zations is not null, the provided list of
- * colorizations will be applied as well.
- *
- * @throws IllegalArgumentException if the frame count of the source
- * and destination images don't match.
+ * Renders and returns the indexth image from each of the
+ * supplied source multi-frame images to a newly created buffered
+ * image. This is used to render a single frame of a composited
+ * character action, and accordingly, the source image array should be
+ * already sorted into the proper rendering order.
*/
- public static void compositeFrames (
- MultiFrameImage[] dest, MultiFrameImage[] src,
- Colorization[] zations)
+ public static Image compositeFrames (
+ int index, MultiFrameImage[] sources, Colorization[][] zations)
{
- for (int orient = 0; orient < DIRECTION_COUNT; orient++) {
- MultiFrameImage sframes = src[orient];
- MultiFrameImage dframes = dest[orient];
+ Image dest = null;
+ Graphics g = null;
- // create blank destination frames if needed
- if (dframes == null) {
- dframes = (dest[orient] = new BlankFrameImage(sframes));
+ long start = System.currentTimeMillis();
+ for (int ii = 0; ii < sources.length; ii++) {
+ BufferedImage simg = (BufferedImage)sources[ii].getFrame(index);
+
+ // create the image now that we know how big it should be
+ if (dest == null) {
+ dest = ImageUtil.createImage(
+ simg.getWidth(null), simg.getHeight(null));
+ g = dest.getGraphics();
}
- // sanity check
- int dsize = dframes.getFrameCount();
- int ssize = sframes.getFrameCount();
- if (dsize != ssize) {
- String errmsg = "Can't composite images with inequal " +
- "frame counts [dest=" + dsize + ", src=" + ssize + "].";
- throw new IllegalArgumentException(errmsg);
- }
-
- // slap the images together
- for (int ii = 0; ii < dsize; ii++) {
- Image dimg = dframes.getFrame(ii);
- BufferedImage simg = (BufferedImage)sframes.getFrame(ii);
-
- // recolor the source image
- if (zations != null) {
- for (int i = 0; i < zations.length; i++) {
- Colorization cz = zations[i];
- if (zations[i] != null) {
- simg = ImageUtil.recolorImage(
- simg, cz.rootColor, cz.range, cz.offsets);
- }
+ // recolor the source image
+ if (zations != null) {
+ int zcount = zations[ii].length;
+ for (int zz = 0; zz < zcount; zz++) {
+ Colorization cz = zations[ii][zz];
+ if (cz != null) {
+ simg = ImageUtil.recolorImage(
+ simg, cz.rootColor, cz.range, cz.offsets);
}
}
-
- // now splat the recolored image onto the target
- Graphics g = dimg.getGraphics();
- g.drawImage(simg, 0, 0, null);
- g.dispose();
}
- }
- }
- /**
- * An implementation of the {@link MultiFrameImage} interface that
- * initializes itself to contain a specified number of blank
- * frames of the requested dimensions.
- */
- protected static class BlankFrameImage implements MultiFrameImage
- {
- /**
- * Constructs a blank frame image based on the dimensions of
- * template multi-frame image.
- */
- public BlankFrameImage (MultiFrameImage template)
- {
- int frameCount = template.getFrameCount();
- _images = new Image[frameCount];
- for (int i = 0; i < frameCount; i++) {
- Image img = template.getFrame(i);
- _images[i] = ImageUtil.createImage(
- img.getWidth(null), img.getHeight(null));
- }
+ // now splat the recolored image onto the target
+ g.drawImage(simg, 0, 0, null);
}
- // documentation inherited
- public int getFrameCount ()
- {
- return _images.length;
+ // clean up after ourselves
+ if (g != null) {
+ g.dispose();
}
- // documentation inherited
- public Image getFrame (int index)
- {
- return _images[index];
- }
+ long now = System.currentTimeMillis();
+ Log.info("Composited " + sources.length + " frames in " +
+ (now-start) + " millis.");
- /** The frame images. */
- protected Image[] _images;
+ return dest;
}
/** The image file name suffix appended to component image file names. */