JAVA: GETTING PIXEL DATA FROM A GIF FILE
BACKGROUND

The Image object in Java is very nice.  It handles many details for you and it is sufficient for most programming tasks; however, there are times when you might want to be able to access the pixel data within an Image object.  For example:

  • To change the transparent color key

  • To swap a color

  • To check a pixel value on a particular position (hot spot check)

OVERVIEW

There are at least two distinct ways to get the pixel data from an Image object.  One way is to write your own ImageProducer code.  This will mean that you will need to understand (or at least dig into) the compression method used by the image format.  For gif, it is not so trivial task, and you could spend days just to do this; although there must be some readily available libraries somewhere on the web. 

The second option is to use the java.awt.image.PixelGrabber class.  This class does exactly what its name says: it takes an Image object an an input, grab the pixels; and it can output the pixels data of the Image.  In the case of gif image, you can either get the RGB pixels, or the indexed pixels.  (See here for indexed pixels va RGB pixels).

The are several constructors for the PixelGrabber class.: 

PixelGrabber(Image img, int x, int y, int w, int h, boolean forceRGB)
PixelGrabber(Image img, int x, int y, int w, int h, int pix[], int off, int scansize)
PixelGrabber(ImageProducer ip, int x, int y, int w, int h, int pix[], int off, int scansize)

When you consult the Java SDK documentation, they're quite straightforward, but here are a few things to clarify:

  • To grab in indexed mode, you need to use the constructor that takes the forceRGB parameter.  

  • The pix[] parameter is a pointer that takes an array of already allocated memory location for the pixel values.  This parameter will be the same one returned by getPixels().  (Why don't the first constructor takes this parameter?  Yes, that seems inconsistent, and I have no idea why.)  

  • The scansize is generally the width of the image.  I said generally because it is possible that an ImageProducer allocates some padding to optimize a memory addressing optimization.  However, for gif and jpg, it will be the width of the image (at least I haven't seen one that doesn't).

METHOD

There are three major steps required in using the PixelGrabber class.

1) Create an instance of a PixelGrabber class.  For an indexed image (ie: gif files), use the first constructor above.  The constructor takes an Image object of which you want to get the pixels data from.  The Image does not necessarily have to be loaded yet.
2) Grab the pixels of the image using PixelGrabber::grabPixels().  Again, before calling grabPixels(), you generally want to be sure that the Image has finished loading.  If not, then grabPixels will fail and you'd have to devise a mechanism to retry the process until it's successfull.
3) Get a pointer to the pixels data using PixelGrabber::getPixels().

To illustrate the process, let us write a class that does the three steps above.  I will call the class MyPixelGrabber.  

import java.awt.image.*;
import java.awt.*;
// notice here that I extends an image observer, this is
// not necessary, but I need the ImageObserver to be passed as
// a paramater to get the width and height of the image
public final class MyGifPixelGrabber implements ImageObserver
{
  private Image m_image=null;   // pointer to original image
  private Object m_pixels=null; // will contains either array of 
				// (for gif, it will be bytes)
  private int m_iNumOfColors=0;  
  private int m_iWidth, m_iHeight;

  MyGifPixelGrabber(Image img)
  {
    m_image=img;
  }

  public void grabPixels()
  {
    m_iWidth=m_image.getWidth(this);
    m_iHeight=m_image.getHeight(this); 
    // the parameter false below tells java that we want to open indexed
    // file.  When used with a gif file, this will cause
    // palletized mode grab.  When used with jpg, you'll get DirectColorModel
    // data
    // you can set that to true and it will cause all images to be grabbed
    // in DirectColorModel

    // CONSTRUCT
    PixelGrabber pixelGrabber=new PixelGrabber(m_image, 0,0, 
      m_iWidth, m_iHeight, false);  

    // GRAB
    try
    {
      pixelGrabber.grabPixels();
    }
    catch (Exception e)
    {
      System.out.println("PixelGrabber exception"); 
    }

    // GET
    m_pixels=(Object)pixelGrabber.getPixels();
    // get the palette of the image, if possible
    m_colorModel=pixelGrabber.getColorModel();
    // if loading a gif, you will get IndexedColorModel, if jpg, you will
    // get DirectColorModel
    if (!(m_colorModel instanceof IndexColorModel))
    {
      // not an indexed file (ie: not a gif file)
    }
    else
    {
      m_iNumOfColors=((IndexColorModel)m_colorModel).getMapSize();
    }
  }

  // you'd need to cast the return values, which will be an array of bytes
  // or an array of ints.  if the file is a gif file, it will return an 
  // array of bytes, if jpg, you will get an array of ints
  public Object getPixels()
  {
    return m_pixels; 
  }

  public int getWidth()
  {
    return m_iWidth;
  }

  public int getHeight()
  {
    return m_iHeight;
  }

  // this won't contain a valid value if you're grabbing
  // a non palletized image (such as jpgs)
  public int getNumOfColors()
  {
    return m_iNumOfColors;
  }

  // returns the red component value of a pixel
  public int getRed(int pixel)
  {
    if ((m_colorModel instanceof IndexColorModel))	  
	return ((IndexColorModel)m_colorModel).getRed(pixel);
    else
	return ((DirectColorModel)m_colorModel).getRed(pixel);
  }

  // returns the green component value of a pixel
  public int getGreen(int pixel)
  {
    if ((m_colorModel instanceof IndexColorModel))	  
	return ((IndexColorModel)m_colorModel).getGreen(pixel);
    else
	return ((DirectColorModel)m_colorModel).getGreen(pixel);
  }

  // returns the blue component value of a pixel
  public int getBlue(int pixel)
  {
    if ((m_colorModel instanceof IndexColorModel))	  
	return ((IndexColorModel)m_colorModel).getBlue(pixel);
    else
	return ((DirectColorModel)m_colorModel).getBlue(pixel);
  }

  public void destroy()
  {
    m_image=null;
    m_pixels=null;
  }

  // we need this method just because we're extending ImageObserver.
  public boolean imageUpdate(Image img, int infoflags, int x, int y, 
    int width, int height) 
  {
    return true;	  
  }  
}
EXAMPLE

Below is an applet that takes a gif image, grab the pixels of the gif, and displays the pixel values on a separate window.  You can also click on the picture and it will display the pixel value of the pixel you clicked.  The full source is here.

 

<<Try some of permadi.com's Java applets>>
<<INDEX>>
(C) F. Permadi