/*File ImgMod02.java.java
Copyright 2004, R.G.Baldwin
The purpose of this program is to make it easy
to experiment with the modification of pixel
data in an image and to display the modified
version of the image along with the original
version of the image.
The program extracts the pixel data from an
image file into a 3D array of type:
int[row][column][depth].
The first two dimensions of the array correspond
to the rows and columns of pixels in the image.
The third dimension always has a value of 4 and
contains the following values by index value:
0 alpha
1 red
2 green
3 blue
Note that these values are stored as type int
rather than type unsigned byte which is the
format of pixel data in the original image.
This type conversion eliminates many problems
involving the requirement to perform unsigned
arithmetic on unsigned byte data.
The program supports gif and jpg files and
possibly some other file types as well.
Operation: This program provides a framework
that is designed to invoke another program to
process the pixels extracted from an image.
In other words, this program extracts the pixels
and puts them in a format that is relatively
easy to work with. A second program is invoked
to actually process the pixels. Typical usage
is as follows:
java ImgMod02 ProcessingProgramName ImageFileName
For test purposes, the source code includes a
class definition for an image-processing program
named ProgramTest.
If the ImageFileName is not specified on the
command line, the program will search for an
image file in the current directory named
junk.gif and will process it using the
processing program specified by the second
command-line argument.
If both command-line arguments are omitted, the
program will search for an image file in the
current directory named junk.gif and will
process it using the built-in processing program
named ProgramTest.
The image file must be provided by the user in
all cases. However, it doesn't have to be in
the current directory if a path to the file is
specified on the command line.
When the program is started, the original image
and the processed image are displayed in a frame
with the original image above the processed
image. A Replot button appears at the bottom of
the frame. If the user clicks the Replot
button, the image-processing method is rerun,
the image is reprocessed and the new version of
the processed image replaces the old version.
The processing program may provide a GUI for
data input making it possible for the user to
modify the behavior of the image-processing
method each time it is run. This capability is
illustrated in the built-in processing program
named ProgramTest.
The image-processing programming must implement
the interface named ImgIntfc02. That interface
declares a single method with the following
signature:
int[][][] processImg(int[][][] threeDPix,
int imgRows,
int imgCols);
The first parameter is a reference to the 3D
array of pixel data stored as type int. The
last two parameters specify the number of rows
of pixels and the number of columns of pixels in
the image.
The image-processing program cannot have a
parameterized constructor. This is because an
object of the class is instantiated by invoking
the newInstance method of the class named Class
on the name of the image-processing program
provided as a String on the command line. This
approach to object instantiation does not
support parameterized constructors.
If the image-processing program has a main
method, it will be ignored.
The processImg method receives a 3D array
containing pixel data. It should make a copy of
the incoming array and modify the copy rather
than modifying the original. Then the program
should return a reference to the modified copy
of the 3D pixel array.
The program also receives the width and the
height of the image represented by the pixels in
the 3D array.
The processImg method is free to modify the
values of the pixels in the array in any manner
before returning the modified array. Note
however that native pixel data consists of four
unsigned bytes. If the modification of the
pixel data produces negative values or positive
value greater than 255, this should be dealt
with before returning the modified pixel data.
Otherwise, the returned values will simply be
masked to eight bits before display, and the
result of displaying those masked bits may not
be as expected.
There are at least two ways to deal with this
situation. One way is to simply clip all
negative values at zero and to clip all values
greater than 255 at 255. The other way is to
perform a further modification so as to map the
range from -x to +y into the range from 0 to 255.
There is no one correct way for all situations.
When the processImg method returns, this program
causes the original image and the modified image
to be displayed in a frame on the screen with
the original image above the modified image.
If the user doesn't specify an image-processing
program, this program will instantiate and use
an object of the class named ProgramTest and an
image file named junk.gif. The class definition
for the ProgramTest class is included in this
source code file. The image file named junk.gif
must be provided by the user in the current
directory. Just about any gif file of an
appropriate size will do. Make certain that it
is small enough so that two copies will fit on
the screen when stacked one above the other.
The processing program named ProgramTest draws a
diagonal white line across the image starting at
the top left corner. The program provides a
dialog box that allows the user to specify the
slope of the line. To change the slope, type a
new slope into the text field and press the
Replot button on the main graphic frame. It
isn't necessary to press the Enter key after
typing the new slope value into the text field,
but doing so won't cause any harm. (Note that
only positive slope values can be used. Entry
of a negative slope value will cause an exception
to be thrown.)
Other than to add the white line, the image
processing program named ProgramTest does not
modify the image. It does draw a visible white
line across transparent areas, making the pixels
underneath the line non-transparent. However,
it may be difficult to see the white line
against the default yellow background in the
frame.
If the program is unable to load the image file
within ten seconds, it will abort with an error
message.
Some operational details follow.
This program reads an image file from the disk
and saves it in memory under the name rawImg.
Then it declares a one-dimensional array of type
int of sufficient size to contain one int value
for every pixel in the image. Each int value
will be populated with one alpha byte and three
color bytes. The name of the array is oneDPix.
Then the program instantiates an object of type
PixelGrabber, which associates the rawImg with
the one-dimensional array of type int.
Following this, the program invokes the
grabPixels method on the object of type
PixelGrabber to cause the pixels in the rawImg
to be extracted into int values and stored in
the array named oneDPix.
Then the program copies the pixel values from
the oneDPix array into the threeDPix array,
converting them to type int in the process. The
threeDPix array is passed to an image-processing
program.
The image-processing program returns a modified
version of the 3D array of pixel data.
This program then creates a new version of the
oneDPix array containing the modified pixel data.
It uses the createImage method of the Component
class along with the constructor for the
MemoryImageSource class to create a new image
from the modified pixel data. The name of the
new image is mo