Comp 170 project 2: Extending ImageViewer, November 29

Due date: By Final Exam (Dec 13)

Images are here!

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

Documentation 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*(1-weight) + 255*weight);
(Note the cast back to int.) Any value of weight in the range 0<weight<1 will mean moving the color closer to 255; the larger the weight the greater the color shift. A weight in the range -1 < weight < 0, on the other hand, is to be interpreted as moving the color value closer to 0 and thus diminishing that color:
        weight = -weight;	// make it positive
        red = (int) (red*(1-weight));	// no need for 0*weight because it's 0

Finally, convert back to a color object, and reset the pixel to that color:

		Color np = new Color(red, green, blue);
		image.setPixel(x, y, np);

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 first create 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 existing one in makeSmaller().

Actually, reversing x and y just "flips" the image with respect to the y=x diagonal axis. A (clockwise) rotation actually sets the new pixel at (x,y) to the old pixel at (y, oldHeight-1-x), where oldHeight = newWidth is the height of the original image.

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 did this by writing 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.