java - 如何使用 Graphics2D 创建的形状剪辑 BufferedImage?(Java)

标签 java graphics2d image-clipping

目前,我正在开发我的第一个 Java 应用程序,用户可以使用该应用程序浏览照片、剪切和旋转照片。我在裁剪图像时遇到问题。我想要实现的目标如下:

  • 用户点击“剪切”选项
  • 图像上出现由repaint方法调用的矩形形状
  • 通过拉伸(stretch)矩形,用户可以选择要剪切的区域
  • 当用户释放鼠标(停止拉伸(stretch)矩形)时,矩形包围的区域将被保留,图像的所有其余部分将被剪切。

目前我有几个问题:

  1. 我的图像集中在 JLabel 上,JLabel 依次添加到 JPanel 中,最后一个添加到 JFrame 中,所以现在,当我想在 JLable 上方添加一个矩形(因此它位于图片上)时,它是不可见的并且仅直接添加到 JPanel 上。
  2. 我用paintComponent绘制了一个图像,但不知道如何移动和拉伸(stretch)它并再次重新绘制矩形。

下面是我的代码部分(我希望)能够更准确地描述我的问题:

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();
    }
}
}

我的想法是添加 MouseListeners,但是我可以将其添加到使用 Graphics2D 创建的形状中吗? 我将非常感谢您的帮助:) 谢谢

最佳答案

在寻找这个问题的解决方案时,我又问了两个问题(也许这对某人有帮助):

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

这进一步帮助我找到了方法。 我的解决方案是创建一个单独的框架,用于在其中显示复制的 BufferedImage,并在该框架上借助 MouseListeners 绘制一个矩形。这是一段代码:

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");
}

附注我知道添加 JFrame 不是最好的解决方案,但我仍在寻找实现它的最佳方法,因此请毫不犹豫地评论我的代码并告诉我什么是错误的或什么是好的)

关于java - 如何使用 Graphics2D 创建的形状剪辑 BufferedImage?(Java),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55628883/

相关文章:

algorithm - 二维对象分布算法

java - 如何将一幅图像的一部分复制到另一幅图像?

java - 查询WAR插件的Gradle依赖关系

java - 装饰模式困惑?

c# - 在 .Net 中清除位图

css - 如何剪辑和转换图像,添加圆 Angular 和透视图?

imagemagick - ImageMagick 在将 PSD 文件转换为 GIF、PNG、JPG 文件时可以在多大程度上保留剪切路径?

java - 在App Engine中,是否有一种方法可以获取所有子实体而不包含相同类型的父实体?

java - 在java上打印播放器结果

java - SVG 到 Java Graphics2D