Skip to main content

Effects in JavaFX: Quality

Posted by campbell on January 7, 2009 at 9:29 AM PST

Spitzenqualität

In my last entry, I showed how easy it is to use the Effect API in JavaFX. This time around, I'll focus on visual quality.

JavaFX includes a scene graph API that is vector-based, meaning that elements in the scene will be rendered with high quality, taking into account the resolution of the output device (i.e., the screen) and any transforms that have been applied to the scene graph nodes. Even in a vector graphics library, applying filter effects necessarily requires painting the relevant scene graph nodes into an offscreen raster image so that the implementation can access and manipulate the raw pixels. Care must be taken by the implementation to create an offscreen representation of sufficient resolution so that it still looks great when it is ultimately rendered to the screen.

Take for instance the following example which shows a rounded Rectangle node with a DropShadow applied to it. The slider controls the scale of the shape node, in the range [0.5, 5.0]. Generally speaking, Effect variables operate in the local coordinate system of the Node to which it is applied. In this example we have a DropShadow.radius of 10, which means that at a 1:1 scale the rectangle will be surrounded by a shadow that extends 10 pixels in every direction. But what happens when we change the scale of the Rectangle node? (Try this yourself by dragging the slider back and forth. Or grab the source bundle here.)

Notice that the shadow scales in proportion to the shape itself, which is the desired behavior in nearly all cases. (Think about it: if this red shape represented a button, you'd want the drop shadow to be rendered with the same proportions regardless of whether it is on a relatively low-pixel-density laptop display or on a hi-dpi mobile device.) Also notice that the actual node "content" (the red rectangle) looks crisp under any transform. Pretty, innit?

There are a few different approaches the implementation could take to make this all work. One naïve approach would be to simply render the rectangle at a 1:1 scale into an offscreen image, create a shadow at the same 1:1 scale, merge the two images, and then render the resulting image with a scaling transform and, say, bilinear filtering (see Fig. 1 below). This would offer reasonable performance, but quality would suffer under non-identity transforms, since we're relying on image filtering to smooth the results of the scaling operation (and all this flies in the face of having a vector-based scene graph in the first place). Another dubious approach would be to render the rectangle in the transformed coordinate space, and then create the shadow with a scaled radius (and a potentially huge Gaussian kernel). This would offer great quality, but performance would take a nose dive due to the processing of the large shadow/blur operation.


raster.png


Fig. 1: Raster scaling (bad)

vector.png


Fig. 2: Hybrid vector scaling (good)

Fortunately, the effects framework in JavaFX currently takes a hybrid approach (see Fig. 2 above) that offers the best of both worlds for compound effects like DropShadow and others: high-quality rendering of the actual node content, a shadow that scales in proportion with the content under any transform, and good performance to boot. The implementation is smart enough to 1) render the original node content into an offscreen image with an identity transform for the purposes of creating the shadow, 2) use a filtered image scaling operation to bring it into the final transformed coordinate space, and then 3) render the original node content again over the shadow using the cumulative transform so that it looks just as lovely on the screen as if the effect hadn't been applied at all. Similar techniques are used for other effects, such as Glow and Lighting, to find a good balance between quality and performance, and to keep things looking great at any resolution.

Well, that was a mouthful. Since I'm trying (and struggling) to keep these blog entries short and sweet, let me just say that if you didn't follow all this, that's okay. The takeaway message is that we go through great lengths to make sure that your applications that use JavaFX, and effects in particular, look their best at any resolution.


In my ears: Public Image Ltd., Second Edition


In my eyes: Orhan Pamuk, My Name Is Red

Related Topics >>

Comments

found a bug report, same symptoms: http://javafx-jira.kenai.com/browse/RT-2870

how does the JavaFX deployment script detect the installed java version? I don't think it uses the deployment toolkit... -I installed 6u12b3 and got the "please install 6u11 popups". -removed 6u12 and installed 6u11 -still get please install 6u11 popups... -if i cancel the installation twice, the applet loads deployment toolkit reports u11 so everything seems to be fine: http://people.fh-landshut.de/~mbien/cafebabe.html btw i get two dialogs + installation attempts per applet this means 4 on the aggregated page

Thank you very much for your work Please check you page without Java plugin installed in your browser or without java installed at all. You get 2 message dialogs only with OK button: "Java is required to run JavaFX applications. You will now be redirected to a Java update site..." -- after redirection to java.com/... "The current java on this system (0 - Java Not enabled) ..." So there is no chance to read you blog without Java because of redirection to Java update site. There should be Yes/No dialog to confirm redirection to Java updates. Without redirection the page should be readable and without running applets... Thank you. Martin JANDA

Reproduced on Vista Ultimate (SP1). Desktop deployment is never an easy task - but at least you have a rock solid language and library to start off with ;-) java version "1.6.0_11" Java(TM) SE Runtime Environment (build 1.6.0_11-b03) Java HotSpot(TM) Client VM (build 11.0-b16, mixed mode, sharing) Microsoft Windows [Version 6.0.6001]

@mbien, @ilazarte: I'm seeing the same thing on Mac OS X with Safari too. Others have reported it to me internally as well. I've filed a bug here: http://javafx-jira.kenai.com/browse/RT-2907 Thanks for bringing these deployment issues up. I purposely decided to embed JavaFX applets in my last two entries exactly for this reason. Yes, it will be a distraction from the topic at hand at first (because of bugs like these), but it helps to shed light on the pain points in the JavaFX deployment story. If we can't successfully embed simple applets like these, then we can forget about success at a larger scale. Anyway, our deployment team is working to fix these things, so I hope soon we can get back to talking about effects without distraction :)

@mbien: Currently, yes, that is correct. However, a long time ago I had an idea that for the downscaling case, like the one you cite, we should be able to scale the kernel and work on the downscaled representation, sort of the opposite of the "dubious approach" I referred to above. I never got around to investigating it more, so I just filed a bug on this (http://javafx-jira.kenai.com/browse/RT-2908); thanks for bringing this up.

skip to the JSL! :) No kidding, this stuff is interesting. bwt, I get the same on the second applet issue: 6u11b3, winxp

only one applet starts if i visit the main blog page: http://weblogs.java.net/blog/campbell/ the second spins forever in the loading icon state. 6u11 winxp

Slider fails to work on Mac OS X 10.5.6 with Java 1.6.0_07.

does this mean that the shadow is rasterised small and scaled up and the rectangle is rendered as vector graphic after transformation (the "obvious way for rectangles ;)")? this would mean that we would get a performance decrease the other way around: fancy shadowed 1024x1024 rectangle scaled down to 48x48 would be rendered slower than a "native" unscaled but still fancy shadowed 48x48 quad is this correct? very good blog entry, there are not many sources which tell us useful things about jfx