Proportional paint object behavior inconsistent and needs to change

Jim Graham james.graham at
Mon Nov 18 14:58:56 PST 2013

Felipe mentioned recently that we encountered some issues in fixing a 
bug with SVGPath.

The outcome of this fix could be a significant change in how your 
proportional gradient fills look and so we'd like to get feedback on the 
various ideas.  You can read about them in that Jira issue, but I'll 
also summarize below.  Discussion would probably be better on the 
mailing list, but we eventually need to work the salient points back 
into the Jira issue for future maintenance.

The basic issue is that we had a disagreement between the way that the 
shape caching code worked and the way that uncached shapes were filled 
with proportional paints.

First, there is the concept of tight and loose bounds.  Loose bounds are 
very cheap to calculate, but can contain area not strictly inside the 
shape.  Tight bounds are more careful to figure out exactly how far 
curved sections of the shape reach, but a fair number of calculations 
and recursions are needed to accurately calculate those extents.

Our current code will use loose bounds of the basic shape (i.e. the part 
that is filled) to calculate the bounds for proportional paints when you 
first paint a shape - whether you stroke it, fill it, or both.

But, if you ran with shape mask caching (the default mode) then after a 
rendering or 2 we would decide to cache the antialias coverage mask and 
we would then use the node's content bounds to figure the bounds for the 
proportional paint.  The content bounds are calculated more precisely as 
tight bounds, though, so they didn't always agree with the bounds used 
in those first few uncached renderings.

The net result is that the proportional paint would shift after the 
first couple of frames unless you animated the shape and then it would 
revert while you were animating and then shift back when it was stable.

There is also the Canvas object that can also render proportional 
paints, and it does so using the same code that the shape nodes use when 
they don't have their coverage mask cached (i.e. loose fill bounds).

We'd like to make all of this as consistent as possible.  Here are the 
various decisions and how they'd impact code:

- Loose bounds are faster to calculate for shapes that aren't likely to 
be reused.  The uncached shape rendering used them because the shapes 
may change before we need the bounds again.  Canvas uses it because it 
is an immediate mode API with no input on how often it may see a 
particular shape again.

- Tight bounds would likely be less suprising to developers and users, 
though.  They are better for hit testing and damage management because 
they generate fewer false positive hits.  They are also fairly fast to 
calculate for most shapes and it is a rare shape that we'd have to 
recurse much to get it right.  Also, for straight edges there is no 
difference in performance to calculate tight or loose bounds.

All in all, it would probably be better for the FX API to standardize on 
tight bounds and treat any cases where we noticeably affect performance 
(which should be very rare) as opportunities for tuning.  This may not 
be compatible with the first rendering of current Shape nodes, but they 
would shift back and forth anyway so we aren't worried about 
incompatibility with an inconsistent system.

The other part of the decision is which bounds to use.

Currently uncached rendering uses fill bounds, and mask-cached rendering 
uses the content bounds which depends on whether you supply a stroke or 
a fill paint so it could be either fill bounds or stroke bounds.

- For filled-only shapes we obviously want to use the "fill bounds".

- For stroked and filled shapes, we have 3 choices:

- - use fill bounds for both paints so that the geometry used for 
proportional stroke and fill paints are similar for both parts of those 
nodes.  This helps line up any color discontinuities or highlights 
between the two.

- - use stroke bounds for both which also means the two are consistent, 
but Canvas can't really do this because it doesn't know if you are 
filling and stroking until it sees the latter operation

- - use fill bounds for fill and stroke bounds for stroke which means 
the geometries of the two are different and it makes it harder to line 
up the transitions of proportional stroke+fill shapes, but Canvas can do 
this so Canvas vs. Shape node remain consistent

I'm not sure which of the above is the best, but I lean towards "fill 
bounds for both" because it allows consistent geometry for stroke+fill 
and it allows consistent behavior between Canvas and Shape node, at the 
possible expense of "0% on a stroke isn't at the edge of the stroke".



More information about the openjfx-dev mailing list