Applying Image Effects


Loading Images

The ImagesLoader class can load four different formats of images, which I call o, n, s, and g images. The images are assumed to be in a local JAR file in a subdirectory Images/ below ImagesLoader. They are loaded as BufferedImages using ImageIO's read( ), so they can become managed images.

The typical way of using an ImagesLoader object is to supply it with a configuration file containing the filenames of the images that should be loaded before game play

Figure 6-2. Class diagrams for ImagesTests


begins. However, it is possible to call ImagesLoader's load methods at any time during execution.

Here is the imsInfo.txt configuration file used in ImagesTests:

     // imsInfo.txt images     o atomic.gif     o balls.jpg     o bee.gif     o cheese.gif     o eyeChart.gif     o house.gif     o pumpkin.png     o scooter.gif     o ufo.gif     o owl.png     n numbers*.gif  6     n figure*.gif 9     g fighter  left.gif right.gif still.gif up.gif         s cars.gif 8     s cats.gif 6     s kaboom.gif 6     o basn6a08.png     o basn6a16.png

Blank lines, and lines beginning with //, are ignored by the loader. The syntax for the four image formats is:

     o <fnm>     n <fnm*.ext> <number>     s <fnm> <number>     g <name> <fnm> [ <fnm> ]*

An o line causes a single filename, called <fnm>, to be loaded from Images/.

A n line loads a series of numbered image files, whose filenames use the numbers 0-<number>-1 in place of the * character in the filename. For example:

     n numbers*.gif  6

This indicates that the files numbers0.gif, numbers1.gif, and so forth, up to numbers5.gif, should be loaded.

An s line loads a strip file (called fnm) containing a single row of <number> images. After the file's graphic has been loaded, it's automatically divided up into the component images. For instance:

     s kaboom.gif 6

This refers to the strip file kaboom.gif containing a row of six images, as shown in Figure 6-3.

Figure 6-3. The kaboom.gif strip file


A g line specifies a group of files with different names. After being loaded, the images can be accessible using a positional notation or by means of their filenames (minus the extension). For example, the fighter g images are defined this way:

     g fighter  left.gif right.gif still.gif up.gif

Subsequently, the image in right.gif can be accessed using the number 1 or the string "right".

Internal Data Structures

The ImagesLoader object creates two main data structures as it loads the images, both of them HashMaps:

     private HashMap imagesMap, gNamesMap;

The imagesMap key is the image's name, and its value is an ArrayList of BufferedImage objects associated with that name. The exact meaning of the name depends on the type of image loaded:

  • For an o image (e.g., "o atomic.gif"), the name is the filename minus its extension (i.e., atomic), and the ArrayList holds just a single image.

  • For an n image (e.g., "n numbers*.gif 6"), the name is the part of the filename before the * (i.e., numbers), and the ArrayList holds several images (six in this case).

  • For an s image (e.g., "s cars.gif 8"), the name is the filename minus the extension (i.e., cars), and the ArrayList holds the images pulled from the strip graphic (eight in this example).

  • For a g image (e.g., "g fighter left.gif right.gif still.gif up.gif"), the name is the string after the g character (i.e., fighter), and the ArrayList is as large as the sequence of filenames given (four).

The loading of g images also causes updates to the gNamesMap HashMap. Its key is the g name (e.g., fighter), but its value is an ArrayList of filename strings (minus their extensions). For instance, the fighter name has an ArrayList associated with it holding the strings "left", "right", "still", and "up".

Getting an Image

The image accessing interface is uniform and independent of whether o, n, s, or g images are being accessed.

Three public getImage( ) methods are in ImagesLoader and getImages( ). Their prototypes are shown here:

     BufferedImage getImage(String name);     BufferedImage getImage(String name, int posn);     BufferedImage getImage(String name, String fnmPrefix);     ArrayList getImages(String name);

The single argument version of getImage( ) returns the image associated with name and is intended primarily for accessing o images, which only have a single image. If an n, s, or g image is accessed, then the first image in the ArrayList will be returned.

The two-argument version of getImage( ), which takes an integer position argument, is more useful for accessing n, s, and g names with multiple images in their ArrayLists. If the supplied number is negative, then the first image will be returned. If the number is too large, then it will be reduced modulo the ArrayList size.

The third getImage( ) method takes a String argument and is aimed at g images. The String should be a filename, which is used to index into the g name's ArrayList.

The getImages( ) method returns the entire ArrayList for the given name.

Using ImagesLoader

ImagesTests employs ImagesLoader by supplying it with an images configuration file:

     ImagesLoader imsLoader = new ImagesLoader("imsInfo.txt");

The ImagesLoader constructor assumes the file (and all the images) is in the Images/ subdirectory below the current directory, and everything is packed inside a JAR.

Details about creating the JAR are given at the end of this chapter.


Loading o images is straightforward:

     BufferedImage atomic = imsLoader.getImage("atomic");

Loading n, s, and g images usually requires a numerical value:

     BufferedImage cats1 = imsLoader.getImage("cats", 1);

A related method is numImages( ), which returns the number of images associated with a given name:

     int numCats = imsLoader.numImage("cats");

g images can be accessed using a filename prefix:

     BufferedImage leftFighter = imsLoader.getImage("fighter", "left");

If a requested image cannot be found, then null will be returned by the loader.

An alternative way of using ImagesLoader is to create an empty loader (in other words, no configuration file is supplied to the constructor). Then public methods for loading o, n, s, and g images can then be called by the application, rather than being handled when a configuration file is loaded:

     ImagesLoader imsLoader = new ImagesLoader( );  // empty loader     imsLoader.loadSingleImage("atomic.gif");  // load images at run-rime     imsLoader.loadNumImages("numbers*.gif", 6);     imsLoader.loadStripImages("kaboom.gif", 6);     String[] fnms = {"left.gif", "right.gif", "still.gif", "up.gif"};     imsLoader.loadGroupImages("fighter", fnms );

Implementation Details

A large part of ImagesLoader is given over to parsing and error checking. The top-level method for parsing the configuration file is loadImagesFile( ):

     private void loadImagesFile(String fnm)     {       String imsFNm = IMAGE_DIR + fnm;       System.out.println("Reading file: " + imsFNm);       try {         InputStream in = this.getClass( ).getResourceAsStream(imsFNm);         BufferedReader br = new BufferedReader(                                   new InputStreamReader(in));         String line;         char ch;         while((line = br.readLine( )) != null) {           if (line.length( ) == 0)  // blank line             continue;           if (line.startsWith("//"))   // comment             continue;           ch = Character.toLowerCase( line.charAt(0) );           if (ch == 'o')  // a single image             getFileNameImage(line);           else if (ch == 'n')  // a numbered sequence of images             getNumberedImages(line);           else if (ch == 's')  // an images strip             getStripImages(line);           else if (ch == 'g')  // a group of images             getGroupImages(line);           else             System.out.println("Do not recognize line: " + line);         }         br.close( );       }       catch (IOException e)       { System.out.println("Error reading file: " + imsFNm);         System.exit(1);       }     }  // end of loadImagesFile( )

One line of the file is read at a time, and a multiway branch decides which syntactic form should be processed, depending on the first character on the input line. The input stream coming from the configuration file is created using Class.getResourceAsStream( ), which is needed when the application and all the resources all wrapped up inside a JAR.

getFileNameImage( ) is typical in that it extracts the tokens from the line and processes them by calling loadSingleImage( ):

     private void getFileNameImage(String line)     // format is   o <fnm>     { StringTokenizer tokens = new StringTokenizer(line);           if (tokens.countTokens( ) != 2)         System.out.println("Wrong no. of arguments for " + line);       else {         tokens.nextToken( );    // skip command label         System.out.print("o Line: ");         loadSingleImage( tokens.nextToken( ) );       }     }

loadSingleImage( ) is the public method for loading an o image. If an entry for the image's name doesn't exist, then imagesMap will be extended with a new key (holding name) and an ArrayList containing a single BufferedImage:

     public boolean loadSingleImage(String fnm)     {       String name = getPrefix(fnm);       if (imagesMap.containsKey(name)) {         System.out.println( "Error: " + name + "already used");         return false;       }       BufferedImage bi = loadImage(fnm);       if (bi != null) {         ArrayList imsList = new ArrayList( );         imsList.add(bi);         imagesMap.put(name, imsList);         System.out.println("  Stored " + name + "/" + fnm);         return true;       }       else         return false;     }

Image Loading

We arrive at the image-loading method, loadImage( ), which is at the heart of the processing of n and g lines. Its implementation is almost identical to the loadImage( ) method described in the section "The Internals of BufferedImage" in Chapter 5:

     public BufferedImage loadImage(String fnm)     {       try {          BufferedImage im =  ImageIO.read(                    getClass( ).getResource(IMAGE_DIR + fnm) );          int transparency = im.getColorModel( ).getTransparency( );          BufferedImage copy =  gc.createCompatibleImage(                                   im.getWidth( ), im.getHeight( ),                                   transparency );          // create a graphics context          Graphics2D g2d = copy.createGraphics( );              // reportTransparency(IMAGE_DIR + fnm, transparency);          // copy image          g2d.drawImage(im,0,0,null);          g2d.dispose( );          return copy;       }       catch(IOException e) {          System.out.println("Load Image error for " +                        IMAGE_DIR + "/" + fnm + ":\n" + e);          return null;       }     } // end of loadImage( ) using ImageIO

reportTransparency( ) is a debugging utility for printing out the transparency value of the loaded image. It's useful for checking if the transparency/translucency of the image has been detected.

As this is the version of code ready to use, reportTransparency( ) is commented out. For debugging purposes, you may want to uncomment this method's invocation.


ImagesLoader contains two other versions of loadImages( ), called loadImages2( ) and loadImages3( ). They play no part in the functioning of the class and are only included to show how BufferedImages can be loaded using ImageIcon or Image's getImage( ). The ImageIcon code in loadImages2( ) uses this code:

     ImageIcon imIcon = new ImageIcon( getClass( ).getResource(IMAGE_DIR + fnm) );

Then, it calls makeBIM( ) to convert its Image into a BufferedImage. makeBIM( ) is described in the section "From Image to BufferedImage" in Chapter 5.

The Image code in loadImage3( ) uses a MediaTracker to delay execution until the image is fully loaded and then calls makeBIM( ) to obtain a BufferedImage.

Loading Strip File Images

The images from a strip file are obtained in steps: First, the entire graphic is loaded from the file, cut into pieces, and each resulting image is placed in an array. This array is subsequently stored as an ArrayList in imagesMap under the s name:

     public BufferedImage[] loadStripImageArray(String fnm, int number)     {       if (number <= 0) {         System.out.println("number <= 0; returning null");         return null;       }       BufferedImage stripIm;       if ((stripIm = loadImage(fnm)) == null) {         System.out.println("Returning null");         return null;       }       int imWidth = (int) stripIm.getWidth( ) / number;       int height = stripIm.getHeight( );       int transparency = stripIm.getColorModel( ).getTransparency( );       BufferedImage[] strip = new BufferedImage[number];       Graphics2D stripGC;       // each BufferedImage from the strip file is stored in strip[]       for (int i=0; i < number; i++) {         strip[i]=gc.createCompatibleImage(imWidth,height,transparency);         // create a graphics context         stripGC = strip[i].createGraphics( );         // copy image         stripGC.drawImage(stripIm,                     0,0, imWidth,height,                     i*imWidth,0, (i*imWidth)+imWidth,height, null);         stripGC.dispose( );       }       return strip;     } // end of loadStripImageArray( )

drawImage( ) is used to clip the images out of the strip.

An alternative approach would be to use a CropImageFilter combined with a FilteredImageSource. However, this is too much work for images that are positioned so simply in their source graphic.




Killer Game Programming in Java
Killer Game Programming in Java
ISBN: 0596007302
EAN: 2147483647
Year: 2006
Pages: 340

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net