3

I have a small problem. In carrying out the method paintComponent() during the animation I have to constantly update the variable bgImage. But it takes a lot of time, so that the animation slows down.

A block of code with the problem:

public class ProblemClass extends JComponent {
    private static final int FRAME_FREQUENCY = 30;
    private final Timer animationTimer;

    public ProblemClass() {
        this.animationTimer = new Timer(1000 / FRAME_FREQUENCY, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                repaint(); // When the animation started is often invoked repaint()
            }
        });
    }

    // Other code...

    /** 
     * Start animation from another class
     */
    public void startAnimation() {
        this.animationTimer.start();
    }

    @Override 
    protected void paintComponent(Graphics g) { 
        // GraphicsUtils.gaussianBlur(...) it's a long-time operation
        BufferedImage bgImage = GraphicsUtils.gaussianBlur(AnotherClass.getBgImage()); 
        g2.drawImage(bgImage, 0, 0, null); 

        // Other code...
    }
}

I read on the Internet that I need run long task in parallel thread (SwingWorker), but I have no idea how to do it in my case. How can I solve this problem?

P.S. Sorry for my bad English, it's not my first language.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
osipxd
  • 1,218
  • 16
  • 23
  • Start with [the documentation](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html). It should b e very simple - just have a `SwingWorker`. Call `AnotherClass.getBgImage()` and pass the result to your `SwingWorker`. In the `done()` method call `get` and draw the image into the screen. Ensure that you have no interaction between `Swing` components and the `SwingWorker` as this will break things. – Boris the Spider Sep 17 '14 at 16:16
  • Will the result of `AnotherClass.getBgImage()` change between calls? – resueman Sep 17 '14 at 16:39
  • A complete example is shown [here](http://stackoverflow.com/a/25043676/230513). – trashgod Sep 17 '14 at 22:18
  • 1
    As an aside: `g2.drawImage(bgImage, 0, 0, null);` should be `g2.drawImage(bgImage, 0, 0, this);` given every `JComponent` is an `ImageObserver`.. – Andrew Thompson Sep 18 '14 at 00:28

3 Answers3

1

There is no generic way to solve this when you generate a new background image every time and then blur it. GaussianBlur is a slow operation, period.

If AnotherClass.getBgImage() delivers images from a predefined set of images, then apply the blur once to each image in the set, problem goes away.

If you create the image in AnotherClass.getBgImage() dynamically, then you may be able to speed it up by changing the image creation to create a blurred image from the start. Depending on what is done to create the image this may or may not be feasible.

If neither of the above works out, you need to investigate other options to produce the blurred image which are faster; there are simpler blurring methods that are generally faster but look somewhat similar to a gaussian.

You see it all boils down to getting rid of calling GaussianBlur repeatedly on the performance critical path.

Durandal
  • 19,919
  • 4
  • 36
  • 70
1

The best you're going to do is having the image update outside of the paint method, and only redraw whenever a new image is ready. Take your existing code, and add a persistent reference to the image, which gets drawn onto the JComponent each paint method. Then have your animation timer do the Gaussian blur and update your image.

public class ProblemClass extends JComponent {
    private static final int FRAME_FREQUENCY = 30;
    private final Timer animationTimer;

    //persistent reference to the image
    private BufferedImage bgImage;

    public ProblemClass() {
        this.animationTimer = new Timer(1000 / FRAME_FREQUENCY, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //Update the image on each call before repainting
                bgImage = GraphicsUtils.gaussianBlur(AnotherClass.getBgImage());
                repaint();
            }
        });
    }

    /** 
     * Start animation from another class
     */
    public void startAnimation() {
        this.animationTimer.start();
    }

    @Override
    protected void paintComponent(Graphics g2) {
        g2.drawImage(bgImage, 0, 0, null);

        // Other code...
    }
}
resueman
  • 10,572
  • 10
  • 31
  • 45
  • Thank you for this idea! I suffered a logic method call to inside the `Timer` and it gave a good effect. But, I had to optimize the method `gaussianBlur()`, but it's all the details. Problem solved, the customer is satisfied :) – osipxd Sep 18 '14 at 15:22
0

You should extract logic from painter. Painters are called constrantly and should be executed very fast.

  BufferedImage bgImage = GraphicsUtils.gaussianBlur(AnotherClass.getBgImage()); 

This line has to be executed every time? maybe you could use a field to store the image and the painter could just paitn the image, not applying each time a gaussianBlur.

Try this: public class ProblemClass extends JComponent {

    private static final int FRAME_FREQUENCY = 30;
    private final Timer animationTimer;
    private final BufferedImage bgImage;

    public ProblemClass() {
        bgImage = GraphicsUtils.gaussianBlur(AnotherClass.getBgImage());
        this.animationTimer = new Timer(1000 / FRAME_FREQUENCY, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                repaint(); // When the animation started is often invoked repaint()
            }
        });
    }

        // Other code...
    /** 
     * Start animation from another class
     */
    public void startAnimation() {
        this.animationTimer.start();
    }

    @Override
    protected void paintComponent(Graphics g2) {
        // GraphicsUtils.gaussianBlur(...) it's a long-time operation
        g2.drawImage(bgImage, 0, 0, null);

        // Other code...
    }
}
Ezequiel
  • 3,477
  • 1
  • 20
  • 28
  • Yes, I've tried to remove this line, and it works quickly, but there is no desired effect. Look at the [program](https://www.dropbox.com/s/0qhvm36oycd96g3/Test.jar?dl=0) and you'll understand why this line is needed. [VirusTotal](https://www.virustotal.com/ru/file/ba3d9716777828ca4d920e164090f748eef806a521f9a14d340e18cfdb065a7b/analysis/1410971424/) – osipxd Sep 17 '14 at 16:29