CS100J        Assignment A8: Images         Due 5 December 2003

The purpose of this assignment is to:

  1. Give you practice with programs that use two-dimensional arrays-tables and
  2. Show you a bit more about GUIs.

You will not be responsible for the GUI stuff. Nevertheless, spending some time studying the program, this handout, and Chapter 17 will take much of the mystery out of GUIs.

Playing with the program

Download this file images.jar. Double click on its icon, and this java application will start and open a window that looks like the one on the right. Use menu item File -> Open to open any picture you want --a navigation window will appear, and you can navigate to a folder of your choice and select a .jpeg or .gif file. A large window will open and will contain that image. Also, a thumbnail of the image will appear in the main window.

You can open up to ten images in this fashion. Each will have a thumbnail of it in the main window. One of these is currently selected: it will appear in the large window, and its title underneath the thumbnail will be red. Click on any thumbnail, and that image becomes selected. You can remove the selected image using menu item File ->Close.

Click on Help -> Show Help to open a window that contains instructions on how to use this GUI. Read the instructions to see how to use the commands in menu Image and menu File. Spend some time experimenting with them, so that you fully understand what this little Java application does.

Obtaining the program

The program we give you has all the capabilities of the one in images.jar except that it will not transpose, horizontally reflect, or vertically reflect an image. Your task is to add these capabilities to the program. Download file a8skeleton.zip, unzip it, and place its files into a new directory. In case you have difficulty with unzipping the .zip file, here are the files in it; you can download them separately. We give you three pictures to play with, but use your own if you want.

ImageHandler.java Extends JFrame. It is the main window and also contains method main
ImageMaintainer.java An instance maintains all information for an open image
ImageJFrame.java Extends JFrame. Contains the full-size view of the selected image
ImagePanel.java An instance is a "panel" that actually contains the selected image; it is a component of ImageJFrame
ImageMap.java An instance contains methods for accessing and changing pixels in an image
ThumbnailPanel.java An instance is a "panel" that contains a thumbnail image; it is a component of the main JFrame.
TextWindow.java An instance is the window with the "help" instructions
hilbert1.jpg A picture of famous mathematician Hilbert
goedel1.gif A picture of famous philosopher-logician Goedel
goedel2.jpg A picture of Goedel and Einstein

What each class does

We give you a short introduction to each class, so you can see something about how a GUI is constructed and can understand a bit about the structure of this program.

ImageHandler. This class, which extends JFrame, is the main window for the applications, which pops up when you start the application. You know what JFrames are, so you can understand this concept. Its constructor builds the menus, constructs some other values that will be used to maintain the thumbnails, and returns. An instance of this class also contains the methods that are called when one of the menu items is selected. Just how the selection of a menu item is connected to a method is beyond the scope of CS100J, but you will learn about it in CS211.

ImageMaintainer. Each instance of this class contains all the necessary information about one image. It contains (0) the image itself, (1) a COPY of the original image, which is used to restore the image when requested, (2) the name (and path) of the file from which the image was read, (3) an instance of ImagePanel, which is a "component" that can go in a JFrame, in order to show the image, (4) a thumbnail panel, which is a panel that contains a small version of the image, and (5) an instance of ImageMap, which contains methods for accessing and changing the image.

This class contains the methods that you will write to transpose and reflect the image.

It is good to have one folder to contain all the information for one image, in this fashion.

ImageJFrame and ImagePanel. The application uses one instance of class ImageJFrame --that's the window that contains the large version of the selected thumbnail. The image itself is placed in an instance of ImagePanel, which is a component that can be placed in a JFrame and that can contain an image.

ThumbnailPanel. An instance is a component that can be placed in an instance of a JFrame --here, an instance of ImageHandler-- and that can contain an image. Each of the small thumbnail pictures is placed in a ThumbnailPanel folder.

TextWindow. An instance contains text --the instructions that you get when you select menu item Help ->Show Help. It extends JFrame.

ImageMap. An Image (an instance of class Image) contains the image as a rectangular array of pixels (picture elements). But this array is private, so you can't change it directly. Instead, you have to use several methods to do it. If you look at class ImageMap, you can see that there are methods for dealing with the array of pixels, which we will explain later.

What to do for this assignment

We ask you to write the bodies of three methods in class ImageMaintainer: methods hreflect, vreflect, and transpose. The specifications are on the methods; just write the bodies to fit the specifications. Do hreflect, then vreflect, and then transpose, in that order. We give more of an explanation about them below. Check each one to make sure it works before moving on to the next one.

Manipulating the two-dimensional array for a picture

Methods hreflect, vreflect, and transpose are supposed to manipulate the two-dimensional array of Pixels (see below) that represent an image on the monitor. These methods are in class ImageMaintainer. You can't directly access the two-dimensional array as is normally done. Instead, consider the following. ImageMaintainer has a field imageMap, which contains a folder of class ImageMap. Looking at class ImageMap, you can see that an instance of ImageMap has the following methods:

/** = The pixel value in the image map at [row,col] */
public int getPixel(int row, int col)

/** Set the pixel value in the image map at [row,col] to pixValue */
public void setPixel(int row, int col, int pixValue)

/** swap the pixel at [a,b] with the pixel at [i,j] */
public void swapPixels(int a, int b, int i, int j)

Thus, in method hreflect of class ImageMaintainer, to swap the element at row r column c with the element at row r1, column c1, execute the call

imageMap.swapPixels(r,c, r1, c1);

Note also that class ImageMap has getter methods to get the number of rows and the number of columns in the array. You will need these.

What is a Pixel?

A pixel (picture element) is a dot of color on your monitor. The color is represented in the RGB (for red-green-blue) system. The amount of each of these colors --red, green, and blue-- in a Pixel is given by an integer in the range 0..255 --0 means the absence of the color and 255 means as much as possible of that color.

There is a fourth value in the range 0..255 in a pixel, which has to do with the "transparency" of the pixel. We don't discuss it here.

You can get some idea of the RGB system of representing a color by looking at method ImageMap.invert. In the body of its two loops you see a pixel rgb being obtained using function getPixel. Then, its components are obtained using methods getRed(), getGreen(), and getBlue(). Each of these components is an integer in the range 0..255 and represents how much of that color is in the pixel. To invert a picture, change each of these color values cv (say) to the value 255–cv and store it back into the array using method setPixel. Did you know that creating a negative image from a positive one was so easy?

Method hreflect

Consider what hreflect has to do. Suppose the array has RW rows. To reflect around the horizontal middle means to:

Swap rows 0 and RW–1–0;
Swap rows 1 and RW–1–1;
Swap rows 2 and RW–1–2;
...
Swap rows RW/2–1 and RW–1–(RW/2–1).

We have been extremely careful in writing this sequence to get it right and to show the pattern that allows a for-loop to be easily written. If you implement this sequence precisely, using a for-loop, you will have no trouble. Here are two examples. If RW is 4, the loop will execute two iterations. If RW is 5, the loop will execute two iterations --the middle row does not have to be swapped.

Method vreflect

This method has to reflect around the vertical middle instead of the horizontal middle. We leave this entirely up to you.

Method transpose

The transpose of an array is another array in which the rows of the original array have become the columns of the result array: row 0 becomes column 0, row 1 becomes column 1, etc. To transpose an array with 5 rows and 8 columns requires creating an array with 8 rows and 5 columns (see the glossary of ProgramLive). Therefore, the body of method transpose has to declare a new instance b (say) of class ImageMap with the right number of rows and columns, copy the old image into b, pixel by pixel, transposing as the copy is being made, and then assign b to variable imageMap.

If you implement this method correctly, clicking item Transpose will cause the selected picture to turn on its side.

Submitting the assignment

Please submit the assignment on CMS on or before 5 December. You need to submit only file ImageMaintainer.java.

Suggestions for more to do

If manipulating images interests you, try the following. Write a new method that will cut the number of rows in half and the number of column in half. This will require throwing away half the rows and half the columns. Which will you throw away? What does the result look like when you throw away every other row and column? Next, rather than simply keeping row 1 and throwing away row 2, try merging them. This will require obtaining the red, green, and blue parts of the pixels and perhaps averaging each in some way. See method ImageMaintainer.invert to see how to obtain the parts of a pixel and create a new pixel. How well does averaging work? How do systems that make pictures smaller really work?