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        @SuppressWarnings("deprecation")
161        public void show() {
162
163                // create the GUI for viewing the image if needed
164                if (frame == null) {
165                        frame = new JFrame();
166
167                        JMenuBar menuBar = new JMenuBar();
168                        JMenu menu = new JMenu("File");
169                        menuBar.add(menu);
170                        JMenuItem menuItem1 = new JMenuItem(" Save...   ");
171                        menuItem1.addActionListener(this);
172                        menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
173                                        Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
174                        menu.add(menuItem1);
175                        frame.setJMenuBar(menuBar);
176
177
178
179                        frame.setContentPane(getJLabel());
180                        // frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);        // closes all windows
181                        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);      // closes only current window
182                        frame.setTitle(filename);
183                        frame.setResizable(false);
184                        frame.pack();
185                        frame.setVisible(true);
186                }
187
188                // draw
189                frame.repaint();
190        }
191
192        /**
193         * Return the height of the picture in pixels.
194         */
195        public int height() {
196                return height;
197        }
198
199        /**
200         * Return the width of the picture in pixels.
201         */
202        public int width() {
203                return width;
204        }
205
206        /**
207         * Return the color of pixel (i, j).
208         */
209        public Color get(int i, int j) {
210                if (isOriginUpperLeft) return new Color(image.getRGB(i, j));
211                else                   return new Color(image.getRGB(i, height - j - 1));
212        }
213
214        /**
215         * Set the color of pixel (i, j) to c.
216         */
217        public void set(int i, int j, Color c) {
218                if (c == null) { throw new RuntimeException("can't set Color to null"); }
219                if (isOriginUpperLeft) image.setRGB(i, j, c.getRGB());
220                else                   image.setRGB(i, height - j - 1, c.getRGB());
221        }
222
223        /**
224         * Is this Picture equal to obj?
225         */
226        public boolean equals(Object obj) {
227                if (obj == this) return true;
228                if (obj == null) return false;
229                if (obj.getClass() != this.getClass()) return false;
230                Picture that = (Picture) obj;
231                if (this.width()  != that.width())  return false;
232                if (this.height() != that.height()) return false;
233                for (int x = 0; x < width(); x++)
234                        for (int y = 0; y < height(); y++)
235                                if (!this.get(x, y).equals(that.get(x, y))) return false;
236                return true;
237        }
238
239
240        /**
241         * Save the picture to a file in a standard image format.
242         * The filetype must be .png or .jpg.
243         */
244        public void save(String name) {
245                save(new File(name));
246        }
247
248        /**
249         * Save the picture to a file in a standard image format.
250         */
251        public void save(File file) {
252                this.filename = file.getName();
253                if (frame != null) { frame.setTitle(filename); }
254                String suffix = filename.substring(filename.lastIndexOf('.') + 1);
255                suffix = suffix.toLowerCase();
256                if (suffix.equals("jpg") || suffix.equals("png")) {
257                        try { ImageIO.write(image, suffix, file); }
258                        catch (IOException e) { e.printStackTrace(); }
259                }
260                else {
261                        System.out.println("Error: filename must end in .jpg or .png");
262                }
263        }
264
265        /**
266         * Opens a save dialog box when the user selects "Save As" from the menu.
267         */
268        public void actionPerformed(ActionEvent e) {
269                FileDialog chooser = new FileDialog(frame,
270                                "Use a .png or .jpg extension", FileDialog.SAVE);
271                chooser.setVisible(true);
272                if (chooser.getFile() != null) {
273                        save(chooser.getDirectory() + File.separator + chooser.getFile());
274                }
275        }
276
277
278        /**
279         * Test client. Reads a picture specified by the command-line argument,
280         * and shows it in a window on the screen.
281         */
282        public static void main(String[] args) {
283                Picture pic = new Picture(args[0]);
284                System.out.format("%d-by-%d\n", pic.width(), pic.height());
285                pic.show();
286        }
287
288}