Java Window Translucency Animation Flicker on Linux

不羁的心 提交于 2019-12-08 02:09:16

问题


I'm trying to do animation in Java with a translucent JFrame. I have modified the demo code from the Oracle Java Tutorials here. Specifically the GradientTranslucentWindowDemo.

The following code works great in Windows XP SP3 up to 8 and Mac OS X Mountain Lion and even in Linux mostly. The problem in Linux, and what I need help with, is the animation is flickering.

I'm running Ubuntu Linux 12.04 LTS 64bit with nVidia drivers, Metacity and Compiz. PERPIXEL_TRANSLUCENT reports true and is working well.

Is there something I'm missing in the following code or is there something on the Linux side I need to change? I tried setDoubleBuffered(true) on the JPanel, but it did not remove the flickering.

Please reference my code changes to the Demo below:

import static java.awt.GraphicsDevice.WindowTranslucency.PERPIXEL_TRANSLUCENT;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.Paint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class GradientTranslucentWindowDemo extends JFrame implements ActionListener {
    private Timer timer = new Timer(100, this);

    private double percentage = 0.0;

    private JPanel surface = new JPanel() {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (g instanceof Graphics2D) {

                final int R = 0;
                final int G = 240;
                final int B = 240;

                Paint p =
                    new GradientPaint(0.0f, 0.0f, new Color(R, G, B, 0),
                        0.0f, getHeight(), new Color(R, G, B, 255), false);
                Graphics2D g2d = (Graphics2D)g;
                // CHANGE 1
                // Clear the previous graphics using a completely transparent fill
                g2d.setBackground(new Color(0, 0, 0, 0));
                g2d.clearRect(0, 0, getWidth(), getHeight());
                g2d.setPaint(p);
                // CHANGE 2
                // Only do a gradient fill for the current percentage of the width
                g2d.fillRect(0, 0, (int)Math.ceil(getWidth() * percentage), getHeight());
            }
        }
    };

    public GradientTranslucentWindowDemo() {
        super("GradientTranslucentWindow");

        setBackground(new Color(0,0,0,0));
        setSize(new Dimension(300,200));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // CHANGE 3
        // I thought this might remove the flicker, nope
        this.surface.setDoubleBuffered(true);

        // CHANGE 4
        // This seems to be required or the g2d.clearRect doesn't do anything
        this.surface.setOpaque(false);
        setContentPane(this.surface);
        setLayout(new GridBagLayout());
        add(new JButton("I am a Button"));
    }

    // CHANGE 5
    // On each tick of the timer increment the percentage until its
    // more than one and always repaint
    @Override
    public void actionPerformed(ActionEvent event) {
        this.percentage += 0.05;
        if (this.percentage > 1.0) {
            this.percentage = 0.0;
        }
        this.surface.repaint();
    }

    public static void main(String[] args) {
        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        boolean isPerPixelTranslucencySupported = 
            gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);

        //If translucent windows aren't supported, exit.
        if (!isPerPixelTranslucencySupported) {
            System.out.println(
                "Per-pixel translucency is not supported");
                System.exit(0);
        }

        JFrame.setDefaultLookAndFeelDecorated(true);

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                GradientTranslucentWindowDemo gtw = new GradientTranslucentWindowDemo();

                // Display the window.
                gtw.setVisible(true);

                // CHANGE 6
                // Wait until the window is visible to start the timer
                gtw.timer.start();
            }
        });
    }
}

UPDATE 1: Removing the translucency and choosing a black background fixes the flicker problem. The flicker is most certainly related to having a translucent window. I also noticed that the flicker becomes worse as the window is enlarged.

UPDATE 2: The line this.surface.setOpaque(false); is what is causing the problem. If this is commented out, the animation does not flicker and there is translucency. However, upon each iteration of the animation it blends with the previous paint (its not clearing the contents before repainting). Doing g2d.setBackground(new Color(0, 0, 0, 0)); and g2d.clearRect(0, 0, getWidth(), getHeight()); does nothing unless this.surface.setOpaque(false); is set. It's almost like this line disables double buffering on linux.

Having a translucent window is a requirement.

来源:https://stackoverflow.com/questions/15704947/java-window-translucency-animation-flicker-on-linux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!