/********************************* * Picture.java * A picture class * The class allows users to create/modify/save pictures in jpg's, png's, * and bitmap's. * * @author Shaun M. McFall, Denison University * Summer, 2008 **/ import javax.swing.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.awt.Color; import javax.imageio.*; import java.io.*; import java.awt.geom.*; import java.awt.BasicStroke; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.lang.Math; public class Picture implements FocusListener, MouseListener, MouseMotionListener, KeyListener { protected int height; protected int width; protected BufferStrategy strategy; protected GraphicsDevice device; protected DisplayMode origDM; protected GraphicsConfiguration config; protected JFrame mainFrame; protected Insets insets; protected Color penColor = Color.black; protected double penX, penY; protected double penWidth = 1.0f; protected Stroke stroke; protected BufferedImage image; protected Graphics2D drawGraphics; protected boolean penUp = true; protected double penDirection = 0.0; protected Font font; // Mouse data private boolean mouse1Down = false; private boolean mouse2Down = false; private boolean mouse3Down = false; private int mouseX = 0; private int mouseY = 0; // Key data private int keyCode = 0; private char keyChar = '\0'; private String keyString = null; private KeyEvent keyEvent = null; private boolean[] keyArray; /** * Constructor that creates a picture of the given width w and height h * @param w The width of the picture * @param h The height of the picture **/ public Picture(int w, int h) { height = h; width = w; setUpFrame(false); image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); drawGraphics = (Graphics2D)image.getGraphics(); drawGraphics.setColor(Color.white); drawGraphics.fillRect(0, 0, width, height); drawGraphics.setColor(penColor); drawGraphics.setFont(font); } /** * Constructor that creates a picture of the given width w and height h * @param w The width of the picture * @param h The height of the picture * @param isDecorated Whether or not the frame is decorated (has frames around it) **/ public Picture(int w, int h, boolean isDecorated) { height = h; width = w; setUpFrame(!isDecorated); image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); drawGraphics = (Graphics2D)image.getGraphics(); drawGraphics.setColor(Color.white); drawGraphics.fillRect(0, 0, width, height); drawGraphics.setColor(penColor); drawGraphics.setFont(font); } /** * Constructor that takes in the pathname as the parameter and creates a * picture. The pathname can be an absolute pathname or pathname relative to * the current working directory. * @param path The absolute pathname of the file. */ public Picture (String path) { mainFrame = new JFrame(path); // Invoke the JFrame constructor first //This is used to get the information to read a buffered image. File pathFile; /* * Creates a file from the given string name. If the path name is null * and a file cannot be created, an error is sent to the console. The * program sees if the file can be used to create a buffered image. If not, * an error is sent to the console. **/ try { pathFile = new File(path); } catch (NullPointerException e ){ System.err.println(e); return; } try { image = ImageIO.read(pathFile); } catch(IOException e) { System.err.println(e); return; } width = image.getWidth(); height = image.getHeight(); setUpFrame(false); //Sets the frame to the appropiate size to fully display //the image. /* * Get the graohics of the buffered Image and set the pen color to a * default black **/ drawGraphics = (Graphics2D)image.getGraphics(); drawGraphics.setColor(penColor); drawGraphics.setFont(font); } /** * Constructor that takes in the pathname as the parameter and creates a * picture. The pathname can be an absolute pathname or pathname relative to * the current working directory. * @param path The absolute pathname of the file. * @param isDecorated Whether or not the frame is decorated (has frames around it) */ public Picture (String path, boolean isDecorated) { mainFrame = new JFrame(path); // Invoke the JFrame constructor first //This is used to get the information to read a buffered image. File pathFile; /* * Creates a file from the given string name. If the path name is null * and a file cannot be created, an error is sent to the console. The * program sees if the file can be used to create a buffered image. If not, * an error is sent to the console. **/ try { pathFile = new File(path); } catch (NullPointerException e ){ System.err.println(e); return; } try { image = ImageIO.read(pathFile); } catch(IOException e) { System.err.println(e); return; } width = image.getWidth(); height = image.getHeight(); setUpFrame(!isDecorated); //Sets the frame to the appropiate size to fully display //the image. /* * Get the graohics of the buffered Image and set the pen color to a * default black **/ drawGraphics = (Graphics2D)image.getGraphics(); drawGraphics.setColor(penColor); drawGraphics.setFont(font); } /** * sets up the frame with the appropiate sizes and insets * also adds the focus listener **/ private void setUpFrame(boolean isUndecorated) { device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); origDM = device.getDisplayMode(); config = device.getDefaultConfiguration(); mainFrame = new JFrame(config); mainFrame.setUndecorated(isUndecorated); mainFrame.addFocusListener(this); mainFrame.addMouseListener(this); mainFrame.addMouseMotionListener(this); mainFrame.addKeyListener(this); mainFrame.setResizable(false); mainFrame.setIgnoreRepaint(true); mainFrame.addNotify(); insets = mainFrame.getInsets(); mainFrame.setSize(width + insets.left + insets.right - 1, height + insets.top + insets.bottom - 1); mainFrame.createBufferStrategy(2); strategy = mainFrame.getBufferStrategy(); mainFrame.setVisible(false); keyArray = new boolean[KeyEvent.KEY_LAST]; for (int i = 0; i < keyArray.length; i++) keyArray[i] = false; font = new Font("arial", Font.PLAIN, 12); } /** * Method to display a picture. The method can also be invoked to display * the picture after it has been updated. **/ public void display() { if (!mainFrame.isVisible()) mainFrame.setVisible(true); ((Graphics2D)strategy.getDrawGraphics()).drawImage(image, null, insets.left, insets.top); strategy.show(); } public void updateNoDisplay() { ((Graphics2D)strategy.getDrawGraphics()).drawImage(image, null, insets.left, insets.top); strategy.show(); } /** * Method to close the picture **/ public void close() { if (mainFrame.isVisible()) mainFrame.setVisible(false); } /** * Method to get the height of the picture * @return Picture height **/ public int getHeight() { return height; } /** * Method to get the width of the picture * @return Picture width **/ public int getWidth() { return width; } /** * Returns the intensity of the red component at the coordinate (x,y) * @param x The x coordinate * @param y The y coordinate * * @return Returns the red value at the given x and y **/ public int getPixelRed(int x, int y) { return new Color(image.getRGB(x,y)).getRed(); } /** * Returns the intensity of the green component at the coordinate (x,y) * @param x The x coordinate * @param y The y coordinate * * @return Returns the green value at the given x and y **/ public int getPixelGreen(int x, int y) { return new Color(image.getRGB(x,y)).getGreen(); } /** * Returns the intensity of the blue component at the coordinate (x,y) * @param x The x coordinate * @param y The y coordinate * * @return Returns the blue value at the given x and y **/ public int getPixelBlue(int x, int y) { return new Color(image.getRGB(x,y)).getBlue(); } /** * Sets the red component of the pixel at coordinate (x,y) to intensity * given by r. Valid values are in the range 0-255 * Returns an error message to the console if the r parameter is not within range. * @param x The x coordinate * @param y The y coordinate * @param r The new value **/ public void setPixelRed(int x, int y, int r) { if (r >= 0 && r <= 255) { Color c = new Color(image.getRGB(x,y)); image.setRGB(x, y, new Color(r, c.getGreen(), c.getBlue()).getRGB()); } else { System.err.println("Color value must be from 0-255"); } } /** * Sets the green component of the pixel at coordinate (x,y) to intensity * given by g. Valid values are in the range 0-255 * Returns an error message to the console if the g parameter is not within range. * @param x The x coordinate * @param y The y coordinate * @param g The new value */ public void setPixelGreen(int x, int y, int g) { if (g >= 0 && g <= 255) { Color c = new Color(image.getRGB(x,y)); image.setRGB(x, y, new Color(c.getRed(), g, c. getBlue()).getRGB()); } else { System.err.println("Color value must be from 0-255"); } } /** * Sets the blue component of the pixel at coordinate (x,y) to intensity * given by b. Valid values are in the range 0-255 * Returns an error message to the console if the b parameter is not within range. * @param x The x coordinate * @param y The y coordinate * @param b The new value **/ public void setPixelBlue(int x, int y, int b) { if (b >= 0 && b <= 255) { Color c = new Color(image.getRGB(x,y)); image.setRGB(x, y, new Color(c.getRed(), c.getGreen(), b).getRGB()); } else { System.err.println("Color value must be from 0-255"); } } /** * Method to get a color object for the pixel given by (x,y) * @param x The x coordinate * @param y The y coordinate * * @return Returns the color at the given x and y **/ public Color getPixelColor(int x, int y) { return new Color(image.getRGB(x, y)); } /** * Sets the color of the pixel at (x,y) to c * @param x The x coordinate * @param y The y coordinate * @param c The new color to be set **/ public void setPixelColor(int x, int y, Color c) { image.setRGB(x, y, c.getRGB()); } /** * Sets the color of the pixel at (x,y) to the given * red, green, and blue values * @param x The x coordinate * @param y The y coordinate * @param r The red value (0-255) * @param g The green value (0-255) * @param b The blue value (0-255) **/ public void setPixelColor(int x, int y, int r, int g, int b) { image.setRGB(x, y, new Color(r, g, b).getRGB()); } /** * Method saves the picture to the user's computer. It supports jpg's, * bitmap's, and png's * @param s The absolute pathname * @return true if the file has been created successfully and false * if the file is not created. * @return false if pathname is null **/ public boolean writeFile(String s) { display(); //If the pathname is null, the method discontinues and false is returned. if (s == null) return false; // Get the extension and determine what to do with it int dot = s.lastIndexOf("."); int val; if (dot != -1) { String ext = s.substring(dot); if (ext.equalsIgnoreCase(".jpg") || ext.equalsIgnoreCase(".jpeg")) val = 1; else if (ext.equalsIgnoreCase(".png")) val = 2; else if (ext.equalsIgnoreCase(".bmp")) val = 3; else { System.err.println("You can only save as .jpg, .png, or .bmp"); return false; } } else { s += ".png"; val = 2; } boolean flag = false; File filename = new File(s); /* * According to the extension inputed by the user, the image is then * saved on the disk with the required file extension **/ if(val >3 || val < 1){ System.err.println("Wrong input for saving a file."); flag = false; }else if(val == 1){ flag = errorManagement("jpg", filename); }else if(val == 2){ flag = errorManagement("png", filename); }else if(val == 3){ flag = errorManagement("bmp", filename); } return flag; } /** * Method is a part of the writeFile method and checks to see if the image * can be written and returns false if it cannot. */ private boolean errorManagement(String ext, File f) { try { ImageIO.write(image, ext, f); } catch(IOException ioException) { System.err.println(ioException); return false; } catch(IllegalArgumentException e) { System.err.println(e); return false; } return true; } /** * Sets the color for the drawing of 2D objects * @param c The Color that you wish to set to **/ public void setPenColor(Color c) { penColor = c; drawGraphics.setColor(c); } /** * The method returns a color object for the pen color at current coordinate. * @return Color object for the present pen color. **/ public Color getPenColor() { return penColor; } /** * Draws a line betweeen (x1,y1) and (x2,y2). After drawing the line, the * current position is (x2,y2). * @param x1 The x co-ordinate of the first point * @param y1 The y co-ordinate of the first point * @param x2 The x co-ordinate of the second point * @param y2 The y co-ordinate of the second point **/ public void drawLine(int x1, int y1, int x2, int y2) { drawGraphics.drawLine(x1, y1, x2, y2); penX = x2; penY = y2; } /** * Draws a line from the present coordinate to the other coordinate * specified (x,y).After drawing the line, the current position is (x,y) * which are the coordinates passed in. * @param x The x co-ordinate of the second point * @param y The y co-ordinate of the second point **/ public void drawLine(int x, int y) { drawGraphics.drawLine((int)penX, (int)penY, x, y); penX = x; penY = y; } /** * Draws an ouline of a circle at the x and y coordinates and the radius * given by user. The current x and y positions is set to the centre of * the circle. * @param x The x coordinate of the centre of the circle. * @param y The y coordinate of the centre of the circle. * @param radius The radius of the circle to be drawn **/ public void drawCircle(int x, int y, int radius) { drawGraphics.drawOval(x - radius, y - radius, radius*2, radius*2); penX = x - radius; penY = y - radius; } /** * Draws a filled circle of the radius given at the coordinate (x,y). The * circle is drawn with the current color. The current positon is set to the * centre of the circle. * @param x The x coordinate of the centre of the circle. * @param y The y coordinate of the centre of the circle. * @param radius The radius of the circle to be drawn **/ public void drawCircleFill(int x, int y, int radius) { drawGraphics.fillOval(x - radius, y - radius, radius*2, radius*2); penX = x - radius; penY = y - radius; } /** * Method draws the outline of an Ellipse given the x and y coordinates * with the lenght of the major and minor axis. The current position is set * to the centre of the ellipse * @param x The x coordinate * @param y The y coordinate * @param minor The lenght of the minor axis * @param major The lenght of the major axis. **/ public void drawEllipse(int x,int y, int minor, int major) { penX = x; penY = y; drawGraphics.draw(new Ellipse2D.Double(penX,penY,minor,major)); } /** * Method draws a filled Ellipse given the x and y coordinates with the * lenght of the major and minor axis. The current position is set * to the centre of the ellipse * @param x The x coordinate * @param y The y coordinate * @param minor The lenght of the minor axis * @param major The lenght of the major axis. **/ public void drawEllipseFill(int x, int y, int minor, int major) { penX = x; penY = y; drawGraphics.fill(new Ellipse2D.Double(penX,penY,minor,major)); } /** * Draws the outline of the rectangle at (x,y) with the height h and width w. * The current position is set to the top left corner of the rectangle. * @param x The x coordinate of the top left corner of the rectangle * @param y The y coordinate of the top left corner of the rectangle * @param w The width of the rectangle * @param h The height of the rectangle **/ public void drawRect(int x, int y, int w, int h) { drawGraphics.drawRect(x, y, w, h); penX = x; penY = y; } /** * Draws the rectangle at (x,y) with the height h and width w. Also fills up * the rectangle with the current color. The current position is set to the * top left corner of the rectangle. * @param x The x coordinate of the top left corner of the rectangle * @param y The y coordinate of the top left corner of the rectangle * @param w The width of the rectangle * @param h The height of the rectangle **/ public void drawRectFill(int x , int y , int w, int h) { drawGraphics.fillRect(x, y, w, h); penX = x; penY = y; } /** * Draws a single pixel using the current pen color. The current position is set to the * given coordinates of the rectangle. * @param x The x coordinate * @param y The y coordinate **/ public void drawPixel(int x, int y) { image.setRGB(x, y, penColor.getRGB()); } /** * Draws a single pixel at the current pen location **/ public void drawPixel() { image.setRGB((int)penX, (int)penY, penColor.getRGB()); } /** * Sets the font given the parameters. * @param fontName The name of the font style (i.e. Arial, Helvetica) * @param style The style, use 0 for plain, 1 for italic, and 2 for bold * @param size The size of the font **/ public void setFont(String fontName, int style, double size) { font = new Font(fontName, style, (int)size); drawGraphics.setFont(font); } /** * Set the size of the font. * @param size The new size for this font **/ public void setFontSize(double size) { font = font.deriveFont((float)size); drawGraphics.setFont(font); } /** * Draws a given string to the screen. The current position is set to the * top left corner of the rectangle. * @param x The X coordinate * @param y The Y coordinate * @param s The string **/ public void drawString(int x, int y, String s) { penX = x; penY = y; drawGraphics.drawString(s, x, y+font.getSize()); } /** * Sets the pen up. **/ public void setPenUp() { penUp = true; } /** * Sets the pen down **/ public void setPenDown() { penUp = false; } /** * Checks to see if the Pen is up. Returns true if the pen is up and false * if it is not. * @return boolean to show pen status **/ public boolean isPenUp() { return penUp; } /** * Changes the x coordinate to that specified by the user * @param x The x co-ordinate of the new point. **/ public void setX (double x) { penX = x; } /** * Changes the y coordinate to that specified by the user * @param y The y co-ordinate of the new point. **/ public void setY (double y) { penY = y; } /** * Method sets the pen to the x,y coordinate of the user's choice. * @param x the new x coordinate * @param y the new y coordinate **/ //------------------------------------------------------------------------- public void setPosition(double x, double y) { penX = x; penY = y; } /** * Method sets the current direction to that of the user's choice. * Negative degrees are mesured clockwise. Positive degrees are measure * counterclockwise * @param d The degree of the direction **/ public void setDirection(double d) { penDirection = d; } /** * Method returns the current direction in degrees. * @return The degree of current direction **/ public double getDirection() { return penDirection; } /** * Method shifts the current positon by moving forward with the value that * the user wishes in accordance with the current pen width. * @param dist The number of pixels by which to go forward **/ public void drawForward(double dist) { double deltaX, deltaY; double radians; /* * The line needs to be drawn in the same direction as the amount of * degree. Trignometry is used to find the coordinates of the podouble to * which the line needs to be drawn using the distance that has been * received.The cosine of the angle gives the x coordinate and the sine of * the angle gives the y coordinate of the new podouble. **/ radians = Math.toRadians(90 + penDirection); deltaX = (penWidth) * Math.cos(radians); deltaY = (penWidth) * Math.sin(radians); /* * since deltaX and deltaY represent the value of the new podouble in a * coordinate system where the starting podouble(x,y) is considered as (0,0); * they need to be converted to the values in the coordinate system of the * picture. The new coordinates also have to be represented as doubleegers so * as to draw the line. **/ double x1 = penX + deltaX/2; double y1 = penY - deltaY/2; radians = Math.toRadians(penDirection); deltaX = (dist) * Math.cos(radians); deltaY = (dist) * Math.sin(radians); double x2 = x1 + deltaX; double y2 = y1 - deltaY; radians = Math.toRadians(270 + penDirection); deltaX = penWidth * Math.cos(radians); deltaY = penWidth * Math.sin(radians); double x3 = x2 + deltaX; double y3 = y2 - deltaY; radians = Math.toRadians(180 + penDirection); deltaX = (dist) * Math.cos(radians); deltaY = (dist) * Math.sin(radians); double x4 = x3 + deltaX; double y4 = y3 - deltaY; /* * Creating two separate arrays storing the values of the x and y * coordinates of the rectangle that needs to be drawn. **/ int[] xarray = {(int)x1,(int)x2,(int)x3,(int)x4}; int[] yarray = {(int)y1,(int)y2,(int)y3,(int)y4}; int npoints = 4; drawGraphics.fillPolygon(xarray, yarray, npoints); /* * Set current position to the mid-podouble of the other height of the * rectangle created. **/ penX = (double)(x3+x2)/2f; penY = (double)(y3+y2)/2f; } /** * Method returns the current X Position * @return The X coordinate of the current position **/ public double getX() { return penX; } /** * Method returns the current Y Position * @return The Y coordinate of the current position **/ public double getY() { return penY; } /** * Method sets the width of the pen * @param pWidth The new width of the pen **/ public void setPenWidth(double pWidth) { penWidth = pWidth; stroke = new BasicStroke((float)penWidth,BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); drawGraphics.setStroke(stroke); } /** * Method returns the width of the pen * @return The width of the pen **/ public double getPenWidth() { return penWidth; } /** * Method returns the red channel of the * current pen color * * @return the red channel of the pen color **/ public int getPenRed() { return penColor.getRed(); } /** * Method returns the green channel of the * current pen color * * @return the green channel of the pen color **/ public int getPenGreen() { return penColor.getGreen(); } /** * Method returns the blue channel of the * current pen color * * @return the blue channel of the pen color **/ public int getPenBlue() { return penColor.getBlue(); } /** * Set the red channel of the pen, leaving the * rest of the colors intact. * * @param r The new red value (0-255) **/ public void setPenRed(int r) { penColor = new Color(r, getPenGreen(), getPenBlue()); drawGraphics.setColor(penColor); } /** * Set the green channel of the pen, leaving the * rest of the colors intact. * * @param g The new green value (0-255) **/ public void setPenGreen(int g) { penColor = new Color(getPenRed(), g, getPenBlue()); drawGraphics.setColor(penColor); } /** * Set the blue channel of the pen, leaving the * rest of the colors intact. * * @param b The new red value (0-255) **/ public void setPenBlue(int b) { penColor = new Color(getPenRed(), getPenGreen(), b); drawGraphics.setColor(penColor); } /** * Set the color of the pen. * * @param r The red value (0-255) * @param g The green value (0-255) * @param b The blue value (0-255) **/ public void setPenColor(int r, int g, int b) { penColor = new Color(r, g, b); drawGraphics.setColor(penColor); } /** * Returns whether the mouse1 button is down * * @return true if the mouse1 button is down, * false if the mouse1 button is up **/ public boolean isMouse1Down() { return mouse1Down; } /** * Returns whether the mouse2 button is down * * @return true if the mouse2 button is down, * false if the mouse2 button is up **/ public boolean isMouse2Down() { return mouse2Down; } /** * Returns whether the mouse3 button is down * * @return true if the mouse3 button is down, * false if the mouse3 button is up **/ public boolean isMouse3Down() { return mouse3Down; } /** * Returns the current mouse X position * * @return The X position of the mouse **/ public int getMouseX() { return mouseX; } /** * Returns the current mouse Y position * * @return The Y position of the mouse **/ public int getMouseY() { return mouseY; } /** * Sets the title of the picture. * @param title The title of the picture. **/ public void setTitle(String title) { mainFrame.setTitle(title); } /** * Sets the location of the picture on the screen. * @param x The x coordinate of the upper left corner of the picture on screen * @param y The y coordinate of the upper left corner of the picture on screen. **/ public void setWindowLocation(int x, int y) { mainFrame.setLocation(x, y); } /** * Get whether or not the given key is down. * Make sure to 'import java.awt.event.KeyEvent'. * See http://java.sun.com/javase/6/docs/api/java/awt/event/KeyEvent.html * @see java.awt.event.KeyEvent * @param keyCode The code corresponding to the key. * @return true if the key is down. **/ public boolean isKeyDown(int keyCode) { return keyArray[keyCode]; } /** * Allows you to draw one picture on to another one * at the given coordinates (specified by the upper left * corner of the source picture) * @param x The X value of the upper left corner of the source picture * @param y The Y value of the upper left corner of the source picture * @param pic The source Picture (the picture you want to draw) **/ public void drawPicture(int x, int y, Picture pic) { pic.updateNoDisplay(); drawGraphics.drawImage(pic.image, null, x, y); } /** * Internal method. Used to redraw the * picture when focus is regained. **/ public void focusGained(FocusEvent e) { display(); } /** * Internal method. Used when the * picture loses focus. **/ public void focusLost(FocusEvent e) { } /** * Internal method. Used when the * mouse is pressed. **/ public void mousePressed(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) mouse1Down = true; else if (e.getButton() == MouseEvent.BUTTON2) mouse2Down = true; else if (e.getButton() == MouseEvent.BUTTON3) mouse3Down = true; } /** * Internal method. Used when the * mouse is released. **/ public void mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) mouse1Down = false; else if (e.getButton() == MouseEvent.BUTTON2) mouse2Down = false; else if (e.getButton() == MouseEvent.BUTTON3) mouse3Down = false; } /** * Internal method. Used when the * mouse enters the frame. **/ public void mouseEntered(MouseEvent e) { } /** * Internal method. Used when the * mouse exits the frame. **/ public void mouseExited(MouseEvent e) { } /** * Internal method. Used when the * mouse is clicked. **/ public void mouseClicked(MouseEvent e) { } /** * Internal method. Used when the * mouse is dragged across the frame. **/ public void mouseDragged(MouseEvent e) { mouseX = e.getX() - insets.left; mouseY = e.getY() - insets.top; } /** * Internal method. Used when the * mouse is moved. **/ public void mouseMoved(MouseEvent e) { mouseX = e.getX() - insets.left; mouseY = e.getY() - insets.top; } /** * Internal method. Used when a * key is pressed. **/ public void keyPressed(KeyEvent e) { keyArray[e.getKeyCode()] = true; } /** * Internal method. Used when a * key is released. **/ public void keyReleased(KeyEvent e) { keyArray[e.getKeyCode()] = false; } /** * Internal method. Used when a * key is typed. **/ public void keyTyped(KeyEvent e) { } }