/**
  * 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;
      }
    }