Java:重复调用paintAll挂起程序

时间:2013-07-19 16:11:05

标签: java awt paint

我正在Java程序中实现视频捕获,方法是在用户定义的时间间隔(对于我的测试,100ms)创建BufferedImage,然后使用这些图像制作电影文件。我尝试记录的JFrame包含JLayeredPane包含的类似仪表板的界面。 JFrame也有两个Canvas3D。我告诉renderpaint这3件事中的每一件事都是他们自己的LinkedBlockingDeque<BufferedImage>,我稍后再将它们合并。仪表板设置为仅渲染每个dashboardFrameRepaintFrequency帧。

Thread captureThread = new Thread(new Runnable() {
    public void run() {
        final Object LOCK = new Object();
            final Thread captureDashboard = new Thread(new Runnable() {
                public void run() {
                    while (m_isCapturingMovie) {
                        synchronized (LOCK) {
                            try {
                                LOCK.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            } finally {
                                System.err.println("Calling getDashboardImage");
                                m_unsavedDash.add(getDashboardImage(m_dashboard.getWidth(), m_dashboard.getHeight(), BufferedImage.TYPE_INT_ARGB));
                                System.err.println("captureDashboard returned from calling m_unsavedDash.add...");
                            }
                        }
                    }
                }
            });
            captureDashboard.start();

            while (m_isCapturingMovie) {
                startTime = System.currentTimeMillis();
                captureCanvases();
                if (++frameCount > dashboardFrameRepaintFrequency) {
                    frameCount = 0;
                    synchronized (LOCK) {
                        LOCK.notify();
                    }
            }
            endTime = System.currentTimeMillis();
            millisToSleep = captureDelayInMillis - (endTime - startTime);
            if (millisToSleep > 0) {
                try {
                    Thread.sleep(millisToSleep);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        synchronized (captureDashboard) {
        captureDashboard.notify();
        }
    }
});

我发现在15-20 notify()之后,程序锁定了 - 它停止记录画布,停止响应键盘输入..我仍然可以改变哪个窗口有焦点,以及按钮(例如关闭窗口的X按钮)仍然会改变鼠标翻转或点击的视觉状态,但它们不会执行命令。

从控制台输出看来,在那些15-20次迭代之后,captureDashboard线程似乎没有从getDashboardImage方法返回:

private BufferedImage getDashboardImage(int width, int height, int type) {
    BufferedImage dashImg = new BufferedImage(m_dashboard.getWidth(), m_dashboard.getHeight(), type);
    Graphics2D g = dashImg.createGraphics();
    m_dashboard.paintAll(g);
    g.dispose();

    return getScaledImage(width, height, dashImg);
}

private BufferedImage getScaledImage(int width, int height, BufferedImage original) {
    BufferedImage scaled = new BufferedImage(width, height, original.getType());

    AffineTransform at = new AffineTransform();
    at.scale((double) scaled.getWidth() / original.getWidth(), (double) scaled.getHeight() / original.getHeight());
    AffineTransformOp scaleOp;
    if (at.getScaleX() + at.getScaleY() > 2.0) {
        scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC); // Better quality for enlargement
    } else {
        scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR); // Better quality for ensmallment
    }

    scaled = scaleOp.filter(original, scaled);
    original.flush();
    return scaled;
}

有什么想法吗?我已经在这工作了几天而且我很难过。

1 个答案:

答案 0 :(得分:1)

问题是我需要从AWT调度线程中调用paintAll

所以而不是:

m_dashboard.paintAll(g);

我需要:

final Graphics2D g = dashImg.createGraphics();

EventQueue.invokeLater(new Runnable() {
    public void run () {
        m_dashboard.paintAll(g);
    }
});

然而,这导致我的程序“超越自己”并在它被绘制之前返回BufferedImage,但仅在程序负载很重的情况下。为了解释这一点,我刚刚添加了以下内容:

final Graphics2D g = dashImg.createGraphics();
final SyncObj LOCK = new SyncObj();

EventQueue.invokeLater(new Runnable() {
    public void run () {
        m_dashboard.paintAll(g);
        LOCK.doNotify();
    }
});

LOCK.doWait();
g.dispose();

SyncObj只是一个简单的地方:

class SyncObj {
    private boolean condition = false;
    private Object obj = new Object();

    public void doWait() {
        synchronized (obj) {
            while (!condition) {
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            condition = false;
        }
    }

    public void doNotify() {
        synchronized (obj) {
            condition = true;
            obj.notify();
        }
    }
}