【问题标题】:Java painting to BufferedImage or ImageIO.write() do not block and png file produced is either blank of half-bakedJava 绘制到 BufferedImage 或 ImageIO.write() 不会阻塞,并且生成的 png 文件是半生不熟的空白
【发布时间】:2014-10-04 19:11:13
【问题描述】:

我正在使用在 Linux CentOS 上的 Tomcat 7 上运行的 servlet 中创建带有简单黑白线条图的小型 png 文件,并使用帧缓冲区 Xvfb 来提供图形资源。然后我的目标是在服务器返回的 html 页面上显示这个 png 文件。我发现偶尔(比如 10 张图片中的一张)显示为空白,有时甚至显示为半画。我检查了服务器上相应的 .png 文件,它们确实在这些情况下生成为空白或半画。

这就是我的代码的样子(更复杂的是,ChimePro 是专有类库的一部分,我没有它的代码;它只是扩展了 Panel 或 Canvas):

Frame f = new Frame();
ChimePro cp = new ChimePro();
f.add(cp);
f.addNotify();
cp.setBounds(0,0,200,200);

BufferedImage bi = new BufferedImage (200,200,BufferedImage.TYPE_BYTE_INDEXED); 
Graphics g = bi.createGraphics()
cp.paint(g);
File ffjj = new File("file.png");


ImageIO.write(bi, "png", ffjj);

因此看起来好像paint(g) 或者可能不太可能的ImageIO.write() 方法没有像它们应该做的那样阻塞,并且即使尚未创建正确的文件,程序也会继续前进。 有趣的是,取决于我在 Tomcat 启动之前如何指向 DISPLAY 变量——无论是指向在同一服务器上模拟 X 环境的 Xvfb 屏幕,还是指向在远程机器上运行的一些真实 X 显示器——空白或损坏图片的概率会发生变化.

关于如何处理此问题以确保我的用户不会看到空白或损坏的图纸有什么想法吗? 我实际上是在写少量的短文件,所以这与速度无关,只是为了避免显示错误绘制的图片。 将不胜感激任何建议。

这是一个例外(仅在某些图纸中任意发生 - 不取决于图纸的内容,但可能取决于某些镀锡):

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerExceptio
at com.mdli.chime.ChimePro.paint(ChimePro.java)
at java.awt.Canvas.update(Canvas.java:142)
at sun.awt.RepaintArea.updateComponent(RepaintArea.java:255)
at sun.awt.X11.XRepaintArea.updateComponent(XRepaintArea.java:60)
at sun.awt.RepaintArea.paint(RepaintArea.java:232)
at sun.awt.X11.XComponentPeer.handleEvent(XComponentPeer.java:591)
at java.awt.Component.dispatchEventImpl(Component.java:4937)

【问题讨论】:

  • 有很多事情可能会出错。如果框架没有实现(附加到本地对等点),它将无法正确绘制并导致错误。由于容器尚未经过验证(基本上已布局),因此它可能无法正确显示。您应该使用 printAll 而不是paint,因为 printAll 不是双缓冲的,除此之外。您还应该在尝试保存之前调用 g.dispose()。 ImageIO.read/write 也保证阻塞
  • 您将花费大量精力来制作简单的黑白线条图。不需要frame或者panel,直接画到BufferedImage即可
  • 我想我仍然需要 ChimePro 实例。我会尝试在没有框架的情况下做到这一点。但是即使没有 Frame,我仍然需要参考一些 X 窗口——比如 Xvfb,或者远程机器;对吗?
  • 我不知道 ChimePro 在做什么。为了使用 printAll,您不需要将组件附加到本机对等节点,这是它的好处之一,但您需要确保组件具有大小并正确布局其内容...跨度>
  • 所以使用 printAll 我不需要以任何形式或形式对 X11 进行任何引用吗?抱歉,我不太熟悉这个本地同行术语,所以我再次询问。

标签: java servlets png gif headless


【解决方案1】:

在 ImageIO.write 之前有 g.dispose() 看看是否有帮助:

Frame f = new Frame();
ChimePro cp = new ChimePro();
f.add(cp);
f.addNotify();
cp.setBounds(0,0,200,200);

BufferedImage bi = new BufferedImage (200,200,BufferedImage.TYPE_BYTE_INDEXED); 
Graphics g = bi.createGraphics()
cp.paint(g);
File ffjj = new File("file.png");
g.dispose();

ImageIO.write(bi, "png", ffjj);

【讨论】:

  • 不会有很大的不同
  • 非常感谢。我只能在晚上/夜间进行测试,所以我会尝试在明天告诉您是否有效。
  • MadProgramer,我知道您也建议执行 g.dispose(),但是您在这里写下它不会有太大的不同?
【解决方案2】:

你需要确保你...

  1. 设置组件的大小
  2. 如果需要,布局容器的子组件
  3. 使用printAll 而不是print,因为它会尝试确保内容被完全绘制并且不会双重缓冲过程(使其更快)
  4. 完成后,Graphics 上下文中的dispose

以下显示了在框架上显示和使用示例代码打印时的面板...

import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class PrintComponent {

    public static void main(String[] args) {
        new PrintComponent();
    }

    public PrintComponent() {

        Example example = new Example();
        example.setSize(example.getPreferredSize());
        example.doLayout();
        BufferedImage bi = new BufferedImage (example.getWidth(), example.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
        Graphics2D g2d = bi.createGraphics();
        example.printAll(g2d);
        g2d.dispose();

        try {
            ImageIO.write(bi, "png", new File("D:/PrintExample.png"));
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public class Example extends JPanel {

        public Example() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(new JLabel("Hello"), gbc);
            add(new JTextField("'ello", 20), gbc);
            add(new JButton("g'day"), gbc);
        }

    }

}

【讨论】:

  • 谢谢,今晚我会全部尝试。与此同时,我在一个独立的应用程序中尝试了这些更改(我可以用图片在文件中绘制,但还没有用空白文件重新创建问题),如果不添加我的 ChimePro 组件,它仍然对我不起作用到框架。 printAll() 和 g.dispose() 当然可以正常工作,但我需要在 Tomcat servlet 中运行它以确保看不到这些空白图片。
  • 好吧,不幸的是没有任何帮助。当我用 printAll() 替换 paint() 时,我得到的只是黑屏,所有图片中都没有其他内容(在正常情况下,它们通常是在白色背景上绘制为黑线的分子图片)。处理图形对象似乎没有任何区别。仔细查看 Tomcat 日志,我实际上在 ChimePro 绘画中发现了 nullpointer 异常:
  • 好吧,不幸的是没有任何帮助。当我用 printAll() 替换 paint() 时,我得到的只是黑屏,所有图片中都没有其他内容(在正常情况下,它们通常是在白色背景上绘制为黑线的分子图片)。处理图形对象似乎没有任何区别。仔细查看 Tomcat 日志,我实际上发现了 ChimePro 绘画中的空指针异常及其与 X11 的交互。也许这就是导致空白屏幕的原因。奇怪的是,我用了很多年同一个组件,老版本的Java/Tomcat居然还行。
  • 如何写更多的扩展评论?作为参与者,我是 stackoverflow 的新手:直到现在才阅读。
  • 好的。您可以尝试将ChimePro 添加到JFrame 并调用pack,这会将其附加到本机对等方,然后尝试在ChimePro 的实例上调用printAll - 但这取决于导致@ 的原因987654333@
猜你喜欢
  • 2018-01-21
  • 2015-07-08
  • 1970-01-01
  • 1970-01-01
  • 2012-03-19
  • 2023-03-04
  • 2012-04-29
  • 1970-01-01
  • 2016-07-06
相关资源
最近更新 更多