java 如何判断 Graphics.drawImage() 何时实际完成

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14532757/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 16:34:08  来源:igfitidea点击:

How to tell when Graphics.drawImage() has actually completed

javaimageawtjava-2d

提问by Jacob

This is my first time asking a question on here, and I'm hoping I get an answer or an idea that will help me.

这是我第一次在这里提问,我希望我得到一个答案或一个想法对我有帮助。

I am drawing a large image and scaling it down with drawImage(). I then immediately draw another image with drawImage() that I expect to be drawn on top of the previous one (second). The problem is that drawImage returns immediately, even if it takes ~50 ms to scale and render the first image. Most of the time the second image ends up underneath the first one because it's painted first while the big first one is being processed. Basically is there anyway to force drawImage() to block until it's done, or to somehow check when it has completed?

我正在绘制一个大图像并使用 drawImage() 将其缩小。然后我立即使用 drawImage() 绘制另一个图像,我希望将其绘制在前一个(第二个)之上。问题是 drawImage 会立即返回,即使缩放和渲染第一个图像需要大约 50 毫秒。大多数情况下,第二张图像最终会出现在第一张图像的下方,因为它是在处理第一张大图像时先绘制的。基本上有没有办法强制 drawImage() 阻塞直到它完成,或者以某种方式检查它何时完成?

I'm aware of the ImageObserver parameter, which works fine when downloading an image from the Internet or something, but when using an already-loaded BufferedImage, it never fires ImageUpdate() just after scaling and drawing. Basically since the first image is already "loaded" it never contacts the ImageObserver, it just takes like ~50 ms in it's own thread to process, never notifying when it completes.

我知道 ImageObserver 参数,它在从 Internet 或其他东西下载图像时工作正常,但是当使用已经加载的 BufferedImage 时,它​​永远不会在缩放和绘图后立即触发 ImageUpdate()。基本上,由于第一个图像已经“加载”,它永远不会联系 ImageObserver,它只需要大约 50 毫秒在它自己的线程中进行处理,从不通知它何时完成。

Does anyone know how to either force it to block or wait until it's completely done scaling and blitting an image? And obviously using Thread.sleep(xx) is a complete hack and not viable due to differences between computer speeds. All this rendering is happened on the Event thread inside the paint(Graphics g) method.

有谁知道如何强制它阻止或等到它完全完成缩放和位图传输?很明显,使用 Thread.sleep(xx) 是一个完全的黑客,由于计算机速度之间的差异而不可行。所有这些渲染都发生在paint(Graphics g) 方法内的事件线程上。

Thanks!

谢谢!

EDIT: The following is code I currently have to give you an idea of the issue:

编辑:以下是我目前必须让您了解问题的代码:

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();
}

EDIT 2: To better explain the problem I'm talking about, I made some sample code that does better what I'm trying to explain. Run it and you will see it flickering where sometimes the first image is on bottom (like it's supposed to be), but most of the time it will be one top (second), which is wrong. Just change the File paths to a small image and a large image.

编辑 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;
    }
}

回答by jarnbjo

The last argument to drawImage(where you pass null) is an ImageObserver. If you provide your own implementation of that interface (see JavaDoc), you will be informed when the image has been actually drawn.

drawImage(传递 null 的地方)的最后一个参数是一个ImageObserver. 如果您提供该接口的您自己的实现(请参阅 JavaDoc),您将在实际绘制图像时收到通知。

回答by MadProgrammer

It is impossible to know when Swing will actually render the contents of a Graphicsobject to the screen. What is know is it won't happen until AFTER the paintmethods return (as the Graphicsobject hasn't been finalised for rendering until it does).

不可能知道 Swing 何时真正将Graphics对象的内容呈现到屏幕上。所知道的是,直到paint方法返回之后它才会发生(因为Graphics对象在它返回之前还没有完成渲染)。

What you should do is let the Componentyou are painting to make the decision as to when something needs to updated, this is the way it was designed...(Componentimplements ImageObserver)

你应该做的是让Component你正在绘制的东西决定什么时候需要更新,这就是它的设计方式......(Component实现ImageObserver

The below example continuously re-scales the master background image as the frame is resized

下面的示例在调整框架大小时连续重新缩放主背景图像

enter image description here

在此处输入图片说明

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);

            }
        }
    }
}

While I'm sure you have your reasons, personally I would avoid Appletin favor of JAppletand in fact, I would personally avoid applets or together. I started my career coding applets, they are simply a pain, especially when all you're trying to do is test an idea.

虽然我相信你有你的理由,但我个人会避免Applet赞成,JApplet事实上,我个人会避免小程序或一起。我的职业生涯开始于编写小程序,它们只是一种痛苦,尤其是当您试图做的只是测试一个想法时。