Effects in JavaFX: 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.
Fig. 1: Raster scaling (bad)
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
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