【问题标题】:Drawing a rounded rectangle with opacity on a BufferedImage在 BufferedImage 上绘制一个不透明的圆角矩形
【发布时间】:2016-12-04 12:21:53
【问题描述】:

我一直在尝试为我正在开发的一个小游戏实现基本的文本气泡。不想太花哨,我从一个带有包含一些文本的边框的基本圆角矩形开始:

然后,我决定文字气泡应该在预设时间后淡出。这就是我偶然发现一个问题的地方:当我尝试在测试窗口中显示气泡时,一切正常,但是当我在游戏中显示它们时,当气泡消失时会出现失真。我又测试了一些,调试了,发现这两种情况的唯一区别是在测试窗口中我使用paintComponent方法的Graphics绘制了气泡,而在游戏中我使用BufferedImages来模拟图层并使用image.createGraphics中的图形。然后我可以成功复制错误:

在这里,您可以看到,当左侧的气泡消失时,其圆角与消失前相比会发生变化,而右侧圆角的气泡不会发生变化。实际上,左边的气泡是在 BufferedImage 上绘制的,然后在面板上绘制,而右边的气泡直接在面板上绘制。
我已经隔离了重现问题所需的代码:

public static void main(String[] args) {

    JFrame frame = new JFrame("Test");
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setSize(400, 400);

    JPanel panel = new JPanel() {

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

            BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics graphics = image.createGraphics();

            paintExampleBubble(graphics, 50, 50);

            g.drawImage(image, 0, 0, this);

            paintExampleBubble(g, 250, 50);
        }
    };

    frame.getContentPane().add(panel);
    frame.setVisible(true);
}

private static final Color background = new Color(1f, 1f, 1f, 0.5f);
private static final Color foreground = new Color(0f, 0f, 0f, 0.5f);
private static final int borderRadius = 16;
private static final int width = 100;
private static final int height = 50;

private static void paintExampleBubble(Graphics g, int x, int y) {

    g.setColor(background);
    g.fillRoundRect(x, y, width, height, borderRadius, borderRadius);
    g.setColor(foreground);
    g.drawRoundRect(x, y, width, height, borderRadius, borderRadius);
}

这是上面代码产生的结果:

无论如何,这表明绘制到 BufferedImage 是导致问题的原因,但目前不能选择放弃 BufferedImage。

我尝试调试代码以查看可能导致这种差异的原因,并且只注意到图形对象在涉及透明度时使用不同的组件进行绘制,但这并不能帮助我解决我的问题,即使它可以强制图形执行我希望它们执行的操作,如果可能,我宁愿避免黑客攻击。

有谁知道解决这个问题的相对简单有效的方法,或者解决它?

无论如何,感谢您花时间阅读本文:)

PS:由于这是我第一次提出问题,我可能错过了一些东西,所以请随时告诉我是否是这种情况!不胜感激。

编辑:正如我在 cmets 中所说,游戏是基于像素艺术的,因此我宁愿不使用抗锯齿,而是保持圆角矩形的基本像素化外观。

【问题讨论】:

  • 不完全确定我是否正确地看到了你的描述,但尝试打开抗锯齿g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) 其中gGraphics2D 的一个实例
  • 感谢您的快速回答!我现在已经尝试过了,它确实有效(例如,当透明度开始时,边框不再改变)。但是,我没有提到这一点,我正在开发的游戏只有像素艺术图形和自动抗锯齿功能太突出了,而且由于分辨率低,它看起来确实很模糊。我会更新我的问题以澄清
  • 并没有真正遵循您正在尝试做的事情,但这里有一个帖子可能(或可能不会)为您提供不同的使用方法:stackoverflow.com/questions/15025092/…
  • @camickr 我正在开发一款游戏,并试图实现相对简单的淡出文本气泡。我已经看到了您链接的问题,它不符合我需求的原因是我不希望气泡成为实际的 Swing 组件,尽管这绝对是一种有趣的方法。我已经有了一个可行的解决方案,我也将其标记为已接受的答案。

标签: java swing transparency paintcomponent


【解决方案1】:

在这里,您可以看到,当左侧的气泡消失时,其圆角与消失前相比会发生变化,而右侧圆角的气泡不会发生变化。事实上,左边的气泡是在一个 BufferedImage 上绘制的,然后再在面板上绘制,而右边的气泡是直接在面板上绘制的。

与其每次都使用不同的 alpha 值重新绘制图像,不如创建一次并使用 AlphaComposite 管理透明度。

下面是您的示例的改编版,带有三个“气泡”:最左边是每次更改前景色时绘制图像,右边两个使用AlphaComposite(中间使用创建一次的图像,最右边直接使用JPanel 图形)。

public class Test {

    public static void main(String[] args) {

        JFrame frame = new JFrame("Test");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setSize(600, 200);
        final BufferedImage image = new BufferedImage(600, 200, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = image.createGraphics();
        paintExampleBubble(graphics, 250, 50, foreground);
        graphics.dispose();
        final JPanel panel = new JPanel() {

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D)g;

                final BufferedImage i2 = new BufferedImage(600, 200, BufferedImage.TYPE_INT_ARGB);
                Graphics2D graphics = i2.createGraphics();
                paintExampleBubble(graphics, 50, 50, alphaForeground);
                graphics.dispose();
                g.drawImage(i2, 0, 0, this);
                //use Alpha Composite for transparency
                Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER , alpha );
                g2d.setComposite(comp);
                g2d.drawImage(image, 0, 0, this);

                paintExampleBubble(g2d, 450, 50, foreground);
            }
        };
        javax.swing.Timer timer = new javax.swing.Timer(100, new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent e) {
                alpha -= 0.05;

                if ( alpha < 0 ){
                    alpha = 1.0f;
                }
                alphaForeground = new Color(0f, 0f, 0f, alpha);
                panel.repaint();
            }

        });
        timer.start();
        frame.getContentPane().add(panel);
        frame.setVisible(true);
    }

    private static float alpha = 1.0f;
    private static final Color background = new Color(1f, 1f, 1f, 1f);
    private static final Color foreground = new Color(0f, 0f, 0f, 1f);
    private static Color alphaForeground = new Color(0f, 0f, 0f, alpha);
    private static final int borderRadius = 16;
    private static final int width = 100;
    private static final int height = 50;

    private static void paintExampleBubble(Graphics g, int x, int y, Color color) {
        g.setColor(background);
        g.fillRoundRect(x, y, width, height, borderRadius, borderRadius);
        g.setColor(color);
        g.drawRoundRect(x, y, width, height, borderRadius, borderRadius);
    }
}

在我的系统上,我看到最左边的失真(使用前景色管理透明度)但没有使用 AlphaComposite 透明度

【讨论】:

  • 谢谢!我已经设法将您的答案移植到我的具体案例(实际上与我发布的代码有很大不同)并使其工作。但是,它会为每个文本气泡实例化一个 BufferedImage,这可能会导致显示大量气泡的性能问题,但目前这不是问题,我会将您的答案标记为已接受,直到有人提出不涉及额外 BufferedImages 的解决方案。
  • 您可以为所有文本气泡使用单个中间缓冲区。您可以每次都清除它,以完全不透明的方式绘制气泡并在组件上进行 alpha 合成。然后下一个气泡可以使用相同的中间缓冲区
  • 您能详细说明一下吗?我不确定我明白你的意思。我猜是静态 BufferedImage 之类的,它是绘制文本气泡的场景大小,然后在屏幕上使用 alpha 绘制图像,然后清除,然后重复下一个气泡。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-25
  • 1970-01-01
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多