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}