From 37a1167fb5705b1a959227c06b06fb40ab6fd895 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Sat, 4 May 2002 19:38:14 +0000 Subject: [PATCH] Revamped component action animation support so that we're not passing around arrays of multi-frame images but instead pass around one object that knows how to render any action frame in any orientation. This apparently fixed some horrible bug that we had introduced in doing JITC and now the whole thing is super fast, just like I always thought it would be. Once we introduce trimmed images, it will blaze! git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1336 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- src/java/com/threerings/cast/ActionCache.java | 8 +- .../com/threerings/cast/ActionFrames.java | 32 ++++++ .../threerings/cast/CharacterComponent.java | 5 +- .../com/threerings/cast/CharacterManager.java | 35 +++--- .../com/threerings/cast/CharacterSprite.java | 25 ++--- .../cast/CompositedActionFrames.java | 106 ++++++++++++++---- .../com/threerings/cast/FrameProvider.java | 6 +- .../bundle/BundledComponentRepository.java | 92 ++++++++++++--- .../com/threerings/cast/util/TileUtil.java | 103 +++-------------- 9 files changed, 237 insertions(+), 175 deletions(-) create mode 100644 src/java/com/threerings/cast/ActionFrames.java diff --git a/src/java/com/threerings/cast/ActionCache.java b/src/java/com/threerings/cast/ActionCache.java index 4250f45cf..231338284 100644 --- a/src/java/com/threerings/cast/ActionCache.java +++ b/src/java/com/threerings/cast/ActionCache.java @@ -1,10 +1,8 @@ // -// $Id: ActionCache.java,v 1.1 2002/02/07 03:20:29 mdb Exp $ +// $Id: ActionCache.java,v 1.2 2002/05/04 19:38:13 mdb Exp $ package com.threerings.cast; -import com.threerings.media.sprite.MultiFrameImage; - /** * A mechanism for caching composited character action animations on disk. */ @@ -14,7 +12,7 @@ public interface ActionCache * Fetches from the cache a composited set of images for a particular * character for a particular action. */ - public MultiFrameImage[] getActionFrames ( + public ActionFrames getActionFrames ( CharacterDescriptor descrip, String action); /** @@ -22,5 +20,5 @@ public interface ActionCache * character be cached. */ public void cacheActionFrames ( - CharacterDescriptor descrip, String action, MultiFrameImage[] frames); + CharacterDescriptor descrip, String action, ActionFrames frames); } diff --git a/src/java/com/threerings/cast/ActionFrames.java b/src/java/com/threerings/cast/ActionFrames.java new file mode 100644 index 000000000..f9ca42c38 --- /dev/null +++ b/src/java/com/threerings/cast/ActionFrames.java @@ -0,0 +1,32 @@ +// +// $Id: ActionFrames.java,v 1.1 2002/05/04 19:38:13 mdb Exp $ + +package com.threerings.cast; + +import java.awt.Graphics; +import java.awt.Image; + +import com.threerings.media.sprite.MultiFrameImage; +import com.threerings.util.DirectionCodes; + +/** + * Encapsulates a set of frames in each of {@link + * DirectionCodes#DIRECTION_COUNT} orientations that are used to render a + * character sprite. + */ +public interface ActionFrames extends MultiFrameImage +{ + /** + * Updates the orientation that is currently being returned by the + * {@link MultiFrameImage} implementation. + */ + public void setOrientation (int orient); + + /** + * Renders the specified frame at the specified offset applying the + * supplied colorizations in the process. Note, the colorizations + * should not be applied to the source image, only the rendered copy. + */ + public void paintColoredFrame ( + Graphics g, int index, int x, int y, Colorization[] zations); +} diff --git a/src/java/com/threerings/cast/CharacterComponent.java b/src/java/com/threerings/cast/CharacterComponent.java index b69136fe0..7fef886c0 100644 --- a/src/java/com/threerings/cast/CharacterComponent.java +++ b/src/java/com/threerings/cast/CharacterComponent.java @@ -1,5 +1,5 @@ // -// $Id: CharacterComponent.java,v 1.5 2002/02/19 22:09:50 mdb Exp $ +// $Id: CharacterComponent.java,v 1.6 2002/05/04 19:38:13 mdb Exp $ package com.threerings.cast; @@ -7,7 +7,6 @@ import java.io.Serializable; import com.samskivert.util.StringUtil; -import com.threerings.media.sprite.MultiFrameImage; import com.threerings.media.sprite.Sprite; /** @@ -46,7 +45,7 @@ public class CharacterComponent implements Serializable * if no animation for the specified action is available for this * component. */ - public MultiFrameImage[] getFrames (String action) + public ActionFrames getFrames (String action) { return _frameProvider.getFrames(this, action); } diff --git a/src/java/com/threerings/cast/CharacterManager.java b/src/java/com/threerings/cast/CharacterManager.java index 3f9041866..02bc8edbb 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.18 2002/05/04 06:57:24 mdb Exp $ +// $Id: CharacterManager.java,v 1.19 2002/05/04 19:38:13 mdb Exp $ package com.threerings.cast; @@ -7,11 +7,10 @@ import java.util.Arrays; import java.util.Iterator; import java.util.HashMap; +import com.samskivert.util.StringUtil; import com.samskivert.util.Tuple; import org.apache.commons.collections.LRUMap; -import com.threerings.media.sprite.MultiFrameImage; - import com.threerings.util.DirectionCodes; import com.threerings.cast.Log; @@ -152,13 +151,13 @@ public class CharacterManager * @exception IllegalArgumentException thrown if any of the components * referenced in the descriptor do not support the specified action. */ - protected MultiFrameImage[] getActionFrames ( + protected ActionFrames getActionFrames ( CharacterDescriptor descrip, String action) throws NoSuchComponentException { // first check the in-memory cache; which is keyed on both values Tuple key = new Tuple(descrip, action); - MultiFrameImage[] frames = (MultiFrameImage[])_frames.get(key); + ActionFrames frames = (ActionFrames)_frames.get(key); // next check the disk cache if (frames == null && _acache != null) { @@ -169,8 +168,6 @@ 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 @@ -193,7 +190,7 @@ public class CharacterManager * @exception IllegalArgumentException thrown if any of the components * referenced in the descriptor do not support the specified action. */ - protected MultiFrameImage[] createCompositeFrames ( + protected ActionFrames createCompositeFrames ( CharacterDescriptor descrip, String action) throws NoSuchComponentException { @@ -215,23 +212,19 @@ public class CharacterManager // sort them into the proper rendering order Arrays.sort(components); - // create the multiframe image and colorization arrays that we'll - // supply to the lazy compositing result multiframe image - CompositedFrameImage[] frames = - new CompositedFrameImage[DIRECTION_COUNT]; + // create an array with the action frames for each component + ActionFrames[] sources = new ActionFrames[ccount]; 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; + for (int ii = 0; ii < ccount; ii++) { + sources[ii] = components[ii].component.getFrames(action); + if (zations != null) { + zations[ii] = components[ii].zations; } - frames[orient] = new CompositedFrameImage( - action + orient, sources, zations); } - return frames; + // use those to create an entity that will lazily composite things + // together as they are needed + return new CompositedActionFrames(action, sources, zations); } /** Used when compositing component frame images. */ diff --git a/src/java/com/threerings/cast/CharacterSprite.java b/src/java/com/threerings/cast/CharacterSprite.java index 4adcf833b..a1ac22bf7 100644 --- a/src/java/com/threerings/cast/CharacterSprite.java +++ b/src/java/com/threerings/cast/CharacterSprite.java @@ -1,9 +1,8 @@ // -// $Id: CharacterSprite.java,v 1.27 2002/04/17 15:53:49 mdb Exp $ +// $Id: CharacterSprite.java,v 1.28 2002/05/04 19:38:13 mdb Exp $ package com.threerings.cast; -import com.threerings.media.sprite.MultiFrameImage; import com.threerings.media.sprite.Path; import com.threerings.media.sprite.ImageSprite; @@ -111,12 +110,13 @@ public class CharacterSprite extends ImageSprite try { // obtain our animation frames for this action sequence - _frames = _charmgr.getActionFrames(_descrip, action); + _aframes = _charmgr.getActionFrames(_descrip, action); + _aframes.setOrientation(_orient); // update the sprite render attributes setOrigin(actseq.origin.x, actseq.origin.y); setFrameRate(actseq.framesPerSecond); - setFrames(_frames[_orient]); + setFrames(_aframes); } catch (NoSuchComponentException nsce) { Log.warning("Character sprite references non-existent " + @@ -129,17 +129,10 @@ public class CharacterSprite extends ImageSprite { super.setOrientation(orient); - // sanity check - if (orient < 0 || orient >= _frames.length) { - String errmsg = "Invalid orientation requested " + - "[orient=" + orient + - ", fcount=" + ((_frames == null) ? -1 : _frames.length) + "]"; - throw new IllegalArgumentException(errmsg); - } - // update the sprite frames to reflect the direction - if (_frames != null) { - setFrames(_frames[orient]); + if (_aframes != null) { + _aframes.setOrientation(orient); + setFrames(_aframes); } } @@ -162,7 +155,7 @@ public class CharacterSprite extends ImageSprite { super.updateRenderOffset(); - if (_frame != null) { + if (_frames != null) { // our location is based on the character origin coordinates _rxoff = -_xorigin; _ryoff = -_yorigin; @@ -223,7 +216,7 @@ public class CharacterSprite extends ImageSprite /** The animation frames for the active action sequence in each * orientation. */ - protected MultiFrameImage[] _frames; + protected ActionFrames _aframes; /** The origin of the sprite. */ protected int _xorigin, _yorigin; diff --git a/src/java/com/threerings/cast/CompositedActionFrames.java b/src/java/com/threerings/cast/CompositedActionFrames.java index 0602d39f2..2a61f92c4 100644 --- a/src/java/com/threerings/cast/CompositedActionFrames.java +++ b/src/java/com/threerings/cast/CompositedActionFrames.java @@ -1,26 +1,29 @@ // -// $Id: CompositedActionFrames.java,v 1.1 2002/05/04 06:57:24 mdb Exp $ +// $Id: CompositedActionFrames.java,v 1.2 2002/05/04 19:38:13 mdb Exp $ package com.threerings.cast; +import java.awt.Graphics; import java.awt.Image; -import com.threerings.media.sprite.MultiFrameImage; + +import com.threerings.util.DirectionCodes; +import com.threerings.media.util.ImageUtil; 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 +public class CompositedActionFrames + implements ActionFrames, DirectionCodes { /** - * 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. + * Constructs a set of composited action frames with the supplied + * source frames and colorization configuration. The actual component + * frame images will not be composited until they are requested. */ - public CompositedFrameImage ( - String action, MultiFrameImage[] sources, Colorization[][] zations) + public CompositedActionFrames ( + String action, ActionFrames[] sources, Colorization[][] zations) { _action = action; _sources = sources; @@ -28,37 +31,94 @@ public class CompositedFrameImage implements MultiFrameImage // 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]; + _frameCount = _sources[0].getFrameCount(); + _images = new Image[DIRECTION_COUNT][_frameCount]; } // documentation inherited public int getFrameCount () { - return _images.length; + return _frameCount; + } + + // documentation inherited from interface + public int getWidth (int index) + { + return getExampleSource().getWidth(index); + } + + // documentation inherited from interface + public int getHeight (int index) + { + return getExampleSource().getHeight(index); + } + + // documentation inherited from interface + public void paintFrame (Graphics g, int index, int x, int y) + { + g.drawImage(getFrame(index), x, y, null); + } + + // documentation inherited from interface + public boolean hitTest (int index, int x, int y) + { + return ImageUtil.hitTest(getFrame(index), x, y); + } + + // documentation inherited from interface + public void setOrientation (int orient) + { + _orient = orient; + } + + // documentation inherited from interface + public void paintColoredFrame ( + Graphics g, int index, int x, int y, Colorization[] zations) + { + throw new RuntimeException("What you talkin' about Willis?"); } // documentation inherited - public Image getFrame (int index) + protected 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]; + if (_images[_orient] == null) { + _images[_orient] = new Image[_frameCount]; + } + if (_images[_orient][index] == null) { +// Log.info("Compositing [action=" + _action + +// ", orient=" + _orient + ", index=" + index + "]."); + _images[_orient][index] = + TileUtil.compositeFrames(_orient, index, _sources, _zations); + } + return _images[_orient][index]; + } + + /** + * When fetching width and height, we can simply use the first + * component's image set since they must all be the same. + */ + protected ActionFrames getExampleSource () + { + ActionFrames source = _sources[0]; + source.setOrientation(_orient); + return source; } protected String _action; - /** Our source frame images. */ - protected MultiFrameImage[] _sources; + /** The orientation we're currently using. */ + protected int _orient; + + /** The number of frames in each orientation. */ + protected int _frameCount; + + /** Our source action frames. */ + protected ActionFrames[] _sources; /** The colorizations for our source components. */ protected Colorization[][] _zations; /** The frame images. */ - protected Image[] _images; + protected Image[][] _images; } diff --git a/src/java/com/threerings/cast/FrameProvider.java b/src/java/com/threerings/cast/FrameProvider.java index 9ee56c895..e8ebbbab1 100644 --- a/src/java/com/threerings/cast/FrameProvider.java +++ b/src/java/com/threerings/cast/FrameProvider.java @@ -1,10 +1,8 @@ // -// $Id: FrameProvider.java,v 1.1 2001/11/27 08:09:35 mdb Exp $ +// $Id: FrameProvider.java,v 1.2 2002/05/04 19:38:13 mdb Exp $ package com.threerings.cast; -import com.threerings.media.sprite.MultiFrameImage; - /** * Provides a mechanism where by a character component can obtain access * to its image frames for a particular action in an on demand manner. @@ -16,6 +14,6 @@ public interface FrameProvider * the specified action of the specified component. May return null if * the specified action does not exist for the specified component. */ - public MultiFrameImage[] getFrames ( + public ActionFrames getFrames ( CharacterComponent component, String action); } diff --git a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java index ba959134b..4957a8496 100644 --- a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java +++ b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java @@ -1,9 +1,11 @@ // -// $Id: BundledComponentRepository.java,v 1.9 2002/03/16 03:15:04 shaper Exp $ +// $Id: BundledComponentRepository.java,v 1.10 2002/05/04 19:38:14 mdb Exp $ package com.threerings.cast.bundle; +import java.awt.Graphics; import java.awt.Image; +import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; @@ -25,8 +27,7 @@ import com.threerings.resource.ResourceBundle; import com.threerings.resource.ResourceManager; import com.threerings.media.ImageManager; - -import com.threerings.media.sprite.MultiFrameImage; +import com.threerings.media.util.ImageUtil; import com.threerings.media.tile.ImageProvider; import com.threerings.media.tile.TileSet; @@ -34,7 +35,9 @@ import com.threerings.media.tile.NoSuchTileException; import com.threerings.util.DirectionCodes; +import com.threerings.cast.ActionFrames; import com.threerings.cast.CharacterComponent; +import com.threerings.cast.Colorization; import com.threerings.cast.ComponentClass; import com.threerings.cast.ComponentRepository; import com.threerings.cast.FrameProvider; @@ -249,7 +252,7 @@ public class BundledComponentRepository } // documentation inherited - public MultiFrameImage[] getFrames ( + public ActionFrames getFrames ( CharacterComponent component, String action) { // get the tileset for this action @@ -270,14 +273,7 @@ public class BundledComponentRepository try { TileSet fset = aset.clone(path); fset.setImageProvider(this); - - // and create the necessary multiframe image instances - MultiFrameImage[] frames = new MultiFrameImage[DIRECTION_COUNT]; - for (int i = 0; i < frames.length; i++) { - frames[i] = new TileSetFrameImage(fset, i); - } - - return frames; + return new TileSetFrameImage(fset); } catch (CloneNotSupportedException cnse) { Log.warning("Unable to clone tileset [action=" + action + @@ -295,16 +291,15 @@ public class BundledComponentRepository * Used to provide multiframe images using data obtained from a * tileset. */ - protected static class TileSetFrameImage implements MultiFrameImage + protected static class TileSetFrameImage implements ActionFrames { /** * Constructs a tileset frame image with the specified tileset and * for the specified orientation. */ - public TileSetFrameImage (TileSet set, int orientation) + public TileSetFrameImage (TileSet set) { _set = set; - _orient = orientation; } // documentation inherited @@ -313,8 +308,71 @@ public class BundledComponentRepository return _set.getTileCount() / DIRECTION_COUNT; } - // documentation inherited - public Image getFrame (int index) + // documentation inherited from interface + public int getWidth (int index) + { + Image img = getImage(index); + return (img == null) ? 0 : img.getWidth(null); + } + + // documentation inherited from interface + public int getHeight (int index) + { + Image img = getImage(index); + return (img == null) ? 0 : img.getHeight(null); + } + + // documentation inherited from interface + public void paintFrame (Graphics g, int index, int x, int y) + { + Image img = getImage(index); + if (img != null) { + g.drawImage(img, x, y, null); + } + } + + // documentation inherited from interface + public boolean hitTest (int index, int x, int y) + { + Image img = getImage(index); + return (img != null) ? ImageUtil.hitTest(img, x, y) : false; + } + + // documentation inherited from interface + public void setOrientation (int orient) + { + _orient = orient; + } + + // documentation inherited from interface + public void paintColoredFrame ( + Graphics g, int index, int x, int y, Colorization[] zations) + { + BufferedImage img = (BufferedImage)getImage(index); + if (img == null) { + return; + } + + // create a recolored image with which to render + if (zations != null) { + int zcount = zations.length; + Colorization cz; + for (int zz = 0; zz < zcount; zz++) { + if ((cz = zations[zz]) == null) { + continue; + } + img = ImageUtil.recolorImage( + img, cz.rootColor, cz.range, cz.offsets); + } + } + + g.drawImage(img, x, y, null); + } + + /** + * Fetches the requested tile image. + */ + protected Image getImage (int index) { int tileIndex = _orient * getFrameCount() + index; try { diff --git a/src/java/com/threerings/cast/util/TileUtil.java b/src/java/com/threerings/cast/util/TileUtil.java index eab56a77f..e4816a197 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.13 2002/05/04 06:57:24 mdb Exp $ +// $Id: TileUtil.java,v 1.14 2002/05/04 19:38:14 mdb Exp $ package com.threerings.cast.util; @@ -7,76 +7,17 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; -import com.threerings.media.sprite.MultiFrameImage; import com.threerings.media.util.ImageUtil; -import com.threerings.util.DirectionCodes; - import com.threerings.cast.Log; +import com.threerings.cast.ActionFrames; import com.threerings.cast.Colorization; /** * Miscellaneous tile-related utility functions. */ 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 and returns the indexth image from each of the * supplied source multi-frame images to a newly created buffered @@ -85,36 +26,29 @@ public class TileUtil * already sorted into the proper rendering order. */ public static Image compositeFrames ( - int index, MultiFrameImage[] sources, Colorization[][] zations) + int orient, int index, ActionFrames[] sources, + Colorization[][] zations) { Image dest = null; Graphics g = null; +// long start = System.currentTimeMillis(); - 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)); + dest = ImageUtil.createImage(sources[ii].getWidth(index), + sources[ii].getHeight(index)); g = dest.getGraphics(); } - // 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); - } - } - } + // make sure the action frames are configured with the right + // orientation before we render + sources[ii].setOrientation(orient); - // now splat the recolored image onto the target - g.drawImage(simg, 0, 0, null); + // render the particular action from this component into the + // target image + Colorization[] cz = (zations != null) ? zations[ii] : null; + sources[ii].paintColoredFrame(g, index, 0, 0, cz); } // clean up after ourselves @@ -122,13 +56,10 @@ public class TileUtil g.dispose(); } - long now = System.currentTimeMillis(); - Log.info("Composited " + sources.length + " frames in " + - (now-start) + " millis."); +// long now = System.currentTimeMillis(); +// Log.info("Composited " + sources.length + " frames in " + +// (now-start) + " millis."); return dest; } - - /** The image file name suffix appended to component image file names. */ - protected static final String IMAGE_SUFFIX = ".png"; }