简体   繁体   English

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

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

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(). 我正在绘制一个大图像并使用drawImage()缩小它。 I then immediately draw another image with drawImage() that I expect to be drawn on top of the previous one (second). 然后我立即用drawImage()绘制另一个图像,我期望在前一个(第二个)之上绘制。 The problem is that drawImage returns immediately, even if it takes ~50 ms to scale and render the first image. 问题是drawImage会立即返回,即使缩放和渲染第一个图像需要大约50毫秒。 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()阻止直到它完成,或以某种方式检查它何时完成?

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. 我知道ImageObserver参数,从Internet或其他东西下载图像时工作正常,但是当使用已经加载的BufferedImage时,它只是在缩放和绘制之后才会触发ImageUpdate()。 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,它只需要约50毫秒的自己的线程来处理,从不通知它何时完成。

Does anyone know how to either force it to block or wait until it's completely done scaling and blitting an image? 有谁知道如何强迫它阻止或等到它完全完成缩放和blitting图像? And obviously using Thread.sleep(xx) is a complete hack and not viable due to differences between computer speeds. 显然使用Thread.sleep(xx)是一个完整的黑客,由于计算机速度之间的差异而不可行。 All this rendering is happened on the Event thread inside the paint(Graphics g) method. 所有这些渲染都发生在paint(Graphics g)方法中的Event线程上。

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. 编辑2:为了更好地解释我正在谈论的问题,我制作了一些示例代码,这些代码做得更好,我正在尝试解释。 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. 只需将文件路径更改为小图像和大图像。

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

The last argument to drawImage (where you pass null) is an ImageObserver . drawImage的最后一个参数(传递null的地方)是一个ImageObserver If you provide your own implementation of that interface ( see JavaDoc ), you will be informed when the image has been actually drawn. 如果您提供自己的接口实现( 请参阅JavaDoc ),则会在实际绘制图像时通知您。

It is impossible to know when Swing will actually render the contents of a Graphics object to the screen. 无法知道Swing何时实际将Graphics对象的内容呈现给屏幕。 What is know is it won't happen until AFTER the paint methods return (as the Graphics object hasn't been finalised for rendering until it does). 知道的是,直到paint方法返回之后才会发生这种情况(因为Graphics对象在渲染之前尚未完成渲染)。

What you should do is let the Component you are painting to make the decision as to when something needs to updated, this is the way it was designed...( Component implements ImageObserver ) 你应该做的是让你正在绘制的Component决定什么时候需要更新,这是它的设计方式...( Component实现ImageObserver

The below example continuously re-scales the master background image as the frame is resized 以下示例在调整帧大小时不断重新缩放主背景图像

在此输入图像描述

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 Applet in favor of JApplet and in fact, I would personally avoid applets or together. 虽然我确定你有自己的理由,但我个人会避免Applet支持JApplet ,事实上,我个人会避免使用applet。 I started my career coding applets, they are simply a pain, especially when all you're trying to do is test an idea. 我开始编写applet的职业生涯,他们只是一种痛苦,特别是当你想要做的就是测试一个想法时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM