/** * When the ERASE or SMUDGE tools are used and the mouse jumps from (x1,y1) to (x2,y2), the tool * has to be applied to a line of pixel positions between the two points in order to cover the * entire line that the mouse moves along. The change is made to the off-screen canvas, and * repaint() is called to copy the changes to the screen. */ void applyToolAlongLine(int x1, int y1, int x2, int y2) { Graphics g = OSC.getGraphics(); g.setColor(fillColor); // (for ERASE only) int w = OSC.getWidth(); // (for SMUDGE only) int h = OSC.getHeight(); // (for SMUDGE only) int dist = Math.max(Math.abs(x2 - x1), Math.abs(y2 - y1)); // dist is the number of points along the line from // (x1,y1) to (x2,y2) at which the tool will be applied. double dx = (double) (x2 - x1) / dist; double dy = (double) (y2 - y1) / dist; for (int d = 1; d <= dist; d++) { // Apply the tool at one of the points (x,y) along the // line from (x1,y1) to (x2,y2). int x = (int) Math.round(x1 + dx * d); int y = (int) Math.round(y1 + dy * d); if (currentTool == Tool.ERASE) { // Erase a 10-by-10 block of pixels around (x,y) g.fillRect(x - 5, y - 5, 10, 10); repaint(x - 5, y - 5, 10, 10); } else { // For the SMUDGE tool, blend some of the color from // the smudgeRed, smudgeGreen, and smudgeBlue arrays // into the pixels in a 7-by-7 block around (x,y), and // vice versa. The effect is to smear out the color // of pixels that are visited by the tool. for (int i = 0; i < 7; i++) { for (int j = 0; j < 7; j++) { int r = y + j - 3; int c = x + i - 3; if (!(r < 0 || r >= h || c < 0 || c >= w || smudgeRed[i][j] == -1)) { int curCol = OSC.getRGB(c, r); int curRed = (curCol >> 16) & 0xFF; int curGreen = (curCol >> 8) & 0xFF; int curBlue = curCol & 0xFF; int newRed = (int) (curRed * 0.7 + smudgeRed[i][j] * 0.3); int newGreen = (int) (curGreen * 0.7 + smudgeGreen[i][j] * 0.3); int newBlue = (int) (curBlue * 0.7 + smudgeBlue[i][j] * 0.3); int newCol = newRed << 16 | newGreen << 8 | newBlue; OSC.setRGB(c, r, newCol); smudgeRed[i][j] = curRed * 0.3 + smudgeRed[i][j] * 0.7; smudgeGreen[i][j] = curGreen * 0.3 + smudgeGreen[i][j] * 0.7; smudgeBlue[i][j] = curBlue * 0.3 + smudgeBlue[i][j] * 0.7; } } } repaint(x - 3, y - 3, 7, 7); } } g.dispose(); }
/** Start a drag operation. */ @Override public void mousePressed(MouseEvent evt) { startX = prevX = currentX = evt.getX(); startY = prevY = currentY = evt.getY(); dragging = true; if (currentTool == Tool.ERASE) { // Erase a 10-by-10 block around the starting mouse position. Graphics g = OSC.getGraphics(); g.setColor(fillColor); g.fillRect(startX - 5, startY - 5, 10, 10); g.dispose(); repaint(startX - 5, startY - 5, 10, 10); } else if (currentTool == Tool.SMUDGE) { // Record the colors in a 7-by-7 block of pixels around the // starting mouse position into the arrays smudgeRed, // smudgeGreen, and smudgeBlue. These arrays hold the // red, green, and blue components of the colors. if (smudgeRed == null) { // Create the arrays, if they have not already been created. smudgeRed = new double[7][7]; smudgeGreen = new double[7][7]; smudgeBlue = new double[7][7]; } int w = OSC.getWidth(); int h = OSC.getHeight(); int x = evt.getX(); int y = evt.getY(); for (int i = 0; i < 7; i++) { for (int j = 0; j < 7; j++) { int r = y + j - 3; int c = x + i - 3; if (r < 0 || r >= h || c < 0 || c >= w) { // A -1 in the smudgeRed array indicates that the // corresponding pixel was outside the canvas. smudgeRed[i][j] = -1; } else { int color = OSC.getRGB(c, r); smudgeRed[i][j] = (color >> 16) & 0xFF; smudgeGreen[i][j] = (color >> 8) & 0xFF; smudgeBlue[i][j] = color & 0xFF; } } } } }
/** * Finish a mouse drag operation. Nothing is done unless the current tool is a shape tool. For * shape tools, the user's shape is drawn to the off-screen canvas, making it a permanent part * of the picture, and then the repaint() method is called to show the modified picture on the * screen. */ @Override public void mouseReleased(MouseEvent evt) { dragging = false; if (SHAPE_TOOLS.contains(currentTool)) { Graphics g = OSC.getGraphics(); g.setColor(currentColor); putCurrentShape(g); g.dispose(); repaint(); } }
/** * Continue a drag operation when the user drags the mouse. For the CURVE tool, a line is drawn * from the previous mouse position to the current mouse position in the off-screen canvas, and * the repaint() method is called for a rectangle that contains the line segment that was drawn. * For shape tools, the off-screen canvas is not changed, but the repaint() method is called so * that the paintComponent() method can redraw the picture with the user's shape in the new * position. For the SMUDGE and ERASE tools, the tool is applied along a line from the previous * mouse position to the current position; */ @Override public void mouseDragged(MouseEvent evt) { currentX = evt.getX(); currentY = evt.getY(); if (currentTool == Tool.CURVE) { Graphics g = OSC.getGraphics(); g.setColor(currentColor); g.drawLine(prevX, prevY, currentX, currentY); g.dispose(); repaintRect(prevX, prevY, currentX, currentY); } else if (SHAPE_TOOLS.contains(currentTool)) { // Repaint the rectangles occupied by the previous position of // the shape and by its current position. repaintRect(startX, startY, prevX, prevY); repaintRect(startX, startY, currentX, currentY); } else { // Tool has to be ERASE or SMUDGE applyToolAlongLine(prevX, prevY, currentX, currentY); } prevX = currentX; prevY = currentY; }
@Override public void actionPerformed(ActionEvent evt) { String command = evt.getActionCommand(); switch (command) { case "New File": { OSC = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); Graphics osg = OSC.getGraphics(); osg.setColor(fillColor); osg.fillRect(0, 0, getWidth(), getHeight()); osg.dispose(); repaint(); break; } case "Open": { JFileChooser chooser = new JFileChooser(); FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG or PNG Images", "jpg", "png"); chooser.setFileFilter(filter); int returnVal = chooser.showOpenDialog(null); if (returnVal == JFileChooser.APPROVE_OPTION) { try { OSC = ImageIO.read(chooser.getSelectedFile()); Graphics2D g2 = (Graphics2D) OSC.getGraphics(); g2.scale((double) 640 / OSC.getWidth(), (double) 480 / OSC.getHeight()); g2.drawImage(OSC, 0, 0, OSC.getWidth(), OSC.getHeight(), null); } catch (Exception e) { } } repaint(); break; } case "Save": { JFileChooser fileDialog = new JFileChooser(); fileDialog.setSelectedFile(new File("image.PNG")); fileDialog.setDialogTitle("Select File to be Saved"); int option = fileDialog.showSaveDialog(null); if (option != JFileChooser.APPROVE_OPTION) return; // User canceled or clicked the dialog’s close box. File selectedFile = fileDialog.getSelectedFile(); if (selectedFile.exists()) { // Ask the user whether to replace the file. int response = JOptionPane.showConfirmDialog( null, "The file \"" + selectedFile.getName() + "\" already exists.\nDo you want to replace it?", "Confirm Save", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (response != JOptionPane.YES_OPTION) return; // User does not want to replace the file. } try { boolean hasFormat = ImageIO.write(OSC, "PNG", selectedFile); if (!hasFormat) throw new Exception("PNG" + " format is not available."); } catch (Exception e) { JOptionPane.showMessageDialog( null, "Sorry, an error occurred while trying to save image."); e.printStackTrace(); } break; } case "Exit": { System.exit(0); } case "Select Drawing Color...": { Color newColor = JColorChooser.showDialog(AdvancedGUIEX1.this, "Select Drawing Color", currentColor); if (newColor != null) currentColor = newColor; break; } case "Fill With Color...": { Color newColor = JColorChooser.showDialog(AdvancedGUIEX1.this, "Select Fill Color", fillColor); if (newColor != null) { fillColor = newColor; Graphics osg = OSC.getGraphics(); osg.setColor(fillColor); osg.fillRect(0, 0, OSC.getWidth(), OSC.getHeight()); osg.dispose(); AdvancedGUIEX1.this.repaint(); } break; } case "Draw With Black": currentColor = Color.BLACK; break; case "Draw With White": currentColor = Color.WHITE; break; case "Draw With Red": currentColor = Color.RED; break; case "Draw With Green": currentColor = Color.GREEN; break; case "Draw With Blue": currentColor = Color.BLUE; break; case "Draw With Yellow": currentColor = Color.YELLOW; break; case "Curve": currentTool = Tool.CURVE; break; case "Line": currentTool = Tool.LINE; break; case "Rectangle": currentTool = Tool.RECT; break; case "Oval": currentTool = Tool.OVAL; break; case "Filled Rectangle": currentTool = Tool.FILLED_RECT; break; case "Filled Oval": currentTool = Tool.FILLED_OVAL; break; case "Smudge": currentTool = Tool.SMUDGE; break; case "Erase": currentTool = Tool.ERASE; break; } }