views:

2343

answers:

4

Hey guys.

I have a quick question. How do I get the image generated by a JComponent.paint or paintComponent?

I have a JComponent which I use as a 'workspace' and where I have overwritten the paintComponent method to my own. The thing is that my workspace JComponent also has children which has their own paintComponent methods.

So when Swing renders my workspace component, it renders the workspace graphics and then its childrens'.

However, I want to get the image my workspace component generates (which includes the workspace graphics and the children's graphics).

How do I do that?

I tried to call the paintComponent/paint-method myself by using my own Graphics, but i just returned a black image. Here is what i tried;

public void paintComponent(Graphics g) {

 if (bufferedImage != null) {
  g.drawImage(bufferedImage, 0, 0, this);
 }
 else {
  g.setColor(Color.WHITE);
  g.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight());
 }
}

public BufferedImage getImage() {

 BufferedImage hello = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
 Graphics g = hello.getGraphics();
 paintComponent( g );

 return hello;
}

Any thoughts or comments are welcome! :)

+2  A: 

If you call getImage too early, your component will not have been displayed yet and will still have a 0 width and height. Have you made sure you're calling it at a sufficient late time? Try printing the component's size to stdout and see what its dimensions are.

Martijn
The method 'getImage' is called by the user, meaning the application has plenty of time.
A: 

Seems like the problem was due to how the BufferedImage was created. When using:

BufferedImage hello = bufferedImage.getSubimage(0,0, getWidth(), getHeight());

Instead it worked. I also had to change switch from paintComponent to paint to render the children.

Problem solved, but if anyone knows. Why did my:

 BufferedImage hello = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);

Always render a black image? Offtopic, but could be interesting to know :)

I had a similar problem - changing the new BufferedImage's type to BufferedImage.TYPE_INT_RGB solved the problem.
Steve McLeod
+1  A: 

Do not call paintComponent() or paint() from the outside. Instead, let your image be created within those (overwritten) methods. Then you can be sure that your image will actually contain the painted content of the component.

Here is an example application. The ImagePanel will actually grab the graphics contents of the component every time it is painted, which might be a bit wasteful, so you may want to adjust the frequency with which this is done.

public class SomeApp extends JFrame {

private static class ImagePanel extends JPanel {
 private BufferedImage currentImage;
 public BufferedImage getCurrentImage() {
  return currentImage;
 }
 @Override
 public void paint(Graphics g) {
  Rectangle tempBounds = g.getClipBounds();
  currentImage = new BufferedImage(tempBounds.width, tempBounds.height, BufferedImage.TYPE_INT_ARGB);
  super.paint(g);
  super.paint(currentImage.getGraphics());
 }
}

public SomeApp() {
 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
 setSize(800,600);
 int matrixSize = 4;
 setLayout(new BorderLayout());
 add(new JLabel("Wonderful Application"), BorderLayout.NORTH);
 final ImagePanel imgPanel = new ImagePanel();
 imgPanel.setLayout(new GridLayout(matrixSize,matrixSize));
 for(int i=1; i<=matrixSize*matrixSize; i++) {
  imgPanel.add(new JButton("A Button" + i));
 }
 add(imgPanel, BorderLayout.CENTER);
 final JPanel buttonPanel = new JPanel();
 buttonPanel.add(new JButton(new AbstractAction("get image") {

  @Override
  public void actionPerformed(ActionEvent e) {
   JOptionPane.showMessageDialog(SomeApp.this, 
     new ImageIcon(imgPanel.getCurrentImage()));
  }

 }));
 add(buttonPanel, BorderLayout.SOUTH);
}

public static void main(String[] args) {
 System.setProperty("swing.defaultlaf", UIManager.getSystemLookAndFeelClassName());
 SwingUtilities.invokeLater(new Runnable() {
  @Override
  public void run() {
   new SomeApp().setVisible(true);
  }
 });
}

}

Like Martijn said, your image was probably black because the component wasn't even painted/displayed yet. You could add a ComponentListener to be notified when it is displayed. Your "solution" with getSubimage has nothing to do with the actual problem. I recommend you remove it.

jmagica
A: 

Changing the new BufferedImage to TYPE_INT_RGB solves this for me. I can't give an explanation though - just a Swing mystery.

public BufferedImage getImage() {

    BufferedImage hello = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics g = hello.getGraphics();
    paintComponent( g );

    return hello;
}
Steve McLeod