Skip to main content

Java2D anti alias rounded corner fidelity problems

14 replies [Last post]
mikaelgrev
Offline
Joined: 2006-09-27
Points: 0

I'm writing some nice looking components, or I'm trying to at least.. Is there any way to affect the anti aliasing "weight" on normal draws, like lines and the rounded corners of a RoundRectangle2D?

They are to heavy in general. You can see this for instance in the roundness of the buttons in the Numbus L&F:

http://galbraiths.org/blog/wp-content/uploads/2007/03/controls_regular.png

Look at the third "Close" button from the left in the top row. The top corners are too thick. This happens only for darker colors though.

While we are on the subject of gfx and Nimbus. Run the webstart app and look at the buttons. You'll see the problem with differently sized corners of RoundeRectangles that I've mentioned several times. Check the top four buttons in the "Color Chooser" tab and you'll see what I mean (the corners have different radius).

http://javadesktop.org/swinglabs/demos/nimbus/nimbus.jnlp

I hope these glitches gets fixed soon as it is hard to create high fidelity stuff that looks as posh as Apples' stuff when the above problems are present.

Btw, this was tested on Windows with Java 1.6.0_01 with the normal pipeline. And I do off course know about PURE strokes, fractional metrics and all those kind of rendering hints...

Cheers,
Mikael Grev

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Chris Campbell

On Jun 29, 2007, at 3:56 AM, java2d@javadesktop.org wrote:
> I'm writing some nice looking components, or I'm trying to at
> least.. Is there any way to affect the anti aliasing "weight" on
> normal draws, like lines and the rounded corners of a
> RoundRectangle2D?
>

As far as I know, there is no way to tweak the way our (current)
antialiasing rasterizer produces coverage values.

> They are to heavy in general. You can see this for instance in the
> roundness of the buttons in the Numbus L&F:
>
> http://galbraiths.org/blog/wp-content/uploads/2007/03/
> controls_regular.png
>
> Look at the third "Close" button from the left in the top row. The
> top corners are too thick. This happens only for darker colors though.
>

Not a very good example, considering that image comes from the Nimbus
spec, which was produced entirely in Photoshop :)

If you show us a simple, pure Java 2D testcase that illustrates your
concerns, we might be able to offer more assistance.

>
> While we are on the subject of gfx and Nimbus. Run the webstart app
> and look at the buttons. You'll see the problem with differently
> sized corners of RoundeRectangles that I've mentioned several
> times. Check the top four buttons in the "Color Chooser" tab and
> you'll see what I mean (the corners have different radius).
>
> http://javadesktop.org/swinglabs/demos/nimbus/nimbus.jnlp
>
>
> I hope these glitches gets fixed soon as it is hard to create high
> fidelity stuff that looks as posh as Apples' stuff when the above
> problems are present.
>

That is an older demo; there is plenty more ongoing work with
Nimbus. If you have specific complaints about Nimbus itself, I
suggest you bring them up on the Nimbus mailing list.

Chris

> Btw, this was tested on Windows with Java 1.6.0_01 with the normal
> pipeline. And I do off course know about PURE strokes, fractional
> metrics and all those kind of rendering hints...
> Cheers,
> Mikael Grev
> [Message sent by forum member 'mikaelgrev' (mikaelgrev)]
>
> http://forums.java.net/jive/thread.jspa?messageID=224661
>
> ======================================================================
> =====
> To unsubscribe, send email to listserv@java.sun.com and include in
> the body
> of the message "signoff JAVA2D-INTEREST". For general help, send
> email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

mikaelgrev
Offline
Joined: 2006-09-27
Points: 0

Hi Chris,

I'll leave the AA thickness as explained in the reply to Kirill above.

What bothers me a tad more is the flat circles that still haunts us. :) I think this is what is the problem with the Webstart app as well. It's solvable by using non-even sizes only though..

I'll try to post a screen shot of the problem here (and I have checked this in Photoshop, there're round..) :

Cheers,
Mikael

Jim Graham

Hi Mikael,

Have you tried using the STROKE_PURE hint?

...jim

java2d@javadesktop.org wrote:
> Hi Chris,
>
> I'll leave the AA thickness as explained in the reply to Kirill above.
>
> What bothers me a tad more is the flat circles that still haunts us. :) I think this is what is the problem with the Webstart app as well. It's solvable by using non-even sizes only though..
>
> I'll try to post a screen shot of the problem here:
>

>
>

> Cheers,
> Mikael
> [Message sent by forum member 'mikaelgrev' (mikaelgrev)]
>
> http://forums.java.net/jive/thread.jspa?messageID=224733
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

mikaelgrev
Offline
Joined: 2006-09-27
Points: 0

Yes, sure have. That solves the roundness but brings on a whole slew of other visual problems (not bugs), especially with RoundedRectangles. Like simple lines being two pixels wide, in gray.

Cheers,
Mikael

Jim Graham

The double wide gray lines are due to the fact that lines straddle the
border between pixels and so extend halfway into each pixel on either side.

Adding .5 to the coordinates should help with that...

...jim

java2d@javadesktop.org wrote:
> Yes, sure have. That solves the roundness but brings on a whole slew of other visual problems (not bugs), especially with RoundedRectangles. Like simple lines being two pixels wide, in gray.
>
> Cheers,
> Mikael
> [Message sent by forum member 'mikaelgrev' (mikaelgrev)]
>
> http://forums.java.net/jive/thread.jspa?messageID=224739
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

mikaelgrev
Offline
Joined: 2006-09-27
Points: 0

Yes, I know.

Given any exact case I can solve that using those kind of tricks. The problem is that I'm creating a generic shape painting framework and I don't want a lot of "if this, then that" if you know what I mean. For instance I should only use the pure if aa is on. Which Shape is painted, what rendering hints are used, what Composite are to be used are all configurable by the user of our API. That's why I need stuff to just work and not fiddle around with specific workarounds for specific problems...

I actually don't understand. As I see it the normal aa routine draws non-round circles when they should be round. That must be a bug. Why not just fix it?

Jim Graham

java2d@javadesktop.org wrote:
> I actually don't understand. As I see it the normal aa routine draws non-round circles when they should be round. That must be a bug. Why not just fix it?
> [Message sent by forum member 'mikaelgrev' (mikaelgrev)]

While I can imagine this is frustrating, let me point out why things
behave the way that they do.

The circles are off because we have to massage the paths to meet some
basic rasterization expectations. STROKE_PURE turns off that massaging
so that you can manage the problems yourself if you so wish.

If you use PURE, then as you've discovered the lines become fuzzy unless
you adjust them. As you've already pointed out, the adjustment can get
tricky when dealing with arbitrary paths.

If we were to stop massaging the paths, then we'd have fuzzy lines like
you see with STROKE_PURE, but the circles would get rounder.

We could stop massaging them and then also bias all paths and then you
can choose whether you want fuzzy lines or fuzzy rectangles or both. No
bias can have both sharp lines and sharp rectangles.

The massaging we apply under STROKE_NORMALIZE does manage to get basic
primitives like lines and rectangles to both be crisp, but it does so
with a more complicated adjustment than a simple bias that,
unfortunately, disturbs the aesthetics of some circles.

We can fix our massaging to work better, but such an algorithm that both
maintains the crispness of lines and rectangles and also preserves
nicely rounded curves has yet to be created. You've already noted that
some sizes of circles are both crisp and round with AA and NORMALIZE - I
imagine that the other sizes have a basic mathematical conflict between
trying to look crisp and trying to be round that might not be solvable.
And, if there is a normalization technique that might make circles
both round and crisp at all sizes, it might not work for the case of a
quarter circle connecting to a horizontal and a vertical line on either
side of it...all TBD...

Until then "fixing this bug" would mean creating some other condition
that would appear to be a bug to someone else. Is a flat circle a worse
bug than a fuzzy horizontal or vertical line?

Hopefully with the magic of OpenJDK someone out there might be able to
come up with a better system...

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

mikaelgrev
Offline
Joined: 2006-09-27
Points: 0

Thanks for the explanation.

I don't know what to say. You go to great length explaining why circles aren't round and why the corner radius on rounded rectangles are different on the different sides, and why you will not fix it. I guess other platforms (Flash, .NET and similar) also have these problems since they aren't fixable?

It turns out that it is too hard to do Java2D graphics in a high fidelity way, at least without resorting to hacks.

Another problem I have is that one can not in Java2D in a generic way outline and fill a Shape and make it look good. The outline will plot outside and inside the shape depending on the direction of the vector (in degrees). This is since you don't provide an INSIDE and OUTSIDE stroke. You have the hanging right/left stroke and the pure stroke.

The pure stroke isn't usable since you either get the lines sharing two pixels for vertical and horizontal lines or you do as you suggest and translate 0.5, in which case you get good lines but shapes that are one pixel too big.

One CAN solve the one-pixel-too-big-shapes-when-using-PURE-and-translate.5-problem by scaling the output with ((width - 1.0)/width) but that only works for rectangles (shapes with equal but mirrored quadrants actually, so also for circles) but fails for general paths, polygons and composite shapes of all kinds.

This problem has shown to unsolvable in a generic way. Only if one has prior knowledge about what type of shape to paint and are in full control of the rendering hints can one get good results for some types of shapes. You can not create an API that takes a Shape, a fill and stroke color and do a decent job painting those at some coordinates.

If you (as in Sun) are sincere in your efforts to take on Flash and Silverlight these kinds of rendering issues must be solved IMO. Given your stance as explained above I guess they won't be until it's too late though. :(

Cheers,
Mikael

Jim Graham

Hi Mikael,

java2d@javadesktop.org wrote:
> I don't know what to say. You go to great length explaining why circles aren't round and why the corner radius on rounded rectangles are different on the different sides, and why you will not fix it. I guess other platforms (Flash, .NET and similar) also have these problems since they aren't fixable?

Actually, I wasn't sure - I just know that the rendering models that we
use are fairly industry standard and there are well-known techniques for
working around them, some can be implemented as heuristics within the
rendering engine (along with a way to turn them off since they aren't
always right) and we've done that, and others have to be implemented as
"care in how one constructs rendering requests".

I know that we've exercised about as much "magic" as we know how to
exercise internally here in the implementation in the heuristics we
created for path normalization, but I believe there could be more
"magic", it just isn't something we have sitting in our back pocket and
it can't be summoned up on command like a "bug fix" normally can. This
isn't a case of "whoops, we forgot to increment that variable" or "we
should probably keep more bits of resolution here", this is a limitation
of how advanced a set of heuristics we know how to create.

And, like I said, I wasn't totally sure about Flash - but I went and
downloaded it and played around with it for a few hours and discovered
that, basically, they use a rendering model that is fairly close to what
we've implemented - very close to the industry standard. If you render
small circles with Flash you get fuzzy outlines on the circles. If you
enable their form of "stroke hinting" then you get lopsided un-round
(but still fuzzy) circles. At least in our case I believe the circles
tend to be crisp (or at least our heuristics try to keep them crisp)
even though they are lop-sided. The "hinted" circles I saw with Flash
were neither crisp nor were they round.

I then tried a few more experiments and found that many of the issues
that people complain about with respect to Java2D rendering are exactly
the behaviors that Flash produces (and they have some problems that we
don't as well).

I haven't done the same experiment with .NET, but in either case - if
you have output from some other rendering engine that does what you want
then please share a real-world example, rather than "guessing" about the
other platforms.

> It turns out that it is too hard to do Java2D graphics in a high fidelity way, at least without resorting to hacks.

And are you sure that it is possible with other systems? I'd like to
see some examples.

> Another problem I have is that one can not in Java2D in a generic way outline and fill a Shape and make it look good. The outline will plot outside and inside the shape depending on the direction of the vector (in degrees). This is since you don't provide an INSIDE and OUTSIDE stroke. You have the hanging right/left stroke and the pure stroke.

Flash provides a very similar rendering model. When you draw 1 pixel
wide strokes at 1:1 scales you get the same "hanging pen" strokes that
we render.

> The pure stroke isn't usable since you either get the lines sharing two pixels for vertical and horizontal lines or you do as you suggest and translate 0.5, in which case you get good lines but shapes that are one pixel too big.

You get the "one pixel too big" problem with Flash too. The stroke
follows the outline and straddles the path - therefore the stroked
version will always be "linewidth" larger than the filled stroke.

> One CAN solve the one-pixel-too-big-shapes-when-using-PURE-and-translate.5-problem by scaling the output with ((width - 1.0)/width) but that only works for rectangles (shapes with equal but mirrored quadrants actually, so also for circles) but fails for general paths, polygons and composite shapes of all kinds.

Scaling the output isn't a very general solution and can be tricky to
get right with oblong shapes - even if they are convex - as you say.

Other techniques for handling stroking involve things like:

- stroke then fill
(even widths == symmetric border)
(odd widths and STROKE_NORMAL == uneven border)
(odd widths and PURE == fuzzy symmetric border in AA)
- fill then stroke
(misalignment of stroke at odd sizes is hidden
behind the stroke)
- use linewidth=2x and stroke then fill
(gives a 1 pixel "outer/halo stroke",
but needs a fill to carve out the stroke)
- use linewidth=2x and compute [strokeShape SUB Shape]
(gives a 1 pixel "outer stroke shape")
- use linewidth=2x and intersect strokeShape and Shape
(gives a 1 pixel "inner stroke shape")

> This problem has shown to unsolvable in a generic way. Only if one has prior knowledge about what type of shape to paint and are in full control of the rendering hints can one get good results for some types of shapes. You can not create an API that takes a Shape, a fill and stroke color and do a decent job painting those at some coordinates.

This is exactly why it is hard to come up with a heuristic that we can
implement for you. It seems that you understand the complexity of the
problem, but somehow perceive that what is "unsolvable" for you is "just
a bug fix" for us. I'm sorry to say that it isn't true. When you use
the PURE stroke hint you are getting the mathematically ideal rendering
and if it doesn't hit the pixels you want then change the geometry you
are using. You'd have to do this with most rendering systems -
professional or free - that I am familiar with.

Problems like these have appeared with tools like Illustrator for a long
time and the community has built up "strategies" and "techniques" for
creating artwork that agrees with the rendering models. The "hinting"
heuristics help in some common cases so that most programmers/designers
don't have to get involved in these strategies, but eventually everyone
outgrows the heuristics and needs more control.

> If you (as in Sun) are sincere in your efforts to take on Flash and Silverlight these kinds of rendering issues must be solved IMO. Given your stance as explained above I guess they won't be until it's too late though. :(

We are sincere. You seem to feel that we lag Flash, but it seems to use
the same rendering model that we do so I'm not sure how we are lagging
there...? Do you have specific issues and examples?

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

mikaelgrev
Offline
Joined: 2006-09-27
Points: 0

Thanks for you great answer Jim!

My frustration comes from creating a middle layer API (a Component structure with advanced shape construction and painting). When I create end user things I haven't generally got any problems with these kind of things, as I can compensate on an issue for issue basis. It's just when I try to guarantee a certain quality of the visuals of my API that I do get these problems.

As you rightfully point out your implementation isn't flawed or buggy when looking at the details. Yet, looking at the specification above the details (the API contract), a circle should be round and a drawn box should be the same size as a filled box. At least IMO. One could say that we are both right, but on different levels.

Would it be possible to have a new STROKE_INSIDE (and possibly STROKE_OUTSIDE)? This is how you can specify the stroke in for instance Photoshop. That would solve everything in a good way.

How much work would it be to implement that kind of stroke? Does OpenGL and DirectDraw have these kind of strokes or would you have to punt to the Java software rendering pipeline if you implemented such a stroke?

Also, would it be much trouble to expose a setting of contrast for anti aliased draws just like you do for LCD text? That way it would be possible to tweak hi fidelity graphics creation in a quite important way (again, IMO).

Note that my intention here is just to get as good rendering with Java2D as possible. I think Java2D is a tremendous API and as I've heard better than any of the competing APIs out there. That doesn't mean that one should try to make it even better since the other APIs are doing their best to catch up! :)

Cheers,

Ken Warner

According to the documentation on:
http://java.sun.com/javase/6/docs/api/java/awt/image/BufferStrategy.html

One is supposed to
do {
Graphics graphics = strategy.getDrawGraphics();
//render here
graphics.dispose();
} while (strategy.contentsRestored());

So I did that where I render:
do {
do {
bg = (Graphics2D)bs.getDrawGraphics();
mis.newPixels(pixels, cm, 0, thisW);
bg.drawImage(canvasImage, 0, 0, thisW, thisH, this);
drawAttribution();
bg.dispose();
} while (bs.contentsRestored());
bs.show();
} while (bs.contentsLost());

So I'm disposing of bg -- the BufferStrategy graphics after I render.

The question is -- how come I can still use the buffer graphics (bg)
at other times to draw stuff. My applet draws onto the BufferStrategy (bs)
in many other places at various times. And it works and all the while
I have dispose()'ed of the buffergraphics (bg) and the loop above is the only
place I getDrawGraphics();

For example -- here's my paint() method -- no problem -- no drawGraphics.

public void paint(Graphics g)
{
//System.err.println("paint()");
bg.drawImage(canvasImage, 0, 0, thisW, thisH, this);
if(DRAWATT)
drawAttribution();
bs.show();
}

I don't understand that...

Ken

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Dmitri Trembovetski

Hi Ken,

first of all, rendering to the same graphics context from
multiple thread is not a good idea. Java2D does not
guarantee thread safety, and doing so could
lead to unpredictable results (and even crashes due
to bugs in our code). So, please don't do that.

As to your question why it "works" - you just got
lucky and the paint on EDT executed while another
thread was in between the getDrawGraphics() and dispose().

After the dispose() all rendering operations to a
graphics context become no-ops, so by definition you
would only see something rendered the paint event
happened to be while the graphics was still valid.

Thank you,
Dmitri
Java2D Team

Ken Warner wrote:
> According to the documentation on:
> http://java.sun.com/javase/6/docs/api/java/awt/image/BufferStrategy.html
>
> One is supposed to
> do {
> Graphics graphics = strategy.getDrawGraphics();
> //render here
> graphics.dispose();
> } while (strategy.contentsRestored());
>
> So I did that where I render:
> do {
> do {
> bg = (Graphics2D)bs.getDrawGraphics();
> mis.newPixels(pixels, cm, 0, thisW);
> bg.drawImage(canvasImage, 0, 0, thisW, thisH, this);
> drawAttribution();
> bg.dispose();
> } while (bs.contentsRestored());
> bs.show();
> } while (bs.contentsLost());
>
> So I'm disposing of bg -- the BufferStrategy graphics after I render.
>
> The question is -- how come I can still use the buffer graphics (bg)
> at other times to draw stuff. My applet draws onto the BufferStrategy (bs)
> in many other places at various times. And it works and all the while
> I have dispose()'ed of the buffergraphics (bg) and the loop above is the
> only
> place I getDrawGraphics();
>
> For example -- here's my paint() method -- no problem -- no drawGraphics.
>
> public void paint(Graphics g)
> {
> //System.err.println("paint()");
> bg.drawImage(canvasImage, 0, 0, thisW, thisH, this);
> if(DRAWATT)
> drawAttribution();
> bs.show();
> }
>
> I don't understand that...
>
> Ken
>
> ===========================================================================
> To unsubscribe, send email to listserv@java.sun.com and include in the body
> of the message "signoff JAVA2D-INTEREST". For general help, send email to
> listserv@java.sun.com and include in the body of the message "help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

kirillcool
Offline
Joined: 2004-11-17
Points: 0

I thought that all those screenshots were done by a professional design company, and Jasper's task is to "translate" these into Java2D. Wouldn't this mean that the corners you are pointing were not implemented with Java2D (at least in the design screenshots)?

mikaelgrev
Offline
Joined: 2006-09-27
Points: 0

You are correct. I thought the process was already completed and this was the Java2D version (which looks similar). Since Java2D does the same as Photoshop I guess one can't really complain. I just thought that since one can set the contrast on LCD AA text that maybe one could do the same for normal AA.

I think the corners look "too fat" (IMO) because the gray scale isn't linear when viewed but treated like that when calculated. new Color(128, 128, 128) isn't viewed by the brain as half way between white and black (it looks darker). Having the AA rasterizer compensate for this could be very costly I guess.

Cheers,
Mikael