Java Passing 2D Graphic as a Parameter

2020-05-06 13:58发布

I have a function that is drawing an image and then saving it immediately after but the problem is that it seems to be drawing it twice, once for the view on the screen and then once to save it to the disk

public class myFrame {

    public static void main(String[] args) {

        JFrame lv_frame = new JFrame();
        // setup jframe here

        lv_frame.add(new image());

        lv_frame.setVisible(true);

    }

}

class image extends JPanel {

    public void paintComponent(Graphics graphic) {

        super.paintComponent(graphic);
        draw(graphic);
        save();

    }

    public void draw(Graphics graphic) {

        Graphics2D graphic2D = (Graphics2D) graphic;
        graphic2D.fillArc(0, 0, 250, 250, 0, 90);

    }

    public void save() {

        BufferedImage image = new BufferedImage(250, 250, BufferedImage.TYPE_4BYTE_ABGR_PRE);
        Graphics2D graphic2D = image.createGraphics();

        try {
            File output = new File("file.png");

            // is it possible to use the graphic I've already
            // drawn here instead of re-drawing?
            draw(graphic2D);

            ImageIO.write(image, "png", output);
        } catch(IOException log) {
            System.out.println(log);
        }

    }

}

I have a function draw which creates the image on the gui screen and another function save which saves it to the disk but the save function is calling draw as well.

Is it possible to save the image that has already been drawn without re-calling the draw function as it is being done in my code?

2条回答
地球回转人心会变
2楼-- · 2020-05-06 14:19

I was answering your question on Display to GUI and Save to Disk with a Single Object/Variable, however it was closed probably due to the similar nature of your question. I think there are several issues which you seemed confused with and I would like to write my solution here which also addresses your question in your duplicated post.


Is it possible to save the image that has already been drawn without re-calling the draw function as it is being done in my code?

Don't be confused between drawing an image on the Panel and saving it. The following shows a working example on saving a drawn image.

class DrawingSpace extends JPanel
{
    private BufferedImage buf;

    public DrawingSpace(){
        setPreferredSize(new Dimension(200, 200));
        buf = new BufferedImage(200, 200, BufferedImage.TYPE_INT_ARGB);
        drawOnBuffer();
    }

    public void drawOnBuffer(){
        Graphics g = buf.createGraphics();
        g.setColor(Color.BLUE);
        g.fillOval(0,0,200,200);
        g.setColor(Color.RED);
        g.fillOval(50,50,100,100);
        g.dispose();
    }

    public void saveBufferAsImage(String pathname){
        String fmt = "";
        if(!(pathname == null || pathname.equals(""))){
            fmt = pathname.substring(pathname.lastIndexOf(".")+1);
            File outputfile = new File(pathname);
            try{
                ImageIO.write(buf, fmt, outputfile);        
            }catch(IOException ioe){System.out.println("Unable to save file");}
        }       
    }

    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.drawImage(buf, 0, 0, 200, 200, null); //Only for user to see
    }
}

To save an image, you not necessarily have to draw and display it in the JPanel first. Note that I created a BufferedImage called buf and I am drawing on buf. Once I have drawn onto a BufferedImage, I can simply save that image as a file (I don't have to draw it to the panel first).

Notice that I did the following:

    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        g.drawImage(buf, 0, 0, 200, 200, null); //Only for user to see
    }

g.drawImage(buf, 0, 0, 200, 200, null) will draw whatever on buf onto the JPanel. It can be deleted and the saving will still work. When I draw it on the panel, it is only for the user to see the outcome of the drawing.

To test the program:

class SaveImageRunner{
    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run(){
                JFrame frame = new JFrame("Saving a buffered image");
                DrawingSpace ds = new DrawingSpace();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(ds);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                ds.saveBufferAsImage("BlueCircle.png");
            }           
        });
    }
}

Saved Image:

enter image description here


Some pointers on drawing:

  • paintComponent(Graphics) shall only contain codes for drawing and nothing else. Do not implement your codes for saving the image to a file inside here.

  • Try not to create a new BufferedImage in the paintComponent(Graphics). If not, a new instance of a BufferedImage will be created on every repaint.

查看更多
smile是对你的礼貌
3楼-- · 2020-05-06 14:26

Change the place where you create the graphics2D object and reuse it.

Try this out.

public class myFrame {

    public static void main(String[] args) {

        JFrame lv_frame = new JFrame();
        // setup jframe here

        lv_frame.add(new image());

        lv_frame.setVisible(true);

    }

}

class image extends JPanel {

    public void paintComponent(Graphics graphic) {

        super.paintComponent(graphic);

        // Image and graphics are now created here
        BufferedImage image = new BufferedImage(250, 250, BufferedImage.TYPE_4BYTE_ABGR_PRE);
        Graphics2D graphic2D = image.createGraphics();

        draw(graphic2D);
        save(image);
    }

    public void draw(Graphics graphic) {

        Graphics2D graphic2D = (Graphics2D) graphic;
        graphic2D.fillArc(0, 0, 250, 250, 0, 90);

    }

    public void save(BufferedImage image) {
        try {
            File output = new File("file.png");
            ImageIO.write(image, "png", output);
        } catch(IOException log) {
            System.out.println(log);
        }

    }

}


EDIT

I have found the answer in another post. It is not a bad idea to make the drawing twice. What you are doing is, like @Hovercraft says in this post, separating the screen drawing from the writing to files so that you don't see your graphics drawing performance hurt.

I have tried to make the drawing only once but you have no easy method to store the drawing Graphics object. Probably, it is implemented to prevent this. If you see the way the method works, you are given the Graphics object with the only purpose to draw on it. Using it in other ways could impact the performance.

查看更多
登录 后发表回答