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
|