Search code examples
javaawtjava-2dgraphics2d

Improving Java Graphics2D Quality


So, I'm drawing 100 concentric circles. However, when I try and render such a large amount of concentric circles with Graphics2D it starts to look a bit blurry and just generally unattractive (see pic).

    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
    g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
    g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
    g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

I've done all this stuff, but this seems to be as good as it gets. Is there anything else I can do or is there another library that could get me a better result?

Example of poor rendering.

EDIT: This is all done by the fillOval method. Something like:

 for (int i = 0; i < 100; i++) {
  x = getX(i);
  y = getY(i);
  length = getLength(i);
  g2.fillOval(x,y,length,length);
 }

Solution

  • The speckles you're seeing are artifacts of antialiased drawing of multiple objects with close-together edges. Since you don't show what getLength(i) is doing, I'm not sure, but it looks like you have multiple circles with very close or identical radii but different colors.

    Try processing your data set (getLength(i)) so that you never draw a circle which is within some small threshold (say, 0.5 pixel) of being the same radius as the previous/next one. This will prevent the speckles from forming.

    If you absolutely need to include all the detail, then I suggest you switch to a “procedural graphics” approach. This means that you generate the image using your own code:

    For each (x, y) in the image bounds:
        Compute distance from center
        Find which circles the distance ± 1 pixel is touching
        Set pixel (x, y) to the weighted average of the colors of those circles based on how much of each circle fills the pixel
    

    This will produce a “perfect” image without over-draw artifacts. This is effectively reimplementing antialiased circle drawing, but taking into account all the circles simultaneously rather than one at a time.

    (Note that you can make this more efficient by computing the pixels for only 1/8 of the circle and mirroring/rotating it to cover the whole image.)