Java Notes

Images - BufferedImage

The java.awt.image.BufferedImage class is used to create images in memory. You might want to do this for unchanging images that take a long time to create. You can create them once, then display them repeatedly, instead of recomputing them each time.

To create a BufferedImage

You can create a new BufferedImage using either the characteristics of an existing component (eg, a JPanel), or by using a BufferedImage constructor, in which case you need to (or can) specify additional parameters like the color model and image type. Specifying an image type allows, for example, an alpha channel for transparency, but rendering images which do not use the the native image model can be slower. This description only considers creating a BufferedImage object from another component.

Some properties components are not known during the initial construction of the interface. For example, the size of a JPanel may change during layout. Therefore, a common technique is to delay the construction of a BufferedImage based on a JPanel until later. This can be done at the beginning of the paintComponent method. The example below tests for the uninitialized value (null). Assume we want to make the bufferer image the same size as the panel we are that are drawing. This buffered image has a grid on it, which will be used as the background for all other drawing. We only have to compute the grid one time.

import java.awt.image.*;
. . .
class Painting extends JPanel {
    . . .
    BufferedImage grid;  // declare the image
    . . .
    public void paintComponent(Graphics g) {
        super.paintComponent(g);       // paint background
        Graphics2D g2 = (Graphics2D)g; // we need a Graphics2D context
    
        if (grid == null) {
            // Compute the grid only one time
            int w = this.getWidth();
            int h = this.getHeight();
            grid = (BufferedImage)(this.createImage(w,h));
            Graphics2D gc = grid.createGraphics();
            for (int x=0; x<w; x+=10) {gc.drawLine(x, 0, x, h);}
            for (int y=0; y<h; y+=10) {gc.drawLine(0, y, w, y);}
        }
        // Draw the grid from the precomputed image
        g2.drawImage(grid, null, 0, 0);
        . . . // draw remaining, dynamic, image

To Draw on a BufferedImage

To draw on anything, you need a Graphics or Graphics2D drawing context. You can get a graphics context from an image like this:

   Graphics2D gp = myImage.createGraphics();
   gp.drawOval(10,10,40,100);
   . . .

To draw the BufferedImage onto another graphics context

You usually use a BufferedImage as part of some other drawing that you are producing. A BufferedImage can be drawn using the drawImage method. Note that drawImage(...) is only defined in the Graphics2D class, but casting the Graphics object to Graphics2D is simple and fast. There are many drawImage methods; some allow you to rescale the image to fit into a specified area, or to apply a filter. If you just want to copy the image directly onto graphics context g2 with with no operations (null) and starting at (100, 200), you can do this in a paintComponent method:

// Draw myImage on the current graphics context
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(myImage, null, 100, 200);

Example: Example - Buffered Analog Clock uses an image to compute the clock face only one time, and then reuse that image every time for the background and draw only the hands dynamically.