package ch.sahits.game.graphic.image.impl;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.RasterFormatException;
import java.io.IOException;
import java.util.HashMap;
import org.apache.log4j.Logger;
import ch.sahits.game.graphic.image.IImageLoader;
import ch.sahits.game.graphic.image.IOpenPatricianPainter;
/**
* Utility class for with methods common to all UI painters
* @author Andi Hotz, (c) Sahits GmbH, 2011
* Created on Mar 26, 2011
*
*/
public class OpenPatricianPainter implements IOpenPatricianPainter {
private static final Logger logger = Logger.getLogger(OpenPatricianPainter.class);
private HashMap<String,Dimension> imgDimensionCache = new HashMap<String,Dimension>();
/**
* Retrieve the image dimensions from the cache or store it there if they are not available
* @param fileName
* @return
*/
private synchronized Dimension getImageDimesnion(String fileName){
if (imgDimensionCache.containsKey(fileName)){
return imgDimensionCache.get(fileName);
} else {
IImageLoader loader = new ImagesLoader();
BufferedImage baseImg = loader.loadImage(fileName);
Dimension dim = new Dimension(baseImg.getWidth(), baseImg.getHeight());
imgDimensionCache.put(fileName, dim);
return dim;
}
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#createTiledImage(java.lang.String, int, int)
*/
@Override
public BufferedImage createTiledImage(String imageName, int width, int heigth){
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gconf = gs.getDefaultConfiguration();
IImageLoader loader = new ImagesLoader();
BufferedImage baseImg = loader.loadImage(imageName);
final int xSize = baseImg.getWidth();
final int ySize = baseImg.getHeight();
int vCount = width/xSize;
if (width%xSize>0){
vCount++;
}
int hCount = heigth/ySize;
if (heigth%ySize>0){
hCount++;
}
BufferedImage img = gconf.createCompatibleImage(vCount*xSize, hCount*ySize, Transparency.BITMASK);
// fill in the image
Graphics2D g = img.createGraphics();
for (int xCount=0;xCount<=vCount;xCount++){
for (int yCount=0;yCount<=hCount;yCount++){
final int xpos = xCount*xSize;
final int ypos = yCount*ySize;
g.drawImage(baseImg, xpos, ypos,null);
}
}
return cropImage(img, width, heigth);
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#drawBackground(java.awt.Graphics, java.lang.String, java.awt.Rectangle)
*/
@Override
public void drawBackground(Graphics g, String imageName, Rectangle rect) {
final int width = rect.width;
final int height = rect.height;
BufferedImage img = createTiledImage(imageName, width, height);
drawTile(img, rect.x, rect.y, width, height, g);
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#drawTile(java.awt.image.BufferedImage, int, int, int, int, java.awt.Graphics)
*/
@Override
public void drawTile(BufferedImage img, int x, int y, int width,
int height, Graphics g) {
if (width>img.getWidth()){
throw new IllegalArgumentException("The drawing is wider than the image: no stretching allowd");
}
if (height>img.getHeight()){
throw new IllegalArgumentException("The drawing is heigher than the image: no stretching allowd");
}
//System.out.println("Draw image with size ("+img.getWidth()+"x"+img.getHeight()+") at ("+x+"x"+y+") with dimensions ("+width+"x"+height+")");
g.drawImage(img, x, y, width, height, null);
}
/**
* Create an image that crops the image on the left and bottom side to create an image of the desired
* proportions.
* @param img Original image
* @param destWidth destination width must be smaller than the image's width
* @param destHeight destination height must be smaller than the image's height
* @return cropped image with the dimension <code>destWidthxdestHeight</code>
*/
private BufferedImage cropImage(BufferedImage img, int destWidth, int destHeight){
if (img.getWidth()<destWidth){
throw new IllegalArgumentException("The destination width must be smaller than the image width");
}
if (img.getHeight()<destHeight){
throw new IllegalArgumentException("The destination height must be smaller than the image height");
}
try {
return img.getSubimage(0, 0, destWidth, destHeight);
} catch (RasterFormatException e) {
logger.error("Original"+img.getWidth()+"x"+img.getHeight()+", "+destWidth+"x"+destHeight, e);
throw e;
}
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#drawString(java.awt.Graphics2D, java.lang.String, int, int, int)
*/
@Override
public void drawString(Graphics2D graphics, String s, int fontSize,
int posX, int posY) {
try {
GlyphVector gv = createGlyphVector(graphics, s, fontSize);
Rectangle2D box = gv.getVisualBounds();
float x = posX-(float)box.getCenterX();
float y = posY-(float)box.getCenterY();
graphics.drawGlyphVector(gv, x, y);
} catch (FontFormatException e) {
} catch (IOException e) {
}
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#createGlyphVector(java.awt.Graphics2D, java.lang.String, int)
*/
@Override
public GlyphVector createGlyphVector(Graphics2D graphics, String s,
int fontSize) throws FontFormatException, IOException {
Font font = FontLoader.getInstance().createStranbergFont(fontSize);
return createGlyphVector(graphics, s, font);
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#createGlyphVector(java.awt.Graphics2D, java.lang.String, java.awt.Font)
*/
@Override
public GlyphVector createGlyphVector(Graphics2D graphics, String s,
Font font) {
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
FontRenderContext frc = graphics.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, s);
return gv;
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#createGlyphVector(java.lang.String, int)
*/
@Override
public GlyphVector createGlyphVector(String s, int fontSize) throws FontFormatException, IOException{
FontRenderContext frc = new FontRenderContext(new AffineTransform(), true, true);
Font font = FontLoader.getInstance().createStranbergFont(fontSize);
GlyphVector gv = font.createGlyphVector(frc, s);
return gv;
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#createSteeringWheel(int, float)
*/
@Override
public BufferedImage createSteeringWheel(int width, float percent){
if (percent<0 || percent>100){
throw new IllegalArgumentException("percent must ly withing [0,100]");
}
String fileName = "SteeringWheel.png";
Dimension dim = getImageDimesnion(fileName);
double scale = width / dim.getWidth();
final double angle = computeSteeringWheelAngle(percent);
OpenPatricianImageRotator rotator = new OpenPatricianImageRotator();
return rotator.rotateImage(fileName, angle, scale, 1, 2);
}
/**
* Compute the angle in degrees based on the percentage.
* @param percentage
* @return
*/
private double computeSteeringWheelAngle(float percentage){
return percentage*160/100+10;
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#getSteeringWheelHandle(int, float)
*/
@Override
public Polygon getSteeringWheelHandle(int width, float percent){
String fileName = "SteeringWheel.png";
Dimension dim = getImageDimesnion(fileName);
double scale = width / dim.getWidth();
final double angle = computeSteeringWheelAngle(percent);
Polygon poly = new Polygon(); // The handle in the unscaled unrotated image
poly.addPoint(0, 235);
poly.addPoint(80, 235);
poly.addPoint(80, 260);
poly.addPoint(0, 260);
//System.out.print("Original polynom: ");
//OpenPatricianImageRotator.print(poly);
//System.out.println("");
OpenPatricianImageRotator rotator = new OpenPatricianImageRotator();
return rotator.rotatePolygon(poly, angle, scale, (int)Math.rint(dim.getWidth()), (int)Math.rint(dim.getHeight()));
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#computeSliderTrack(int, float, float)
*/
@Override
public Rectangle computeSliderTrack(int width, float castMin, float castMax){
Polygon poly = getSteeringWheelHandle(width, castMin);
int minX = (int) Math.rint(poly.getBounds().getMinX());
int maxY = (int)Math.rint(poly.getBounds().getMaxY());
poly = getSteeringWheelHandle(width, castMax);
int maxX = (int)Math.rint(poly.getBounds().getMaxX());
poly = getSteeringWheelHandle(width, (castMax-castMax)/2);
int minY = (int)Math.rint(poly.getBounds().getMinY());
return new Rectangle(minX, minY, maxX-minX, maxY-minY);
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#scaleX(java.awt.image.BufferedImage, int)
*/
@Override
public BufferedImage scaleX(BufferedImage img, int destWidth){
if (destWidth<=0){
throw new IllegalArgumentException("The final width must be larger than 0");
}
if (img==null) return null; // can happen when quitting
float xScale = (float) destWidth / img.getWidth();
float yScale = 1;
AffineTransform at = new AffineTransform();
at.scale(xScale, yScale);
// instantiate and apply affine transformation filter
BufferedImageOp bio = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
return bio.filter(img, null);
}
/* (non-Javadoc)
* @see ch.sahits.game.graphic.image.impl.IOpenPatricianPainter#scaleY(java.awt.image.BufferedImage, int)
*/
@Override
public BufferedImage scaleY(BufferedImage img, int destHeight){
if (destHeight<=0){
throw new IllegalArgumentException("The final height must be larger than 0");
}
if (img==null) return null; // can happen when quitting
float xScale = 1;
float yScale = (float) destHeight / img.getHeight();
AffineTransform at = new AffineTransform();
at.scale(xScale, yScale);
// instantiate and apply affine transformation filter
BufferedImageOp bio = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
return bio.filter(img, null);
}
}