/*
* $Id$
*
* $Log$
*/
/* Lots of graphical/image libraries are needed here. */
import java.awt.Canvas;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.RGBImageFilter;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
/**
* Represents an image that can be broken into pieces. The pieces are basically tiles for
* a game map and must all be the same height/width.
*
* @author Andy Fanton (arf3827@cs.rit.edu)
* @author Jessica D. Bayliss (jdb@cs.rit.edu)
*
*/
public class TiledImage {
/** The default tile height in pixels */
public static final int DEFAULT_TILE_HEIGHT = 32;
/** The default tile width in pixels */
public static final int DEFAULT_TILE_WIDTH = 32;
/* The big image containing sub-images (representing tiles on a game map) */
protected BufferedImage image;
/* The big image's height/width and rows/cols */
protected int height, width, rows, cols;
/* Where the big image comes from */
protected String sourceFile;
/**
* Creates a TiledImage from a default file name. Problems occur if the default
* is not in the right place in the file system.
*/
public TiledImage() {
// The PondMazeEditor has a hard coded reference to a default load
// file in it. The variable is called TILESET. Call the constructor
// below with the TILESET variable in PondMazeEditor.
this(PondMazeEditor.TILESET);
}
/**
* Creates a TiledImage from a given filename.
*
* @param fileName The file to create the tiled image from.
*/
public TiledImage( String fileName ) {
// use the this operator and call the method below with the variables:
// filename, DEFAULT_TILE_HEIGHT, and DEFAULT_TILE_WIDTH
this(fileName,DEFAULT_TILE_HEIGHT,DEFAULT_TILE_WIDTH);
}
/**
* Full Constructor
*
* @param fileName the name of the file to create the image from
* @param tileHeight the height of the individual tiles
* @param tileWidth the width of the individual tiles
*/
public TiledImage( String fileName, int tileHeight, int tileWidth ) {
// ah-ha! we must do the actual work of creating the image here
// setup the instance variables with the appropriate value:
// sourceFile should have value fileName
// height should have value tileHeight
// width should have value tileWidth
sourceFile=fileName;
height=tileHeight;
width=tileWidth;
// Get an icon representing the file
// Icons are of type ImageIcon, so we must create a new
// ImageIcon object with the fileName as an argument.
// Then set rows/cols to be the icon height/width
// divided by the tile height/width
// so that we're only dealing with the appropriate icon size.
ImageIcon icon=new ImageIcon(fileName);
rows=icon.getIconHeight()/tileHeight;
cols=icon.getIconWidth()/tileWidth;
// Give the image variable a value:
// image should be of type BufferedImage which is created with the values
// cols*width, rows*height, and BufferedImage.TYPE_INT_RGB
image=new BufferedImage(cols*width,rows*height,BufferedImage.TYPE_INT_RGB);
// Get the source image:
// sourceImage is of type Image and is gotten from the above
// BufferedImage with its method
// called getImage() (no args)
Image sourceImage=icon.getImage();
// Draw image into buffer - if we don't do this,
// we get black where we should draw
// Uncomment the following code for this part:
Graphics g = image.getGraphics();
g.drawImage( sourceImage, 0, 0, null );
}
/**
* Filters the image to create transparency. Assume that this
* method works.
*
* @param sourceImage
* @return
*/
public void addTransparency( Component context, final int backgroundColor ) {
// Create anonymous RBG Filter
// Anonymous classes are covered in CS2 and give you a way to create very small
// classes inside of methods (with their own methods/var's for cases where there isn't enough stuff there
// to bother creating a separate class file for it. You will notice that the parent class
// (TiledImage) creates a couple of extra class files (TiledImage$1.class, etc.) and these
// anonymous classes are the reason for that.
RGBImageFilter filter =
new RGBImageFilter() {
{ canFilterIndexColorModel = true; }
public int filterRGB( int x, int y, int rgb ) {
if ( rgb == backgroundColor ) {
//System.out.println( "(" + x + "," + y + "):clear " );
return 0x000000ff;
} else {
//System.out.println( "(" + x + "," + y + "):" + rgb );
return rgb;
}
}
};
// Create a filtered image source
FilteredImageSource newSource =
new FilteredImageSource( image.getSource(), filter );
// Create new image using a given component
Image newImage = context.createImage( newSource );
image = new BufferedImage( cols * width, rows * height,
BufferedImage.TYPE_INT_RGB );
// make sure we draw into the buffer
Graphics g = image.getGraphics();
g.drawImage( newImage, 0, 0, null );
}
/**
* Gets the whole image represented by this object
*
* @return the image held by this object
*/
public Image getFullImage(){
// return the real image variable instead of null
return image;
}
/**
* Gets the specified tile as an image
*
* @param x the x index of the desired tile
* @param y the y index of the desired tile
* @return
*/
public Image getImage( int x, int y ) {
// Image has a method called getSubimage that should be called with
// x*width, y*height, width-1, and height-1
// That subimage should then be returned
return image.getSubimage(x*width,y*height,width-1,height-1);
}
/**
* Gets the specified tile as an image icon
*
* @param row the row index of the desired tile
* @param col the col index of the desired tile
* @return
*/
public ImageIcon getIcon( int row, int col ) {
// return a new ImageIcon object that is created with the Image returned
// by getImage above with the appropriate row/col
return new ImageIcon(getImage(col,row));
}
/**
* Gets all of the tiles as an array of images
*
* @return an image array containing all of the tiles
*/
public Image[][] getImages() {
// oooooh, double array
Image[][] returnArray = new Image[rows][cols];
// now write the double for loop that calls getImage across
// all columns and then incrementing rows. Note that columns are in
// the x direction (horizontal) and rows are in the y direction (vertical).
// Then return the array.
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
returnArray[i][j]=getImage(j,i);
}
}
return returnArray;
}
/**
* Get the height of each tile
*
* @return the tile height
*/
public int getTileHeight(){
// return the real height variable
return height;
}
/**
* Get the width of each tile
*
* @return the tile width
*/
public int getTileWidth(){
// return the real width variable
return width;
}
/**
* Gets all of the tiles as an array of image icons
*
* @return an image icon array containing all of the tiles
*/
public ImageIcon[][] getIcons() {
ImageIcon[][] returnArray = new ImageIcon[rows][cols];
// go through a double for look and give the return array
// a list of icons - call getIcon with the two for loop indices
// as arguments.
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
returnArray[i][j]=getIcon(j,i);
}
}
// Then return the array.
return returnArray;
}
/**
* Gets all of the tiles as a single-dimension array
*
* @return a single dimension array holding all of the icons
*/
public ImageIcon[] getIconList() {
ImageIcon[] iconList = new ImageIcon[rows * cols];
int offset = 0;
// This should be a double for loop that goes across columns
// and then rows, except that it returns a single dimensional
// array - you'll have to do some basic "offset" math inside
// the array brackets to figure out where the icons should go
// getIcon(i, j) (if i and j are your indices) will be assigned
// to each element in the imageList variable. You just have to
// figure out how to put them into a 1d array in the correct order.
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
iconList[offset+j]=getIcon(i,j);
}
offset+=cols;
}
return iconList;
}
/**
* Gets all of the tiles as a single-dimension array
*
* @return a single dimension array holding all of the icons
*/
public Image[] getImageList() {
Image[] imageList = new Image[rows * cols];
int offset = 0;
// This is almost exactly like the above except that it
// assigns images rather than icons: getImage(i, j)
// Again, there's a double for loop involved.
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
imageList[offset+j]=getImage(i,j);
}
offset+=cols;
}
// Now return the image list.
return imageList;
}
/**
* Gets the file from which this image was made
*
* @return name of image file
*/
public String getSource() {
// return the real sourceFile variable
return sourceFile;
}
/**
* Main method; tests this object. This is a very basic version of a test class involving
* showing several images and tiling them across the screen. Can you test more of the
* functionality of the class by drawing different items in different places???
*
* @param args The image file to use as a test file.
*/
public static void main( String[] args ) {
// when you have pieces of code finished, uncomment
// this code or write more to test your code.
// Make sure there was at least one command line argument
if ( args.length < 1 ) {
System.out.println( "Please specify image file" );
System.exit(0);
}
// Create test object
final ImageIcon sourceImage = new ImageIcon( args[0] );
final TiledImage testImage = new TiledImage( args[0] );
final Image[][] images = testImage.getImages();
// Create GUI
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
// set the window size - you may need to change it as you change this class
frame.setSize( 250, 250 );
// Create anonymous canvas
// Anonymous classes are covered in CS2 and give you a way to create very small
// classes inside of methods (with their own methods/var's for cases where there isn't enough stuff there
// to bother creating a separate class file for it. You will notice that the parent class
// (TiledImage) creates a couple of extra class files (TiledImage$1.class, etc.) and these
// anonymous classes are the reason for that.
Canvas canvas = new Canvas(){
public void paint( Graphics g ) {
for ( int i = 0; i < images.length; i++ ) {
for ( int j = 0; j < images[i].length; j++ ) {
g.drawImage( images[i][j], i * 32, j * 32, 32, 32, null );
}
}
}
};
Container content = frame.getContentPane();
content.add( canvas, null, 0 );
frame.setVisible( true );
}
}