Search code examples
javaswingpaintjlabeljava-2d

Minimal way to make a cleanable drawing area


I got a class with a resizable background. There are paintings over that background(using a paint method and Java2D). How can i delete everything that was drawn every time that the background gets a resize? (To eventually draw again in the correct places) Is there any sort of transform i can do on the already-drawn objects(like scaling to fit the image again)?

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Background extends JLabel implements ChangeListener  {
    private ImageIcon background;
    private BufferedImage image;

    public Background(JPanel parent){
        super();
        parent.add(this);
        try {
            image = ImageIO.read(new File("/example/background"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.background = new ImageIcon(image);
        this.setIcon(background);
    }
    public void stateChanged(ChangeEvent e) {
        int value = ((JSlider) e.getSource()).getValue();
        double scale = value / 100.0;
        BufferedImage scaled = getScaledImage(scale);
        this.setIcon(new ImageIcon(scaled));
        this.revalidate();
    }
    private BufferedImage getScaledImage(double scale) {
        int w = (int) (scale * image.getWidth());
        int h = (int) (scale * image.getHeight());
        BufferedImage bi = new BufferedImage(w, h, image.getType());
        Graphics2D g2 = bi.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        AffineTransform at = AffineTransform.getScaleInstance(scale, scale);
        g2.drawRenderedImage(image, at);
        g2.dispose();
        return bi; 
    }
    @Override
    public void paint(Graphics g){
        super.paint(g);
        Graphics2D graphObj = (Graphics2D) g;
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);rh.put(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
        graphObj.setRenderingHints(rh);
        graphObj.fillOval(500, 500, 20, 20);
        graphObj.finalize();
    }
}

Solution

  • Consider:

    • Drawing in a paintComponent(...) override, not a paint(...) override.
    • Save a List<Point> where each Point is normalized, say to a 1000 by 1000 size.
    • Then in the paintComponent method, iterate through each Point in a for loop, scaling it to the current component size, and drawing it.
    • You'll want to scale any image drawn in the component in a ComponentListener, and then call repaint().
    • Or perhaps even better, scale the image drawn using the Graphics#drawImage(...) overload that takes width and height parameters.

    e.g.,

    import java.awt.Graphics;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    public class MyBackground extends JPanel {
       private BufferedImage img;
    
       public MyBackground(BufferedImage img) {
          this.img = img;
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          if (img != null) {
             g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
          }
       }
    
       private static void createAndShowGui() {
          String comfyChair = "https://duke.kenai.com/comfyChair/ComfyChairRad.png";
          BufferedImage img;
          try {
             URL url = new URL(comfyChair);
             img = ImageIO.read(url);
    
             MyBackground mainPanel = new MyBackground(img);
    
             JFrame frame = new JFrame("MyBackground");
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             frame.getContentPane().add(mainPanel);
             frame.pack();
             frame.setLocationByPlatform(true);
             frame.setVisible(true);
          } catch (MalformedURLException e) {
             e.printStackTrace();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }
    

    example 2:

    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.RenderingHints;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.geom.Point2D;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    
    public class MyBackground extends JPanel {
       public static final double NORM_CONST = 1.0;
       private BufferedImage img;
       private List<List<Point2D>> normalizedPoints = new ArrayList<List<Point2D>>();
       private List<Point2D> pointSubList;
    
       public MyBackground(BufferedImage img) {
          this.img = img;
          MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
          addMouseListener(myMouseAdapter);
          addMouseMotionListener(myMouseAdapter);
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g);
          if (img != null) {
             g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
          }
          Graphics2D g2 = (Graphics2D) g;
          g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
    
          for (List<Point2D> pointList : normalizedPoints) {
             if (pointList.size() > 1) {
                for (int i = 1; i < pointList.size(); i++) {
                   Point p1 = deNormalize(pointList.get(i - 1));
                   Point p2 = deNormalize(pointList.get(i));
                   g2.drawLine(p1.x, p1.y, p2.x, p2.y);
                }
             }
          }
    
          if (pointSubList != null && pointSubList.size() > 1) {
             for (int i = 1; i < pointSubList.size(); i++) {
                Point p1 = deNormalize(pointSubList.get(i - 1));
                Point p2 = deNormalize(pointSubList.get(i));
                g2.drawLine(p1.x, p1.y, p2.x, p2.y);
             }
          }
       }
    
       private Point deNormalize(Point2D p2d) {
          int x = (int) (p2d.getX() * getWidth() / NORM_CONST);
          int y = (int) (p2d.getY() * getHeight() / NORM_CONST);
          return new Point(x, y);
       }
    
       private class MyMouseAdapter extends MouseAdapter {
    
          @Override
          public void mousePressed(MouseEvent e) {
             Point2D p = normalizePoint(e.getPoint());
             pointSubList = new ArrayList<>();
             pointSubList.add(p);
          }
    
          @Override
          public void mouseReleased(MouseEvent e) {
             Point2D p = normalizePoint(e.getPoint());
             pointSubList.add(p);
             normalizedPoints.add(pointSubList);
             pointSubList = null;
             repaint();
          }
    
          @Override
          public void mouseDragged(MouseEvent e) {
             Point2D p = normalizePoint(e.getPoint());
             pointSubList.add(p);
             repaint();
          }
    
          private Point2D normalizePoint(Point point) {
             double x = (NORM_CONST * point.x) / getWidth();
             double y = (NORM_CONST * point.y) / getHeight();
             Point2D result = new Point2D.Double(x, y);
             return result;
          }
    
    
       }
    
       private static void createAndShowGui() {
          String comfyChair = "https://duke.kenai.com/comfyChair/ComfyChairRad.png";
          BufferedImage img;
          try {
             URL url = new URL(comfyChair);
             img = ImageIO.read(url);
    
             MyBackground mainPanel = new MyBackground(img);
    
             JFrame frame = new JFrame("MyBackground");
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             frame.getContentPane().add(mainPanel);
             frame.pack();
             frame.setLocationByPlatform(true);
             frame.setVisible(true);
          } catch (MalformedURLException e) {
             e.printStackTrace();
          } catch (IOException e) {
             e.printStackTrace();
          }
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }