first commit

This commit is contained in:
Jose Caban
2025-06-07 01:59:34 -04:00
commit 388ac241f0
3558 changed files with 9116289 additions and 0 deletions

View File

@@ -0,0 +1,645 @@
package edu.gatech.cs2335.lemmings.graphics;
import java.util.List;
import java.util.Vector;
//import java.net.URL;
import java.awt.Point;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
/**
* The tileset class simplifies loading bitmap files that contain
* several tiles. It is a fairly flexible class, but it does impose
* certain restrictions on the way the file is organized. First of
* all, there needs to be a set of control pixels in the upper-right
* corner of the image. The control pixels start at the coordinate
* (width-1, 0) and go down from there in the following order:
* transparency pixel, empty-region pixel, outside anchor pixel,
* used-region pixel, inside anchor pixel; five in all. The
* transparency pixel is used for color-keying, and all the pixels of
* that color in the image will be made transparent. The outside and
* inside anchor pixels do the same thing currently, and are used to
* denote the image anchor. When the tile is blitted using the
* putTile method, the anchor has the coordinates passed in to the
* method, and the rest of the image is drawn relatively to it. In
* other words, suppose we blit to rectangular tiles using the same
* target coordinates. One of the tiles has its anchor in the
* top-left corner, and the other - in the center. Then the center of
* the latter tile will coincide with the top-left corner of the
* former tile. The empty-region and used-region pixels are used to
* denote where the useful areas of the tile are, and which ones can
* be discarded. The reason for that is that the tiles in the tileset
* file have to be arranged in a table. In other words, all tiles in
* one column have to be the same width, and all the tiles in one row
* have to be the same height. That is not to say, however, that the
* width and the height have to be the equal, or that the tiles in
* different rows have to have the same height, and in different
* columns - the same width. Suppose a game has only two tiles. We
* can arrange them in a column. One of the tiles is square, and the
* other - a very thin rectangle. Then, we could mark the whole first
* tile as used, and for the second tile - only the space that is
* actually used up. Since we arranged the tiles in a column, the
* widths of the tiles have to be the same. However, we can mark the
* unused areas of the thin tile with the unused-area pixel color,
* and they will be discarded when blitting.
*
* <PRE>
* Revision History:
* v1.0 (Mar. 11, 2004) - Created the TileSet class
* </PRE>
*
* @author <A HREF="mailto:gtg308i@mail.gatech.edu">Vladimir Urazov</A>
* @version Version 1.0, Mar. 11, 2004
*/
public class TileSet {
/**
* Show debug output?
*/
protected static final boolean DEBUG = false;
/**
* Show lots of debug output?
*/
protected static final boolean VERBOSE = false;
/**
* The list of informations pertaining to the frames in this set.
*/
private FrameInformation[] frameList;
/**
* The name of the file we want to load the image from, in case it
* needs to be reloaded somehow.
*/
private String fileName;
/**
* The image, where the whole tileset is stored.
*/
private BufferedImage framesetImage;
/**
* Creates a new <code>TileSet</code> instance.
*/
public TileSet() {
frameList = null;
fileName = null;
framesetImage = null;
}
/**
* Loads the tileset from a file. Returns true upon success or false
* upon failure.
*
* @param name a <code>String</code> value
* @return a <code>boolean</code> value
*/
public boolean loadTileset(String name) {
//Dimensions of the image
int width = 0;
int height = 0;
//Number of tiles horizontally and vertically
// int xCount = 0;
// int yCount = 0;
//Lists of coordinates for the tile bounds
List horizontalTileBounds = new Vector();
List verticalTileBounds = new Vector();
//Color control
Color transparentColor = null;
Color boundColor = null;
Color anchorColor = null;
Color insideColor = null;
Color insideAnchorColor = null;
Color testColor = null;
//Image info
Raster tilesetRaster = null;
ColorSpace tilesetSpace = null;
//Miscellaneous temporaries
int x = 0;
int y = 0;
int row = 0;
int col = 0;
int currentTile = 0;
// boolean found = false;
//First, clean up if we already have something loaded:
if (!unloadTileset()) {
//Could not clean up...
return false;
}
//Save the name of the file:
fileName = name;
//Load the image from the file:
framesetImage = ImageLoader.getInstance().loadLocalImage(name);
if (framesetImage == null) {
System.err.println("TileSet.loadTileset - could not load image.");
System.err.flush();
return false;
}
//Get image info:
tilesetRaster = framesetImage.getRaster();
tilesetSpace = framesetImage.getColorModel().getColorSpace();
//Get image dimensions:
width = tilesetRaster.getWidth();
height = tilesetRaster.getHeight();
//Define color control variables:
transparentColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 0);
boundColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 1);
anchorColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 2);
insideColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 3);
insideAnchorColor = ImageUtilities.getInstance()
.getPixel(framesetImage, width - 1, 4);
testColor = Color.black;
//Find the coordinates of the vertical tile bounds:
for (x = 0; x < width; x++) {
testColor = ImageUtilities.getInstance().getPixel(framesetImage, x, 0);
if (testColor.equals(transparentColor)) {
//Add the coordinate to the list:
if (VERBOSE) {
System.out.println("TileSet: Adding x coordinate - " + x);
System.out.flush();
}
verticalTileBounds.add(new Integer(x));
}
}
//Find the coordinates of the horizontal tile bounds:
for (y = 0; y < height; y++) {
testColor = ImageUtilities.getInstance().getPixel(framesetImage, 0, y);
if (testColor.equals(transparentColor)) {
//Add the coordinate to the list:
if (VERBOSE) {
System.out.println("TileSet: Adding y coordinate - " + y);
System.out.flush();
}
horizontalTileBounds.add(new Integer(y));
}
}
//Allocate the list of frame infos:
frameList = new FrameInformation[(verticalTileBounds.size() - 1)
* (horizontalTileBounds.size() - 1)];
//Scan the tiles in:
for (row = 0; row < horizontalTileBounds.size() - 1; row++) {
for (col = 0; col < verticalTileBounds.size() - 1; col++) {
currentTile = col + row * (verticalTileBounds.size() - 1);
if (DEBUG) {
System.out.println("TileSet: Processing tile (" + col + ", "
+ row + ") - #" + currentTile);
System.out.flush();
}
frameList[currentTile] = new FrameInformation();
//Determine the anchor point:
frameList[currentTile].getAnchor().x
= findFirstOccurrence(((Integer) verticalTileBounds.get(col))
.intValue(),
((Integer) verticalTileBounds.get(col + 1))
.intValue(),
((Integer) horizontalTileBounds.get(row))
.intValue(),
anchorColor, insideAnchorColor, false);
frameList[currentTile].getAnchor().y
= findFirstOccurrence(((Integer) horizontalTileBounds.get(row))
.intValue(),
((Integer) horizontalTileBounds.get(row + 1))
.intValue(),
((Integer) verticalTileBounds.get(col))
.intValue(),
anchorColor, insideAnchorColor, true);
//Find the first tile pixel:
frameList[currentTile].getSource().x
= findFirstOccurrence(((Integer) verticalTileBounds.get(col))
.intValue(),
((Integer) verticalTileBounds.get(col + 1))
.intValue(),
((Integer) horizontalTileBounds.get(row))
.intValue(),
insideColor, insideAnchorColor, false);
frameList[currentTile].getSource().y
= findFirstOccurrence(((Integer) horizontalTileBounds.get(row))
.intValue(),
((Integer) horizontalTileBounds.get(row + 1))
.intValue(),
((Integer) verticalTileBounds.get(col))
.intValue(),
insideColor, insideAnchorColor, true);
if (frameList[currentTile].getSource().x == 0) {
//Could not find. Use default:
frameList[currentTile].getSource().x
= ((Integer) verticalTileBounds.get(col)).intValue() + 1;
}
if (frameList[currentTile].getSource().y == 0) {
//Could not find. Use default:
frameList[currentTile].getSource().y
= ((Integer) horizontalTileBounds.get(row)).intValue() + 1;
}
//Find the last tile pixel:
frameList[currentTile].getSource().width
= findLastOccurrence(((Integer) verticalTileBounds.get(col))
.intValue(),
((Integer) verticalTileBounds.get(col + 1))
.intValue(),
((Integer) horizontalTileBounds.get(row))
.intValue(),
insideColor, insideAnchorColor, false)
- frameList[currentTile].getSource().x + 1;
frameList[currentTile].getSource().height
= findLastOccurrence(((Integer) horizontalTileBounds.get(row))
.intValue(),
((Integer) horizontalTileBounds.get(row + 1))
.intValue(),
((Integer) verticalTileBounds.get(col))
.intValue(),
insideColor, insideAnchorColor, true)
- frameList[currentTile].getSource().y + 1;
if (frameList[currentTile].getSource().width <= 0) {
//Could not find. Use default:
frameList[currentTile].getSource().width
= ((Integer) verticalTileBounds.get(col + 1)).intValue()
- frameList[currentTile].getSource().x;
}
if (frameList[currentTile].getSource().height <= 0) {
//Could not find. Use default:
frameList[currentTile].getSource().height
= ((Integer) horizontalTileBounds.get(row + 1)).intValue()
- frameList[currentTile].getSource().y;
}
//Calculate tile extent:
frameList[currentTile]
.setExtent(new Rectangle(frameList[currentTile].getSource()));
frameList[currentTile].getExtent()
.translate(-frameList[currentTile].getAnchor().x,
-frameList[currentTile].getAnchor().y);
}
}
specialLoad();
return true;
}
/**
* Describe <code>specialLoad</code> method here.
*
* @return a <code>boolean</code> value
*/
protected boolean specialLoad() {
return true;
}
/**
* Describe <code>getFrameList</code> method here.
*
* @return a <code>FrameInformation[]</code> value
*/
protected FrameInformation[] getFrameList() {
return frameList;
}
/**
* Describe <code>getFramesetImage</code> method here.
*
* @return a <code>BufferedImage</code> value
*/
protected BufferedImage getFramesetImage() {
return framesetImage;
}
/**
* Finds the coordinate of the first occurrence of one of the test colors.
*
* @param lowBound an <code>int</code> value
* @param highBound an <code>int</code> value
* @param coordinate an <code>int</code> value
* @param c1 a <code>Color</code> value
* @param c2 a <code>Color</code> value
* @param vertical a <code>boolean</code> value
* @return an <code>int</code> value
*/
protected int findFirstOccurrence(int lowBound, int highBound,
int coordinate,
Color c1, Color c2, boolean vertical) {
Color testColor = null;
int x = 0;
int y = 0;
for (int i = lowBound + 1; i < highBound; i++) {
if (vertical) {
x = coordinate;
y = i;
} else {
x = i;
y = coordinate;
}
testColor = ImageUtilities.getInstance().getPixel(framesetImage, x, y);
if (testColor.equals(c1) || testColor.equals(c2)) {
return i;
}
}
return 0;
}
/**
* Finds the coordinate of the last occurrence of one of the test colors.
*
* @param lowBound an <code>int</code> value
* @param highBound an <code>int</code> value
* @param coordinate an <code>int</code> value
* @param c1 a <code>Color</code> value
* @param c2 a <code>Color</code> value
* @param vertical a <code>boolean</code> value
* @return an <code>int</code> value
*/
protected int findLastOccurrence(int lowBound, int highBound,
int coordinate,
Color c1, Color c2, boolean vertical) {
Color testColor = null;
int x = 0;
int y = 0;
for (int i = highBound - 1; i > lowBound; i--) {
if (vertical) {
x = coordinate;
y = i;
} else {
x = i;
y = coordinate;
}
testColor = ImageUtilities.getInstance().getPixel(framesetImage, x, y);
if (testColor.equals(c1) || testColor.equals(c2)) {
return i;
}
}
return 0;
}
/**
* If the surface was lost, reloads it, but does not reparse the file.
*
* @return a <code>boolean</code> value
*/
public boolean reloadTileset() {
//Load the image from the file:
framesetImage = ImageLoader.getInstance().loadLocalImage(getFileName());
if (framesetImage == null) {
System.err.println("TileSet.reloadTileset - could not load image");
System.err.flush();
return false;
}
return true;
}
/**
* Performs all the necessary clean-up operations.
*
* @return a <code>boolean</code> value
*/
public boolean unloadTileset() {
//Clean up the tile list if necessary:
if (frameList != null) {
frameList = null;
fileName = null;
framesetImage = null;
}
return true;
}
/**
* Returns the number of tiles in the tileset.
* @return an <code>int</code> value
*/
public int getTileCount() {
return frameList.length;
}
/**
* Returns the image on which the tileset resides.
* @return a <code>BufferedImage</code> value
*/
public BufferedImage getImage() {
return framesetImage;
}
/**
* Returns the name of the file containing the tileset in a string.
* @return a <code>String</code> value
*/
public String getFileName() {
return fileName;
}
/**
* Returns the dimensions of the largest tile in the tileset.
*
* @return a <code>Dimension</code> value
*/
public Dimension getLargestDimension() {
Dimension result = new Dimension();
for (int i = 0; i < frameList.length; i++) {
int width = frameList[i].getSource().width;
int height = frameList[i].getSource().height;
if (width > result.width) {
result.width = width;
}
if (height > result.height) {
result.height = height;
}
}
return result;
}
/**
* Returns the dimensions of the tile with the specified id.
*
* @param tileNum an <code>int</code> value
* @return a <code>Dimension</code> value
*/
public Dimension getDimension(int tileNum) {
Dimension result = new Dimension();
result.width = frameList[tileNum].getSource().width;
result.height = frameList[tileNum].getSource().height;
return result;
}
/**
* Returns the extent of the tile with the specified id.
*
* @param tileNum an <code>int</code> value
* @return a <code>Rectangle</code> value
*/
public Rectangle getExtent(int tileNum) {
Rectangle result = new Rectangle(frameList[tileNum].getExtent());
return result;
}
/**
* Puts the tile with the specified number onto the graphics context
* passed in at the specified coordinates. Note that the position of
* the tile will depend on its anchor point, which will be put
* exactly at the coordinates passed in.
*
* @param destination the context to which we want to draw the tile.
* @param coordinates the coordinates at which we want to draw the tile.
* @param tileNum the number of the tile we want to draw. The tiles
* will be numbered automatically, from left to right, from top to
* bottom, when the image file is parsed.
*/
public void drawTile(Graphics destination, Point coordinates, int tileNum) {
if (DEBUG) {
System.out.println("TileSet.drawTile - Drawing tile " + tileNum + " at ("
+ coordinates.x + ", " + coordinates.y + ")");
System.out.flush();
}
frameList[tileNum].getExtent().translate(coordinates.x, coordinates.y);
//Fetch raster:
Rectangle src = frameList[tileNum].getSource();
if (VERBOSE) {
System.out.println("TileSet.drawTile: src rectangle - " + src);
System.out.flush();
}
Raster data = framesetImage.getData(src);
if (VERBOSE) {
System.out.println("TileSet.drawTile: data - " + data);
System.out.flush();
}
BufferedImage temp = new BufferedImage(data.getWidth(),
data.getHeight(),
framesetImage.getType());
temp.setData(data.createRaster(data.getSampleModel(),
data.getDataBuffer(), null));
if (VERBOSE) {
System.out.println("TileSet.drawTile: temp - " + temp);
System.out.flush();
}
//Render tile:
destination.drawImage(temp,
frameList[tileNum].getExtent().x,
frameList[tileNum].getExtent().y,
null);
frameList[tileNum].getExtent().translate(-coordinates.x, -coordinates.y);
}
/**
* Information structure for one tile in the tileset.
*/
protected class FrameInformation {
/**
* This rectangle represents the location of the frame in the
* master frame set.
*/
private Rectangle source;
/**
* The extent of the frame.
*/
private Rectangle extent;
/**
* The anchor point of the frame. That is, when we say that we
* want to render the frame at certain coordinates, the point with
* the coordinates stored here, relative to the top-left corner of
* the frame, will be drawn at the specified coordinates.
*/
private Point anchor;
/**
* Creates a new <code>FrameInformation</code>
* instance. Initializes all of the data members to contain all
* default values (zeros).
*/
public FrameInformation() {
source = new Rectangle();
extent = new Rectangle();
anchor = new Point();
}
/**
* Get the value of source.
* @return value of source.
*/
public Rectangle getSource() {
return source;
}
/**
* Set the value of source.
* @param v Value to assign to source.
*/
public void setSource(Rectangle v) {
this.source = v;
}
/**
* Get the value of extent.
* @return value of extent.
*/
public Rectangle getExtent() {
return extent;
}
/**
* Set the value of extent.
* @param v Value to assign to extent.
*/
public void setExtent(Rectangle v) {
this.extent = v;
}
/**
* Get the value of anchor.
* @return value of anchor.
*/
public Point getAnchor() {
return anchor;
}
/**
* Set the value of anchor.
* @param v Value to assign to anchor.
*/
public void setAnchor(Point v) {
this.anchor = v;
}
}
}