Results 1 to 3 of 3
  1. #1
    zirbinator is offline Member
    Join Date
    Feb 2011
    Posts
    13
    Rep Power
    0

    Default Image Render Speed

    Hello,

    I've been working on various java projects, including some games, and I've run into some serious render speed issues.

    I've been using the game loop and full-screen exclusive window methods illustrated in Developing Games in Java by David Brakeen which basically ignores paint() methods and uses an update-render loop, BufferStrategy, JFrame, and Graphics/Graphics2D. This method requires the update-render game loop to execute in about 30 milliseconds (ms) for 30 fps and 15 ms for 60 fps, which makes the the speeds that I've been getting unacceptable.

    I've worked with three different image drawing methods. First is the simple use of java.awt.Image to draw. I load (from the path "filename") and draw (with the Graphics object g from the BufferStrategy) an image in the following manner:

    Java Code:
    Image image = ImageIO.read(new File("filename"));
    g.drawImage(image, 0, 0, null);
    The second method that I've used is drawing with java.awt.image.BufferedImage. I'm pretty sure that BufferedImage isn't meant to be used the way that I'm using it here, but I tried this anyway. With the same filename and Graphics, my code is:

    Java Code:
    try
    {
    	image = ImageIO.read(new File(filename));
    }
    catch (IOException ex) { }
    Finally, I've been working with java.awt.image.VolatileImage. I'm a little fuzzy on the details of how it works, but I created an AcceleratedImage class that I use to draw with VolatileImages. I've forgotten where I got the base of this code, but here is it plus my own additions:

    Java Code:
    import java.awt.AlphaComposite;
    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.GraphicsConfiguration;
    import java.awt.Transparency;
    import java.awt.image.BufferedImage;
    import java.awt.image.VolatileImage;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    
    /**
     * Gives a way of drawing hardware-accelerated images which can have significant performance boosts.
     * When created, an Image is stored as a BufferedImage which can be slow to draw.
     * When the drawing method is called, a new VolitileImage, a fast to draw Image which can lose its contents, is drawn to using the drawToVolitleImage method.
     * Every time the draw method is called, the contents of the VolatileImage's contents are checked.
     * If they are lost, the VolitleImage is drawn to again and then once the VolatileImage has contents, the VolatileImage itself is drawn.
     */
    public class AcceleratedImage
    {
    	private BufferedImage bi = null;
    	
    	int quality;
    	public static final int OPAQUE = Transparency.OPAQUE;
    	public static final int BITMASK = Transparency.BITMASK;
    	public static final int TRANSLUCENT = Transparency.TRANSLUCENT;
    	
    	private VolatileImage vi = null;
    	
    	/**
    	 * Creates a new AcceleratedImage with the given BufferedImage and quality.
    	 * 
    	 * @param bi - the Image that should be drawn
    	 * @param quality - the quality: opaque, bitmask-transparent, or translucent
    	 */
    	public AcceleratedImage(BufferedImage bi, int quality)
    	{
    		this.bi = bi;
    		this.quality = quality;
    	}
    	
    	/**
    	 * Creates a new AcceleratedImage with the Image found at the filename and quality.
    	 * 
    	 * @param filename - the location of the Image that should be drawn
    	 * @param quality - the quality: opaque, bitmask-transparent, or translucent
    	 */
    	public AcceleratedImage(String filename, int quality)
    	{
    		try
    		{
    			bi = ImageIO.read(new File(filename));
    		}
    		catch (IOException e) 
    		{
    			System.out.println("The image at " + filename + " could not be read.");
    			System.out.println("The program will now exit.");
    			System.exit(0);
    		}
    		this.quality = quality;
    	}
    	
    	/**
    	 * Loads the given Image found at the filename, with the given quality.
    	 * Returns a flag that determines whether or not the Image was properly loaded.
    	 * If the image was not correctly loaded, the BufferedImage will remain the same as the one given at instantiation.
    	 * 
    	 * @param filename - the location of the Image to be loaded
    	 * @param quality - the quality: opaque, bitmask-transparent, or translucent
    	 * @return loaded - true if the the image was properly loaded, false otherwise
    	 */
    	public boolean loadImage(String filename, int quality)
    	{
    		try
    		{
    			bi = ImageIO.read(new File(filename));
    		}
    		catch (IOException e) 
    		{
    			return false;
    		}
    		this.quality = quality;
    		return true;
    	}
    	
    	/**
    	 * Creates a blank VolatileImage with the width and height of the given Image and accessing the given GraphicsConfiguration 
    	 * 
    	 * @param bi - the Image that the VolatileImage's size should be based on
    	 * @param config - the current GraphicsConfiguration
    	 * @return VolatileImage - the blank, accelerated VolatileImage with the same size as the given Image
    	 */
    	private VolatileImage createVolatileImage(BufferedImage bi, GraphicsConfiguration config)
    	{
    		return config.createCompatibleVolatileImage(bi.getWidth(), bi.getHeight(), quality);
    	}
    	
    	/**
    	 * Draws the Image that this AcceleratedImage was constructed with at a significantly faster speed.
    	 * First, the current GraphicsConfiguration is found using the given Graphics.
    	 * If the VolatileImage is null (this is the first time that the VolatileImage has been drawn),
    	 *  the VolatileImage is created using createVolatileImage and then is drawn to.
    	 * Next, the common drawing sequence is initiated.
    	 * First, if the VolatileImage has been restored the contents have been lost and the VolatileImage is drawn to.
    	 * Next, if the VolatileImage's state is incompatible with the current GraphicsConfiguration it is completely remade and redrawn.
    	 * Finally, the VolatileImage is drawn using the originally given Graphics context.
    	 * If, at the end of the common drawing sequence, the VolatileImage has lost its contents, the common drawing sequence is looped.
    	 * 
    	 * @param x - the x-coordinate where the AcceleratedImage should be drawn
    	 * @param y - the y-coordinate where the AcceleratedImage should be drawn
    	 * @param g - the Graphics context
    	 */
    	public void draw(int x, int y, Graphics2D g)
    	{
    		GraphicsConfiguration config = g.getDeviceConfiguration();
    		if (vi == null)
    		{
    			vi = createVolatileImage(bi, config);
    			drawToVolatileImage(config);
    		}
    		do
    		{
    			int returnCode = vi.validate(config);
    			if (returnCode == VolatileImage.IMAGE_RESTORED)
    			{
    				drawToVolatileImage(config);
    			}
    			else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE)
    			{
    				vi = createVolatileImage(bi, config);
    				drawToVolatileImage(config);
    			}
    			g.drawImage(vi, x, y, null);
    		} 
    		while (vi.contentsLost());
    	}
    	
    	/**
    	 * Using the VolatileImage's Graphics context, the BufferedImage is drawn onto the VolatileImage.
    	 * To accomplish this, the VolatileImage must first be filled with a completely transparent color.
    	 * Next, the BufferedImage is drawn at 0,0.
    	 * The BufferedImage should have the same size as the VolatileImage because the VolatileImage was created based on the BufferedImage.
    	 * This entire sequence is looped if, at the end, the VolatileImage has lost its contents.
    	 * 
    	 * @param config - the current GraphicsConfiguration
    	 */
    	private void drawToVolatileImage(GraphicsConfiguration config)
    	{
    		do
    		{
    			if (vi.validate(config) == VolatileImage.IMAGE_INCOMPATIBLE)
    			{
    				vi = createVolatileImage(bi, config);
    			}
    			Graphics2D viGraphics = vi.createGraphics();
    			viGraphics.setComposite(AlphaComposite.Src);
    			viGraphics.setColor(new Color(0, 0, 0, 0));
    			viGraphics.fillRect(0, 0, vi.getWidth(), vi.getHeight());
    			viGraphics.drawImage(bi, 0, 0, null);
    			viGraphics.dispose();
    		}
    		while (vi.contentsLost());
    	}
    	
    	/**
    	 * Returns the width of the image by finding the width of the BufferedImage.
    	 * 
    	 * @return width - the width of the AcceleratedImage
    	 */
    	public int getWidth()
    	{
    		return bi.getWidth();
    	}
    	
    	/**
    	 * Return the height of the image by finding the height of the BufferedImage.
    	 * 
    	 * @return height - the height of the AcceleratedImage
    	 */
    	public int getHeight()
    	{
    		return bi.getHeight();
    	}
    }
    I use initialize an AcceleratedImage with the AccleratedImage(String filename, int quality) constructor and use draw(int x, int y, Graphics g) to draw. Lots of stuff (extra constructors, etc.) is in the code for convenience.

    Anyway, I've run some tests with all three of these methods and I'm getting some annoyingly slow results. All of them are roughly even (which really surprised me), here are some results for a 300x200 translucent image drawing once (I ran 100 trials and averaged them) in ms on my moderately fast desktop:
    Image: 0.08525978
    BufferedImage: 0.07984223
    AcceleratedImage: 0.074485299
    While these are not bad if that's all I needed to draw, if I'm drawing 200 such images, that's already taking 15 ms, giving me basically no time for updates, ai, etc. These averages also do not include the time that the BufferStrategy takes to render to the screen, which is around 0.13 (though I only need to do that once per update/draw).

    What I'm wondering is if there's a better way to draw images that I've missed here. I'd rather avoid moving to a different graphics library like OpenGL, and I'd like to stay within the reaches of the standard API. If you haven't had enough reading my rambling, I can post some test code so you can look through that. I'm pretty sure it's all fine, though. I'm just surrounding the drawing code with something like:
    Java Code:
    long time = System.nanoTime();
    // draw
    long drawTime = System.nanoTime() - time;
    which gives me the time it took to draw in nanoseconds.

    Whew! Thanks for reading, and any suggestions would be most appreciated!

  2. #2
    DarrylBurke's Avatar
    DarrylBurke is offline Forum Police
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,413
    Rep Power
    20

    Default

    Any partial transparency slows down painting. Can you replace at least some of your 'translucent' images with opaque ones?

    As I'm posting this on the basis of what I've red in these and other forums, and not from personal experience, you might like to run a timing test using non-transparent images to check whether it's correct.

    db

  3. #3
    zirbinator is offline Member
    Join Date
    Feb 2011
    Posts
    13
    Rep Power
    0

    Default

    Hello,

    I've done tests with all three kinds of transparency: translucent, bitmask-transparent, and opaque, and the opaque ones are indeed faster, so I use them as often as possible. However, most of the stuff that I draw in a game - sprites, buttons, etc., are at least bitmask-transparent and usually translucent. Anyway, I'm just looking for alternate image drawing procedures that might be able to speed up some of the images.

    Sorry for the late post, I've been unexpectedly busy the last few days.

Similar Threads

  1. Render an image into a given quadrilateral
    By JeremyWilms in forum Java 2D
    Replies: 19
    Last Post: 08-24-2011, 07:26 AM
  2. Replies: 12
    Last Post: 04-14-2011, 01:58 PM
  3. Best Way To Render
    By rp181 in forum Java 2D
    Replies: 1
    Last Post: 03-24-2011, 06:20 PM
  4. Why wont my object render properly
    By toecutter in forum AWT / Swing
    Replies: 3
    Last Post: 10-22-2009, 01:43 PM
  5. Loki Render 0.3
    By levent in forum Java Software
    Replies: 0
    Last Post: 07-26-2007, 08:31 PM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •