/****************************************************************************** * Compilation: javac StdPicture.java * Execution: java StdPicture filename.jpg * Dependencies: Picture.java * ******************************************************************************/ /** * The {@code StdPicture} class provides static methods for manipulating * the individual pixels of an image using the RGB color model. * You can either initialize a blank image (of a given dimension) or read an * image in a supported file format (typically JPEG, PNG, GIF, TIFF, and BMP). * This class also includes methods for displaying the image in a window * and saving it to a file. * *

* Use in the curriculum. * The {@code StdPicture} class is intended for early usage in the * curriculum, before objects. * The {@link Picture} class is an object-oriented version that supports * manipulating multiple pictures at the same time. * *

* Getting started. * To use this class, you must have {@code StdPicture} in your Java classpath. * Here are three possible ways to do this: *

* *

* As a test, cut-and-paste the following short program into your editor: *

 *   public class TestStdPicture {
 *       public static void main(String[] args) {
 *           StdPicture.read("https://introcs.cs.princeton.edu/java/stdlib/mandrill.jpg");
 *           StdPicture.show();
 *       }
 *   }
 *  
*

* If you compile and execute the program, you should see a picture of a mandrill * (a colorful monkey native to west-central Africa) in a window. * *

* Anatomy of an image. * An image is a width-by-height grid of pixels, with pixel (0, 0) * in the upper-left corner. * Each pixel has a color that is represented using the RGB color model, * which specifies the levels of red (R), green (G), and blue (B) * on an integer scale from 0 to 255. * *

* anatomy of an image *
* *

* Initializing the picture. * You can use the following methods to initialize the picture: *

*

* The first method reads an image in a supported file format * (typically JPEG, PNG, GIF, TIFF, and BMP) * and initializes the picture to that image. * The second method initializes a width-by-height * picture, with each pixel black. * *

* Getting and setting the colors of the individual pixels. * You can use the following methods to retrieve the RGB components * of a specified pixel: *

*

* The first three methods return the red, green, and blue components of pixel * (col, row). Each component is an integer between 0 and 255, * with 0 corresponding to the absence of that component and 255 corresponding * to full intensity of that component. * The last method sets the red, green, and blue components of pixel * (col, row) to the specified values. * *

Iterating over the pixels. * A common operation in image processing is to iterate over and process * all of the pixels in an image. * Here is a prototypical example that converts a color image * to grayscale, using the NTSC formula * Y = 0.299r + 0.587g + 0.114b. * Note that if the red, green, and blue components are all equal, * then the color is a shade of gray. *

 *  StdPicture.read("https://introcs.cs.princeton.edu/java/stdlib/mandrill.jpg");
 *  for (int col = 0; col < StdPicture.width(); col++) {
 *      for (int row = 0; row < StdPicture.height(); row++) {
 *          int r = StdPicture.getRed(col, row);
 *          int g = StdPicture.getGreen(col, row);
 *          int b = StdPicture.getBlue(col, row);
 *          int y = (int) (Math.round(0.299*r + 0.587*g + 0.114*b));
 *          StdPicture.setRGB(col, row, y, y, y);
 *      }
 *  }
 *  StdPicture.show();
 *  
* *

Transparency. * The {@code StdPicture} class supports transparent images, using the * ARGB color model. The following methods are useful for this: *

*

* The first method gets the alpha component of pixel (col, row). * The second methods sets the alpha, red, green, and bluecomponents of * pixel (col, row). * The alpha value defines the transparency of a color, with 0 corresponding to * completely transparent and 255 to completely opaque. If transparency is not * explicitly used, the alpha value is 255. * *

Saving files. * The {@code StdPicture} class supports writing images to a supported * file format (typically JPEG, PNG, GIF, TIFF, and BMP). * You can save the picture to a file two method: *

* *

Alternatively, you can save the picture interactively * by using the menu option File → Save from the picture window. * *

File formats. * The {@code StdPicture} class supports reading and writing images to any of the * file formats supported by {@link javax.imageio} (typically JPEG, PNG, * GIF, TIFF, and BMP). * The file extensions corresponding to JPEG, PNG, GIF, TIFF, and BMP, * are {@code .jpg}, {@code .png}, {@code .gif}, {@code .tif}, * and {@code .bmp}, respectively. * The file formats JPEG and BMP do not support transparency. * *

Memory usage. * A W-by-H picture uses ~ 4 W H bytes of memory, * since the color of each pixel is encoded as a 32-bit int. * *

Additional documentation. * For additional documentation, see * Section 3.1 of * Computer Science: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class StdPicture { // the default picture width and height private static final int DEFAULT_SIZE = 512; // the underlying picture private static Picture picture = new Picture(DEFAULT_SIZE, DEFAULT_SIZE); // singleton pattern: client can't instantiate private StdPicture() { } /** * Initializes a {@code width}-by-{@code height} picture, with {@code width} columns * and {@code height} rows, where each pixel is black. * * @param width the width of the picture * @param height the height of the picture * @throws IllegalArgumentException if {@code width} is negative or zero * @throws IllegalArgumentException if {@code height} is negative or zero */ public static void init(int width, int height) { if (picture.isVisible()) { hide(); picture = new Picture(width, height); show(); } else { picture = new Picture(width, height); } } /** * Initializes the picture by reading a JPEG, PNG, GIF, BMP, or TIFF image * from a file or URL. * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, * {@code .bmp}, or {@code .tif}. * * @param filename the name of the file or URL * @throws IllegalArgumentException if cannot read image * @throws IllegalArgumentException if {@code name} is {@code null} */ public static void read(String filename) { Picture newPicture = new Picture(filename); // same dimension, so copy pixels instead of using new Picture and GUI if (newPicture.width() == picture.width() && newPicture.height() == picture.height()) { for (int col = 0; col < picture.width(); col++) { for (int row = 0; row < picture.height(); row++) { picture.setARGB(col, row, newPicture.getARGB(col, row)); } } } // different dimension, so need to use new Picture and GUI else if (picture.isVisible()) { hide(); picture = newPicture; show(); } // no GUI, so use the new Picture else { picture = newPicture; } } /** * Is the window containing the picture visible? * @return {@code true} if the picture is visible, and {@code false} otherwise */ public static boolean isVisible() { return picture.isVisible(); } /** * Displays the picture in a window on the screen. */ public static void show() { picture.show(); } /** * Hides the window containing the picture. */ public static void hide() { picture.hide(); } /** * Pauses for t milliseconds. This method is intended to support computer animation. * @param t number of milliseconds * @throws IllegalArgumentException if {@code t} is negative */ public static void pause(int t) { if (t < 0) throw new IllegalArgumentException("argument must be non-negative"); try { Thread.sleep(t); } catch (InterruptedException e) { System.out.println("Error sleeping"); } } /** * Returns the height of the picture. * * @return the height of the picture (in pixels) */ public static int height() { return picture.height(); } /** * Returns the width of the picture. * * @return the width of the picture (in pixels) */ public static int width() { return picture.width(); } /** * Returns the alpha component of the color of pixel ({@code col}, {@code row}). * * @param col the column index * @param row the row index * @return the alpha component of the color of pixel ({@code col}, {@code row}) * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public static int getAlpha(int col, int row) { int rgb = picture.getARGB(col, row); return (rgb >> 24) & 0xFF; } /** * Returns the red component of the color of pixel ({@code col}, {@code row}). * * @param col the column index * @param row the row index * @return the red component of the color of pixel ({@code col}, {@code row}) * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public static int getRed(int col, int row) { int rgb = picture.getARGB(col, row); return (rgb >> 16) & 0xFF; } /** * Returns the green component of the color of pixel ({@code col}, {@code row}). * * @param col the column index * @param row the row index * @return the green component of the color of pixel ({@code col}, {@code row}) * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public static int getGreen(int col, int row) { int rgb = picture.getARGB(col, row); return (rgb >> 8) & 0xFF; } /** * Returns the blue component of the color of pixel ({@code col}, {@code row}). * * @param col the column index * @param row the row index * @return the blue component of the color of pixel ({@code col}, {@code row}) * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public static int getBlue(int col, int row) { int rgb = picture.getARGB(col, row); return (rgb >> 0) & 0xFF; } /** * Returns the ARGB color of pixel ({@code col}, {@code row}). * * @param col the column index * @param row the row index * @return the ARGB color of pixel ({@code col}, {@code row} as a 32-bit integer * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public static int getARGB(int col, int row) { int argb = picture.getARGB(col, row); return argb; } /** * Sets the color of pixel ({@code col}, {@code row}) to the given RGB color using * red, green, and blue components. The alpha component is set to 255 (no transparency). * * @param col the column index * @param row the row index * @param r the red component of the color * @param g the green component of the color * @param b the blue component of the color * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} * @throws IllegalArgumentException unless {@code 0 <= r < 256}, {@code 0 <= g < 256}, * and {@code 0 <= b < 256}. */ public static void setRGB(int col, int row, int r, int g, int b) { validateComponent(r, "red"); validateComponent(g, "green"); validateComponent(b, "blue"); int a = 255; int argb = (a << 24) | (r << 16) | (g << 8) | (b << 0); picture.setARGB(col, row, argb); } /** * Sets the color of pixel ({@code col}, {@code row}) to the given ARGB color using * alpha, red, green, and blue components. * * @param col the column index * @param row the row index * @param a the alpha component of the color * @param r the red component of the color * @param g the green component of the color * @param b the blue component of the color * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} * @throws IllegalArgumentException unless {@code 0 <= a < 256}, {@code 0 <= r < 256}, * {@code 0 <= g < 256}, and {@code 0 <= b < 256}. */ public static void setARGB(int col, int row, int a, int r, int g, int b) { validateComponent(a, "alpha"); validateComponent(r, "red"); validateComponent(g, "green"); validateComponent(b, "blue"); // internally represented using ARGB, not RGBA int argb = (a << 24) | (r << 16) | (g << 8) | (b << 0); picture.setARGB(col, row, argb); } /** * Sets the color of pixel ({@code col}, {@code row}) to the given RGB color. * * @param col the column index * @param row the row index * @param rgb the RGB color, represented as a 24-bit integer * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} * @throws IllegalArgumentException unless {@code 0 <= rgb < 2^24}. */ /* public static void setRGB(int col, int row, int rgb) { int a = 255; int argb = (a << 24) | (rgb); picture.setARGB(col, row, argb); } */ /** * Sets the color of pixel ({@code col}, {@code row}) to the given ARGB color. * * @param col the column index * @param row the row index * @param argb the ARGB color, represented as a 32-bit integer * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public static void setARGB(int col, int row, int argb) { picture.setARGB(col, row, argb); } /** * Sets the title of this picture. * @param title the title * @throws IllegalArgumentException if {@code title} is {@code null} */ public static void setTitle(String title) { picture.setTitle(title); } /** * Saves the picture to a file in a supported file format * (typically JPEG, PNG, GIF, TIFF, and BMP). * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif}, * {@code .bmp}, or {@code .tif}. * If the file format does not support transparency (such as JPEG * or BMP), it will be converted to be opaque (with purely * transparent pixels converted to black). * * @param filename the name of the file * @throws IllegalArgumentException if {@code filename} is {@code null} * @throws IllegalArgumentException if {@code filename} is the empty string */ public static void save(String filename) { picture.save(filename); } private static void validateComponent(int val, String name) { if (val < 0 || val >= 256) { throw new IllegalArgumentException(name + " must be between 0 and 255"); } } /** * Unit tests this {@code StdPicture} data type. * Reads a picture specified by the command-line argument, * and shows it in a window on the screen. * * @param args the command-line arguments */ public static void main(String[] args) { StdPicture.read(args[0]); System.out.printf("%d-by-%d\n", picture.width(), picture.height()); StdPicture.show(); } }