Comp 170 project 2: Extending ImageViewer, April 26

Due date: By Final Exam (Mon, May 8).

You are to add the following features to the final ImageViewer project (available here):

Documenation on shiftcolor is now here.

For the redden filter, the idea is to get the old red, green, and blue levels:

        Color p = image.getPixel(x,y);
        int blue = p.getBlue();
        int red = p.getRed();
        int green = p.getGreen();
and then tweak the red level, by taking a weighted average of the old level and 255 (the max level):
        double weight = 0.4;
        red = (int) ( red*weight + 255*(1-weight));
For the sliders (later), a weight in the range 0<weight<1 will mean the above: moving the color closer to 255. A weight in the range -1 < weight < 0, on the other hand, is to be interpreted as moving closer to 0:
        weight = -weight;	// make it positive
        red = (int) (red*weight);	// no need for 0*(1-weight) because it's 0
Note added later: To conform with the ShiftFilter, I should have had the weighted average be red*(1-weight)+255*weight; this way, a weight close to 0 has little effect.

Finally, use the documentation for the Color class in java.awt (not java.awt.color!) to figure out how to get back to a Color object, and then use setPixel to reset the color.

The redden filter should be installed like the other filters; see the example in ImageViewer.java of a series of calls of the form "filterlist.add(...)".

For rotate, you will have a method rotate() in class ImageViewer in which you create a new OFImage and set all the pixels; as a first draft we will just reverse x and y from the original. You'll have for loops for x and y, and do something like:

        newImage.setPixel(y,x,currentImage.getPixel(x,y));		// note reversed y,x
Model the loop after the one in makeSmaller().

Actually, reversing x and y just "flips" the image with respect to the y=x diagonal axis. A rotation actually sets the new pixel at (x,y) to the old pixel at (newheight-1-y, x), where newheight is that of the new image. Note added later: to do a clockwise rotation, have the new pixel at (x,y) be the old pixel at (y, newwidth-1-x)

You should create a button for "Rotate" below the existing button "Larger". Copy the example of the creation of the ActionListener for that button (with appropriate changes).

Finally, for undo, add a field prevImage to ImageViewer, and make sure that before each change you set prevImage = currentImage. You will have to identify every place the image can be changed. That's (superficially) easy for filters (just set prevImage in ImageViewer.applyFilter()), but each button will have to be modified separately. The undo command then simply replaces currentImage with prevImage. There is one problem with filters: the filters modify the image "in place"; you will have to set prevImage to a copy of currentImage. I wrote a copyImage method, modeled again after makeSmaller, which made prevImage a copy of currentImage (but did not make it the "current" image).

The Save operation isn't undoable. The best solution for undoing an Undo operation is to simply redo it; that is, have undo() also set prevImage = currentImage. A nice touch is to be sure nothing happens if prevImage==null.

There are also a couple other things you have to do after setting currentImage, involving setImage and frame.pack. See the tail end of makeSmaller for an example.

You can create a button or menu item for Undo. A nice touch is to create an Edit menu to put the Undo item in; after all, that is the standard location.

Email me either your modified files only, or else the whole thing zipped up.