Hi Jasper,

I had a long reply over the issues of consistency and then I got hit on the 
head by an idea hammer.  Here's another wrench in the equation.

Only Path and SVGPath objects have ever had an ambiguity, but Rectangle objects 
have always had a consistent behavior.  And, that behavior is to have both 
stroke and fill match the fill bounds.

Below is a small example to show that the stroke on a rectangle has its 0.0 and 
1.0 at the fill bounds.  You can even click on the window to cause multiple 
repaints and see that the behavior remains stable over multiple frames (we 
don't cache rectangle coverage masks like we do for arbitrary shapes).

So, in order to be consistent with the non-path objects, I think we've already sunk this 
behavior into fairly firm ground here.  Unfortunately I think we're going to have to 
modify the broken Path objects to be consistent with this behavior since they are clearly 
now the "odd man out" in the compatibility equation...

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.shape.*;
import javafx.scene.paint.*;
import javafx.scene.input.*;
import javafx.event.*;

public class Test extends Application {
    public void start(Stage stage) {
        Rectangle r = new Rectangle(50, 50, 200, 200);
        r.setStrokeWidth(80);
        r.setStroke(new LinearGradient(0, 0, 0, 1, true, CycleMethod.REPEAT,
                                       new Stop(0, Color.BLUE), new Stop(1, 
Color.RED)));

        Scene scene = new Scene(new Group(r), 300, 300);
        scene.addEventHandler(MouseEvent.MOUSE_PRESSED, new 
EventHandler<MouseEvent>() {
            public void handle(MouseEvent e) {
                scene.setFill(new Color(Math.random(), Math.random(), 
Math.random(), 1.0));
            }
        });
        stage.setScene(scene);
        stage.show();
    }
}

                        ...jim

On 11/18/2013 10:48 PM, Jasper Potts wrote:
I agree with Scott, it seems bad if gradient doesn't match stroke bounds when 
applied to stroke.

Jasper

On Nov 18, 2013, at 9:36 PM, Scott Palmer <swpal...@gmail.com> wrote:

It wasn't clear from your original message that the stroke bounds were much 
more expensive. Given that additional info I don't think my idea is practical.  
That makes it harder to decide. I like your choice of fill bounds to ease the 
ability to align the gradients, but I don't like the idea of not being able to 
use a gradient that goes to the edge of the stroke.  Depending on the stroke 
width that could be a significant and very visible limitation.

I wonder how frequently aligning the gradient in the stroke and the fill comes 
up?  It seems using the stroke bounds for the stroke and the fill bounds for 
the fill is more correct, even if it makes aligning the gradients more 
difficult.  That alignment is nice, but having the gradient work consistently 
in terms of what 0% and 100% mean is probably important.

Scott

On Nov 18, 2013, at 8:17 PM, Jim Graham <james.gra...@oracle.com> wrote:

Hi Scott,

That's an odd take on it.  It wouldn't be readily obvious to a developer why 
their background rectangle had the gradient a little off if they never planned 
to ever stroke it.

Also, keep in mind that while it might be slightly more expensive to calculate tight bounds than 
loose bounds, it is MUCH more expensive to calculate stroke bounds on a shape.  It's not a trivial 
"OK, add a few pixels for the stroke" type of case, you have to trace out the path and 
compute the perpendicular extensions in many cases.  So, basically you are saying "always do 
the most expensive bounds operation for ever shape that is filled even if the extra work would 
never come into play"...?

           ...jim

On 11/18/2013 4:58 PM, Scott Palmer wrote:
I would lean towards using the stroke bounds for both.  Those being the "real" 
bounds, and resulting in less mis-aligned gradients.  Always calculate the stroke bounds 
as if the shape will be stroked, so it doesn't affect Canvas.
If you don't want that to affect the bounds used for a gradient fill when you 
aren't stroking, set the stoke width to 0.


Scott

On Nov 18, 2013, at 5:58 PM, Jim Graham <james.gra...@oracle.com> wrote:

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".

Thoughts?

           ...jim

Reply via email to