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}