001package stdlib;
002/* ***********************************************************************
003 *  Compilation:  javac Picture.java
004 *  Execution:    java Picture imagename
005 *
006 *  Data type for manipulating individual pixels of an image. The original
007 *  image can be read from a file in jpg, gif, or png format, or the
008 *  user can create a blank image of a given size. Includes methods for
009 *  displaying the image in a window on the screen or saving to a file.
010 *
011 *  % java Picture mandrill.jpg
012 *
013 *  Remarks
014 *  -------
015 *   - pixel (x, y) is column x and row y, where (0, 0) is upper left
016 *
017 *   - see also GrayPicture.java for a grayscale version
018 *
019 *************************************************************************/
020
021import java.awt.Color;
022import java.awt.FileDialog;
023import java.awt.Toolkit;
024import java.awt.event.ActionEvent;
025import java.awt.event.ActionListener;
026import java.awt.event.KeyEvent;
027import java.awt.image.BufferedImage;
028import java.io.File;
029import java.io.IOException;
030import java.net.URL;
031import javax.imageio.ImageIO;
032import javax.swing.ImageIcon;
033import javax.swing.JFrame;
034import javax.swing.JLabel;
035import javax.swing.JMenu;
036import javax.swing.JMenuBar;
037import javax.swing.JMenuItem;
038import javax.swing.KeyStroke;
039import javax.swing.WindowConstants;
040
041
042/**
043 *  This class provides methods for manipulating individual pixels of
044 *  an image. The original image can be read from a file in JPEG, GIF,
045 *  or PNG format, or the user can create a blank image of a given size.
046 *  This class includes methods for displaying the image in a window on
047 *  the screen or saving to a file.
048 *  <p>
049 *  By default, pixel (x, y) is column x, row y, where (0, 0) is upper left.
050 *  The method setOriginLowerLeft() change the origin to the lower left.
051 *  <p>
052 *  For additional documentation, see
053 *  <a href="http://introcs.cs.princeton.edu/31datatype">Section 3.1</a> of
054 *  <i>Introduction to Programming in Java: An Interdisciplinary Approach</i>
055 *  by Robert Sedgewick and Kevin Wayne.
056 */
057public final class Picture implements ActionListener {
058        private BufferedImage image;               // the rasterized image
059        private JFrame frame;                      // on-screen view
060        private String filename;                   // name of file
061        private boolean isOriginUpperLeft = true;  // location of origin
062        private final int width, height;           // width and height
063
064        /**
065         * Create a blank w-by-h picture, where each pixel is black.
066         */
067        public Picture(int w, int h) {
068                width = w;
069                height = h;
070                image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
071                // set to TYPE_INT_ARGB to support transparency
072                filename = w + "-by-" + h;
073        }
074
075        /**
076         * Copy constructor.
077         */
078        public Picture(Picture pic) {
079                width = pic.width();
080                height = pic.height();
081                image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
082                filename = pic.filename;
083                for (int i = 0; i < width(); i++)
084                        for (int j = 0; j < height(); j++)
085                                image.setRGB(i, j, pic.get(i, j).getRGB());
086        }
087
088        /**
089         * Create a picture by reading in a .png, .gif, or .jpg from
090         * the given filename or URL name.
091         */
092        public Picture(String filename) {
093                this.filename = filename;
094                try {
095                        // try to read from file in working directory
096                        File file = new File(filename);
097                        if (file.isFile()) {
098                                image = ImageIO.read(file);
099                        }
100
101                        // now try to read from file in same directory as this .class file
102                        else {
103                                URL url = getClass().getResource(filename);
104                                if (url == null) { url = new URL(filename); }
105                                image = ImageIO.read(url);
106                        }
107                        width  = image.getWidth(null);
108                        height = image.getHeight(null);
109                }
110                catch (IOException e) {
111                        // e.printStackTrace();
112                        throw new RuntimeException("Could not open file: " + filename);
113                }
114        }
115
116        /**
117         * Create a picture by reading in a .png, .gif, or .jpg from a File.
118         */
119        public Picture(File file) {
120                try { image = ImageIO.read(file); }
121                catch (IOException e) {
122                        e.printStackTrace();
123                        throw new RuntimeException("Could not open file: " + file);
124                }
125                if (image == null) {
126                        throw new RuntimeException("Invalid image file: " + file);
127                }
128                width  = image.getWidth(null);
129                height = image.getHeight(null);
130                filename = file.getName();
131        }
132
133        /**
134         * Return a JLabel containing this Picture, for embedding in a JPanel,
135         * JFrame or other GUI widget.
136         */
137        public JLabel getJLabel() {
138                if (image == null) { return null; }         // no image available
139                ImageIcon icon = new ImageIcon(image);
140                return new JLabel(icon);
141        }
142
143        /**
144         * Set the origin to be the upper left pixel.
145         */
146        public void setOriginUpperLeft() {
147                isOriginUpperLeft = true;
148        }
149
150        /**
151         * Set the origin to be the lower left pixel.
152         */
153        public void setOriginLowerLeft() {
154                isOriginUpperLeft = false;
155        }
156
157        /**
158         * Display the picture in a window on the screen.
159         */
160        public void show() {
161
162                // create the GUI for viewing the image if needed
163                if (frame == null) {
164                        frame = new JFrame();
165
166                        JMenuBar menuBar = new JMenuBar();
167                        JMenu menu = new JMenu("File");
168                        menuBar.add(menu);
169                        JMenuItem menuItem1 = new JMenuItem(" Save...   ");
170                        menuItem1.addActionListener(this);
171                        menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
172                                        Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
173                        menu.add(menuItem1);
174                        frame.setJMenuBar(menuBar);
175
176
177
178                        frame.setContentPane(getJLabel());
179                        // frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);        // closes all windows
180                        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);      // closes only current window
181                        frame.setTitle(filename);
182                        frame.setResizable(false);
183                        frame.pack();
184                        frame.setVisible(true);
185                }
186
187                // draw
188                frame.repaint();
189        }
190
191        /**
192         * Return the height of the picture in pixels.
193         */
194        public int height() {
195                return height;
196        }
197
198        /**
199         * Return the width of the picture in pixels.
200         */
201        public int width() {
202                return width;
203        }
204
205        /**
206         * Return the color of pixel (i, j).
207         */
208        public Color get(int i, int j) {
209                if (isOriginUpperLeft) return new Color(image.getRGB(i, j));
210                else                   return new Color(image.getRGB(i, height - j - 1));
211        }
212
213        /**
214         * Set the color of pixel (i, j) to c.
215         */
216        public void set(int i, int j, Color c) {
217                if (c == null) { throw new RuntimeException("can't set Color to null"); }
218                if (isOriginUpperLeft) image.setRGB(i, j, c.getRGB());
219                else                   image.setRGB(i, height - j - 1, c.getRGB());
220        }
221
222        /**
223         * Is this Picture equal to obj?
224         */
225        public boolean equals(Object obj) {
226                if (obj == this) return true;
227                if (obj == null) return false;
228                if (obj.getClass() != this.getClass()) return false;
229                Picture that = (Picture) obj;
230                if (this.width()  != that.width())  return false;
231                if (this.height() != that.height()) return false;
232                for (int x = 0; x < width(); x++)
233                        for (int y = 0; y < height(); y++)
234                                if (!this.get(x, y).equals(that.get(x, y))) return false;
235                return true;
236        }
237
238
239        /**
240         * Save the picture to a file in a standard image format.
241         * The filetype must be .png or .jpg.
242         */
243        public void save(String name) {
244                save(new File(name));
245        }
246
247        /**
248         * Save the picture to a file in a standard image format.
249         */
250        public void save(File file) {
251                this.filename = file.getName();
252                if (frame != null) { frame.setTitle(filename); }
253                String suffix = filename.substring(filename.lastIndexOf('.') + 1);
254                suffix = suffix.toLowerCase();
255                if (suffix.equals("jpg") || suffix.equals("png")) {
256                        try { ImageIO.write(image, suffix, file); }
257                        catch (IOException e) { e.printStackTrace(); }
258                }
259                else {
260                        System.out.println("Error: filename must end in .jpg or .png");
261                }
262        }
263
264        /**
265         * Opens a save dialog box when the user selects "Save As" from the menu.
266         */
267        public void actionPerformed(ActionEvent e) {
268                FileDialog chooser = new FileDialog(frame,
269                                "Use a .png or .jpg extension", FileDialog.SAVE);
270                chooser.setVisible(true);
271                if (chooser.getFile() != null) {
272                        save(chooser.getDirectory() + File.separator + chooser.getFile());
273                }
274        }
275
276
277        /**
278         * Test client. Reads a picture specified by the command-line argument,
279         * and shows it in a window on the screen.
280         */
281        public static void main(String[] args) {
282                Picture pic = new Picture(args[0]);
283                System.out.format("%d-by-%d\n", pic.width(), pic.height());
284                pic.show();
285        }
286
287}