Inside Collection (Course): Object-Oriented Programming (OOP) with Java
Summary: Learn how to write an editor program that you can use to modify the colors in an image on a pixel-by-pixel basis.
This module is one of a series of modules designed to teach you about Object-Oriented Programming (OOP) using Java.
The program described in this module requires the use of the Guzdial-Ericson multimedia class library. You will find download, installation, and usage instructions for the library at Java OOP: The Guzdial-Ericson Multimedia Class Library .
I recommend that you open another copy of this document in a separate browser window and use the following links to easily find and view the figures and listings while you are reading about them.
In this module, you will learn how to write an editor program that you can use to modify the colors in an image on a pixel-by-pixel basis.
Such a program could be useful, for example, for manually correcting "red eye" problems in digital images.
Program output at startup
Figure 1 shows the program output at startup.
The output consists of
| Program output at startup. |
|---|
![]() |
Program output after zooming the image
Figure 2 shows the program output after placing the PictureExplorer cursor on the penguin's nose, and zooming the PictureExplorer image by 500%
If you look carefully, you should be able to see the crosshair cursor on the penguin's nose.
| Program output after zooming the image. |
|---|
![]() |
Program output after changing the pixel color to yellow
Figure 3 shows the result of selecting the color yellow with the GUI, and clicking the button labeled Update Pixel Color
If you look carefully, you should see that the pixel next to the crosshair cursor on the penguin's nose has changed from black to yellow.
| Program output after changing the pixel color to yellow. |
|---|
![]() |
This program requires modifications to Ericson's PictureExplorer class. I will deal with that issue first.
A complete listing of the modified version of the PictureExplorer class is shown in Listing 10 .
Changes to Ericson's PictureExplorer class
The PictureExplorer class was modified to add getter methods to cause the following values to be accessible from outside the object:
Also the call to the setDefaultCloseOperation method was disabled.
Code for methods that were added
Listing 1 shows the code for the methods that were added to the PictureExplorer class.
//Method to get the xIndex value.
public int getXIndex(){
return xIndex;
}//end getXIndex
//Method to get the yIndex value.
public int getYIndex(){
return yIndex;
}//end getYIndex
//Method to get the zoomFactor value.
public double getZoomFactor(){
return zoomFactor;
}//end getZoomFactor
//Method to get a reference to the frame
public JFrame getFrame(){
return pictureFrame;
}//end getFrame()
Modified setDefaultCloseOperation method
Listing 2 shows the modified version of the setDefaultCloseOperation method.
Note the parameter that is passed to the method when it is called.
explorerFrame.setDefaultCloseOperation(
WindowConstants.DO_NOTHING_ON_CLOSE);
Similar to previous code
Much of the code in this program is similar to code that I explained in earlier modules, and I won't repeat those explanations.
I will skip down to the event handler registered on the button labeled Update Pixel Color in Figure 1 . That code begins in Listing 3 .
updateButton.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
//Get properties of PictureExplorer object.
xIndex = explorer.getXIndex();
yIndex = explorer.getYIndex();
zoomFactor = explorer.getZoomFactor();
Important to preserve properties
The only thing that changes when you click the update button is the color of the pixel at the crosshair cursor.
Therefore, the program must preserve the state of the explorer object including such items as the zoom factor and the location of crosshair cursor.
Listing 3 gets and saves the property values that determine the state of the explorer object.
Convert zoom factor to String
The getZoomFactor method returns the zoom factor as type double . However, we need the zoom factor as type String . The code in Listing 4 converts the zoom factor to type String .
//Save zoom factor as a string.
String zoomString = "100%";
if(zoomFactor == 0.25){
zoomString = "25%";
}else if(zoomFactor == 0.50){
zoomString = "50%";
}else if(zoomFactor == 0.75){
zoomString = "75%";
}else if(zoomFactor == 1.0){
zoomString = "100%";
}else if(zoomFactor == 1.5){
zoomString = "150%";
}else if(zoomFactor == 2.0){
zoomString = "200%";
}else if(zoomFactor == 5.0){
zoomString = "500%";
}else{
zoomString = "100%";//in case no match
}//end else
Set pixel color in original image
The code in Listing 5
At this point, the color of the pixel in the original image has been modified.
Color newColor = new Color(
redInt,greenInt,blueInt);
pix.getPixel(xIndex,yIndex).setColor(newColor);
Create and populate a new PictureExplorer object
Listing 6 begins by disposing of the original PictureExplorer object. Then Listing 6 creates a new PictureExplorer object containing the modified image.
Finally Listing 6 calls the setDefaultCloseOperation on the JFrame that houses the PictureExplorer object disabling the X-button in the upper-right corner.
//Dispose of the existing explorer and create a
// new one.
explorerFrame.dispose();
explorer = new PictureExplorer(pix);
//Get reference to the new frame
explorerFrame = explorer.getFrame();
explorerFrame.setDefaultCloseOperation(
WindowConstants.DO_NOTHING_ON_CLOSE);
Set the state of the PictureExplorer object
You will probably need to study the event handling code in the PictureExplorer class to fully understand the remaining code in this discussion.
When you click the image in the PictureExplorer object, a MouseListener object registered on the PictureExplorer object calls the mousePressed method belonging to that object
Simulate a physical click
We can simulate a physical click on that image by calling the same method from outside the PictureExplorer object.
Call the mousePressed method
Listing 7 calls the mousePressed method on the PictureExplorer object passing the crosshair coordinates as parameters. Other parameters are also passed to satisfy the requirements of the mousePressed method.
(I will leave it as an exercise for the student to investigate those other parameters.)
//Now set the state of the new explorer.
//Simulate a mouse pressed event in the picture
// to set the cursor and the text in the
// coordinate fields.
explorer.mousePressed(new MouseEvent(
new JButton("dummy component"),
MouseEvent.MOUSE_PRESSED,
(long)0,
0,
xIndex,
yIndex,
0,
false));
PictureExplorer object handles crosshair cursor
The code in Ericson's mousePressed method takes care of all the requirements that result from setting the location of the crosshair cursor, such as setting the coordinate values in the PictureExplorer object's text fields shown in Figure 3 .
Set the zoom state
Manually selecting a zoom level from the Zoom menu in the PictureExplorer object causes the actionPerformed method belonging to an ActionListener object registered on the PictureExplorer object to be executed.
As before, we can simulate the manual selection of a Zoom menu value by calling that actionPerformed method from outside the object.
Set the zoom state
Listing 8 calls Ericson's actionPerformed method, passing the String representation of the zoom factor as a parameter.
(I will leave it as an exercise for the student to investigate the other parameters.)
//Simulate an action event on the zoom menu to
// set the zoom.
explorer.actionPerformed(new ActionEvent(
explorer,
ActionEvent.ACTION_PERFORMED,
zoomString));
}//end actionPerformed
}//end newActionListener
);//end addActionListener
Conclusion of discussion
That concludes the discussion of the ActionListener object registered on the button labeled Update Pixel Color in Figure 1 .
It also concludes the discussion of the program.
I encourage you to copy the code from Listing 9 and Listing 10 and download the image file named Prob15.jpg . Compile the code and execute it. Experiment with the code, making changes, and observing the results of your changes. Make certain that you can explain why your changes behave as they do.
In this module, you learned how to write an editor program that you can use to modify the colors in an image on a pixel-by-pixel basis.
This section contains a variety of miscellaneous information.
Financial : Although the Connexions site makes it possible for you to download a PDF file for this module at no charge, and also makes it possible for you to purchase a pre-printed version of the PDF file, you should be aware that some of the HTML elements in this module may not translate well into PDF.
I also want you to know that, I receive no financial compensation from the Connexions website even if you purchase the PDF version of the module.
In the past, unknown individuals have copied my modules from cnx.org, converted them to Kindle books, and placed them for sale on Amazon.com showing me as the author. I neither receive compensation for those sales nor do I know who does receive compensation. If you purchase such a book, please be aware that it is a copy of a module that is freely available on cnx.org and that it was made and published without my prior knowledge.
Affiliation : I am a professor of Computer Information Technology at Austin Community College in Austin, TX.
Complete listings of the source code discussed in this module are shown in Listing 9 and Listing 10 below.
/*File Prob15 Copyright 2012 R.G.Baldwin
The purpose of this program is demonstrate one way to
change the color of a pixel in a Picture object that is
encapsulated in a PictureExplorer object.
The pixel to be modified is selected by placing the
cursor in the PictureExplorer object.
*********************************************************/
import java.awt.event.MouseEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.border.LineBorder;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Dimension;
import javax.swing.JColorChooser;
public class Prob15{
public static void main(String[] args){
new Prob15Runner();
}//end main method
}//end class Prob15
//======================================================//
class Prob15Runner extends JFrame{
private Prob15Runner jFrameObj = null;
private PictureExplorer explorer = null;
private Picture pix;
private JFrame explorerFrame;
private JPanel controlPanel = new JPanel();
private JPanel colorPanel = new JPanel();
private JPanel buttonPanel = new JPanel();
private JPanel colorIndicatorPanel = new JPanel();
private JTextField redField = new JTextField("000000");
private JTextField greenField =
new JTextField("000000");
private JTextField blueField = new JTextField("000000");
private int redInt = 0;
private int greenInt = 0;
private int blueInt = 0;
//Copies of properties of the PictureExplorer object
private int xIndex = 0;
private int yIndex = 0;
private double zoomFactor = 0;
private JButton chooseButton =
new JButton("Choose Color");
private JButton updateButton =
new JButton("Update Pixel Color");
//----------------------------------------------------//
public Prob15Runner(){//constructor
pix = new Picture("Prob15.jpg");
pix.addMessage("Dick Baldwin",10,20);
explorer = new PictureExplorer(pix);
explorerFrame = explorer.getFrame();
explorerFrame.setDefaultCloseOperation(
WindowConstants.DO_NOTHING_ON_CLOSE);
//Set the location for the control GUI
// immediately below the PictureExplorer object,
// and set its default close operation.
setLocation(0,pix.getHeight() + 128);
setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE);
controlPanel.setLayout(new GridLayout(2,1));
controlPanel.add(colorPanel);
controlPanel.add(buttonPanel);
colorPanel.setBackground(Color.GREEN);
colorPanel.add(new JLabel("Red = "));
colorPanel.add(redField);
colorPanel.add(new JLabel(" Green = "));
colorPanel.add(greenField);
colorPanel.add(new JLabel(" Blue = "));
colorPanel.add(blueField);
colorPanel.add(colorIndicatorPanel);
redField.setEditable(false);
greenField.setEditable(false);
blueField.setEditable(false);
colorIndicatorPanel.setBorder(
new LineBorder(Color.black,1));
colorIndicatorPanel.setPreferredSize(
new Dimension(20,20));
buttonPanel.setBackground(Color.BLUE);
buttonPanel.add(chooseButton);
buttonPanel.add(updateButton);
// buttonPanel.add(darkerButton);
//Color the swatch for the first time.
paintColorSwatch();
//Add the controlPanel to the content pane, adjust to
// the correct size, and set the tiele.
getContentPane().add(controlPanel);
pack();
setTitle("Dick Baldwin");
//Make the GUI visible
setVisible(true);
//--------------------------------------------------//
//Register listeners on the user input components.
//--------------------------------------------------//
chooseButton.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
Color selColor = JColorChooser.showDialog(
chooseButton,"Choose color",new Color(
redInt,greenInt,blueInt));
if(selColor != null){
//Don't change the color if the user cancels
// out.
redField.setText("" + selColor.getRed());
greenField.setText(
"" + selColor.getGreen());
blueField.setText("" + selColor.getBlue());
}//end if
}//end action performed
}//end new ActionListener
);//end addActionListener
//--------------------------------------------------//
updateButton.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
//Get properties of PictureExplorer object.
xIndex = explorer.getXIndex();
yIndex = explorer.getYIndex();
zoomFactor = explorer.getZoomFactor();
//Save zoom factor as a string.
String zoomString = "100%";
if(zoomFactor == 0.25){
zoomString = "25%";
}else if(zoomFactor == 0.50){
zoomString = "50%";
}else if(zoomFactor == 0.75){
zoomString = "75%";
}else if(zoomFactor == 1.0){
zoomString = "100%";
}else if(zoomFactor == 1.5){
zoomString = "150%";
}else if(zoomFactor == 2.0){
zoomString = "200%";
}else if(zoomFactor == 5.0){
zoomString = "500%";
}else{
zoomString = "100%";//in case no match
}//end else
Color newColor = new Color(
redInt,greenInt,blueInt);
pix.getPixel(xIndex,yIndex).setColor(newColor);
//Dispose of the existing explorer and create a
// new one.
explorerFrame.dispose();
explorer = new PictureExplorer(pix);
//Get reference to the new frame
explorerFrame = explorer.getFrame();
explorerFrame.setDefaultCloseOperation(
WindowConstants.DO_NOTHING_ON_CLOSE);
//Now set the state of the new explorer.
//Simulate a mouse pressed event in the picture
// to set the cursor and the text in the
// coordinate fields.
explorer.mousePressed(new MouseEvent(
new JButton("dummy component"),
MouseEvent.MOUSE_PRESSED,
(long)0,
0,
xIndex,
yIndex,
0,
false));
//Simulate an action event on the zoom menu to
// set the zoom.
explorer.actionPerformed(new ActionEvent(
explorer,
ActionEvent.ACTION_PERFORMED,
zoomString));
}//end actionPerformed
}//end newActionListener
);//end addActionListener
//--------------------------------------------------//
//Register a document listener on the red text field.
// This listener will respond when the contents of
// the text field are modified either by the program
// or by the user.
redField.getDocument().addDocumentListener(
new DocumentListener(){
public void changedUpdate(DocumentEvent e){
//Empty method - not needed
}//end changedUpdate
public void removeUpdate(DocumentEvent e){
try{
redInt = Integer.parseInt(
redField.getText());
if((redInt >= 0) && (redInt <= 255)){
paintColorSwatch();
}//end if
}catch(Exception ex){
//do nothing on exception
}//end catch
}//end removeUpdate
public void insertUpdate(DocumentEvent e){
try{
redInt = Integer.parseInt(
redField.getText());
if((redInt >= 0) && (redInt <= 255)){
paintColorSwatch();
}//end if
}catch(Exception ex){
//do nothing on exception
}//end catch
}//end insertUpdate
}//end new DocumentListener
);//end addDocumentListener
//--------------------------------------------------//
//Register a document listener on the green text
// field. Essentially the same as the above.
greenField.getDocument().addDocumentListener(
new DocumentListener(){
public void changedUpdate(DocumentEvent e){}
public void removeUpdate(DocumentEvent e){
try{
greenInt = Integer.parseInt(
greenField.getText());
if((greenInt >= 0) && (greenInt <= 255))
{
paintColorSwatch();
}//end if
}catch(Exception ex){
//do nothing on exception
}//end catch
}//end removeUpdate
public void insertUpdate(DocumentEvent e){
try{
greenInt = Integer.parseInt(
greenField.getText());
if((greenInt >= 0) && (greenInt <= 255))
{
paintColorSwatch();
}//end if
}catch(Exception ex){
//do nothing on exception
}//end catch
}//end insertUpdate
}//end new DocumentListener
);//end addDocumentListener
//--------------------------------------------------//
//Register a document listener on the blue text
// field. Essentially the same as the above.
blueField.getDocument().addDocumentListener(
new DocumentListener(){
public void changedUpdate(DocumentEvent e){}
public void removeUpdate(DocumentEvent e){
try{
blueInt = Integer.parseInt(
blueField.getText());
if((blueInt >= 0) && (blueInt <= 255)){
paintColorSwatch();
}//end if
}catch(Exception ex){
//do nothing on exception
}//end catch
}//end removeUpdate
public void insertUpdate(DocumentEvent e){
try{
blueInt = Integer.parseInt(
blueField.getText());
if((blueInt >= 0) && (blueInt <= 255)){
paintColorSwatch();
}//end if
}catch(Exception ex){
//do nothing on exception
}//end catch
}//end insertUpdate
}//end new DocumentListener
);//end addDocumentListener
//--------------------------------------------------//
}//end constructor
//----------------------------------------------------//
//The purpose of this method is to color a swatch
// located next to the RGB color values.
private void paintColorSwatch(){
colorIndicatorPanel.setBackground(
new Color(redInt,greenInt,blueInt));
}//end paintColorSwatch
//----------------------------------------------------//
//The purpose of this method is to absorb any exceptions
// that may be thrown by the parseInt method in order
// to avoid having the program abort. In the event that
// an exception is thrown, this method simply returns an
// int value of 0;
private int goParseInt(String string){
int result = 0;
try{
result = Integer.parseInt(string);
}catch(Exception e){
result = 0;
}//end catch
return result;
}//end goParseInt
//----------------------------------------------------//
}//end class Prob15Runner
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
import javax.swing.border.*;
/*
05/16/12 Modified by Baldwin to add getter methods to
cause the following values to be accessible from
outside the object:
int xIndex
int yIndex
double zoomFactor
JFrame pictureFrame
Also disabled the call to setDefaultCloseOperation
*/
/**
* Displays a picture and lets you explore the picture by displaying the x, y, red,
* green, and blue values of the pixel at the cursor when you click a mouse button or
* press and hold a mouse button while moving the cursor. It also lets you zoom in or
* out. You can also type in a x and y value to see the color at that location.
*
* Originally created for the Jython Environment for Students (JES).
* Modified to work with DrJava by Barbara Ericson
*
* Copyright Georgia Institute of Technology 2004
* @author Keith McDermottt, gte047w@cc.gatech.edu
* @author Barb Ericson ericson@cc.gatech.edu
*/
public class PictureExplorer implements MouseMotionListener, ActionListener, MouseListener
{
// current x and y index
private int xIndex = 0;
private int yIndex = 0;
//Main gui variables
private JFrame pictureFrame;
private JScrollPane scrollPane;
//information bar variables
private JLabel xLabel;
private JButton xPrevButton;
private JButton yPrevButton;
private JButton xNextButton;
private JButton yNextButton;
private JLabel yLabel;
private JTextField xValue;
private JTextField yValue;
private JLabel rValue;
private JLabel gValue;
private JLabel bValue;
private JLabel colorLabel;
private JPanel colorPanel;
// menu components
private JMenuBar menuBar;
private JMenu zoomMenu;
private JMenuItem twentyFive;
private JMenuItem fifty;
private JMenuItem seventyFive;
private JMenuItem hundred;
private JMenuItem hundredFifty;
private JMenuItem twoHundred;
private JMenuItem fiveHundred;
/** The picture being explored */
private DigitalPicture picture;
/** The image icon used to display the picture */
private ImageIcon scrollImageIcon;
/** The image display */
private ImageDisplay imageDisplay;
/** the zoom factor (amount to zoom) */
private double zoomFactor;
/** the number system to use, 0 means starting at 0, 1 means starting at 1 */
private int numberBase=0;
/**
* Public constructor
* @param picture the picture to explore
*/
public PictureExplorer(DigitalPicture picture)
{
// set the fields
this.picture=picture;
zoomFactor=1;
// create the window and set things up
createWindow();
}
//===Methods added by Baldwin on 05/16/12===============//
//Method to get the xIndex value.
public int getXIndex(){
return xIndex;
}//end getXIndex
//Method to get the yIndex value.
public int getYIndex(){
return yIndex;
}//end getYIndex
//Method to get the zoomFactor value.
public double getZoomFactor(){
return zoomFactor;
}//end getZoomFactor
//Method to get a reference to the frame
public JFrame getFrame(){
return pictureFrame;
}//end getFrame()
//===End methods added by Baldwin on 05/16/12===========//
/**
* Changes the number system to start at one
*/
public void changeToBaseOne()
{
numberBase=1;
}
/**
* Set the title of the frame
*@param title the title to use in the JFrame
*/
public void setTitle(String title)
{
pictureFrame.setTitle(title);
}
/**
* Method to create and initialize the picture frame
*/
private void createAndInitPictureFrame()
{
pictureFrame = new JFrame(); // create the JFrame
pictureFrame.setResizable(true); // allow the user to resize it
pictureFrame.getContentPane().setLayout(new BorderLayout()); // use border layout
//Disabled by Baldwin on 05/16/12
//pictureFrame.setDefaultCloseOperation(
// JFrame.DISPOSE_ON_CLOSE);
pictureFrame.setTitle(picture.getTitle());
PictureExplorerFocusTraversalPolicy newPolicy = new PictureExplorerFocusTraversalPolicy();
pictureFrame.setFocusTraversalPolicy(newPolicy);
}
/**
* Method to create the menu bar, menus, and menu items
*/
private void setUpMenuBar()
{
//create menu
menuBar = new JMenuBar();
zoomMenu = new JMenu("Zoom");
twentyFive = new JMenuItem("25%");
fifty = new JMenuItem("50%");
seventyFive = new JMenuItem("75%");
hundred = new JMenuItem("100%");
hundred.setEnabled(false);
hundredFifty = new JMenuItem("150%");
twoHundred = new JMenuItem("200%");
fiveHundred = new JMenuItem("500%");
// add the action listeners
twentyFive.addActionListener(this);
fifty.addActionListener(this);
seventyFive.addActionListener(this);
hundred.addActionListener(this);
hundredFifty.addActionListener(this);
twoHundred.addActionListener(this);
fiveHundred.addActionListener(this);
// add the menu items to the menus
zoomMenu.add(twentyFive);
zoomMenu.add(fifty);
zoomMenu.add(seventyFive);
zoomMenu.add(hundred);
zoomMenu.add(hundredFifty);
zoomMenu.add(twoHundred);
zoomMenu.add(fiveHundred);
menuBar.add(zoomMenu);
// set the menu bar to this menu
pictureFrame.setJMenuBar(menuBar);
}
/**
* Create and initialize the scrolling image
*/
private void createAndInitScrollingImage()
{
scrollPane = new JScrollPane();
BufferedImage bimg = picture.getBufferedImage();
imageDisplay = new ImageDisplay(bimg);
imageDisplay.addMouseMotionListener(this);
imageDisplay.addMouseListener(this);
imageDisplay.setToolTipText("Click a mouse button on a pixel to see the pixel information");
scrollPane.setViewportView(imageDisplay);
pictureFrame.getContentPane().add(scrollPane, BorderLayout.CENTER);
}
/**
* Creates the JFrame and sets everything up
*/
private void createWindow()
{
// create the picture frame and initialize it
createAndInitPictureFrame();
// set up the menu bar
setUpMenuBar();
//create the information panel
createInfoPanel();
//creates the scrollpane for the picture
createAndInitScrollingImage();
// show the picture in the frame at the size it needs to be
pictureFrame.pack();
pictureFrame.setVisible(true);
}
/**
* Method to set up the next and previous buttons for the
* pixel location information
*/
private void setUpNextAndPreviousButtons()
{
// create the image icons for the buttons
Icon prevIcon = new ImageIcon(SoundExplorer.class.getResource("leftArrow.gif"),
"previous index");
Icon nextIcon = new ImageIcon(SoundExplorer.class.getResource("rightArrow.gif"),
"next index");
// create the arrow buttons
xPrevButton = new JButton(prevIcon);
xNextButton = new JButton(nextIcon);
yPrevButton = new JButton(prevIcon);
yNextButton = new JButton(nextIcon);
// set the tool tip text
xNextButton.setToolTipText("Click to go to the next x value");
xPrevButton.setToolTipText("Click to go to the previous x value");
yNextButton.setToolTipText("Click to go to the next y value");
yPrevButton.setToolTipText("Click to go to the previous y value");
// set the sizes of the buttons
int prevWidth = prevIcon.getIconWidth() + 2;
int nextWidth = nextIcon.getIconWidth() + 2;
int prevHeight = prevIcon.getIconHeight() + 2;
int nextHeight = nextIcon.getIconHeight() + 2;
Dimension prevDimension = new Dimension(prevWidth,prevHeight);
Dimension nextDimension = new Dimension(nextWidth, nextHeight);
xPrevButton.setPreferredSize(prevDimension);
yPrevButton.setPreferredSize(prevDimension);
xNextButton.setPreferredSize(nextDimension);
yNextButton.setPreferredSize(nextDimension);
// handle previous x button press
xPrevButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
xIndex--;
if (xIndex < 0)
xIndex = 0;
displayPixelInformation(xIndex,yIndex);
}
});
// handle previous y button press
yPrevButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
yIndex--;
if (yIndex < 0)
yIndex = 0;
displayPixelInformation(xIndex,yIndex);
}
});
// handle next x button press
xNextButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
xIndex++;
if (xIndex >= picture.getWidth())
xIndex = picture.getWidth() - 1;
displayPixelInformation(xIndex,yIndex);
}
});
// handle next y button press
yNextButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
yIndex++;
if (yIndex >= picture.getHeight())
yIndex = picture.getHeight() - 1;
displayPixelInformation(xIndex,yIndex);
}
});
}
/**
* Create the pixel location panel
* @param labelFont the font for the labels
* @return the location panel
*/
public JPanel createLocationPanel(Font labelFont) {
// create a location panel
JPanel locationPanel = new JPanel();
locationPanel.setLayout(new FlowLayout());
Box hBox = Box.createHorizontalBox();
// create the labels
xLabel = new JLabel("X:");
yLabel = new JLabel("Y:");
// create the text fields
xValue = new JTextField(Integer.toString(xIndex + numberBase),6);
xValue.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
displayPixelInformation(xValue.getText(),yValue.getText());
}
});
yValue = new JTextField(Integer.toString(yIndex + numberBase),6);
yValue.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
displayPixelInformation(xValue.getText(),yValue.getText());
}
});
// set up the next and previous buttons
setUpNextAndPreviousButtons();
// set up the font for the labels
xLabel.setFont(labelFont);
yLabel.setFont(labelFont);
xValue.setFont(labelFont);
yValue.setFont(labelFont);
// add the items to the vertical box and the box to the panel
hBox.add(Box.createHorizontalGlue());
hBox.add(xLabel);
hBox.add(xPrevButton);
hBox.add(xValue);
hBox.add(xNextButton);
hBox.add(Box.createHorizontalStrut(10));
hBox.add(yLabel);
hBox.add(yPrevButton);
hBox.add(yValue);
hBox.add(yNextButton);
locationPanel.add(hBox);
hBox.add(Box.createHorizontalGlue());
return locationPanel;
}
/**
* Create the color information panel
* @param labelFont the font to use for labels
* @return the color information panel
*/
private JPanel createColorInfoPanel(Font labelFont)
{
// create a color info panel
JPanel colorInfoPanel = new JPanel();
colorInfoPanel.setLayout(new FlowLayout());
// get the pixel at the x and y
Pixel pixel = new Pixel(picture,xIndex,yIndex);
// create the labels
rValue = new JLabel("R: " + pixel.getRed());
gValue = new JLabel("G: " + pixel.getGreen());
bValue = new JLabel("B: " + pixel.getBlue());
// create the sample color panel and label
colorLabel = new JLabel("Color at location: ");
colorPanel = new JPanel();
colorPanel.setBorder(new LineBorder(Color.black,1));
// set the color sample to the pixel color
colorPanel.setBackground(pixel.getColor());
// set the font
rValue.setFont(labelFont);
gValue.setFont(labelFont);
bValue.setFont(labelFont);
colorLabel.setFont(labelFont);
colorPanel.setPreferredSize(new Dimension(25,25));
// add items to the color information panel
colorInfoPanel.add(rValue);
colorInfoPanel.add(gValue);
colorInfoPanel.add(bValue);
colorInfoPanel.add(colorLabel);
colorInfoPanel.add(colorPanel);
return colorInfoPanel;
}
/**
* Creates the North JPanel with all the pixel location
* and color information
*/
private void createInfoPanel()
{
// create the info panel and set the layout
JPanel infoPanel = new JPanel();
infoPanel.setLayout(new BorderLayout());
// create the font
Font largerFont = new Font(infoPanel.getFont().getName(),
infoPanel.getFont().getStyle(),14);
// create the pixel location panel
JPanel locationPanel = createLocationPanel(largerFont);
// create the color informaiton panel
JPanel colorInfoPanel = createColorInfoPanel(largerFont);
// add the panels to the info panel
infoPanel.add(BorderLayout.NORTH,locationPanel);
infoPanel.add(BorderLayout.SOUTH,colorInfoPanel);
// add the info panel
pictureFrame.getContentPane().add(BorderLayout.NORTH,infoPanel);
}
/**
* Method to check that the current position is in the viewing area and if
* not scroll to center the current position if possible
*/
public void checkScroll()
{
// get the x and y position in pixels
int xPos = (int) (xIndex * zoomFactor);
int yPos = (int) (yIndex * zoomFactor);
// only do this if the image is larger than normal
if (zoomFactor > 1) {
// get the rectangle that defines the current view
JViewport viewport = scrollPane.getViewport();
Rectangle rect = viewport.getViewRect();
int rectMinX = (int) rect.getX();
int rectWidth = (int) rect.getWidth();
int rectMaxX = rectMinX + rectWidth - 1;
int rectMinY = (int) rect.getY();
int rectHeight = (int) rect.getHeight();
int rectMaxY = rectMinY + rectHeight - 1;
// get the maximum possible x and y index
int maxIndexX = (int) (picture.getWidth() * zoomFactor) - rectWidth - 1;
int maxIndexY = (int) (picture.getHeight() * zoomFactor) - rectHeight - 1;
// calculate how to position the current position in the middle of the viewing
// area
int viewX = xPos - (int) (rectWidth / 2);
int viewY = yPos - (int) (rectHeight / 2);
// reposition the viewX and viewY if outside allowed values
if (viewX < 0)
viewX = 0;
else if (viewX > maxIndexX)
viewX = maxIndexX;
if (viewY < 0)
viewY = 0;
else if (viewY > maxIndexY)
viewY = maxIndexY;
// move the viewport upper left point
viewport.scrollRectToVisible(new Rectangle(viewX,viewY,rectWidth,rectHeight));
}
}
/**
* Zooms in the on picture by scaling the image.
* It is extremely memory intensive.
* @param factor the amount to zoom by
*/
public void zoom(double factor)
{
// save the current zoom factor
zoomFactor = factor;
// calculate the new width and height and get an image that size
int width = (int) (picture.getWidth()*zoomFactor);
int height = (int) (picture.getHeight()*zoomFactor);
BufferedImage bimg = picture.getBufferedImage();
// set the scroll image icon to the new image
imageDisplay.setImage(bimg.getScaledInstance(width, height, Image.SCALE_DEFAULT));
imageDisplay.setCurrentX((int) (xIndex * zoomFactor));
imageDisplay.setCurrentY((int) (yIndex * zoomFactor));
imageDisplay.revalidate();
checkScroll(); // check if need to reposition scroll
}
/**
* Repaints the image on the scrollpane.
*/
public void repaint()
{
pictureFrame.repaint();
}
//****************************************//
// Event Listeners //
//****************************************//
/**
* Called when the mouse is dragged (button held down and moved)
* @param e the mouse event
*/
public void mouseDragged(MouseEvent e)
{
displayPixelInformation(e);
}
/**
* Method to check if the given x and y are in the picture
* @param x the horiztonal value
* @param y the vertical value
* @return true if the x and y are in the picture and false otherwise
*/
private boolean isLocationInPicture(int x, int y)
{
boolean result = false; // the default is false
if (x >= 0 && x < picture.getWidth() &&
y >= 0 && y < picture.getHeight())
result = true;
return result;
}
/**
* Method to display the pixel information from the passed x and y but
* also converts x and y from strings
* @param xString the x value as a string from the user
* @param yString the y value as a string from the user
*/
public void displayPixelInformation(String xString, String yString)
{
int x = -1;
int y = -1;
try {
x = Integer.parseInt(xString);
x = x - numberBase;
y = Integer.parseInt(yString);
y = y - numberBase;
} catch (Exception ex) {
}
if (x >= 0 && y >= 0) {
displayPixelInformation(x,y);
}
}
/**
* Method to display pixel information for the passed x and y
* @param pictureX the x value in the picture
* @param pictureY the y value in the picture
*/
private void displayPixelInformation(int pictureX, int pictureY)
{
// check that this x and y is in range
if (isLocationInPicture(pictureX, pictureY))
{
// save the current x and y index
xIndex = pictureX;
yIndex = pictureY;
// get the pixel at the x and y
Pixel pixel = new Pixel(picture,xIndex,yIndex);
// set the values based on the pixel
xValue.setText(Integer.toString(xIndex + numberBase));
yValue.setText(Integer.toString(yIndex + numberBase));
rValue.setText("R: " + pixel.getRed());
gValue.setText("G: " + pixel.getGreen());
bValue.setText("B: " + pixel.getBlue());
colorPanel.setBackground(new Color(pixel.getRed(), pixel.getGreen(), pixel.getBlue()));
}
else
{
clearInformation();
}
// notify the image display of the current x and y
imageDisplay.setCurrentX((int) (xIndex * zoomFactor));
imageDisplay.setCurrentY((int) (yIndex * zoomFactor));
}
/**
* Method to display pixel information based on a mouse event
* @param e a mouse event
*/
private void displayPixelInformation(MouseEvent e)
{
// get the cursor x and y
int cursorX = e.getX();
int cursorY = e.getY();
// get the x and y in the original (not scaled image)
int pictureX = (int) (cursorX / zoomFactor + numberBase);
int pictureY = (int) (cursorY / zoomFactor + numberBase);
// display the information for this x and y
displayPixelInformation(pictureX,pictureY);
}
/**
* Method to clear the labels and current color and reset the
* current index to -1
*/
private void clearInformation()
{
xValue.setText("N/A");
yValue.setText("N/A");
rValue.setText("R: N/A");
gValue.setText("G: N/A");
bValue.setText("B: N/A");
colorPanel.setBackground(Color.black);
xIndex = -1;
yIndex = -1;
}
/**
* Method called when the mouse is moved with no buttons down
* @param e the mouse event
*/
public void mouseMoved(MouseEvent e)
{}
/**
* Method called when the mouse is clicked
* @param e the mouse event
*/
public void mouseClicked(MouseEvent e)
{
displayPixelInformation(e);
}
/**
* Method called when the mouse button is pushed down
* @param e the mouse event
*/
public void mousePressed(MouseEvent e)
{
displayPixelInformation(e);
}
/**
* Method called when the mouse button is released
* @param e the mouse event
*/
public void mouseReleased(MouseEvent e)
{
}
/**
* Method called when the component is entered (mouse moves over it)
* @param e the mouse event
*/
public void mouseEntered(MouseEvent e)
{
}
/**
* Method called when the mouse moves over the component
* @param e the mouse event
*/
public void mouseExited(MouseEvent e)
{
}
/**
* Method to enable all menu commands
*/
private void enableZoomItems()
{
twentyFive.setEnabled(true);
fifty.setEnabled(true);
seventyFive.setEnabled(true);
hundred.setEnabled(true);
hundredFifty.setEnabled(true);
twoHundred.setEnabled(true);
fiveHundred.setEnabled(true);
}
/**
* Controls the zoom menu bar
*
* @param a the ActionEvent
*/
public void actionPerformed(ActionEvent a)
{
if(a.getActionCommand().equals("Update"))
{
this.repaint();
}
if(a.getActionCommand().equals("25%"))
{
this.zoom(.25);
enableZoomItems();
twentyFive.setEnabled(false);
}
if(a.getActionCommand().equals("50%"))
{
this.zoom(.50);
enableZoomItems();
fifty.setEnabled(false);
}
if(a.getActionCommand().equals("75%"))
{
this.zoom(.75);
enableZoomItems();
seventyFive.setEnabled(false);
}
if(a.getActionCommand().equals("100%"))
{
this.zoom(1.0);
enableZoomItems();
hundred.setEnabled(false);
}
if(a.getActionCommand().equals("150%"))
{
this.zoom(1.5);
enableZoomItems();
hundredFifty.setEnabled(false);
}
if(a.getActionCommand().equals("200%"))
{
this.zoom(2.0);
enableZoomItems();
twoHundred.setEnabled(false);
}
if(a.getActionCommand().equals("500%"))
{
this.zoom(5.0);
enableZoomItems();
fiveHundred.setEnabled(false);
}
}
/**
* Test Main. It will ask you to pick a file and then show it
*/
public static void main( String args[])
{
Picture p = new Picture(FileChooser.pickAFile());
PictureExplorer test = new PictureExplorer(p);
}
/**
* Class for establishing the focus for the textfields
*/
private class PictureExplorerFocusTraversalPolicy
extends FocusTraversalPolicy {
/**
* Method to get the next component for focus
*/
public Component getComponentAfter(Container focusCycleRoot,
Component aComponent) {
if (aComponent.equals(xValue))
return yValue;
else
return xValue;
}
/**
* Method to get the previous component for focus
*/
public Component getComponentBefore(Container focusCycleRoot,
Component aComponent) {
if (aComponent.equals(xValue))
return yValue;
else
return xValue;
}
public Component getDefaultComponent(Container focusCycleRoot) {
return xValue;
}
public Component getLastComponent(Container focusCycleRoot) {
return yValue;
}
public Component getFirstComponent(Container focusCycleRoot) {
return xValue;
}
}
}
-end-