Search code examples
locationjlabeldouble-buffering

Position of JLabel in Graphics when double buffering


I am using a thread to draw some animations, so I need to repaint the label for every frame. To do this without flickering I am updating the label with my backbuffer graphics object (using the lbl.update(bufferedGraphics); method), but when I do this, the label gets repainted in the top left of the Graphics object, and not where setLocation has specified.

How do I specify the location of the label within the graphics, instead of within the panel that owns the label?

Here is an SSCCE:

import javax.swing.*;
import java.awt.*;

public class LabelSSCCE extends JApplet implements Runnable {
JPanel pnl;
JLabel lbl;
Image buffer;
Graphics bufferedGraphics;
Thread t;

public void init (){
    pnl = new JPanel (null);
    lbl = new JLabel ();

    lbl.setText ("Text");
    lbl.setOpaque(true);

    add(pnl);
    pnl.add (lbl);
    lbl.setLocation(100, 100);
    lbl.setBounds (100, 100, 200, 20);

    buffer = createImage (500, 500);
    bufferedGraphics = buffer.getGraphics ();

    t = new Thread (this, "Label");
    t.start ();
}// init method

public void paint (Graphics g){
    if (g != null)
        g.drawImage (buffer, 0, 0, this);
}//paint

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

public void render (){
    bufferedGraphics.setColor (Color.WHITE);
    bufferedGraphics.fillRect (0, 0, 500, 500);
    lbl.update (bufferedGraphics);

    update(getGraphics());
}//render

public void run (){
    while (true){
        try{
            render ();
            t.sleep (20);
        } catch (InterruptedException e){
            e.printStackTrace ();
        }//catch
    }//while
}//run
}//LabelSSCCE

Solution

  • First, convert the JLabel to a BufferedImage:

        public BufferedImage componentToImage(Component component)
    {
        BufferedImage img = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
        Graphics g = img.getGraphics();
        g.setColor(component.getForeground());
        g.setFont(component.getFont());
        component.paintAll(g);
        Rectangle region = new Rectangle(0, 0, img.getWidth(), img.getHeight());
        return img.getSubimage(region.x, region.y, region.width, region.height);
    }
    

    Then, change the render method to something like this:

    public void render() {
        bufferedGraphics.setColor(Color.WHITE);
        bufferedGraphics.fillRect(0, 0, 500, 500);      
        BufferedImage bi = componentToImage(lbl);
        bufferedGraphics.drawImage(bi, lbl.getX(), lbl.getY(), null);
    
        update(getGraphics());
    }