Thursday, May 20, 2010

LCD Subpixel Rendering

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


While I'm on the topic of pixels -- here's another tip. This one is both Mac OSX and NetBeans (well, Swing) specific.



Subpixel rendering, an antialiasing technique, makes text look much sharper (wikipedia article).
Subpixel rendering was added to Java 6, so the NetBeans source editor looks great on Windows and Linux provided you are running Java 6. Antialiasing was never an issue on Macs, where the JDK used its own native graphics renderer which had subpixel rendering all along - until now!!

Here's NetBeans on Linux and Windows:



Let's zoom in. Notice the "strange" colors at the perimiter of the font strokes; they look strange here but oh-so-good at the proper resolution on an LCD:



Unfortunately, when Apple released Java 6 on the Mac, they switched the default graphics renderer over from their own "Quartz" renderer to the standard JDK one. That shouldn't be a problem, since Java already renders LCD text, right? Wrong! For reasons I don't know, subpixel rendering is NOT working on the Mac with the standard renderer. Therefore, by default, NetBeans text looks blurry (because it is only grayscale antialiased) on Macs.

This is easy to "fix". Just switch the rendering back to Quartz. You can do that with the following flag, added to the netbeans.conf config file:


-J-Dapple.awt.graphics.UseQuartz=true

The -J flag just tells the NetBeans launcher that the rest of the flag is a flag for the Java interpreter, and the -D flag says set the system property apple.awt.graphics.UseQuartz to true. Therefore, in your own Swing applications you can do the same thing, just drop the -J flag.

(The config file is in a place like /Applications/NetBeans/NetBeans 6.9 Beta.app/Contents/Resources/NetBeans/etc/netbeans.conf and you would add these flags to the netbeans_default_options line.)

Here is a before-versus-after screenshot; this will only make visual sense to you if you're viewing this on an LCD screen!



The subpixel rendering text is on the left.

Here's a zoomed in view, which shows the default (non-LCD renderered text):



And here's what you get when you turn on Quartz:



I presume Apple turned off Quartz by default in Java 6 for a reason. Does anyone know what it is? The JavaFX Release Notes mention some memory leaks. I've been using the flag to turn LCD text back on since I switched to Java 6 on the Mac a year ago, and it doesn't seem to have any adverse effects for normal NetBeans usage (mostly text oriented; I don't do UML editing etc).

More importantly, does anybody know why subpixel rendering from the JDK doesn't work on the Mac since it works everywhere else? Is there a way to trick it to work with rendering hints etc?

P.S. Speaking of launcher arguments - I have one other customization too. I add -J-d32. This tells the JDK that I want a 32-bit VM. I haven't measured personally what this buys me, but I saw that Charlie Nutter recommended it for NetBeans usage, and he certainly knows his way about VM tuning parameters!


Tuesday, May 11, 2010

Bay Area JUG Roundup 2010

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.



If you live in the San Francisco bay area, I hope you can come to the Bay Area Java User Group Roundup 2010 tomorrow night! It is going to be held at the Oracle conference center, and both beer and dinner will be provided. There will be an update on the java.net migration, and the Java Posse are going to record a live podcast. Note - you need to sign up in advance so visit the event details page.

Hope to see you there!



Monday, May 10, 2010

Pixel Considerations

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


Antialiasing makes lines and shapes look smooth - though sometimes at the expense of sharpness. What if you're trying to draw a horizontal or vertical line where you don't need antialiasing? You might be under the impression that if you position your shapes at round integer positions, you will avoid antialiasing.
But it's not quite that simple - so avoid trying to be smart with layout code like this:


...
label.layoutX = (width - labelWidth) / 2 as Integer;
....

The idea here is that when you're placing something in the middle you might end up with a fractional value, say 42.5, and so you added the "as Integer" to round things to a whole number to avoid introducing antialiasing.

Well, this may have exactly the opposite effect! Take a look at the following picture, which shows two rectangles. Both rectangles have a stroke width of 1.0, and one of them is positioned at a round integer, and the other one is positioned at 0.5.



Here's a zoomed in view which makes things clearer:



Obviously, the rectangle on the left is blurry because antialiasing is attempting to show the line as being somewhere in the middle between them. The rectangle
on the right on the other hand is clear and crisp because the lines overlap EXACTLY with the pixel grid the line is rendered into.

Here's the thing though: The rectangle on the left is the one that was positioned at round integers, and the rectangle on the right is the one positioned
at round integer + 0.5 !

So should you run out and ensure that all your horizontal and vertical edges are positioned at 0.5? No. The key here is the stroke width. Take a look at the following figure, where I have position rectangles with different stroke widths (1, 2, 3) and different pixel positions (0, 0.25, 0.5).



Zoomed in:





As you can see, whether you match the pixel grid perfectly depends on the stroke width and the pixel positions. This actually makes sense. Think of your pixel grid as having x coordinates at the boundaries of each pixel. In other words, "0.0" is the left edge of the first pixel, and 1.0 is the right edge of the first pixel. The line position has to be the center of the stroke. So if you want to have a line of thickness 1, then that line will run exactly through the pixel, so we must position its center at x=0.5. When the stroke width increases to 2 however, the center will be in the middle (e.g. 1), and so we should position it at a round number. And so on.

When you're dealing with large shapes this isn't a big deal. But if you're trying to paint a grid (like the one below), a pattern, or small controls (like disclosure arrows - which is how I came to look into this), it can pay off.



By the way -- on OSX there's a nice screen zoom (hold the Option key and then do a two-fingered drag on the trackpad up or down) which makes it easy to zoom in and look at the pixels for anything on the screen. But unfortunately it doesn't show pixels as square, it does more blending, so it's much harder to tell what's going on at the individual pixel level. Get an image editor which lets you zoom in with clear pixel boundaries, or even a screen magnifying lens. Here's how the builtin screen zoom looks - as you can see it's not as clear as the pixel zooms above:



UPDATE: Marius taught me in the comments that you can turn off the OSX zoom smoothing in the Universal Access options. Sweet! I can now instantly check the pixels without going to an intermediate screenshot! Thanks!

Finally: Jonathan Giles from the JavaFX controls team has been doing a great job aggregating and summarizing interesting FX articles each week -- highly recommended if you're doing anything with JavaFX.