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. */