How do you write each call to paintComponent concurrently with the BufferedImage?

How do you write each call to repaint (direct or not) with a type JPanel

(i.e. a custom class that extends

/ inherits from JPanel

) to BufferedImage

?

Doing this sort of thing inside a custom class paintComponent

doesn't work:

protected void paintComponent(Graphics g) {
    super.paintComponent(g); 
    Graphics2D G2 = (Graphics2D) g;

    // ... draw objects

    BufferedImage imageBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
    G2 = imageBuffer.createGraphics();

    // Which doesn't work, because with that method it seems you would
    // need to call paint() on Graphics2D reference here. 
    // And to do so would then throw an Illegal Exception.
}

      

The resultant BufferedImage

is the correct size of the JPanel

-type class it calls paintComponent

, but the image is black (i.e. empty) - this is completely logical because it createGraphics()

does nothing.

I know the Rob Camick ScreenImage code , but this seems to be meant to take a single screenshot while initializing the program.

What confuses me is what paintComponent

should be kept in memory before being displayed on the screen ... So is there a way to grab this every time and store it in BufferedImage

?

+3


source to share


3 answers


How about this approach? The difference from your posted code is that you need to do all your own drawing on BufferedImage

. Then you will cause only one component per component: BufferedImage

.



public void paintComponent(Graphics g) {
    super.paintComponent(g); 
    Graphics2D g2 = (Graphics2D) g;

    BufferedImage imageBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics imageG2 = imageBuffer.createGraphics();

    // do custom painting onto imageG2...

    // save imageBuffer

    g2.drawImage(imageBuffer, 0, 0, this);  // draw onto component
}

      

+1


source


The description and some details may not be entirely clear. But basically it looks like you wanted to create a component that can be used like a regular component JPanel

, but everything written in the method paintComponent

must also be saved as BufferedImage

.

There are different solutions for this. You can make special calls like

myComponent.paintComponent(bufferedImageGraphics);

      

and process the image accordingly, as suggested in one answer. Another answer suggested creating a custom method paintComponent

that does image processing.

However, I would like to suggest the following solution: you can create a class ImageSavingComponent

that extends JPanel

. In this class, you are not overriding a method paintComponent

, but a method paint

. The method paint

has two functions:



  • It calls super.paint(bufferedImageGraphics)

    by drawing into an image
  • It calls super.paint(componentGraphics)

    , draws on the screen

Thus, you can simply extend this class and implement the method paintComponent

as you would for anyone JPanel

. Image processing can be done completely transparently.

In the example below, I've added an interface Consumer

that receives images and processes them appropriately. If Consumer

- null

, then images will not be generated. For testing purposes, I created an implementation of this interface that simply saves images to files every 200ms. (This interface can be replaced with Java 8 interface if needed Consumer

)

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class ImageSavingComponentTest {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final ImageSavingComponent c = new ExampleImageSavingComponent();
        f.getContentPane().add(c);

        final Timer timer = new Timer(50, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                c.repaint();
            }
        });
        timer.start();

        final Consumer<? super BufferedImage> consumer = new ImageSaver();
        final JToggleButton b = new JToggleButton("Capture");
        b.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (b.isSelected()) {
                    c.setConsumer(consumer);
                } else {
                    c.setConsumer(null);
                }
            }
        });
        c.add(b);

        f.setSize(300, 300);
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

class ImageSaver implements Consumer<BufferedImage>
{
    private int counter = 0;
    private long previousFrameMillis = -1;
    private long intervalMillis = 200;

    @Override
    public void accept(BufferedImage t) {

        if (System.currentTimeMillis() < previousFrameMillis + intervalMillis)
        {
            return;
        }
        previousFrameMillis = System.currentTimeMillis();

        String fileName = String.format("image%04d.png", counter);
        counter++;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(fileName);
            ImageIO.write(t, "png", fos);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}




class ExampleImageSavingComponent extends ImageSavingComponent {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        double time = (System.currentTimeMillis() % 2000) / 2000.0;
        double ca = Math.cos(time * Math.PI * 2);
        double sa = Math.sin(time * Math.PI * 2);
        g.setColor(Color.RED);
        double d = 50;
        int x = getWidth() / 2 + (int) (ca * d);
        int y = getHeight() / 2 + (int) (sa * d);
        int r = 10;
        g.fillOval(x - r, y - r, r + r, r + r);
    }
}

interface Consumer<T> {
    void accept(T t);
}

class ImageSavingComponent extends JPanel {
    private Consumer<? super BufferedImage> consumer;

    void setConsumer(Consumer<? super BufferedImage> consumer) {
        this.consumer = consumer;
    }

    @Override
    public final void paint(Graphics g) {

        if (consumer != null && getWidth() > 0 && getHeight() > 0) {
            BufferedImage image = new BufferedImage(getWidth(), getWidth(),
                    BufferedImage.TYPE_INT_ARGB);
            Graphics2D imageGraphics = image.createGraphics();
            super.paint(imageGraphics);
            imageGraphics.dispose();
            consumer.accept(image);
        }
        super.paint(g);
    }
}

      

+1


source


You must create BufferedImage

outside the paint function of your component and then call that paint function with a parameter BufferedImage

as a parameter.

BufferedImage imageBuffer = new BufferedImage(comp.getWidth(), cmp.getHeight(), BufferedImage.TYPE_INT_RGB);
cmp.paint(imageBuffer.getGraphics());

      

0


source







All Articles