2
\$\begingroup\$

I'm trying to draw a bunch of translucent circles on a Swing JComponent. This isn't exactly fast, and I was wondering if there is a way to speed it up. My custom JComponent has the following paintComponent method:

public void paintComponent(Graphics g) {
    Rectangle view = g.getClipBounds();

    VolatileImage image = createVolatileImage(view.width, view.height);
    Graphics2D buffer = image.createGraphics();
    // translate to camera location
    buffer.translate(-cx, -cy);
    // renderables contains all currently visible objects
    for(Renderable r : renderables) {
        r.paint(buffer);
    }
    g.drawImage(image.getSnapshot(), view.x, view.y, this);
}

The paint method of my circles is as follows:

public void paint(Graphics2D graphics) {
    graphics.setPaint(paint);
    graphics.fillOval(x, y, radius, radius);
}

The paint is just an rgba color with a < 255:

Color(int r, int g, int b, int a)

It works fast enough for opaque objects, but is there a simple way to speed this up for translucent ones?

\$\endgroup\$
6
  • \$\begingroup\$ Why are you rendering the circles through a volatile image instead of directly to the Graphics object of the paintComponent method? \$\endgroup\$
    – msell
    Nov 2, 2013 at 18:24
  • \$\begingroup\$ @msell I would like to add a BufferedImageOp in the g.drawImage() method later on. According to this document, a VolatileImage should be fast (although I must admit I don't fully understand it). If there's another way to filter, I would be happy to try it. \$\endgroup\$
    – mdriesen
    Nov 2, 2013 at 20:14
  • \$\begingroup\$ I see two possible performance issues in the given code. 1) You are recreating the volatile image in every paintComponent and 2) You could draw the image directly instead of using image.getSnaposhot(). See the example in the beginning of VolatileImage documentation for how to deal with both of the problems. \$\endgroup\$
    – msell
    Nov 3, 2013 at 5:59
  • \$\begingroup\$ @msell Not recreating the image actually gave a noticeable speed boost. Thanks! I can't use your second suggestion though, as I need the following method for my filter: Graphics2D.drawImage(BufferedImage img, BufferedImageOp op, int x, int y), which doesn't work with VolatileImages. \$\endgroup\$
    – mdriesen
    Nov 3, 2013 at 10:03
  • \$\begingroup\$ Instead of using VolatileImage, you could try using createCompatibleImage, which returns a BufferedImage that might be accelerated. On the other hand the BufferedImageOp could be really slow and make other optimizations useless. What do you plan to do with the BufferedImageOp? What do you want to achieve with all of this rendering? \$\endgroup\$
    – msell
    Nov 3, 2013 at 10:49

2 Answers 2

1
\$\begingroup\$

The immediate potential performance problems I see in your code are:

  1. You are recreating the volatile image in every paintComponent
  2. Calling image.getSnapshot() to convert the VolatileImage to BufferedImage.

There is a good example in the beginning of VolatileImage documentation on how to deal with both of the problems.

In the comments you say that 2. is required so that you can later use BufferedImageOp to filter the rendered image. I think this is a mistake, because as far as I know, BufferedImageOp processes each individual pixel of the image on CPU. This operation alone is likely much slower than any problems regarding image creation or conversion.

I recommend you to take a look at AlphaComposite and the related tutorial. Alternatively depending on what exactly you want to achieve, you could just fill another semi-transparent rectangle on top of everything. With a TexturePaint you can probably create the noise filter you are looking for.

In addition instead of using VolatileImages, you could try GraphicsConfiguration.createCompatibleImage to create accelerated BufferedImages. Benchmark if this has the same performance as VolatileImage. If it does, it will be easier to manage than VolatileImage.

\$\endgroup\$
0
1
\$\begingroup\$

Pixel-pushing speeds are a known limitation of Java's out-of-box software renderer. Even with GPU hardware rendering, built for pushing pixels, overdraw / pixel blending / fill rates are known to have profound impacts on performance. Nevertheless, this is one of the reasons we have GPUs. If it's mission critical, I suggest a low-level library that provides access to native GPU functions, like LWJGL or JOGL, or else google "fast 2D graphics libraries Java".

P.S. Under the hood, millions of division and conditional operations have to take place every tick, in software, in order to support what you're trying to do, and all on a single thread / core (the main thread). GPUs handle this load through being massively parallel -- several tens to several hundreds of cores will be operating on large blocks of pixels simultaneously, reducing blending costs by orders of magnitude.

Slick2D may be a good option for you, as it is a 2D library that utilises LWJGL / OpenGL / your GPU under the hood.

\$\endgroup\$
5
  • \$\begingroup\$ Could painting a translucent texture possibly be any less resource-intensive than what the question asker is doing now? \$\endgroup\$ Nov 2, 2013 at 14:56
  • 1
    \$\begingroup\$ The only things which affect the performance, aside from system CPU, are (1) the number of discrete entities to be blended onto the background surface, (2) the size of those entities, and (3) the complexity of the logical and arithmetic operations applied as per the blend mode, to each individual pixel. In software, I can guarantee you the performance will differ by environment. In hardware, given that you are only performing 2D ops, performance isn't even going to be a concern. So from the point of view of mitigating risk on less powerful systems, GPU or a custom-built 2D library is best. \$\endgroup\$
    – Engineer
    Nov 2, 2013 at 15:03
  • \$\begingroup\$ "GPU or a custom-built 2D library is best." Could you clarify? Something like using shaders? \$\endgroup\$ Nov 2, 2013 at 15:12
  • \$\begingroup\$ @nick I had a look at LWJGL a while ago, and it seems very interesting. But I would first like to try plain java2d before going that way. \$\endgroup\$
    – mdriesen
    Nov 2, 2013 at 20:19
  • \$\begingroup\$ You'll find even fairly professional software-rendering libraries like PulpCore are going to have problems with fill rates. Slick2D wraps LWJGL, so this may be a viable option for you. See edit. \$\endgroup\$
    – Engineer
    Nov 3, 2013 at 21:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .