The image processing parts of Java are buried within the java

advertisement
Chapter 12
Images
Images are objects of the image class which is part of the java.awt.package. Images are manipulated
using the classes found in the java.awt.image package.
The image processing parts of Java are buried within the java.awt.image package. The package
consists of three interfaces and eleven classes, two of which are abstract. Some of them are specifed below:




The ImageObserver interface provides the single method necessary to support the asynchronous
loading of images. The interface implementers watch the production of an image and can react when
certain conditions arise.
The ImageConsumer and ImageProducer interfaces provide the means for low level image creation.
The ImageProducer provides the source of the pixel data that is used by the ImageConsumer to create
an Image.
The PixelGrabber and ImageFilter classes, along with the CropImageFilter and RGBImageFilter
subclasses, provide the tools for working with images. PixelGrabber consumes pixels from an Image
into an array. The ImageFilter classes modify an existing image to produce another Image instance.
CropImageFilter makes smaller images; RGBImageFilter alters pixel colors.
MemoryImageSource and FilteredImageSource produce new images. MemoryImageSource takes an
array and creates an image from it. FilteredImageSource uses an ImageFilter to read and modify
data from another image and produces the new image based on the original. Both MemoryImageSource
and FilteredImageSource implement ImageProducer because they produce new pixel data.
The classes in the java.awt.image package let you create Image objects at run-time. These classes can be
used to rotate images, make images transparent, create image viewers for unsupported graphics formats, and
more.
12.1 Simple Image Loader
In java, the image class is used to refer to images in memory and to images that must be loaded from
external sources. Hence there are three common operations that occur when you work with images.



Creating an image
Loading an image
Dispalying an image.
Creating an image object
The component class in java.awt has a factory method called createImage( ) that is used to create
image objects. The two different form of method is shown below.
1. public Image createImage (int width, int height)
The createImage() method creates an empty Image of size width x height. The returned Image is
an in-memory image that can be drawn on for double buffering to manipulate an image in the
background. If an image of size width x height cannot be created, the call returns null.
2. public Image createImage (ImageProducer producer)
This createImage() method allows you to take an existing image and modify it in some way to
produce a new Image. This can be done through ImageFilter and FilteredImageSource or a
MemoryImageSource, which accepts an array of pixel information.
Loading an image
To load the image, we use the getImage( ) method defined by the Applet class. It has the following
forms.
1. public Image getImage(URL url)
This method returns an image object that encapsulates the image found at the location specified by url.
2. public Image getImage(URL url, String imageName)
This returns an Image object that encapsulates the image found at the location specified by URL and
having the specified by imageName.
Displaying an Image
Finally, To display the image we use drawImage( ) which is a member of the Graphics class. The four
different forms of drawImage methods are shown below:
1. public abstract boolean drawImage(Image img, int
2. public abstract boolean drawImage(Image img, int
ImageObserver observer);
3. public abstract boolean drawImage(Image img, int
ImageObserver observer);
4. public abstract boolean drawImage(Image img, int
height,Color bgcolor,ImageObserver observer);
x, int y, ImageObserver observer);
x, int y, int width, int height,
x, int y,Color bgcolor,
x, int y, int width, int
In all these methods it passes the image itself and the coordinates of its top-left corner is specified
and an ImageObserver is provided. ImageObserver is an interface that is implemented by the Component
class.. ImageObservers are useful because they can be sent information about the image as it is being loaded.
Because Applet implements the ImageObserver interface, you can pass this as the ImageObserver parameter.
The second drawImage method is passed parameters for width and height. This causes the image to be
scaled so that it appears in the specified rectangle. The final two drawImage methods are the same as the first
two with the addition of being able to specify a background color for the image.
Example : public class MyClass extends Applet
{
public void init()
{
myImage = getImage(getDocumentBase(), "savannah.jpg");
}
public void paint(Graphics g)
{
g.drawImage(myImage, 100, 100, this);
}
}
12.2 ImageObserver
ImageObserver is an interface used to receive notification as an image is being generated. The
ImageObserver interface includes a single method. This method, imageUpdate, is called whenever additional
information about an image becomes available. For example, it might take time to retrieve a large image
across the Internet. The ImageObserver interface can monitor the progress of an image retrieval. An applet
could then possibly display a progress message, an estimated time to complete, or take any other useful action.
Therefore, when you call drawImage(), you can pass this as the final argument; the component on
which you are drawing serves as the ImageObserver for the drawing process. The communication between
the image observer and the image consumer happens behind the scenes; you never have to worry about it,
unless you want to write your own imageUpdate() method that does something special as the image is being
loaded.
If you call drawImage() to display an image created in local memory (either from double buffering or
from a MemoryImageSource), you can set the ImageObserver parameter of drawImage() to null because no
asynchrony is involved; the entire image is available immediately, so an ImageObserver isn't needed.
The signature of imageUpdate is as follows:
public abstract boolean imageUpdate(Image img, int infoflags, int x, int y, int
width, int height);
The first parameter represents the image being updated. The second parameter represents a
combination of various flags that give information about the image. These flags are described in Table 14.2.
The remaining parameters usually represent a rectangle indicating the portion of the image that has been
retrieved so far. Depending on the values in the infoflags parameter, some of these parameters might be
invalid. The imageUpdate method should return true if you want to continue receiving updates, or false
otherwise.
Flag
Table 14.2. Flags used in the imageUpdate method.
Description
ABORT
Retrieval of the image was aborted.
ALLBITS
All bits of the image have been retrieved.
ERROR
An error occurred while retrieving the image.
FRAMEBITS
A frame that is part of a multi-frame image has been completely
retrieved.
HEIGHT
The height parameter now represents the final height of the
image.
PROPERTIES
The properties of the image have been retrieved.
SOMEBITS
More bits have been retrieved.
WIDTH
The width parameter now represents the final width of the
image.
As an example of how imageUpdate can be used, consider EX14I, as shown in Listing 14.9. In this
example, a text area is created that will be used to display status messages. The imageUpdate method
compares the value in the infoflags parameter against ImageObserver.ERROR and
ImageObserver.ALLBITS. When one of these flags is set, a message is appended to the text area.
Listing 14.9. EX14I.java illustrates the use of the ImageObserver interface.
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class EX14I extends Applet
{
private Image myImage;
TextArea status;
public void init()
{
resize(520, 470);
// create the image
myImage = getImage(getDocumentBase(), "savannah.jpg");
// create a text area for displaying status information
status = new TextArea(5, 20);
add(status);
}
public void paint(Graphics g)
{
// paint the image in the specified rectangle
g.drawImage(myImage, 0, 110, 350, 350, this);
}
public synchronized boolean imageUpdate(Image img,
int infoflags, int x, int y, int width, int height)
{
// if an error occurs, display the message
if ((infoflags & ImageObserver.ERROR) != 0)
status.appendText("Error\r\n");
// once all the bits have been received display
// a message and repaint the applet
if ((infoflags & ImageObserver.ALLBITS) != 0)
{
status.appendText("Allbits\r\n");
repaint();
}
return true;
}
}
12.3 Graphical feedback
Double Buffering
Double buffering means drawing to an offscreen graphics context and then displaying this graphics
context to the screen in a single operation. By using the double buffering technique, you can take your time
drawing to another graphics context that isn't displayed. When you are ready, you tell the system to display
the completely new image at once. Doing so eliminates the possibility of seeing partial screen updates and
flickering.
The first thing you need to do is create an image as your drawing canvas. To get an image object, call
the createImage() method. createImage() method is as follow:
Image im = createImage (300, 300); // width and height
Once you have an Image object, to draw associated Image we need to get a Graphics context from the
Image. To do so, call the getGraphics() method of the Image class, and use that Graphics context for your
drawing:
Graphics buf = im.getGraphics();
Now you can do all your drawings with buf. To display the drawing, the paint() method only needs to call
drawImage(im, top, left, width, heigth).
Another feature of buffering is that you do not have redraw the entire image with each call to paint().
The buffered image you're working on remains in memory, and you can add to it at will. If you are drawing
directly to the screen, you would have to recreate the entire drawing each time paint() is called; remember,
paint() always hands you a completely new Graphics object
Example 2.3 puts it all together for you. It plays a game, with one move drawn to the screen each cycle. We
still do the drawing within paint(), but we draw into an offscreen buffer; that buffer is copied onto the
screen by g.drawImage(im, 0, 0, this). If we were doing a lot of drawing, it would be a good idea to
move the drawing operations into a different thread, but that would be overkill for this simple applet.
Example 2.3: Double Buffering--Who Won?
import java.awt.*;
import java.applet.*;
import java.awt.image.*;
public class buffering extends Applet {
Image im;
Graphics buf;
int pass=0;
public void init () {
// Create buffer
im = createImage (size().width, size().height);
// Get its graphics context
buf = im.getGraphics();
// Draw Board Once
buf.setColor (Color.red);
buf.drawLine ( 0, 50, 150, 50);
buf.drawLine ( 0, 100, 150, 100);
buf.drawLine ( 50,
0, 50, 150);
buf.drawLine (100,
0, 100, 150);
buf.setColor (Color.black);
}
public void paint (Graphics g) {
// Draw image - changes are written onto buf
g.drawImage (im, 0, 0, this);
// Make a move
switch (pass) {
case 0:
buf.drawLine
buf.drawLine
break;
case 1:
buf.drawOval
break;
case 2:
buf.drawLine
buf.drawLine
break;
case 3:
buf.drawOval
break;
case 4:
buf.drawLine
buf.drawLine
break;
case 5:
buf.drawOval
break;
case 6:
buf.drawLine
buf.drawLine
break;
case 7:
buf.drawOval
break;
case 8:
buf.drawLine
buf.drawLine
break;
}
pass++;
if (pass <= 9)
repaint (500);
(50, 50, 100, 100);
(50, 100, 100, 50);
(0, 0, 50, 50);
(100, 0, 150, 50);
(150, 0, 100, 50);
(0, 100, 50, 50);
(0, 50, 50, 100);
(0, 100, 50, 50);
(100, 50, 50, 50);
(50, 0, 100, 50);
(50, 50, 100, 0);
(50, 100, 50, 50);
(100, 100, 150, 150);
(150, 100, 100, 150);
}
}
12.4 MediaTracker
When multiple images are to be loaded synchronously imageUpdate( ) does not work well. Hence
MediaTracker class is used belonging to java.awt package
A MediaTracker is an object that will check the status of an arbitrary number of images in parallel.
First an object of MediaTracker has to be created. Then to use MediaTracker, we need to create a object and
addImage( ) method to track the loading status of an image.
Adding images
The addImage() methods add objects for the MediaTracker to track. When placing an object under
MediaTracker's control, you must provide an identifier for grouping purposes. When multiple images are
grouped together, you can perform operations on the entire group with a single request. addImage( ) has the
following general forms.
1. public synchronized void addImage (Image image, int id, int width, int height)
a
The addImage() method tells the MediaTracker object that it needs to track the loading of image.
The id is used as a grouping. Width and height specify the dimensions of the object when it is
displayed.
2. public void addImage (Image image, int id)
The addImage() method tells the MediaTracker object that it needs to track the loading of image.
The id is used as a grouping.
Note: ID number do not need to be unique. You can use the same number with several images as a means of
identifying them as part of a group.
Once the image is registered we can check the status of an image using checkID( ) and checkAll()
methods.
Boolean checkID(int imgid) :Here imgid specfies the ID of the image you want to check. If all images having a particular id have
been loaded, it returns true otherwise false.
Boolean checkAll( )
is used to know if all the imageTracker have been loaded while the other images are being download.
Removing images
The removeImage() methods, allow you to remove objects from the MediaTracker. Getting rid of loaded
images results in better performance because the tracker has fewer objects to check.
public void removeImage (Image image)
The removeImage() method tells the MediaTracker to remove all objects of image from its tracking
list.
public void removeImage (Image image, int id)
The removeImage() method tells the MediaTracker to remove all objects of image from group id of
its tracking list.
public void removeImage (Image image, int id, int width, int height)
This removeImage() method tells the MediaTracker to remove all objects of image from group id
and scale width x height of its tracking list.
12.3 ImageProducer
The ImageProducer is an interface for object that want to produce data for image. It is used for creating an
image given a byte or integer array. Image producers serve as sources for pixel data; they may compute the
data themselves or interpret data from some external source, like a GIF file. No matter how it generates the
data, an image producer's job is to hand that data to an image consumer, which usually renders the data on the
screen.
An image producer works as follows:



It waits for image consumers to register their interest in an image.
As image consumers register, it stores them in a Hashtable, Vector, or some other collection
mechanism.
As image data becomes available, it loops through all the registered consumers and calls their methods
to transfer the data
ImageProducer Interface
Methods
public void addConsumer (ImageConsumer ic)
The addConsumer() method registers ic as an ImageConsumer interested in the Image information.
Once an ImageConsumer is registered, the ImageProducer can deliver Image pixels immediately or
wait until startProduction() has been called.
public boolean isConsumer (ImageConsumer ic)
The isConsumer() method checks to see if ic is a registered ImageConsumer for this ImageProducer.
If ic is registered, true is returned. If ic is not registered, false is returned.
public void removeConsumer (ImageConsumer ic)
The removeConsumer() method removes ic as a registered ImageConsumer for this ImageProducer.
If ic was not a registered ImageConsumer, nothing should happen. This is not an error that should
throw an exception. Once ic has been removed from the registry, the ImageProducer should no
longer send data to it.
public void startProduction (ImageConsumer ic)
The startProduction() method registers ic as an ImageConsumer interested in the Image
information and tells the ImageProducer to start sending the Image data immediately. The
ImageProducer sends the image data to ic and all other registered ImageConsumer objects, through
addConsumer().
The method crateImage( ) method takes an ImageProducer object as its argument to produce a new
image. There are 2 image producer in the image package. Namely


MemoryImageSource.
FilterImageSource
FilteredImageSource
The FilteredImageSource class combines an ImageProducer and an ImageFilter to create a new Image.
The image producer generates pixel data for an original image. The FilteredImageSource takes this data
and uses an ImageFilter to produce a modified version: the image may be scaled, clipped, or rotated, or the
colors shifted, etc. The FilteredImageSource is the image producer for the new image. The ImageFilter
object transforms the original image's data to yield the new image; it implements the ImageConsumer
interface
public FilteredImageSource (ImageProducer original, ImageFilter filter)
The FilteredImageSource constructor creates an image producer that combines an image, original,
and a filter, filter, to create a new image. The ImageProducer of the original image is the
constructor's first parameter; given an Image, you can acquire its ImageProducer by using the
getSource() method. The following code shows how to create a new image from an original.
Image image = getImage (new URL
("http://www.ora.com/graphics/headers/homepage.gif"));
Image newOne = createImage (new FilteredImageSource
(image.getSource(), new SomeImageFilter()));
MemoryImageSource
MemoryImageSource is a class that creates a new image from an array of data.
MemoryImageSource is constructed out of an array of integer specified by pixel, in the
default RGB color model to produce data for an image object.
The MemoryImageSource class allows you to create images completely in memory; you generate pixel
data, place it in an array, and hand that array and a ColorModel to the MemoryImageSource constructor. The
MemoryImageSource is an image producer that can be used with a consumer to display the image on the
screen. You could also use a MemoryImageSource to modify a pre-existing image.
It defines several constructors. One of them is
MemoryImageSource(int width, int height, int pixel[],int offset,int scanLineWidth);
Example:
import java.awt.*;
import java.applet.*;
import java.awt.image.*;
public class mig extends Applet {
Image im;
public void init( )
{
Dimension d=getSize( );
int w = d.width;
int h=d.height;
int p[]=new int[w*h];
int i=0;
for(int y=0; y<h;y++)
{
for (int x=0;x<w;x++)
{
int r=(x^y)&0xff;
int g=(x*2^y*2)&0xff;
int b=(x*4^y)&0xff;
p[i++]=(255<<24) | (r<<16) |(g<<16) |(g<<8) |b;
}
}
im=createImage(new MemoryImageSource(w,h,p,0,w));
}
public void paint(Graphics g)
{
g.drawImage(im,0,0,this);
}
}
Image Consumer
It is an interface for objects that want to take pixel data from images and supply it as another kind of
data. It creates int or byte array that represents pixels from the image object. An object that implements the
ImageConsmuer is PixelGrabber Class which is defined within java.lang.image.
PixelGrabber:This class is the inverse of the Memory ImageSource Class. It takes an image and grabs the pixels
array from it. First create an array to hold pixel data and create an object of PixelGrabber Class and finally use
the method grabPixels( ).
12.5 ImageFilter
Image filters provide another way to modify images. An ImageFilter is used in conjunction with a
FilteredImageSource object. The ImageFilter, which implements ImageConsumer (and Cloneable),
receives data from an ImageProducer and modifies it; the FilteredImageSource, which implements
ImageProducer, sends the modified data to the new consumer.
The ImageFilter class implements a "null" filter that does nothing to the image. To modify an image, we
must use any two subclass of ImageFilter. They are


CropImageFilter.
RGBImageFilter;
it is useful for filtering an image on the basis of a pixel's color.
To use an ImageFilter, you pass it to the FilteredImageSource constructor, which serves as an
ImageProducer to pass the new pixels to their consumer. The following code runs the image logo.jpg through
an image filter, SomeImageFilter, to produce a new image. The constructor for SomeImageFilter is called
within the constructor for FilteredImageSource, which in turn is the only argument to createImage().
Image image = getImage (new URL (
"http://www.ora.com/images/logo.jpg"));
Image newOne = createImage (new FilteredImageSource (image.getSource(),
new SomeImageFilter()));
ImageFilter Methods
Variables
protected ImageConsumer consumer;
The actual ImageConsumer for the image. It is initialized automatically for you by the
getFilterInstance() method.
Constructor
public ImageFilter ()
The only constructor for ImageFilter is the default one, which takes no arguments. Subclasses can
provide their own constructors if they need additional information.
RGBImageFilter
RGBImageFilter
is an abstract subclass of ImageFilter that provides a shortcut for building the most
common kind of image filters: filters that independently modify the pixels of an existing image, based only on
the pixel's position and color. Because RGBImageFilter is an abstract class, you must subclass it before you
can do anything. The only method your subclass must provide is filterRGB(), which produces a new pixel
value based on the original pixel and its location. A handful of additional methods are in this class;.
Creating your own RGBImageFilter is fairly easy. One of the more common applications for an
RGBImageFilter is to make images transparent by setting the alpha component of each pixel. To do so, we
extend the abstract RGBImageFilter class. The filter in Example 12.9 makes the entire image translucent,
based on a percentage passed to the class constructor. Filtering is independent of position, so the constructor
can set the canFilterIndexColorModel variable. A constructor with no arguments uses a default alpha value
of 0.75.
Example 12.9: TransparentImageFilter Source
import java.awt.image.*;
class TransparentImageFilter extends RGBImageFilter {
float alphaPercent;
public TransparentImageFilter () {
this (0.75f);
}
public TransparentImageFilter (float aPercent)
throws IllegalArgumentException {
if ((aPercent < 0.0) || (aPercent > 1.0))
throw new IllegalArgumentException();
alphaPercent = aPercent;
canFilterIndexColorModel = true;
}
public int filterRGB (int x, int y, int rgb) {
int a = (rgb >> 24) & 0xff;
a *= alphaPercent;
return ((rgb & 0x00ffffff) | (a << 24));
}
}
CropImageFilter
The CropImageFilter is an ImageFilter that crops an image to a rectangular region. When used with
FilteredImageSource, it produces a new image that consists of a portion of the original image. The cropped
region must be completely within the original image. It is never necessary to subclass this c
If you crop an image and then send the result through a second ImageFilter, the pixel array received by the
filter will be the size of the original Image, with the offset and scansize set accordingly. The width and
height are set to the cropped values; the result is a smaller Image with the same amount of data.
CropImageFilter keeps the full pixel array around, partially empty
Example 12.10 uses a CropImageFilter to extract the center third of a larger image. No subclassing is
needed; the CropImageFilter is complete in itself. The output is displayed in Figure 12.7.
Example 12.10: Crop Applet Source (1)
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class Crop extends Applet {
Image i, j;
public void init () {
MediaTracker mt = new MediaTracker (this);
i = getImage (getDocumentBase(), "rosey.jpg");
mt.addImage (i, 0);
try {
mt.waitForAll();
int width
= i.getWidth(this);
int height
= i. getHeight(this);
j = createImage (new FilteredImageSource (i.getSource(),
new CropImageFilter (width/3, height/3,
width/3, height/3)));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void paint (Graphics g) {
g.drawImage (i, 10, 10, this);
// regular
if (j != null) {
g.drawImage (j, 10, 90, this);
// cropped
}
}
}
Listing 14.7. EX14G.java.(2)
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public class EX14G extends Applet
{
private Image myImage;
private Image croppedImage;
public void init()
{
resize(630, 470);
myImage = getImage(getDocumentBase(), "savannah.jpg");
// create a filter that will crop the image to the
// area starting at point (195, 0) with a width of 140
// and a height of 150
CropImageFilter myCropFilter = new CropImageFilter(195,0,
140, 150);
// create a new image source based on the original image
// and using the newly created filter
FilteredImageSource imageSource = new
FilteredImageSource(myImage.getSource(),
myCropFilter);
// create the cropped image
croppedImage = createImage(imageSource);
}
public void paint(Graphics g)
{
// paint the cropped image
g.drawImage(croppedImage, 0, 0, this);
// paint the original image to the right of the cropped one
g.drawImage(myImage, 160, 0, this);
}
}
Figure 14.11 : The CropImageFilter can be used to crop images.
Figure 14.12 : The RedactFilter can be used to conceal the identity of a Mafia informant.
Listing 14.8. EX14H.java illustrates the creation of a custom filter.
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
class RedactFilter extends RGBImageFilter
{
int startX, startY, endX, endY;
// the constructor is passed the coordinates of the area
// to redact and stores these values
public RedactFilter(int x, int y, int width, int height)
{
startX = x;
startY = y;
endX
= startX + width;
endY
= startY + height;
}
public
{
//
//
//
if
int filterRGB(int x, int y, int rgb)
if the (x,y) position is in the redacted area
return red, otherwise return the same color that
was passed in
(x >= startX && x <= endX && y >= startY && y <= endY)
return 0xff0000ff;
else
return rgb;
}
}
public class EX14H extends Applet
{
private Image myImage;
private Image redactedImage;
public void init()
{
resize(570, 470);
// get the original image
myImage = getImage(getDocumentBase(), "savannah.jpg");
// create a filter and specify the range to be redacted
ImageFilter filter = new RedactFilter(220, 80, 80, 15);
// create a new image source based on the original
// image and the new filter
FilteredImageSource imageSource=new FilteredImageSource(
myImage.getSource(), filter);
// create the redacted image from the image source
redactedImage = createImage(imageSource);
}
public void paint(Graphics g)
{
// paint the redacted image
g.drawImage(redactedImage, 0, 0, this);
}
}
Download