// // $Id: TileSet.java,v 1.29 2002/06/19 08:27:06 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.StringUtil; import com.threerings.media.Log; import com.threerings.media.util.Colorization; import com.threerings.media.util.ImageUtil; /** * A tileset stores information on a single logical set of tiles. It * provides a clean interface for the {@link TileManager} or other * entities to retrieve individual tiles from the tile set and * encapsulates the potentially sophisticated process of extracting the * tile image from a composite tileset image. * *
Tiles are referenced by their tile id. The tile id is essentially * the tile number, assuming the tile at the top-left of the image is tile * id zero and tiles are numbered, in ascending order, left to right, top * to bottom. * *
This class is serializable and will be serialized, so derived
* classes should be sure to mark non-persistent fields as
* transient.
*/
public abstract class TileSet
implements Cloneable, Serializable
{
/**
* Configures this tileset with an image provider that it can use to
* load its tileset image. This will be called automatically when the
* tileset is fetched via the {@link TileManager}.
*/
public void setImageProvider (ImageProvider improv)
{
_improv = improv;
}
/**
* Returns the tileset name.
*/
public String getName ()
{
return _name;
}
/**
* Specifies the tileset name.
*/
public void setName (String name)
{
_name = name;
}
/**
* Sets the path to the image that will be used by this tileset. This
* must be called before the first call to {@link #getTile}.
*/
public void setImagePath (String imagePath)
{
_imagePath = imagePath;
// clear out any reference to a loaded image
_tilesetImg = null;
}
/**
* Returns the path to the composite image used by this tileset.
*/
public String getImagePath ()
{
return _imagePath;
}
/**
* Returns the number of tiles in the tileset.
*/
public abstract int getTileCount ();
/**
* Creates a copy of this tileset with the supplied colorizations
* applied to its source image.
*/
public TileSet cloneColorized (Colorization[] zations)
{
TileSet tset = null;
try {
tset = (TileSet)clone();
} catch (CloneNotSupportedException cnse) {
Log.warning("Unable to clone tileset prior to colorization " +
"[tset=" + this +
", zations=" + StringUtil.toString(zations) +
", 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;
}
/**
* Returns a new tileset that is a clone of this tileset with the
* image path updated to reference the given path. Useful for
* configuring a single tileset and then generating additional
* tilesets with new images with the same configuration.
*/
public TileSet clone (String imagePath)
throws CloneNotSupportedException
{
TileSet dup = (TileSet)clone();
dup.setImagePath(imagePath);
return dup;
}
/**
* Creates a {@link Tile} object from this tileset corresponding to
* the specified tile id and returns that tile. A null tile will never
* be returned, but one with an error image may be returned if a
* problem occurs loading the underlying tileset image.
*
* @param tileIndex the index of the tile in the tileset. Tile indexes
* start with zero as the upper left tile and increase by one as the
* tiles move left to right and top to bottom over the source image.
*
* @return the tile object.
*
* @exception NoSuchTileException thrown if the specified tile index
* is out of range for this tileset.
*/
public Tile getTile (int tileIndex)
throws NoSuchTileException
{
// bail if there's no such tile
if (tileIndex < 0 || tileIndex >= getTileCount()) {
throw new NoSuchTileException(tileIndex);
}
// 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 and initialize the tile object
Tile tile = createTile(tileIndex, tsimg);
initTile(tile);
return tile;
}
/**
* Computes and returns the bounds for the specified tile based on the
* mechanism used by the derived class to do such things. The width
* and height of the bounds should be the size of the tile image and
* the x and y offset should be the offset in the tileset image for
* the image data of the specified tile.
*
* @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);
/**
* 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.
*
* @return a configured tile.
*/
protected Tile createTile (int tileIndex, Image tilesetImage)
{
return new Tile(tilesetImage,
computeTileBounds(tileIndex, tilesetImage));
}
/**
* Initializes the supplied tile. Derived classes can override this
* method to add in their own tile information, but should be sure to
* call super.initTile().
*
* @param tile the tile to initialize.
*/
protected void initTile (Tile tile)
{
// 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 #getTileImage} or {@link #getTile} is used.
*
* @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.
*/
public String toString ()
{
StringBuffer buf = new StringBuffer("[");
toString(buf);
return buf.append("]").toString();
}
/**
* Derived classes can override this, calling
* super.toString(buf) and then appending additional
* information to the buffer.
*/
protected void toString (StringBuffer buf)
{
buf.append("name=").append(_name);
buf.append(", path=").append(_imagePath);
buf.append(", tileCount=").append(getTileCount());
}
/** The path to the file containing the tile images. */
protected String _imagePath;
/** The tileset name. */
protected String _name;
/** 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;
/** Temporary HACKOLA. */
protected static Colorization[] ZATIONS = new Colorization[] {
new Colorization(-1, new Color(0x8A1A76),
new float[] { .1f, .3f, 0.6f },
new float[] { -.85f, .3f, 0f }),
new Colorization(-1, new Color(0x2A864E),
new float[] { .1f, .3f, 0.6f },
new float[] { .71f, 0f, .07f }),
};
}