1

I got some CPU problems when painting a VolatileImage (created from a BufferedImage) to a JPanel. In my application, I'm reading image data from a webcam. These images have a resolution of 1920*1080 pixels and the logic which is reading from the webcam allows about 30 FPS. This happens in an own thread. In the loop which is reading from the webcam, I get a BufferedImage and draw it into a VolatileImage. Then I call the repaint method of the JPanel which draws the VolatileImage. The webcam output is smooth and fast. The problem is the CPU. It's 95%-100% CPU-usage.

I already tried to NOT repaint every webcam-frame but only every 30 milliseconds. The CPU usage is the same. If I do not repaint the JPanel (so there is no image shown), but only read the frames from the camera and store them in the bufferedImage, the CPU-usage stays low. So the drawing part is the CPU-consuming part. Not the reading of the frames from the webcam.

This is the paintComponent method of my JPanel (nothing special...)

public void paintComponent(Graphics g) {
    g.drawImage(cameraModel.volatileImage, 0, 0, null);
}

And this is the method which is requesting the frames from the webcam:

public void run() {
    running = true;
    while(running){
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        dartCamPanel.repaint();
        if (camera.read(frame)){
            ByteBuffer bb = frame.createBuffer();
            bb.get(byteBuffer, 0, bb.capacity());
            volatileContext.drawImage(bufferedImage, 0, 0, null);
        }
    }
}

Am I doing something completely wrong?

Zub
  • 808
  • 3
  • 12
  • 23
simon1389
  • 11
  • 1
  • 1
    So, several things jump to mind immditaly, first, you should be calling `super.paintComponent`, unless you're prepared to take over it's responsibilities. `this` should be passed as the `ImageObserver` to `drawImage`, probably not an issue, but it's good convention. You're risking a thread race condition between the thread creating the images and the component trying to paint them. I might consider using a `SwingWorker` instead to generate snapshots which can be safely painted (won't change while they are been painted). – MadProgrammer Dec 13 '17 at 21:01
  • 1
    You could try creating a [Compatible image](https://stackoverflow.com/questions/19486459/slow-java2d-drawing-on-integrated-graphics/19486532#19486532), which will generate an image with the same color model as the screen, this saves the API from having to do it while it's trying to paint the image – MadProgrammer Dec 13 '17 at 21:02
  • My guess is, there's an internal conversion of the image from it's original format to a suitable format for the screen, it could also be issues with the `VolatileImage`, as the API might discard some images if it detects that the contents has been lost in memory - but my experience with `VolatileImage` is limited – MadProgrammer Dec 13 '17 at 21:06
  • I tried using a SwingWorker , but the CPU-usage stays high. Is there a advantage in performance with the SwingWorker ? I thought it just makes some things easier to handle than it is with a Thread and Runnable ? The VolatileImage i'm drawing on the JPanel is created by the `createCompatibleVolatileImage` method – simon1389 Dec 13 '17 at 21:54
  • 1
    The advantage of `SwingWorker` is that data is delivered in the Event Dispatching Thread. My concern is, you're trying to update the image while the EDT is trying to paint it, which could be causing issues. I'd personally create "frames" and `publish` them, maybe establishing some kind of cache so you can re-use frames when they've been painted (maybe by having a single "safe" frame which you paint the other frames to within the `process` method, and only painting this "safe" frame. But since I don't have the means to replicate your issue, it's all just guess work – MadProgrammer Dec 13 '17 at 21:57

0 Answers0