1

i have read about a lot of articles of drawing images, but i cant get those to work when i need to keep the background. I'm trying to rotate an image which is over another image after click on a JButton. The background image is generated on a JPanel by:

public void paintComponent(Graphics g){

    int index = 0;

    Graphics2D g2 = (Graphics2D) g;

    super.paintComponent(g);    

    try {
        image = ImageIO.read(url1);
        image2 = ImageIO.read(url2);
        image3 = ImageIO.read(url3);
    } catch (IOException e) {
    }


    g.drawImage(image, 0, 0, null);
    g.drawImage(image3, 0, 0, null);

    if(scaleDrawnFlag == 0){

        for(index = 0; index < 60; index ++){
            tx = AffineTransform.getRotateInstance(Math.toRadians(6*index), this.getHeight()/2, this.getWidth()/2);
            op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
            g.drawImage(op.filter(image3, null), 0, 0, null);
        }       

        scaleDrawnFlag = 1;
    }

    g.drawImage(image2, 0, 0, null);

}

Which is into a JPanel named panel and draw a image only one time to keep the refresh performance, only for the animated image. This draws a scale for a tachometer, by a total of 60 lines, where each line is a copy of image3

The animated image, is generated by pressing a JButton, and is made by:

public void paintComponent(Graphics g){

    super.paintComponent(g);        
    BufferedImage img = new BufferedImage(370, 370, BufferedImage.TRANSLUCENT);
    Graphics2D g2d = img.createGraphics();
    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
    g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.0f));
    Graphics2D temp = (Graphics2D) g;
    tx = AffineTransform.getRotateInstance(Math.toRadians(degrees), this.getHeight()/2, this.getWidth()/2);
    op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
    temp.drawImage(op.filter(image2, null), 0, 0, null);
    temp.dispose();
}

Which is into another JPanel named overPanel, which is over the initial JPanel.

But, when i call the methods:

public void up(){
    degrees ++;
    if(degrees == 360) degrees = 0;
    repaint();
}

public void down(){
    degrees --;
    if(degrees == -360) degrees = 0;
    repaint();
}

Which are on the overPanel class, the JPanel is entirely cleared. The animation is working well but, the Background disappear.

What must i do to keep the Background?

I also tried another solution, by drawing the 60 lines again on every up() and down() call. The background is repainted, but takes too much time to complete, so, the animation to rotate the tachometer's indicator lags.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
GTRONICK
  • 366
  • 4
  • 14

2 Answers2

3

Never dispose of a Graphics object given to you by the JVM. You're doing this:

// temp **is** the same object as g and is the Graphics object given by the JVM
Graphics2D temp = (Graphics2D) g; 
//....
temp.dispose();

and shouldn't do it since it completely breaks the painting chain. You should instead be disposing of the g2d object, one you created.

Also, this would be OK

Graphics2D temp = (Graphics2D) g.create(); // temp **is** a new object
//....
temp.dispose();  // this is OK

Other issues:

  • I also wouldn't be creating my BufferedImage inside of paintComponent but rather would make it a field of the class, and display it inside paintComponent.
  • Your top code shows ignored critical exceptions -- you don't want to do this.
  • It also shows reading in of image files within a painting method, something that will unnecessarily slow down graphics. Again, don't do this, read in the images once outside of any painting method, store the results, and use them in the painting method.
  • The paintComponent method is protected, not public. Avoid increasing its visibility unnecessarily.

In your minimal example program, your scaleDrawnFlag variable and its associated if-block appear to be messing you up. What is the purpose of that variable and the if block? If you get rid of the variable and the if block, your background would persist. Myself, I'd do things differently by creating a stable background image and drawing it every time in the paintComponent(...) method. I would not override update(...) either as that's an AWT kludge and not for Swing graphics. I also try to avoid null layouts and setBounds(...) like the plague since that leads to inflexible, rigid GUI's that are very difficult to debug, maintain and enhance. For example:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.*;

@SuppressWarnings("serial")
public class MyMainPanel extends JPanel {
   private MyDrawingPanel myDrawingPanel;

   public MyMainPanel() {
      try {
         myDrawingPanel = new MyDrawingPanel();
      } catch (IOException e) {
         e.printStackTrace();
         System.exit(-1);
      }

      JPanel rightPanel = new JPanel();
      rightPanel.setLayout(new GridLayout(0, 1, 5, 5));
      rightPanel.add(new JButton(new MyUpAction("Up", KeyEvent.VK_U)));
      rightPanel.add(new JButton(new MyDownAction("Down", KeyEvent.VK_D)));

      JPanel rightWrapPanel = new JPanel(new BorderLayout());
      rightWrapPanel.add(rightPanel, BorderLayout.PAGE_START);

      setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
      setLayout(new BorderLayout());
      add(myDrawingPanel, BorderLayout.CENTER);
      add(rightWrapPanel, BorderLayout.LINE_END);
   }

   private class MyUpAction extends AbstractAction {
      public MyUpAction(String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         myDrawingPanel.up();
      }
   }

   private class MyDownAction extends AbstractAction {
      public MyDownAction(String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      @Override
      public void actionPerformed(ActionEvent e) {
         myDrawingPanel.down();
      }
   }

   private static void createAndShowGui() {
      JFrame frame = new JFrame("MyMainPanel");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(new MyMainPanel());
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

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

@SuppressWarnings("serial")
class MyDrawingPanel extends JPanel {
   private static final String NEEDLE_IMG_PATH = "http://1.bp.blogspot.com/"
         + "-fq-oPGBSLp4/Ttoj7DoAMWI/AAAAAAAABtc/t7gKJlfRQuo/s400/secondHand.png";
   private static final String ORANGE_DISK_IMG_PATH = "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Circle_Fulvous_Solid.svg/200px-Circle_Fulvous_Solid.svg.png";
   private static final String GREEN_LINE_IMG_PATH = "http://www.xtremeskater.com/math/images/circle_radius.png";
   private static final int MAX_DEGREES = 360;

   private int imgWidth = 0;
   private int imgHeight = 0;
   private BufferedImage needleImg = null;
   private BufferedImage orangeDiskImg = null;
   private BufferedImage greenLineImg = null;
   private BufferedImage backgroundImg = null;
   private int degrees; 

   public MyDrawingPanel() throws IOException {
      URL needleUrl = new URL(NEEDLE_IMG_PATH);
      URL orangeDiskUrl = new URL(ORANGE_DISK_IMG_PATH);
      URL greenLineUrl = new URL(GREEN_LINE_IMG_PATH);
      needleImg = ImageIO.read(needleUrl);
      orangeDiskImg = ImageIO.read(orangeDiskUrl);
      greenLineImg = ImageIO.read(greenLineUrl);

      imgWidth = Math.max(orangeDiskImg.getWidth(), 
            greenLineImg.getWidth());
      imgHeight = Math.max(orangeDiskImg.getHeight(),
            greenLineImg.getHeight());
      backgroundImg = new BufferedImage(imgWidth, imgHeight,
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = backgroundImg.createGraphics();
      drawBackground(g2, imgWidth, imgHeight);
      g2.dispose();
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(imgWidth, imgHeight);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (backgroundImg != null) {
         g.drawImage(backgroundImg, 0, 0, null);
      }
      AffineTransform tx = AffineTransform.getRotateInstance(Math.toRadians(degrees),
            this.getHeight() / 2, this.getWidth() / 2);
      AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
      g.drawImage(op.filter(needleImg, null), 0, 0, null);
   }

   public void up() {
      degrees++;
      degrees %= MAX_DEGREES;
      repaint();
   }

   public void down() {
      degrees--;
      degrees += MAX_DEGREES;
      degrees %= MAX_DEGREES;
      repaint();
   }

   public int getDregrees() {
      return degrees;
   }

   private void drawBackground(Graphics2D g2, int biWidth, int biHeight) {
      int index;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.drawImage(orangeDiskImg, 0, 0, null);
      g2.drawImage(greenLineImg, 0, 0, null);
      AffineTransform tx = AffineTransform.getRotateInstance(Math.toRadians(6),
            biWidth / 2, biHeight / 2);
      for (index = 0; index < 60; index++) {
         g2.transform(tx);
         g2.drawImage(greenLineImg, 0, 0, null);
      }
   }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thanks for answer. I did the change to `g2d.dispose();`. Then, i moved the line `BufferedImage img = new BufferedImage(370, 370, BufferedImage.TRANSLUCENT);` to the top, making it a field for the class. However, i keep having the same behavior. Thanks for your suggestion. oh!, i did not see your edit, jejeje. Ok, i will do all you say, i'm learning a lot!. – GTRONICK Apr 14 '15 at 16:20
  • @GTRONICK: consider creating and posting a [minimal example program](http://stackoverflow.com/help/mcve) or [sscce](http://sscce.org) where you create a small simple GUI uses images from the internet and that shows us your problem. – Hovercraft Full Of Eels Apr 14 '15 at 16:24
  • I'm doing it, but i'm having problems with the URL from internet. i get the malformed url exception. I will keep trying. – GTRONICK Apr 14 '15 at 16:58
  • @GTRONICK: for example, check my answer [here](http://stackoverflow.com/a/8720123/522444) to see how to read in online images, or also [these answers](http://stackoverflow.com/search?tab=votes&q=user%3a522444%20%5bjava%5d%20ImageIO.read(imageUrl)). – Hovercraft Full Of Eels Apr 14 '15 at 17:03
  • Well, at last i have it: – GTRONICK Apr 14 '15 at 17:21
0

i have found the way to make the Background permanent, by first creating it into a Buffered image, then on each action, repaint that image, without redraw all the primitive forms or the image rotations. I mean, first, i create the Background rotating a basic image multiple times. This is created using a Buffered image. Then, on the paintComponent() method, i redraw the buffered image:

public MyPanel(){
    try {
        image = ImageIO.read(url1);
        image2 = ImageIO.read(url2);
        image3 = ImageIO.read(url3);

    } catch (IOException e) {
    }

    img = createImage();
}

private Image createImage(){

    double index = 0;

  BufferedImage bufferedImage = new BufferedImage(370,370,BufferedImage.TYPE_INT_ARGB);

  Graphics g = bufferedImage.getGraphics();

  for(index = 0; index <= scale; index = index + count){

      tx = AffineTransform.getRotateInstance(Math.toRadians(deg2), 185, 185);
      op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
      g.drawImage(op.filter(image3, null), 0, 0, null);
      deg2 = deg2 + (270.0/(scale/count));
      }

      return bufferedImage;
   }

protected void paintComponent(Graphics g){

    super.paintComponent(g);

    g.drawImage(image, 0, 0, null);     //Indicator drawing
    g.drawImage(img, 0, 0, null);       //Scale drawing


    //Indicator rotation

    tx = AffineTransform.getRotateInstance(Math.toRadians(degrees), this.getHeight()/2, this.getWidth()/2);
    op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
    g.drawImage(op.filter(image2, null), 0, 0, null);   
}

Here the complete code for a basic example:

package rotateOnImage;

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Image;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import javax.swing.AbstractAction;

import java.awt.event.ActionEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

import javax.swing.Action;

public class MainFrame extends JFrame {

/**
 * 
 */
private static final long serialVersionUID = 1L;
private JPanel contentPane;
private MyPanel panel = new MyPanel();
private final Action upAction = new upAction();
private final Action dnAction = new dnAction();

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                MainFrame frame = new MainFrame();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the frame.
 */
public MainFrame() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 345, 227);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    contentPane.setLayout(null);

    JButton btnNewButton = new JButton("UP");
    btnNewButton.setAction(upAction);
    btnNewButton.setBounds(212, 12, 117, 25);
    contentPane.add(btnNewButton);

    JButton button = new JButton("DN");
    button.setAction(dnAction);
    button.setBounds(212, 49, 117, 25);
    contentPane.add(button);

    panel.setBounds(0, 0, 200, 200);

    contentPane.add(panel);
}
private class upAction extends AbstractAction {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public upAction() {
        putValue(NAME, "UP");

    }
    public void actionPerformed(ActionEvent e) {

        panel.up();
    }
}
private class dnAction extends AbstractAction {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public dnAction() {
        putValue(NAME, "DN");

    }
    public void actionPerformed(ActionEvent e) {

        panel.down();
    }
}
}

class MyPanel extends JPanel{

private static final long serialVersionUID = 1L;
/**
 * 
 */

private int degrees  = 0;
private AffineTransform tx = null;
private AffineTransformOp op = null;

private BufferedImage image1 = null;
private BufferedImage image2 = null;
private BufferedImage image3 = null;

Image img = null;

public static final String IMAGE_PATH1 = "http://1.bp.blogspot.com/"+"-fq-oPGBSLp4/Ttoj7DoAMWI/AAAAAAAABtc/t7gKJlfRQuo/s400/secondHand.png";
public static final String IMAGE_PATH2 = "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Circle_Fulvous_Solid.svg/200px-Circle_Fulvous_Solid.svg.png";
public static final String IMAGE_PATH3 ="http://www.xtremeskater.com/math/images/circle_radius.png";

public MyPanel(){

    try {
        URL url1 = new URL(IMAGE_PATH1);
        URL url2 = new URL(IMAGE_PATH2);            
        URL url3 = new URL(IMAGE_PATH3);
        image1 = ImageIO.read(url1);
        image2 = ImageIO.read(url2);
        image3 = ImageIO.read(url3);
    } catch (IOException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

    img = createImage();
    }

public void up(){
    degrees ++;
    if(degrees == 360) degrees = 0;
    repaint();
}

public void down(){
    degrees --;
    if(degrees == -360) degrees = 0;
    repaint();
}

public int getDregrees(){
    return degrees;
}

private Image createImage(){

    double index = 0.0;

    BufferedImage bufferedImage = new BufferedImage(200,200,BufferedImage.TYPE_INT_ARGB);

    Graphics g = bufferedImage.getGraphics();

    for(index = 0.0; index <= 36; index ++){

      tx = AffineTransform.getRotateInstance(Math.toRadians(index*10), 100, 100);
      op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
      g.drawImage(op.filter(image3, null), 0, 0, null);
  }

      return bufferedImage;
}

public void update(Graphics g){
    paint(g);
}

protected void paintComponent(Graphics g){


    super.paintComponent(g);    

    g.drawImage(image2, 0, 0, null);        //Dibujado de la manecilla
    g.drawImage(img, 0, 0, null);       //Dibujado de la escala 


    //Rotación de la manecilla

    tx = AffineTransform.getRotateInstance(Math.toRadians(degrees), this.getHeight()/2, this.getWidth()/2);
    op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
    g.drawImage(op.filter(image1, null), 0, 0, null);
}
}
GTRONICK
  • 366
  • 4
  • 14