001 // This class is based on the FishDisplay class, version 1 August 2002 002 // by Julie Zenekski 003 004 // Original copyright notice: 005 006 // AP(r) Computer Science Marine Biology Simulation: 007 // The FishImageDisplay class is copyright(c) 2002 College Entrance 008 // Examination Board (www.collegeboard.com). 009 // 010 // This class is free software; you can redistribute it and/or modify 011 // it under the terms of the GNU General Public License as published by 012 // the Free Software Foundation. 013 // 014 // This class is distributed in the hope that it will be useful, 015 // but WITHOUT ANY WARRANTY; without even the implied warranty of 016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 // GNU General Public License for more details. 018 019 package model.fish.display; 020 021 import sysModel.fish.IFishDisplay; 022 023 import javax.swing.*; 024 import java.awt.*; 025 import java.awt.geom.Ellipse2D; 026 import java.awt.image.FilteredImageSource; 027 import java.awt.image.RGBImageFilter; 028 import java.util.HashMap; 029 import java.net.URL; 030 031 /** 032 * Use an image file to display a fish. 033 * 034 * @author Mathias Ricken 035 */ 036 public class ImageFishDisplay implements IFishDisplay { 037 private ImageIcon _icon; 038 private Image _originalImage; 039 private double _radians; 040 private HashMap<Color,Image> _tintedVersions = new HashMap<Color,Image>(); 041 042 /** 043 * Make an object that knows how to display a fish as an image. Looks for the named file first in the jar file, then 044 * in the current directory. If the named file is not found or the file is malformed, a colored circle will be 045 * substituted instead. 046 * 047 * @param imageFilename name of file containing image 048 * @param radians radians the fish has to be rotated by to make it face north 049 */ 050 public ImageFishDisplay(String imageFilename, double radians) { 051 URL urlInJarFile = getClass().getResource(imageFilename); 052 if (null != urlInJarFile) { 053 _icon = new ImageIcon(urlInJarFile); 054 } 055 else { 056 String path = System.getProperty("user.dir") + java.io.File.separator + imageFilename; 057 _icon = new ImageIcon(path); 058 } 059 _radians = radians; 060 _originalImage = _icon.getImage(); 061 } 062 063 064 /** 065 * Draw the fish. The Graphics2D object has been set up so that the origin is in the center of the fish. A fish that 066 * is 32x32 wide should thus drawFish from (-16,-16) to (16,16). 067 * 068 * @param g graphics object to drawFish on. 069 * @param comp the component to drawFish on 070 * @param fishColor color of the fish 071 */ 072 public void draw(Graphics2D g, Component comp, Color fishColor) { 073 if (MediaTracker.COMPLETE != _icon.getImageLoadStatus()) { 074 // Image failed to load, so fall back to default display. 075 g.setPaint(fishColor); 076 g.fill(new Ellipse2D.Double(-.4, -.4, .8, .8)); 077 return; 078 } 079 080 // Rotate drawing surface to compensate for the direction of the fish 081 // in the image. 082 g.rotate(_radians); 083 084 // Scale to shrink or enlarge the image to fit the size 1x1 cell. 085 g.scale(1.0 / _icon.getIconWidth(), 1.0 / _icon.getIconHeight()); 086 087 // Compose image with fish color using an image filter. 088 Image tinted = (Image)_tintedVersions.get(fishColor); 089 if (null == tinted) { // not cached, need new filter for color 090 FilteredImageSource src = new FilteredImageSource(_originalImage.getSource(), new TintFilter(fishColor)); 091 tinted = comp.createImage(src); 092 // Cache tinted image in map by color, we're likely to need it again. 093 _tintedVersions.put(fishColor, tinted); 094 } 095 096 _icon.setImage(tinted); 097 _icon.paintIcon(comp, g, -_icon.getIconWidth() / 2, -_icon.getIconHeight() / 2); 098 } 099 100 /** 101 * An image filter class that tints colors based on the tint provided to the constructor (the color of a fish). 102 */ 103 private static class TintFilter extends RGBImageFilter { 104 private int tintR; 105 private int tintG; 106 private int tintB; 107 108 /** 109 * Construct an image filter for tinting colors in an image. 110 * 111 * @param color tint 112 */ 113 public TintFilter(Color color) { 114 canFilterIndexColorModel = true; 115 int rgb = color.getRGB(); 116 tintR = (rgb >> 16) & 0xff; 117 tintG = (rgb >> 8) & 0xff; 118 tintB = rgb & 0xff; 119 } 120 121 public int filterRGB(int x, int y, int argb) { 122 // Separate pixel into its RGB coomponents. 123 int alpha = (argb >> 24) & 0xff; 124 int red = (argb >> 16) & 0xff; 125 int green = (argb >> 8) & 0xff; 126 int blue = argb & 0xff; 127 128 // Use NTSC/PAL algorithm to convert RGB to grayscale. 129 int lum = (int)(0.2989 * red + 0.5866 * green + 0.1144 * blue); 130 131 // Interpolate along spectrum black->white with tint at midpoint 132 double scale = Math.abs((lum - 128) / 128.0); // absolute distance from midpt 133 int edge = 128 > lum ? 0 : 255; // going towards white or black? 134 red = tintR + (int)((edge - tintR) * scale); // scale from midpt to edge 135 green = tintG + (int)((edge - tintG) * scale); 136 blue = tintB + (int)((edge - tintB) * scale); 137 return (alpha << 24) | (red << 16) | (green << 8) | blue; 138 } 139 } 140 } 141