Home : Course Map : Chapter 11 : Java : Supplements :
Pixel Handling with the BufferedImage
JavaTech
Course Map
Chapter 11

Introduction
Image Class
Image Loading
  Demo 1 Demo 2  
Pixels/Transparency
  Demo 3
Pixel Handling
  Demo 4  
Demo 5
Exercises

    Supplements
Java 2D Imaging
BufferedImage
Creating Buf.Image
Pixel Handling
  Demo 1 Demo 2
Filters
  Convolutions
     Demo 3
  AffineTransforms
     Demo 4
  LookupTable
     Demo 5 Demo 6
  Rescale
     Demo 7
  Color Conversion
     Demo 8
  Custom
     Demo 9
Exercises
Java Adv Imaging
AWT Flicker:
  Override Update
     Demo 1  Demo 2
  Clipping
     Demo 3
  Double Buffer
     Demo 4

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

In the discussion of BufferedImage class, we noted that it offers much more access to and control of the image data than the Image class. Internally the BufferedImage consists of the ColorModel, Raster, DataBuffer, and SampleModel objects, which allow for a wide variety of image types and packing arrangements for the pixel data.

However, for routine pixel handling tasks, you don't need to delve into the details of these classes to work with images at the pixel level. For example, you can easily create an image from a pixel array packed with ARGB pixel data as follows:

buffered_image = new BufferedImage (width, height,
                                    bufferedImage.TYPE_INT_ARGB);
buffered_image.setRGB (0, 0, width, height, pixels, 0, width);

The constructor parameters specify the dimensions of the image and the type of image. In setRGB() the first four parameters specify the top left corner position and the width and height of the area of the image to be filled by the pixels array. The last two parameters give the offset into the pixel array where the data begins and the scan size for a row of pixels (usually just set to the image width).

Conversely, the ARGB pixel data for an image can be obtained via

int[] pixels =
   buffered_image.getRGB (0, 0, width, height, array, 0, width);

This method returns an array with the ARGB data for the area of the image specified by the first four parameters. If not null, the fifth parameter should be an int array large enough to hold the pixel data. The next to last parameter specifies the offset into the pixel array where the filling should begin and the last parameter is again the scan size.

To deal with other types of images, a little more work must be done. The following GrayBufImagePanel class creates a byte array with values from 0 to 255 that represent gray levels. A BufferedImage of the TYPE_BYTE_GRAY is created. To fill it with our byte array we need to get the raster for the image and it must be a WritableRaster type than allows us to modify the pixels.

This obtained is obtained with

WritableRaster wr = fBufferedImage.getRaster ();

Then the pixel data is set with.

wr.setDataElements (0, 0, fWidth, fHeight, fPixels);

The program CreateBufferedGrayImageApplet shown below displays an instance of the GrayBufImagePanel.

CreateBufferedGrayImageApplet

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;

/** Display the DrawingPanel with BufferedImage on the applet.**/
public class CreateBufferedGrayImageApplet extends JApplet
{
  GrayBufImagePanel fBufImagePanel = null;
  public void init ()   {
    Container content_pane = getContentPane ();

    // Create an instance of BufImagePanel
    fBufImagePanel = new GrayBufImagePanel ();

    // Add the BufImagePanel to the contentPane.
    content_pane.add (fBufImagePanel);
  } // init

  // Invoke the init method in BufImagePanel to build the image.
  public void start () {
    fBufImagePanel.makeImage ();
  }
} // class CreateBufferedGrayImageApplet

/** Create an image from a pixel array using BufferedImage. **/
class GrayBufImagePanel extends JPanel
{
  BufferedImage fBufferedImage = null;
  int fWidth = 0, fHeight = 0;
  byte [] fPixels = null;

  /** Build a BufferedImage from a pixel array. **/
  void makeImage () {

    fWidth  = getSize ().width;
    fHeight = getSize ().height;
    fPixels = new byte [fWidth * fHeight];

    // Create an array of pixels with varying aRGB components
    int i=0;
    int half_width = fWidth/2;
    for  (int y = 0; y < fHeight; y++){
      for ( int x = 0; x < fWidth; x++){

        // Peak white in middle
        int gray = (255 * x)/half_width;
        if (x > half_width) gray = 510 - gray;
        fPixels[i++] = (byte) gray;
      }
    }
    // Create a BufferedIamge of the gray values in bytes.
    fBufferedImage =
       new BufferedImage (fWidth, fHeight, BufferedImage.TYPE_BYTE_GRAY);

    // Get the writable raster so that data can be changed.
    WritableRaster wr = fBufferedImage.getRaster();

    // Now write the byte data to the raster
    wr.setDataElements (0, 0, fWidth, fHeight, fPixels);
  }

  /** Draw the image on the panel. **/
  public void paintComponent (Graphics g) {
    super.paintComponent (g);
    if (fBufferedImage != null)
        g.drawImage (fBufferedImage, 0, 0, this );
  } // makeImage

} // class GrayBufImagePanel

Animations with BufferedImage

In Chapter 11: Java: Pixel Handling we showed how to create animations with the Image class directly from modifications to the pixel data with the aid of the MemoryImageSource class.With the BufferedImage we can also create an animation by altering the pixel array for each frame, invoking the setRGB() method to reload the pixel data, and then invoking repaint().

A more sophisticated approach, which more closely resembles the MemoryImageSource animation technique, is to build the BufferedImage with a DataBufferInt object that holds the pixels plus a RGB ColorModel and a WritableRaster that allows direct modification of the raster data. For each frame of the animation, you modify the pixel array and then invoke repaint().

The program AnimationWritableRasterApplet and the class DrawingPanelWR shown below illustrate this approach. A BufferedImage object is created with a WritableRaster and made big enough to cover the panel. A loop in a thread modifies the pixel data and when the image is repainted, it will use the new pixels.

AnimationWriteableRasterApplet

import javax.swing.*;
import java.awt.*;
import java.awt.image.*;

/**
  * Demonstrate animation of a BufferedImage object by direct
  * modification of its pixel data.
**/
public class AnimationWritableRasterApplet extends JApplet
                       implements Runnable
{
  // Will use thread reference as a flag
  Thread fThread;
  //msecs of sleep time between frames
  int fDeltaT = 10;
  // Need a reference to the panel for the thread loop.
  DrawingPanelWR fDrawingPanel;

  /**
    * Add an instance of the DrawingPanelWR on which
    * the animation will take place.
   **/
  public void init () {
    // Create an instance of DrawingPanel
    fDrawingPanel = new DrawingPanelWR ();

    // Add the DrawingPanel to the JApplet pane.
    add (fDrawingPanel);

  } // init

  /** Called by browser when web page and applet loaded. **/
  public void start () {
    // Tell the panel to create the image source.
    fDrawingPanel.init ();

    // If the thread reference not null then a
    // thread is already running. Otherwise, create
    // a thread and start it.
    if (fThread == null) {
        fThread = new Thread (this);
        fThread.start ();
    }
  }

  /**  Called by browser when web page and applet unloaded. **/
  public void stop () {
     // Setting thread to null will cause loop in
     // run () to finish and the process return from
     // run () thus killing the thread.
     fThread = null;
  } // stop

  public void run ()  {
    // Loop through sleep periods until
    // thread stopped by setting thread
    // reference to null.
    while (fThread != null) {
      try {
        Thread.sleep (fDeltaT);
      }
      catch (InterruptedException e)
      { }

      // Send request for create new image
      fDrawingPanel.newFrame ();

      // Repaint now that the pixel data has changed.
      repaint ();
    }
  } // run

} // class AnimationWritableRasterApplet

/** A JPanel subclass on which a BufferedImage animation will be
  * displayed. Use a WritableRaster to modify the pixel data.
 **/
class DrawingPanelWR extends JPanel
{
  BufferedImage fBufferedImage;
  int width, height;
  int [] fPixels;

  int frame=0;

  /**
    * Build a BufferedImage the size of the panel. Create
    * it with a WriteableRaster so that we can modify the
    * pixel data for each frame of an animation.
   **/
  void init ()  {
    width  = getSize ().width;
    height = getSize ().height;
    int numPixels = width * height;

    fPixels = new int [numPixels];

    // Create an integer data buffer to hold the pixel array
    DataBuffer data_buffer = new DataBufferInt (fPixels, numPixels);

    // Need bit masks for the color bands for the ARGB color model
    int [] band_masks = {0xFF0000, 0xFF00, 0xff, 0xff000000};

    // Create a WritableRaster that will modify the image
    // when the pixels are modified.
    WritableRaster write_raster =
      Raster.createPackedRaster (data_buffer, width, height, width,
                                 band_masks, null);

    // Create a RGB color model
    ColorModel color_model = ColorModel.getRGBdefault ();

    // Finally, build the image from the
    fBufferedImage =
      new BufferedImage (color_model,write_raster,false,null);

  } // init


  /** Modify the pixels for each frame. **/
  void newFrame () {
    int index = 0;
    byte mask =  (byte)(frame & 0xff);
    int alpha = 255;

    for (int y = 0; y < height; y++){
        for (int x = 0; x < width; x++) {
          // Modify the color components each frame.
          int red =  (y * 255) / (width - 1);
          red = red & mask;

          int green =  (x * 255) / (height - 1);
          green = green & mask;

          int blue = 255 -  (255 * (x - width))/width;
          blue = blue & mask;

          fPixels[index++] =
             (alpha << 24) | (red << 16) | (green << 8) | blue;
       }
    }
    frame++;
  } // new Frame

  /** Just draw the image. **/
  public void paintComponent (Graphics g) {
    g.drawImage (fBufferedImage, 0, 0, this);
  }

} // class DrawingPanelWR

 


References & Web Resources

 

Latest update: May 24, 2006

              Tech
Fractals
Fractal Drawing
   Demo 1
Fractal Draw Code
Fractal Images
  Demo 2
Image Processing
  Demo 3
Histogram Image
  Demo 4
Exercises

           Physics
Calibration/SysError
SimWithCal/SysErr
  Demo 1
Analysis
  Demo 2
Examples

Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.