package com.indyforge.twod.engine.graphics; import java.applet.Applet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.Frame; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsEnvironment; import java.awt.HeadlessException; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import java.lang.reflect.InvocationTargetException; import com.indyforge.twod.engine.graphics.rendering.scenegraph.SceneProcessor; import com.indyforge.twod.engine.graphics.rendering.scenegraph.math.MathExt; import com.indyforge.twod.engine.graphics.sprite.Sprite; import com.indyforge.twod.engine.resources.assets.AssetManager; /** * Utility class to bundle some default graphics routines like creating frames. * * @author Christopher Probst * @author Matthias Hesse */ public final class GraphicsRoutines { /** * Optimizes this image to be compatible with the screen. * * @param image * The image you want to optimize. * @return a optimized copy of the given image. */ public static BufferedImage optimizeImage(BufferedImage image) { if (image == null) { throw new NullPointerException("image"); } // Create a new optimized image BufferedImage optimizedImage = GraphicsRoutines.createImage( image.getWidth(), image.getHeight(), image.getTransparency()); // Get graphics context Graphics2D g2d = optimizedImage.createGraphics(); try { // Draw the loaded image into the optimized image g2d.drawImage(image, 0, 0, null); } finally { g2d.dispose(); } return optimizedImage; } /** * Clear the given image with the given background color. * * @param image * The image you want to clear. * @param background * The background color. If null the image is not touched. * @return the image. */ public static BufferedImage clear(BufferedImage image, Color background) { if (image == null) { throw new NullPointerException("image"); } // Clear background if not null if (background != null) { Graphics2D g2d = image.createGraphics(); try { g2d.setBackground(background); g2d.clearRect(0, 0, image.getWidth(), image.getHeight()); } finally { g2d.dispose(); } } return image; } /** * Creates a compatible image. * * @param width * The width of the new image. * @param height * The height of the new image. * @return the new compatible image. */ public static BufferedImage createImage(int width, int height) { return getGC().createCompatibleImage(width, height); } /** * Creates a compatible image. * * @param width * The width of the new image. * @param height * The height of the new image. * @param transparency * The transparency of the new image. * @return the new compatible image. */ public static BufferedImage createImage(int width, int height, int transparency) { return getGC().createCompatibleImage(width, height, transparency); } /** * Creates a compatible image. * * @param imageDesc * The image description. * @return the new compatible image. */ public static BufferedImage createImage(ImageDesc imageDesc) { if (imageDesc == null) { throw new NullPointerException("imageDesc"); } return getGC().createCompatibleImage(imageDesc.width(), imageDesc.height(), imageDesc.transparency()); } /** * Creates a compatible volatile image. * * @param width * The width of the new volatile image. * @param height * The height of the new volatile image. * @return the new compatible volatile image. */ public static VolatileImage createVolatileImage(int width, int height) { return getGC().createCompatibleVolatileImage(width, height); } /** * Creates a compatible volatile image. * * @param width * The width of the new volatile image. * @param height * The height of the new volatile image. * @param transparency * The transparency of the new volatile image. * @return the new compatible volatile image. */ public static VolatileImage createVolatileImage(int width, int height, int transparency) { return getGC().createCompatibleVolatileImage(width, height, transparency); } /** * @return the active graphics configuration. */ public static GraphicsConfiguration getGC() { if (AssetManager.isHeadless()) { throw new HeadlessException(); } return GraphicsEnvironment.getLocalGraphicsEnvironment() .getDefaultScreenDevice().getDefaultConfiguration(); } /** * Reads a sprite sub image. * * @param image * The image which contains the sub images. * @param rasterX * The columns of the image. * @param rasterY * The rows of the image. * @param x * The column of the sub image. * @param y * The row of the sub image. * @return the sub image. * @see Sprite */ public static BufferedImage getSpriteSubImage(BufferedImage image, int rasterX, int rasterY, int x, int y) { if (image == null) { throw new NullPointerException("image"); } else if (rasterX <= 0) { throw new IllegalArgumentException("rasterX must be > 0"); } else if (rasterY <= 0) { throw new IllegalArgumentException("rasterY must be > 0"); } // Clamp the coords x = MathExt.clamp(x, 0, rasterX - 1); y = MathExt.clamp(y, 0, rasterY - 1); // Calculating the size of one sub image int sizeXPlate = image.getWidth(null) / rasterX; int sizeYPlate = image.getHeight(null) / rasterY; // Return the optimized copy of the sub-image return optimizeImage(image.getSubimage(x * sizeXPlate, y * sizeYPlate, sizeXPlate, sizeYPlate)); } /** * Creates a new visible frame which contains the processor. * * @param processor * The processor of the frame. * @param title * The title of the frame. * @param width * The width of the frame. * @param height * The height of the frame. * @return a new visible frame. * @throws InvocationTargetException * @throws InterruptedException */ public static Frame createFrame(SceneProcessor processor, String title, int width, int height) throws InterruptedException, InvocationTargetException { return createFrame(processor, title, width, height, true); } /** * Creates a new frame which contains the processor. * * @param processor * The processor of the frame. * @param title * The title of the frame. * @param width * The width of the frame. * @param height * The height of the frame. * @param visible * The visible flag of the frame. * @return a new frame. * @throws InvocationTargetException * @throws InterruptedException */ public static Frame createFrame(final SceneProcessor processor, final String title, final int width, final int height, final boolean visible) throws InterruptedException, InvocationTargetException { if (processor == null) { throw new NullPointerException("processor"); } /* * Create a new frame for viewing. Should be ok that it is not invoked * on EDT since this is a new variable and the EDT does not know it yet * so there cannot be any cached data yet. */ final Frame frame = new Frame(title); /* * Invoke the setup on the EDT. */ EventQueue.invokeAndWait(new Runnable() { @Override public void run() { // Set border layout frame.setLayout(new BorderLayout()); // Disable the repaint events frame.setIgnoreRepaint(true); // Add to frame frame.add(processor.canvas(), BorderLayout.CENTER); // Set size frame.setSize(width, height); // Shutdown hook frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { // Shutdown request processor.shutdownRequest(true); } }); // Make visible (Does not need the EDT) frame.setVisible(visible); } }); return frame; } public static Applet setupApplet(Applet applet, SceneProcessor processor) { // Set border layout applet.setLayout(new BorderLayout()); // Disable the repaint events applet.setIgnoreRepaint(true); // Add to applet applet.add(processor.canvas(), BorderLayout.CENTER); return applet; } // Should not be instantiated... private GraphicsRoutines() { } }