edu.rit.image
Class PJGImage

java.lang.Object
  extended by edu.rit.image.PJGImage
Direct Known Subclasses:
BaseColorImage, PJGGrayImage

public abstract class PJGImage
extends Object

Class PJGImage is the abstract base class for an image that is read from or written to a file in Parallel Java Graphics (PJG) format. PJG image files are designed to be generated by parallel programs.

Compared to PNG files, PJG files use a compression algorithm that yields somewhat larger file sizes but has a much smaller running time. In one test of a set of 24-bit color images, the PJG file sizes were 2-17% of the raw data sizes, whereas the PNG file sizes were 2-11% of the raw data sizes. The PJG file sizes were 30-50% larger than the corresponding PNG file sizes. However, the time to write the PJG files was only 1/10th to 1/20th the time to write the PNG files. This small running time to write a PJG file can substantially reduce a parallel program's sequential fraction, with a corresponding increase in the program's scalability.

In addition, sections of an image can be written to a PJG image file in arbitrary order. Sections of an image can also be scattered across multiple files. This lets the processes of a cluster parallel program generate portions of an image independently, without needing to gather the whole image into one process.

The PJG program can be used to display an image stored in one PJG file or scattered among multiple PJG files; to save the displayed image in a single PJG file; and to save the image in a PNG file or PostScript file.

Usage

To create a PJGImage object, instantiate one of the subclasses of class PJGImage, depending on the type of image. The image's pixel data is stored in an external matrix, as described in the documentation for each subclass. The available subclasses are:

Subclasses for other types of images will be added in later releases.

To get and set the image's pixel data, use methods in the subclass. You only need to allocate storage in the pixel data matrix for the portions of the image you are actually accessing; the complete matrix need not be allocated. Class Arrays has static methods for allocating portions of a matrix.

To write a PJGImage object to a PJG image file, call the prepareToWrite() method, specifying the output stream to write. The prepareToWrite() method returns an instance of class PJGImage.Writer. Call the methods of the PJG image writer object to write the pixel data, or sections of the pixel data, to the output stream. When finished, close the PJG image writer.

To read a PJGImage object from a PJG image file, call the prepareToRead() method, specifying the input stream to read. The prepareToRead() method returns an instance of class PJGImage.Reader. Call the methods of the PJG image reader object to read the pixel data, or sections of the pixel data, from the input stream. When finished, close the PJG image reader.

The previous paragraph assumes you already have a PJGImage object which is an instance of the correct subclass to hold the pixel data that will be read from the PJG image file. To both create an instance of the correct PJGImage subclass and read a PJG image file into that instance, call the static readFromStream() method. To create an instance of the correct PJGImage subclass to hold the contents of a PJG image file without actually reading in the pixel data, call the static createFromStream() method.

To get a BufferedImage object that uses the same underlying pixel data matrix as the PJGImage object, call the getBufferedImage() method. You can then do all the following with the BufferedImage: display it on the screen, draw into it using a graphics context, copy another BufferedImage into it, read it from or write it to a file using package javax.imageio (which typically supports PNG, JPG, and GIF formats). The rows and columns of the underlying matrix need not all be allocated when accessing the BufferedImage. If you get a pixel from the BufferedImage in an unallocated row or column, a pixel value of 0 (black) is returned. If you set a pixel in the BufferedImage in an unallocated row or column, the pixel value is discarded.

Note: Class PJGImage is not multiple thread safe.

PJG Image File Format

A PJG image file consists of a sequence of segments. Each segment consists of the following:

Header segment (segment type 0). The first segment in the file must be a header segment. There must be exactly one header segment in the file. The segment contents are:

Thus, the first eight bytes of a PJG image file must be 0x00, 0x50, 0x4A, 0x47, 0x00, 0x00, 0x00, 0x01.

Image type segment (segment type 1). The image type segment must come after the header segment and before any pixel data segments. There must be exactly one image type segment in the file. The segment contents are:

Height segment (segment type 2). The height segment must come after the header segment and before any pixel data segments. There must be exactly one height segment in the file. The segment contents are:

Width segment (segment type 3). The width segment must come after the header segment and before any pixel data segments. There must be exactly one width segment in the file. The segment contents are:

Creation time segment (segment type 4). The creation time segment must come after the header segment and before any pixel data segments. There must be at most one creation time segment in the file. The segment contents are:

Comment segment (segment type 5). A comment segment must come after the header segment and before any pixel data segments. There may be zero or more comment segments in the file. The segment contents are:

Run length encoded 24-bit color pixel data segment (segment type 6). A run length encoded 24-bit color pixel data segment may appear only in a file with image type = 0 (24-bit color image). A run length encoded 24-bit color pixel data segment may appear anywhere in the file after the header, image type, height, width, creation time, and comment segments. There may be zero or more run length encoded 24-bit color pixel data segments in the file.

The pixel data segment contains pixel data for one block of the image. The block encompasses certain rows and certain columns of the image (not necessarily all the rows and columns). Blocks may appear in any order in the file. The file need not contain pixel data for all the rows and columns in the image.

There is an implicit dictionary that associates consecutive indexes 0, 1, 2, . . . with pixel data values. The dictionary is not stored in the file; rather, the contents of the dictionary are built up as the pixel data segments are written or read. The dictionary persists between pixel data segments and is used for all the pixel data segments in the file.

The first four fields of the pixel data segment are:

The pixel data for the block is encoded as a sequence of runs. Each run consists of one or more consecutive pixels with identical values. The runs are found by scanning the pixel rows from lowest to highest row index. Within each row, the runs are found by scanning the pixels from lowest to highest column index. A run ends at the end of a row, or when a different pixel value is encountered.

Each run is encoded as follows. Let n be one less than the number of pixels in the run. Let p be the 24-bit pixel value (8-bit red component, 8-bit green component, 8-bit blue component) of the pixels in the run. Let x be the index associated with pixel value p in the dictionary. First encode the run length:

Then encode the pixel value:

Huffman delta encoded 8-bit grayscale pixel data segment (segment type 7). A Huffman delta encoded 8-bit grayscale pixel data segment may appear only in a file with image type = 1 (8-bit grayscale image). A Huffman delta encoded 8-bit grayscale pixel data segment may appear anywhere in the file after the header, image type, height, width, creation time, and comment segments. There may be zero or more Huffman delta encoded 8-bit grayscale pixel data segments in the file.

The pixel data segment contains pixel data for one block of the image. The block encompasses certain rows and certain columns of the image (not necessarily all the rows and columns). Blocks may appear in any order in the file. The file need not contain pixel data for all the rows and columns in the image.

The first four fields of the pixel data segment are:

Each pixel value is an integer from 0 (black) through 255 (white) inclusive.

Certain pixel data in each row of the block is encoded in the form of deltas rather than the pixel values themselves. The delta in column i is equal to the pixel value in column i minus the pixel value in column i-1, except the delta in the block's first column is equal to the pixel value in the block's first column.

The pixel data for the block is encoded by scanning the pixel rows from lowest to highest row index. Within each row, the pixels are scanned from lowest to highest column index. Each column is encoded using a modified Huffman encoding as follows:

The above encoding process yields a bit string. Each group of 8 bits is packed into a byte, from most significant bit to least significant bit. Any unused least significant bits in the last byte are set to zero. The resulting sequence of bytes forms the remainder of the pixel data segment.

Huffman delta encoded 24-bit hue pixel data segment (segment type 8). A Huffman delta encoded 24-bit hue pixel data segment may appear only in a file with image type = 2 (24-bit hue image). A Huffman delta encoded 24-bit hue pixel data segment may appear anywhere in the file after the header, image type, height, width, creation time, and comment segments. There may be zero or more Huffman delta encoded 24-bit hue pixel data segments in the file.

The pixel data segment contains pixel data for one block of the image. The block encompasses certain rows and certain columns of the image (not necessarily all the rows and columns). Blocks may appear in any order in the file. The file need not contain pixel data for all the rows and columns in the image.

The first four fields of the pixel data segment are:

Each 24-bit pixel value consists of an 8-bit red component, an 8-bit green component, and an 8-bit blue component. Each component is an integer from 0 through 255 inclusive.

First, the red components of all the pixels are treated as an 8-bit grayscale image and are encoded using Huffman delta encoding as described above, yielding a bit string.

Second, the green components of all the pixels are treated as an 8-bit grayscale image and are encoded using Huffman delta encoding as described above, yielding a bit string.

Third, the blue components of all the pixels are treated as an 8-bit grayscale image and are encoded using Huffman delta encoding as described above, yielding a bit string.

Finally, the above three bit strings are concatenated yielding a longer bit string. Each group of 8 bits is packed into a byte, from most significant bit to least significant bit. Any unused least significant bits in the last byte are set to zero. The resulting sequence of bytes forms the remainder of the pixel data segment.


Nested Class Summary
 class PJGImage.Reader
          Class PJGImage.Reader is the abstract base class for an object with which to read a PJGImage from an input stream.
 class PJGImage.Writer
          Class PJGImage.Writer is the abstract base class for an object with which to write a PJGImage to an output stream.
 
Method Summary
 void addComment(String theComment)
          Add the given comment to this image.
 void clearComments()
          Remove all of the comments in this image.
static PJGImage createFromStream(InputStream theStream)
          Create an instance of class PJGImage based on the given input stream.
abstract  BufferedImage getBufferedImage()
          Obtain a BufferedImage whose pixel data comes from this image's underlying matrix.
 Iterable<String> getComments()
          Returns an iterable collection of the comments in this image.
 Long getCreationTime()
          Returns this image's creation time.
abstract  Displayable getDisplayable()
          Obtain a Displayable object with which to display this image in a Swing UI.
 int getHeight()
          Returns this image's height.
 int getWidth()
          Returns this image's width.
abstract  PJGImage.Reader prepareToRead(InputStream theStream)
          Prepare to read this image from the given input stream.
abstract  PJGImage.Writer prepareToWrite(OutputStream theStream)
          Prepare to write this image to the given output stream.
static PJGImage readFromStream(InputStream theStream)
          Create an instance of class PJGImage and read the image's pixel data from the given input stream.
 void setCreationTime(Long time)
          Set this image's creation time.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

getHeight

public int getHeight()
Returns this image's height.

Returns:
Image height in pixels.

getWidth

public int getWidth()
Returns this image's width.

Returns:
Image width in pixels.

getCreationTime

public Long getCreationTime()
Returns this image's creation time.

Returns:
Creation time in milliseconds since midnight 01-Jan-1970 UTC, or null if creation time is not set.

setCreationTime

public void setCreationTime(Long time)
Set this image's creation time.

Parameters:
time - Creation time in milliseconds since midnight 01-Jan-1970 UTC. If null, the creation time is not set.

getComments

public Iterable<String> getComments()
Returns an iterable collection of the comments in this image. The returned collection is unmodifiable.

Returns:
Comment strings.

clearComments

public void clearComments()
Remove all of the comments in this image.


addComment

public void addComment(String theComment)
Add the given comment to this image.

Parameters:
theComment - Comment string.
Throws:
NullPointerException - (unchecked exception) Thrown if theComment is null.

prepareToWrite

public abstract PJGImage.Writer prepareToWrite(OutputStream theStream)
                                        throws IOException
Prepare to write this image to the given output stream. Certain header information is written to the output stream at this time. To write this image's pixel data, call methods on the returned PJG image writer, then close the PJG image writer.

For improved performance, specify an output stream with buffering, such as an instance of class java.io.BufferedOutputStream.

Parameters:
theStream - Output stream.
Returns:
PJG image writer object with which to write this image.
Throws:
NullPointerException - (unchecked exception) Thrown if theStream is null.
IOException - Thrown if an I/O error occurred.

prepareToRead

public abstract PJGImage.Reader prepareToRead(InputStream theStream)
                                       throws IOException
Prepare to read this image from the given input stream. Certain header information is read from the input stream at this time. To read this image's pixel data, call methods on the returned PJG image reader, then close the PJG image reader.

For improved performance, specify an input stream with buffering, such as an instance of class java.io.BufferedInputStream.

Parameters:
theStream - Input stream.
Returns:
PJG image reader object with which to read this image.
Throws:
NullPointerException - (unchecked exception) Thrown if theStream is null.
IOException - Thrown if an I/O error occurred.

createFromStream

public static PJGImage createFromStream(InputStream theStream)
                                 throws IOException
Create an instance of class PJGImage based on the given input stream. An instance of the correct subclass of class PJGImage is created, depending on the contents of the input stream. The image's header information is read from the input stream, then the input stream is reset to its position when createFromStream() was called. The input stream is not closed.

The createFromStream() method calls the mark() and reset() methods on the input stream. If the input stream does not support these methods, an IOException is thrown.

For improved performance, specify an input stream with buffering, such as an instance of class java.io.BufferedInputStream. (BufferedInputStream supports mark() and reset().)

Parameters:
theStream - Input stream.
Returns:
PJG image object.
Throws:
NullPointerException - (unchecked exception) Thrown if theStream is null.
IOException - Thrown if an I/O error occurred.

readFromStream

public static PJGImage readFromStream(InputStream theStream)
                               throws IOException
Create an instance of class PJGImage and read the image's pixel data from the given input stream. An instance of the correct subclass of class PJGImage is created, depending on the contents of the input stream. The image's pixel data is read from the input stream, then the input stream is closed.

The readFromStream() method calls the mark() and reset() methods on the input stream. If the input stream does not support these methods, an IOException is thrown.

For improved performance, specify an input stream with buffering, such as an instance of class java.io.BufferedInputStream. (BufferedInputStream supports mark() and reset().)

Parameters:
theStream - Input stream.
Returns:
PJG image object containing image read from theStream.
Throws:
NullPointerException - (unchecked exception) Thrown if theStream is null.
IOException - Thrown if an I/O error occurred.

getBufferedImage

public abstract BufferedImage getBufferedImage()
Obtain a BufferedImage whose pixel data comes from this image's underlying matrix.

Returns:
BufferedImage.

getDisplayable

public abstract Displayable getDisplayable()
Obtain a Displayable object with which to display this image in a Swing UI.

Returns:
Displayable object.


Copyright © 2005-2012 by Alan Kaminsky. All rights reserved. Send comments to ark­@­cs.rit.edu.