As you have already learned from the Images lesson, Images are described by a width and a height, measured in pixels, and have a coordinate system that is independent of the drawing surface.
There are a number of common tasks when working with images.
  • Loading an external GIF, PNG JPEG image format file into Java 2D™'s internal image representation.
  • Directly creating a Java 2D image and rendering to it.
  • Drawing the contents of a Java 2D image on to a drawing surface.
  • Saving the contents of a Java 2D image to an external GIF, PNG, or JPEG image file.
This lesson teaches you the basics of loading, displaying, and saving images.
The are two main classes that you must learn about to work with images:
  • The java.awt.Image class is the superclass that represents graphical images as rectangular arrays of pixels.
  • The java.awt.image.BufferedImage class, which extends the Image class to allow the application to operate directly with image data (for example, retrieving or setting up the pixel color). Applications can directly construct instances of this class.
The BufferedImage class is a cornerstone of the Java 2D immediate-mode imaging API. It manages the image in memory and provides methods for storing, interpreting, and obtaining pixel data. Since BufferedImage is a subclass of Image it can be rendered by the Graphics and Graphics2D methods that accept an Image parameter.
BufferedImage is essentially an Image with an accessible data buffer. It is therefore more efficient to work directly with BufferedImage. A BufferedImage has aColorModel and a Raster of image data. The ColorModel provides a color interpretation of the image's pixel data.
The Raster performs the following functions:
  • Represents the rectangular coordinates of the image
  • Maintains image data in memory
  • Provides a mechanism for creating multiple subimages from a single image data buffer
  • Provides methods for accessing specific pixels within the image
The basic operations with images are represented in the following sections:

Reading/Loading an image

This section explains how to load an image from an external image format into a Java application using the Image I/O API

Drawing an image

This section teaches how to display images using the drawImage method of the Graphics and Graphics2D classes.

Creating and drawing To an image

This section describes how to create an image and how to use the image itself as a drawing surface.

Writing/saving an image

This section explains how to save created images in an appropriate format.



Reading/Loading an Image
When you think of digital images, you probably think of sampled image formats such as the JPEG image format used in digital photography, or GIF images commonly used on web pages. All programs that can use these images must first convert them from that external format into an internal format.
Java 2D™ supports loading these external image formats into its BufferedImage format using its Image I/O API which is in the javax.imageio package. Image I/O has built-in support for GIF, PNG, JPEG, BMP, and WBMP. Image I/O is also extensible so that developers or administrators can "plug-in" support for additional formats. For example, plug-ins for TIFF and JPEG 2000 are separately available.
To load an image from a specific file use the following code:
BufferedImage img = null;
try {
    img = ImageIO.read(new File("strawberry.jpg"));
} catch (IOException e) {
}
Image I/O recognises the contents of the file as a JPEG format image, and decodes it into a BufferedImage which can be directly used by Java 2D.
LoadImageApp.java shows how to display this image.
If the code is running in an applet, then its just as easy to obtain the image from the applet codebase :
try {
   URL url = new URL(getCodeBase(),
                    "strawberry.jpg");
   img = ImageIO.read(url);
} catch (IOException e) {
}
The getCodeBase method used in this example returns the URL of the directory containing this applet.
The following example shows how to use the getCodeBase method to load the strawberry.jpg file.

LoadImageApp.java contains the complete code for this example and this applet requires the strawberry.jpg image file.
In addition to reading from files or URLS, Image I/O can read from other sources, such as an InputStreamImageIO.read() is the most straightforward convenience API for most applications, but the javax.imageio.ImageIO class provides many more static methods for more advanced usages of the Image I/O API. The collection of methods on this class represent just a subset of the rich set of APIs for discovering information about the images and for controlling the image decoding (reading) process.
We will explore some of the other capabilities of Image I/O later in the Writing/Saving an Image section.


Drawing an Image

As you have already learned, the Graphics.drawImage method draws an image at a specific location:
boolean Graphics.drawImage(Image img,
                   int x, int y,
                   ImageObserver observer);
The x,y location specifies the position for the top-left of the image. The observer parameter notifies the application of updates to an image that is loaded asynchronously.
Ghi chú: Đối tượng ImageObserver sẽ override hàm imageUpdate () để quan sát, theo dõi, giám sát thông tin về tình trạng tải ảnh "manually". Đối tượng MediaTracker giám sát việc tải của một hay nhiều image synchronously.
The observer parameter is not frequently used directly and is not needed for the BufferedImage class, so it usually is null.
The described method addresses only the case where the entire image is to be drawn, mapping image pixels to user space coordinates 1:1. Sometimes applications require to draw a part of the image (a sub-image), or scale the image to cover a particular area of the drawing surface, or transform or filter the image before drawing.
The overloads of the drawImage() method perform these operations. For example, the following overload of the drawImage() method enables you to draw as much of a specified area of the specified image as is currently available, scaling it to fit inside the specified area of the destination drawable surface:
boolean Graphics.drawImage(Image img,
       int dstx1, int dsty1, int dstx2, int dsty2,
       int srcx1, int srcy1, int srcx2, int srcy2,
       ImageObserver observer);
The src parameters represent the area of the image to copy and draw. The dst parameters display the area of the destination to cover by the the source area. Thedstx1, dsty1 coordinates define the location to draw the image. The width and height dimensions on the destination area are calculated by the following expressions:(dstx2-dstx1), (dsty2-dsty1). If the dimensions of the source and destinations areas are different, the Java 2D™ API will scale up or scale down, as needed.
The following code example divides an image into four quadrants and randomly draws each quadrant of the source image into a different quadrant of the destination.

The complete code for this applet is in JumbledImageApplet.java.
This example uses the following code to paint the jumbled duke_skateboard.jpg image. It iterates over the four sub-images of the source, drawing each in turn into a randomly selected destination quadrant.
/* divide the image 'bi' into four rectangular
 * areas and draw each of these areas in to a
 * different part of the image, so as to jumble
 * up the image.  'cells' is an array which has
 * been populated with values which redirect
 * drawing of one subarea to another subarea.
 */
int cellWidth = bi.getWidth(null)/2;
int cellHeight = bi.getHeight(null)/2;
for (int x=0; x<2; x++) {
    int sx = x*cellWidth;
    for (int y=0; y<2; y++) {
        int sy = y*cellHeight;
        int cell = cells[x*2+y];
        int dx = (cell / 2) * cellWidth;
        int dy = (cell % 2) * cellHeight;
        g.drawImage(bi,
                    dx, dy, 
                    x+cellWidth, dy+cellHeight,
                    sx, sy,
                    sx+cellWidth, sy+cellHeight,
                    null);
    }
}

Filtering Images

In addition to copying and scaling images, the Java 2D API also filter an image. Filtering is drawing or producing a new image by applying an algorithm to the pixels of the source image. Image filters can be applied by using the following method:
void Graphics2D.drawImage(BufferedImage img,
                          BufferedImageOp op,
                          int x, int y)
The BufferedImageOp parameter implements the filter. The following applet represents an image drawn on top of text. Drag the slider to show more or less of the text through the image and make the image more or less transparent.

The following code shows how the filter action is done by operating on a BufferedImage object with an alpha channel and rescales that alpha channel by using theRescaleOp object. The alpha channel determines the translucency of each pixel. It also specifies the degree to which this image overwrites.
/* Create an ARGB BufferedImage */
BufferedImage img = ImageIO.read(imageSrc);
int w = img.getWidth(null);
int h = img.getHeight(null);
BufferedImage bi = new
    BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.getGraphics();
g.drawImage(img, 0, 0, null);

/*
 * Create a rescale filter op that makes the image
 * 50% opaque.
 */
float[] scales = { 1f, 1f, 1f, 0.5f };
float[] offsets = new float[4];
RescaleOp rop = new RescaleOp(scales, offsets, null);

/* Draw the image, applying the filter */
g2d.drawImage(bi, rop, 0, 0);
The complete example represented in SeeThroughImageApplet.java includes the code that uses the slider to adjust the transparency from the initial 50%. This example also requires the duke_skateboard.jpg image.
The RescaleOp object is just one of many filters that can be created. The Java 2D API has several built in filters including the following:
  • ConvolveOp. Each output pixel is computed from surrounding pixels in the source image. It may be used to blur or sharpen images.
  • AffineTransformOp. This filter maps pixels in the source to a different position in the destination by applying a transformation on the pixel location.
  • LookupOp. This filter uses an application supplied lookup table to remap pixel colors.
  • RescaleOp. This filter multiplies the colors by some factor. Can be used to lighten or darken the image, to increase or reduce its opacity, etc.
The following example uses each of the described filters as well as scaling:

The complete code for this applet is in ImageDrawingApplet.java and this applet requires the bld.jpg image.
Use the drop-down menu to select an image scaling or filtering operation.


Creating and Drawing to an Image

We already know how to load an existing image, which was created and stored in your system or in any network location. But, you probably would like also to create an new image as a pixel data buffer.
In this case, you can create a BufferedImage object manually, using three constructors of this class:
  • new BufferedImage(width, height, type) - constructs a BufferedImage of one of the predefined image types.
  • new BufferedImage(width, height, type, colorModel) - constructs a BufferedImage of one of the predefined image types: TYPE_BYTE_BINARY orTYPE_BYTE_INDEXED.
  • new BufferedImage(colorModel, raster, premultiplied, properties) - constructs a new BufferedImage with a specified ColorModel andRaster.
On the other hand, we can use methods of the Component class. These methods can analyze the display resolution for the given Component orGraphicsConfiguration and create an image of an appropriate type.
  • Component.createImage(width, height)
  • GraphicsConfiguration.createCompatibleImage(width, height)
  • GraphicsConfiguration.createCompatibleImage(width, height, transparency)
GraphicsConfiguration returns an object of BufferedImage type, but the Component returns an object of Image type, if you need a BufferedImage object instead then you can perform an instanceof and cast to a BufferedImage in your code.
As was already mentioned in the previous lessons, we can render images not only on screen. An images itself can be considered as a drawing surface. You can use acreateGraphics() method of the BufferedImage class for this purpose:
...

BufferedImage off_Image =
  new BufferedImage(100, 50,
                    BufferedImage.TYPE_INT_ARGB);

Graphics2D g2 = off_Image.createGraphics();
Another interesting use of offscreen images is an automaticdouble buffering. This feature allows to avoid flicker in animated images by drawing an image to a back buffer and then copying that buffer onto the screen instead of drawing directly to the screen.
Java 2D™ also allows access to hardware acceleration for offscreen images, which can provide the better performance of rendering to and copying from these images. You can get the benefit of this functionality by using the following methods of the Image class:
  • The getCapabilities method allows you to determine whether the image is currently accelerated.
  • The setAccelerationPriority method lets you set a hint about how important acceleration is for the image.
  • The getAccelerationPriority method gets a hint about the acceleration importance.



Writing/Saving an Image

This lesson started with an explanation for using the javax.imageio package, to load images from an external image format into Java 2D™'s internal BufferedImageformat. Then it explains how to use the Graphics.drawImage() to draw that image, with optional filtering.
The final stage is saving a BufferedImage object into an external image format. This may be an image that was originally loaded by the Image I/O class from an external image format and perhaps modified using the Java 2D APIs, or it may be one that was created by Java 2D.
The Image I/O class provides a simple way to save images in a variety of image formats in the following example:
static boolean ImageIO.write(RenderedImage im, 
                             String formatName,
                             File output)  throws IOException

Note: The BufferedImage class implements the RenderedImage interface.

.
The formatName parameter selects the image format in which to save the BufferedImage.
try {
    // retrieve image
    BufferedImage bi = getMyImage();
    File outputfile = new File("saved.png");
    ImageIO.write(bi, "png", outputfile);
} catch (IOException e) {
    ...
}
The ImageIO.write method calls the code that implements PNG writing a “PNG writer plug-in”. The term plug-in is used since Image I/O is extensible and can support a wide range of formats.
But the following standard image format plugins : JPEG, PNG, GIF, BMP and WBMP are always be present.
Each image format has its advantages and disadvantages:
FormatPlusMinus
GIFSupports animation, and transparent pixelsSupports only 256 colors and no translucency
PNGBetter alternative than GIF or JPG for high colour lossless images, supports translucencyDoesn't support animation
JPGGreat for photographic imagesLoss of compression, not good for text, screenshots, or any application where the original image must be preserved exactly
For most applications it is sufficient to use one of these standard plugins. They have the advantage of being readily available. The Image I/O class provides a way to plug in support for additional formats which can be used, and many such plug-ins exist. If you are interested in what file formats are available to load or save in your system, you may use the getReaderFormatNames and getWriterFormatNames methods of the ImageIO class. These methods return an array of strings listing all of the formats supported in this JRE.
String writerNames[] = ImageIO.getWriterFormatNames();
The returned array of names will include any additional plug-ins that are installed and any of these names may be used as a format name to select an image writer. The following code example is a simple version of a complete image edit/touch up program which uses a revised version of the ImageDrawingApplet.java sample program which can be used as follows :
  • An image is first loaded via Image I/O
  • The user selects a filter from the drop down list and a new updated image is drawn
  • The user selects a save format from the drop down list
  • Next a file chooser appears and the user selects where to save the image
  • The modified image can now be viewed by other desktop applications
The complete code of this example is represented in SaveImage.java.
In this lesson you have learned just the basics of Image I/O, which provides extensive support for writing images, including working directly with an ImageWriter plug-in to achieve finer control over the encoding process. ImageIO can write multiple images, image metadata, and determine quality vs. size tradeoffs. For more information see Java Image I/O API Guide.