Search code examples
javadoublebuffereddouble-bufferingjwindow

Double buffering with a JWindow


I am trying to double buffer a transparent JWindow however it seems like the technique used takes no effect (different cycle values are drawn over each other).

public final class Overlay extends JWindow {

    public static final Color TRANSPARENT = new Color(0, true);
    public static Font standardFont = null;

    public static Overlay open() {
        return new Overlay();
    }

    private Overlay() {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setAlwaysOnTop(true);
        setBounds(0, 0, screenSize.width, screenSize.height);
        setBackground(TRANSPARENT);
    }

    @Override
    public void paint(Graphics g) {
        BufferedImage bufferedImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D g2d = bufferedImage.createGraphics();
        paintUs(g2d);

        Graphics2D g2dComponent = (Graphics2D) g;
        g2dComponent.drawImage(bufferedImage, null, 0, 0);
    }

    private void paintUs(Graphics2D g) {
        int height = 420;
        int x = 20;
        g.setColor(TRANSPARENT);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setFont(standardFont == null ? standardFont = g.getFont().deriveFont(17f) : standardFont);
        for (Plugin plugin : Abendigo.plugins()) {
            g.setColor(Abendigo.plugins().isEnabled(plugin) ? Color.GREEN : Color.RED);
            g.drawString(plugin.toString(), x + 5, getHeight() - height);
            height += 20;
        }
        height += 20;
        g.setColor(Color.YELLOW);
        g.drawString("Cycle: " + Abendigo.elapsed + "ms", x, getHeight() - height);
    }

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

}

Solution

  • Why!?!? Swing components are already double buffered? Simple create a custom component, extending from something like JPanel, override it's paintComponent and perform your custom painting there. Make sure you set the component to be transparent (setOpaque(false)) and add this to an instance of JWindow

    See Painting in AWT and Swing and Performing Custom Painting for more details.

    One of the immediate issues you are facing is the fact the Swing windows have a series of compound components already attached to them (JRootPane, contentPane, etc), all of which can be painted independently of you window, meaning that they can cover over what every you've been attempting to paint directly to the window. Instead, avoid painting directly to the window and use a custom component instead.