javagraphics2dimage-clipping

How to clip a BufferedImage with a shape created with Graphics2D?(Java)


Currently, I'm working on my first Java application with which a user can look through photos, cut them and rotate. I have an issue with clipping an image. What I want to achieve is the following:

As of for now I have several issues:

  1. My image is centralized on JLabel which in its turn is added to JPanel and the last is added to JFrame, so now, when I want to add a rectangle above JLable (so it is to be located on the picture) it's invisible and is added only on JPanel directly.
  2. I drew an image with paintComponent but can't figure out how to move and stretch it and repaint the rectangle again.

Below is the part of my code which (I hope) will describe my problems more precisely:

public class GraphicalUserInterface {


static JPanel background;
static JLabel labelIcon;

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new GraphicalUserInterface().go();
        }
    });
}


public void go() {
    buildGui();
}

public void buildGui() {

    frame = new JFrame("PicMove");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    BorderLayout layout = new BorderLayout();
    background = new JPanel(layout);

   /**To center picture on the background**/
    labelIcon = new JLabel();
    labelIcon.setHorizontalAlignment(JLabel.CENTER);
    labelIcon.setVerticalAlignment(JLabel.CENTER);

    background.add(BorderLayout.SOUTH, bottom);
    background.add(BorderLayout.PAGE_START, bar);
    background.add(BorderLayout.CENTER, labelIcon);
    background.add(BorderLayout.EAST, chatPanel);

    frame.getContentPane().add(BorderLayout.CENTER, background);
    frame.setJMenuBar(menuBar);
    frame.setVisible(true);
    frame.setSize(1300, 1200);}


static class CutImage extends JPanel implements Runnable {
    boolean clip;

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;

        if (clip) {
            BasicStroke bs = new BasicStroke(50, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                    10, null, 0);
            g2d.setStroke(bs);
            QuadCurve2D.Float qc = new QuadCurve2D.Float(20, 50, 100, 140, 460, 170);
            g2d.setClip(qc);
        }
        BasicStroke bs = new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                10, new float[]{10}, 0);
        g2d.setStroke(bs);
        g2d.drawRect(260, 50, 80, 120);

    }

    @Override
    public void run() {
        CutImage cutPanel = new CutImage();
        GraphicalUserInterface.background.add(cutPanel).repaint();
    }
}
public class PicChanges implements Runnable{

static BufferedImage newImage;
static File [] selectedFile;
static int currentImage;



FileNameExtensionFilter filter;
JFileChooser fileChooser;

public void openPic() {
    currentImage = 0;
    try {
        fileChooser = new JFileChooser();
        fileChooser.setCurrentDirectory(new java.io.File((System.getProperty("user.home"))));
        filter = new FileNameExtensionFilter("*.images", "jpg", "gif", "png");
        fileChooser.addChoosableFileFilter(filter);
        fileChooser.setMultiSelectionEnabled(true);
        int result = fileChooser.showOpenDialog(null);
        if (result == JFileChooser.OPEN_DIALOG) {
            selectedFile = fileChooser.getSelectedFiles();
            for (File image : selectedFile) {
                if ((image.isFile()) && (selectedFile.length > 0)){
                    newImage = ImageIO.read(selectedFile[0]);
                    GraphicalUserInterface.labelIcon.setIcon(new ImageIcon(
                            new ImageIcon(newImage).getImage().getScaledInstance(
                                    450, 620, Image.SCALE_DEFAULT)));
                } else if (result == JFileChooser.CANCEL_OPTION) {
                    System.out.println("No Pics Selected");
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
@Override
public void run() {
    Thread.currentThread().interrupt();
    openPic();
}

public static void nextPic() {
    currentImage++;
    try {
        newImage = ImageIO.read(selectedFile[currentImage]);
    } catch (IOException e) {
        e.printStackTrace();
        System.out.println("No pictures left");
        System.out.println("next"+currentImage);
    }
    GraphicalUserInterface.labelIcon.setIcon(new ImageIcon(
            new ImageIcon(newImage).getImage().getScaledInstance(
                    450, 620, Image.SCALE_DEFAULT)));
}

static class NextPicture implements Runnable{
    @Override
    public void run() {
        Thread.currentThread().interrupt();
        nextPic();
    }
}

public static void previousPic () {
    currentImage--;
    try {
        newImage = ImageIO.read(selectedFile[currentImage]);
    } catch (IOException e) {
        e.printStackTrace();
        System.out.println("previous "+currentImage);
    }
    GraphicalUserInterface.labelIcon.setIcon(new ImageIcon(
            new ImageIcon(newImage).getImage().getScaledInstance(
                    450, 620, Image.SCALE_DEFAULT)));
}

static class PreviousPic implements Runnable{
    @Override
    public void run() {
        Thread.currentThread().interrupt();
        previousPic();
    }
}
}

My idea was to add MouseListeners but can I add it to the shape created with Graphics2D? I would be greatful for the help :) Thank you


Solution

  • Being in search of finding a solution to this question I've asked two more questions(maybe it will be helpful for someone) :

    1) Why BufferedImage is not cut according to the drawn in paintComponent method rectangle(its height is calculated wrong)?

    2) Repaint() method doesn't invoke paint() & paintComponent() methods one by one, only paintComponent () method is working

    that further helped me to find out the way. My solution is to create a separate frame for displaying copied BufferedImage in it and on this frame to draw a rectangle with the help of MouseListeners. This is the piece of code:

    public class ImageScreenShot extends JFrame implements MouseListener, MouseMotionListener {
    
    @Override
    public Dimension getPreferredSize() {
        return super.getPreferredSize();
    }
    
    private static Thread screenShotThread;
    
    public static Thread getScreenShotThread() {
        return screenShotThread;
    }
    
    public static void setScreenShotThread(Thread screenShot) {
        ImageScreenShot.screenShotThread = screenShot;
    }
    
    private int drag_status = 0, c1, c2, c3, c4;
    
    public int getC1() {
        return c1;
    }
    
    public int getC2() {
        return c2;
    }
    
    public int getC3() {
        return c3;
    }
    
    public int getC4() {
        return c4;
    }
    
    class AdditionalPanel extends JLabel {
        private BufferedImage img;
    
        public BufferedImage getImg() {
            return img;
        }
    
        public AdditionalPanel(BufferedImage img) {
            this.img = img;
            setPreferredSize(new Dimension(2560, 1600));
            getPreferredSize();
            setLayout(null);
        }
    
        @Override
        public Dimension getPreferredSize() {
            return super.getPreferredSize();
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawImage(img, 0, 0, null);
            System.out.println("Additional panel class paint method was invoked");
        }
    }
    
    public void cut() {
        AdditionalPanel apanel = new AdditionalPanel(PicChanges.getNewImage());
        JScrollPane scrollPane = new JScrollPane(apanel);
        scrollPane.addMouseMotionListener(this);
        scrollPane.addMouseListener(this);
        getContentPane().add(scrollPane, BorderLayout.CENTER);
        setPreferredSize(new Dimension(2560, 1600));
        getPreferredSize();
        pack();
        setVisible(true);
    }
    
    private void draggedScreen() throws Exception {
        int w = c1 - c3;
        int h = c2 - c4;
        w = w * -1;
        h = h * -1;
        Robot robot = new Robot();
        BufferedImage img = robot.createScreenCapture(new Rectangle(c1, c2, w, h));
        File save_path = new File("screen1.jpg");
        ImageIO.write(img, "JPG", save_path);
        GraphicalUserInterface.getLabelIcon().setIcon(new ImageIcon(new ImageIcon(img).getImage().getScaledInstance(img.getWidth(), img.getHeight(), Image.SCALE_SMOOTH)));
        JOptionPane.showConfirmDialog(this, "Would you like to save your cropped Pic?");
        System.out.println("Cropped image saved successfully.");
    }
    
    @Override
    public void mouseClicked(MouseEvent arg0) {
    }
    
    @Override
    public void mouseEntered(MouseEvent arg0) {
    }
    
    @Override
    public void mouseExited(MouseEvent arg0) {
    }
    
    @Override
    public void mousePressed(MouseEvent arg0) {
        repaint();
        c1 = arg0.getXOnScreen();
        c2 = arg0.getYOnScreen();
        System.out.println("pressed");
    }
    
    @Override
    public void mouseReleased(MouseEvent arg0) {
        repaint();
        if (drag_status == 1) {
            c3 = arg0.getXOnScreen();
            c4 = arg0.getYOnScreen();
            try {
                repaint();
                draggedScreen();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    @Override
    public void mouseDragged(MouseEvent arg0) {
        repaint();
        drag_status = 1;
        c3 = arg0.getXOnScreen();
        c4 = arg0.getYOnScreen();
    }
    
    @Override
    public void mouseMoved(MouseEvent arg0) {
    }
    
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        int w = c1 - c3;
        int h = c2 - c4;
        w = w * -1;
        h = h * -1;
        if (w < 0)
            w = w * -1;
        g.setColor(Color.RED);
        g.drawRect(c1, c2, w, h);
        System.out.println("Paint component was invoked in imagescreenshot class");
    }
    

    P.S. I know that adding JFrame is not the best solution but I'm still in search for the best way to implement it so do not hesitate to comment on my code and tell what is wrong or what is good)