Java 如何判断Graphics.drawImage()何时实际完成
这是我第一次在这里提问,我希望我能得到一个对我有帮助的答案或想法 我正在绘制一个大图像,并使用drawImage()将其缩小。然后,我立即用drawImage()绘制另一幅图像,我希望它是在前一幅(第二幅)的基础上绘制的。问题是drawImage会立即返回,即使缩放和渲染第一幅图像需要约50毫秒。大多数情况下,第二个图像最终位于第一个图像的下方,因为它是在第一个大图像正在处理时首先绘制的。基本上,是强制drawImage()阻塞直到它完成,还是以某种方式检查它何时完成 我知道ImageObserver参数,它在从Internet或其他地方下载图像时可以正常工作,但在使用已加载的BuffereImage时,它不会在缩放和绘制之后触发ImageUpdate()。基本上,因为第一个图像已经“加载”,它从不联系ImageObserver,它只需要在自己的线程中处理大约50毫秒,不会在完成时通知 是否有人知道如何强制它阻止或等待,直到它完全完成缩放和光点图像?显然,使用Thread.sleep(xx)是一种完全的黑客行为,由于计算机速度的不同,它是不可行的。所有这些渲染都发生在paint(Graphics g)方法内部的事件线程上 谢谢 编辑:以下是我目前必须向您介绍的代码:Java 如何判断Graphics.drawImage()何时实际完成,java,image,awt,java-2d,Java,Image,Awt,Java 2d,这是我第一次在这里提问,我希望我能得到一个对我有帮助的答案或想法 我正在绘制一个大图像,并使用drawImage()将其缩小。然后,我立即用drawImage()绘制另一幅图像,我希望它是在前一幅(第二幅)的基础上绘制的。问题是drawImage会立即返回,即使缩放和渲染第一幅图像需要约50毫秒。大多数情况下,第二个图像最终位于第一个图像的下方,因为它是在第一个大图像正在处理时首先绘制的。基本上,是强制drawImage()阻塞直到它完成,还是以某种方式检查它何时完成 我知道ImageObser
public void paint(Graphics window)
{
window.setColor(Color.WHITE);
window.fillRect(0, 0, Settings.width * Settings.aaFactor, Settings.height * Settings.aaFactor);
Graphics2D window2D = (Graphics2D) window;
window2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
window2D.drawImage(this.image, 0, 0, Settings.width, Settings.height, null);
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
window2D.drawImage(this.image2, 0, 0, null);
repaint();
}
编辑2:为了更好地解释我所说的问题,我制作了一些示例代码,这些代码更好地解释了我要解释的内容。运行它,你会看到它闪烁的地方,有时第一个图像是在底部(就像它应该是),但大多数时候它将是一个顶部(第二),这是错误的。只需将文件路径更改为小图像和大图像
public class Main extends Applet implements ImageObserver
{
private BufferedImage imageA;
private BufferedImage imageB;
@Override
public void init()
{
try
{
this.imageA = ImageIO.read(new File("C:\\LargeImage.jpg"));
this.imageB = ImageIO.read(new File("C:\\SmallImage.jpg"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void update(Graphics g)
{
paint(g);
}
@Override
public void paint(Graphics g)
{
Graphics2D w = (Graphics2D) g;
w.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
w.drawImage(this.imageA, 0, 0, 50, 50, this);// This takes a while to do (scaling down and drawing)...
w.drawImage(this.imageB, 10, 10, null);// While this is drawn quickly, before A is done.
repaint();
}
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height)
{
System.out.println("ImageObserver fired, done drawing image. NEVER CALLED!");
return false;
}
}
drawImage
(传递null)的最后一个参数是ImageObserver
。如果您提供该接口()的自己实现,则在实际绘制图像时将通知您。不可能知道Swing何时实际将图形对象的内容呈现到屏幕上。我们所知道的是,在绘制
方法返回之前,它不会发生(因为图形
对象在返回之前尚未完成渲染)
您应该做的是让正在绘制的组件
决定什么时候需要更新,这就是它的设计方式…(组件
实现图像观察者
)
下面的示例在调整帧大小时连续重新缩放主背景图像
public class TestPaint03 {
public static void main(String[] args) {
new TestPaint03();
}
public TestPaint03() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PaintPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintPane extends JPanel {
private BufferedImage background;
private BufferedImage foreground;
private Image scaled;
public PaintPane() {
try {
background = ImageIO.read(new File("/path/to/background/image));
foreground = ImageIO.read(new File("path/to/foreground/image"));
} catch (Exception e) {
}
}
@Override
public void invalidate() {
scaled = null;
super.invalidate();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
if (scaled == null) {
int size = Math.min(getWidth(), getHeight());
scaled = background.getScaledInstance(-1, size, Image.SCALE_SMOOTH);
}
int x = (getWidth() - scaled.getWidth(this)) / 2;
int y = (getHeight() - scaled.getHeight(this)) / 2;
g.drawImage(scaled, x, y, this);
x = (getWidth() - foreground.getWidth()) / 2;
y = (getHeight() - foreground.getHeight()) / 2;
g.drawImage(foreground, x, y, this);
}
}
}
}
虽然我相信你有你的理由,但我个人会避免使用Applet
,而选择使用JApplet
,事实上,我个人会避免使用Applet或两者一起使用。我开始了编写小程序的职业生涯,它们只是一种痛苦,尤其是当你只想测试一个想法的时候。我做过这件事,但那不起作用。正如我在第二段中所解释的,我已经实现了ImageObserver和imageUpdate()方法,并且实现了System.out.println(“Fired”);如果有人叫过,但从来没有人叫过。我将final drawImage()参数从null改为this。但是如果图像已经预计算并准备好绘制,drawImage将返回true(并且不会通知ImageObserver)。你查过了吗?drawImage是否真的立即返回真值而不实际绘制图像?这正是问题所在。它确实返回true(因为它在read-in中被“加载”并被转换),但是它仍然会立即返回,即使它需要大约100毫秒的时间来缩小它的大小并进行blit。到那时,第二个小图像已经被不整洁地(错误地)显示。imageUpdate()从未被调用。在这里,我添加了第二个可以复制/粘贴的示例,它将演示这个问题。在第二个示例中,您在绘制方法的末尾调用repaint(),从而触发立即重新绘制,从而引发无休止的重新绘制循环。闪烁使我无法看到哪个图像是先画的。如果删除重绘调用,则首先缩放并绘制第一个图像,然后再在其顶部绘制小图像(如预期的那样)。第一次调用drawImage大约需要130毫秒,第二次调用需要0.06毫秒。在Windows上使用OracleJDK1.7.0_07进行测试。我无法确定Java何时会实际渲染图形对象的输出。您可以确定的一件事是,直到paint方法返回后才会发生,因此Thread.sleep除了降低程序的速度之外什么都不做。如果要通过某种组件进行绘制,请将其作为ImageObserver传递给drawInage方法,并让它在准备好后进行自我更新。另一种选择是在后台线程public class Main extensed Applet..
中缩放图像,因为此Applet使用的图像不在本地磁盘上,因此该线程在此处不起作用。人们运行小程序也比运行应用程序困难。我建议你发布一个基于框架的图片,或者是图片的热链接,或者是用代码生成图片。