Question

I've had a look around and tried using other queries, but I just can't seem to get this to work.

I am trying to retain an image from a JPanel drawn via the g.draw/fill methods.

I've attempted to save the drawing in a buffered image, but when I display it in a messageDialog all I get is the background and none of the drawings

These two methods are the important code (from the DrawingPanel class):

        public void loadDrawing(BufferedImage bi) {
            //opens a message dialog and displays the image parameter
            JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
            System.out.println("w:" + bi.getWidth() + " h:" + bi.getHeight());
        }

        public BufferedImage getScreenShot() {

            BufferedImage image = new BufferedImage(this.getWidth(),
                    this.getHeight(), BufferedImage.TYPE_INT_RGB);
            // call the Panels's paint method, using
            // the Graphics object of the image.
            this.paint(image.getGraphics());
            return image;
        }

They get called here:

        @Override
    public void actionPerformed(ActionEvent ae) {
        BufferedImage bi = dp.getScreenShot();
        dp.loadDrawing(bi);
    }

Here is the whole program, it should run.

      import java.awt.BorderLayout;
      import java.awt.Color;
      import java.awt.Graphics;
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import java.awt.event.MouseEvent;
      import java.awt.event.MouseListener;
      import java.awt.event.MouseMotionListener;
      import java.awt.image.BufferedImage;
      import javax.swing.ImageIcon;
      import javax.swing.JButton;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JOptionPane;
      import javax.swing.JPanel;

public class TestClass extends JFrame implements ActionListener {

    DrawingPanel dp;

    public TestClass() {
        setSize(400, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel top = new JPanel();
        JButton load = new JButton("Load Image");
        load.addActionListener(this);
        top.add(load);

        dp = new DrawingPanel();
        dp.setBackground(Color.CYAN);

        add(top, BorderLayout.NORTH);
        add(dp, BorderLayout.CENTER);
        setVisible(true);
    }

    public static void main(String[] args) {
        new TestClass();
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        BufferedImage bi = dp.getScreenShot();
        dp.loadDrawing(bi);
    }

    private class DrawingPanel extends JPanel implements 
      MouseListener, MouseMotionListener {

        private int xPos, yPos;//mouse positions

        private DrawingPanel() {
            addMouseListener(this);
            addMouseMotionListener(this);
        }

        @Override
        public void mousePressed(MouseEvent me) {
            xPos = me.getX();
            yPos = me.getY();
        }

        @Override
        public void mouseDragged(MouseEvent me) {
            int x = me.getX(), y = me.getY();
            Graphics g = getGraphics();
            g.setColor(Color.BLACK);
            g.drawOval(xPos, yPos, 30, 30);
            xPos = x;
            yPos = y;
        }

        public void loadDrawing(BufferedImage bi) {
            //opens a message dialog and displays the image parameter
            JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
            System.out.println("w:" + bi.getWidth() + " h:" + bi.getHeight());
        }

        public BufferedImage getScreenShot() {

            BufferedImage image = new BufferedImage(this.getWidth(),
                    this.getHeight(), BufferedImage.TYPE_INT_RGB);
            // call the Panels's paint method, using
            // the Graphics object of the image.
            this.paint(image.getGraphics());
            return image;
        }

        //unused abstract method
        @Override
        public void mouseClicked(MouseEvent me) {
        }

        @Override
        public void mouseReleased(MouseEvent me) {
        }

        @Override
        public void mouseEntered(MouseEvent me) {
        }

        @Override
        public void mouseExited(MouseEvent me) {
        }

        @Override
        public void mouseMoved(MouseEvent me) {
        }
    }
}

I need to be able to store the graphics from the panel and retrieve it.

Help would be heavily appreciated.

Was it helpful?

Solution

Every attempt to draw something into the Graphics object that was obtained from a Component by calling getGraphics will fail sooner or later. This Graphics object merely serves as a "path" to the actual screen (that is only valid while the component is actually painted on the screen). It is not a "buffer", and it does not "store" anything that was drawn.

If you want to create a simple painting program, you should draw to a BufferedImage. And when the DrawingPanel is to be painted, you only paint this BufferedImage. The additional advantage here is that when you want to make a screenshot, you basically just have to return a copy of this BufferedImage.

I sketeched the basic approach in your DrawingPanel class, with some in-lined comments. It could be cleaned up and beautified, and there are some aspects to consider (e.g. what should happen when the DrawingPanel is resized?), but it shows how it should work in general:

private class DrawingPanel extends JPanel implements 
    MouseListener, MouseMotionListener {

    // The image that will store what was drawn. In the
    // mouseDragged method, the painting operations will
    // go directly to this image. When this panel is
    // painted, then ONLY this image will be painted.
    private BufferedImage bufferedImage;

    private int xPos, yPos;//mouse positions

    private DrawingPanel() {
        addMouseListener(this);
        addMouseMotionListener(this);
    }

    // Make sure that the "bufferedImage" is non-null
    // and has the same size as this panel
    private void validateImage()
    {
        if (bufferedImage == null)
        {
            bufferedImage = new BufferedImage(
                getWidth(), getHeight(), 
                BufferedImage.TYPE_INT_ARGB);
            Graphics g = bufferedImage.getGraphics();
            g.setColor(getBackground());
            g.fillRect(0,0,getWidth(),getHeight());
            g.dispose();

        }
        if (bufferedImage.getWidth() != getWidth() ||
            bufferedImage.getHeight() != getHeight())
        {
            BufferedImage newBufferedImage = new BufferedImage(
                getWidth(), getHeight(), 
                BufferedImage.TYPE_INT_ARGB);
            Graphics g = newBufferedImage.getGraphics();
            g.setColor(getBackground());
            g.fillRect(0,0,getWidth(),getHeight());
            g.drawImage(bufferedImage, 0,0,null);
            g.dispose();
            bufferedImage = newBufferedImage;
        }
    }

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

        // Paint the bufferedImage which stores
        // what was drawn until now
        g.drawImage(bufferedImage, 0, 0, null);
    }


    @Override
    public void mousePressed(MouseEvent me) {
        xPos = me.getX();
        yPos = me.getY();
    }


    @Override
    public void mouseDragged(MouseEvent me) {
        int x = me.getX(), y = me.getY();
        validateImage();

        // Paint directly into the bufferedImage here
        Graphics g = bufferedImage.getGraphics();
        g.setColor(Color.BLACK);
        g.drawOval(xPos, yPos, 30, 30);
        repaint();
        xPos = x;
        yPos = y;
    }

    public void loadDrawing(BufferedImage bi) {
        //opens a message dialog and displays the image parameter
        JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
        System.out.println("w:" + bi.getWidth() + " h:" + bi.getHeight());
    }

    public BufferedImage getScreenShot() {

        // This basically returns a "copy" of the
        // bufferedImage that stores what was drawn
        BufferedImage image = new BufferedImage(
            getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        g.drawImage(bufferedImage, 0, 0, null);
        g.dispose();
        return image;
    }

    //unused abstract method
    @Override
    public void mouseClicked(MouseEvent me) {
    }

    @Override
    public void mouseReleased(MouseEvent me) {
    }

    @Override
    public void mouseEntered(MouseEvent me) {
    }

    @Override
    public void mouseExited(MouseEvent me) {
    }

    @Override
    public void mouseMoved(MouseEvent me) {
    }
}

OTHER TIPS

Your getting your Graphics object by calling getGraphics() on a component, and images drawn with this will not persist. Why not instead draw to a BufferedImage with its Graphics object, and then simply save that BufferedImage. This would simplify things greatly, and your program would work.

For example:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class TestClass2 extends JPanel {
   private DrawingPanel drawingPanel = new DrawingPanel();

   public TestClass2() {     
      JPanel northPanel = new JPanel();
      northPanel.add(new JButton(new GetImageAction("Get Image")));
      northPanel.add(new JButton(new ClearImageAction("Clear Image")));

      setLayout(new BorderLayout(5, 5));
      add(drawingPanel, BorderLayout.CENTER);
      add(northPanel, BorderLayout.NORTH);

   }

   private class GetImageAction extends AbstractAction {
      public GetImageAction(String name) {
         super(name);
         putValue(MNEMONIC_KEY, KeyEvent.VK_G);
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         BufferedImage img = drawingPanel.getMainImage();
         ImageIcon icon = new ImageIcon(img);
         JOptionPane.showMessageDialog(TestClass2.this, icon);
      }
   }

   private class ClearImageAction extends AbstractAction {
      public ClearImageAction(String name) {
         super(name);
         putValue(MNEMONIC_KEY, KeyEvent.VK_C);
      }

      @Override
      public void actionPerformed(ActionEvent evt) {
         drawingPanel.clearImage();
         drawingPanel.repaint();
      }
   }


   private static void createAndShowGui() {
      TestClass2 mainPanel = new TestClass2();

      JFrame frame = new JFrame("TestClass2");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

class DrawingPanel extends JPanel {
   public static final int BI_WIDTH = 400;
   public static final int BI_HEIGHT = BI_WIDTH;
   private static final Color BACKGROUND = Color.CYAN;
   public static final Color DRAW_COLOR = Color.black;
   public static final int OVAL_WIDTH = 30;
   private BufferedImage mainImage;

   public DrawingPanel() {
      MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
      addMouseListener(myMouseAdapter);
      addMouseMotionListener(myMouseAdapter);
      clearImage();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (mainImage != null) {
         g.drawImage(mainImage, 0, 0, this);
      }
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(BI_WIDTH, BI_HEIGHT);
   }

   public BufferedImage getMainImage() {
      return mainImage;
   }

   public void clearImage() {
      mainImage = new BufferedImage(BI_WIDTH, BI_HEIGHT, BufferedImage.TYPE_INT_ARGB);
      Graphics g = mainImage.getGraphics();
      g.setColor(BACKGROUND);
      g.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
      g.dispose();
   }

   private class MyMouseAdapter extends MouseAdapter {

      @Override
      public void mousePressed(MouseEvent mEvt) {
         draw(mEvt);
      }

      @Override
      public void mouseDragged(MouseEvent mEvt) {
         draw(mEvt);
      }

      private void draw(MouseEvent mEvt) {
         Graphics2D g2 = mainImage.createGraphics();
         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);
         g2.setColor(DRAW_COLOR);
         g2.drawOval(mEvt.getX() - OVAL_WIDTH / 2, mEvt.getY() - OVAL_WIDTH / 2, OVAL_WIDTH, OVAL_WIDTH);
         g2.dispose();
         repaint();
      }
   }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top