w3c / svgwg

SVG Working Group specifications
Other
708 stars 133 forks source link

Radial gradients: "fully overlapping" vs "focal point on the edge" - which takes precedence? #648

Open fsoder opened 5 years ago

fsoder commented 5 years ago

If you have a radial gradient with a start radius (fr) of 0, end radius (r) of 0 and corresponding points have the same coordinates (i.e <cx,cy,r> == <fx,fy,fr>), it could be said that the circles are both fully overlapping and that the focal point is on the edge of the end circle (if a degenerate circle can be said to have an edge that is).

The "fully overlapping" rule comes from [1]. The "focal point on the edge" rule doesn't appear to be that well-defined (at least I only see it in the annotations and figures in [1] and sections above mention it but only for spreadMethod=repeat).

Based on a simple test [2], there is currently some different interpretations: Gecko renders that test with yellow while Blink renders it blue.

[1] https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes [2] https://jsfiddle.net/0v3tuoq4/

longsonr commented 5 years ago

If you implement the gradient in canvas do Firefox and Chrome still disagree?

fsoder commented 5 years ago

No, then they both produce blue (https://jsfiddle.net/agyxt2d1/).

dirkschulze commented 5 years ago

Safari fills with yellow too. That is what I would expect. However, it is not good that SVG differs from Canvas.

@fsoder Are there more edge cases or is this the only one you've found so far?

AmeliaBR commented 5 years ago

fr was new in SVG 2, and designed to match with canvas. But it's possible that the default state (fr="0") does not match against what SVG 1.1 does.

I suspect that some or all of the browsers haven't changed their implementations to support fr at all.

Which would mean that the result is more about what to do when the gradient vector has length zero and/or when multiple stops compute to the same final position on the gradient vector. I do recall in testing that there was cross-browser incompatibilities for linear gradients in this case, and we made a point of specifying behavior in SVG 2, but I don't know whether bugs were filed on the non-conforming browsers.

fsoder commented 5 years ago

@dirkschulze this is what I'm aware of ATM. I know that other degenerate cases have been considered in SVG and other spec contexts before, but it may be reasonable to revisit (and make sure there are tests).

@AmeliaBR I too suspected that "implementation inertia" could explain part of it, but I don't know to what degree that is true. It is probably more about having a zero-length gradient vector than about the initial value of fr (which should match the behavior of SVG 1.1). Gradient rendering tend to suffer from numerical issues, so the exact formulation used by the "solver" can have an effect [1].

[1] I.e a renderer that supports fr may use a different formulation than one that doesn't (i.e only supports it as 0). I suspect this is why the "... on the edge" rule was there in the first place (for certain formulations you'll hit infinity on the edge, and probably have numerical instability close to the edge).

css-meeting-bot commented 5 years ago

The SVG Working Group just discussed Radial gradients: "fully overlapping" vs "focal point on the edge" - which takes precedence?.

The full IRC log of that discussion <myles> topic: Radial gradients: "fully overlapping" vs "focal point on the edge" - which takes precedence?
<myles> GitHub: https://github.com/w3c/svgwg/issues/648
<myles> krit: As AmeliaBR pointed out, we introduced "fr" attribute. It's a focal radius. If you don't know what that means, radial gradients consist of 2 circles. The starting circle and the end circle, and interpolate between. The focal radius is a generalization of the focal point (whcih is a focal radius of 0). What happens if we have an fr and a normal radius being 0, and cx, cy, fx, fy, are on top of the radius for focal and normal radius?
<myles> krit: There's an example in the issue.
<myles> krit: You see a yellow box and a blue box. Yellow uses SVG. You have a radial gradient. Both distances are 0. The focal distance is placed on top, which is 0. Which color should the rectangle with teh radius get filled with? Should it be yellow? Should it not have a color and the blue shines through from below?
<myles> AmeliaBR: There's no way in the spec that it should be transparent because radial gradient is always padded, no matter what other attributes, so it always should be padded with one of the stops. So Chrome / WebKit is treating the radial gradient as an error, in which case it should be orange, or they pad it with yellow. I haven't gone into the details ...
<myles> AmeliaBR: That's the one weird thing of the focal radius, it doesn't necessarily draw whereever, even when spreadMethod is padded?
<myles> krit: Yeah...
<chris_> Edge does the same as Firefox, fwiw
<myles> AmeliaBR: We don't have an example, it would be different from other SVG gradients
<myles> AmeliaBR: We don't have a spread method that is "Stop drawing the gradient outside of the circle." Instead, it's just pad, reflect, or repeat.
<myles> AmeliaBR: So that would be problematic.
<myles> krit: I thought the right answer would be yellow.
<myles> krit: But if you compare the canvas, you get blue.
<myles> krit: We tried to work on the focal radius to maek the canvas gradient as compatible with SVG gradient
<myles> Tavmjong: They can't agree.
<myles> Tavmjong: The limiting case if they're not quite on top of each other, you would expect to stop drawing otuside as soon as they overlap
<myles> krit: right.
<myles> krit: I get what you mean.
<myles> krit: We should explicitly point that out in the spec.
<myles> krit: There's a difference between canvas and SVG. SVG might not be able to be fully compatible, but pointing it out would help, maybe as an example. Doesn't have to be normative. Normative text already covers this.
<myles> Tavmjong: We already have a note about another difference between canvas and SVG. When the focal point is distanced by the right side of the circle. If you look in the spec, you'll see it in 15.2.3.2, the second green box
<myles> s/15.2.3.2/14.2.3.2/
<myles> Tavmjong: "Notes on radial gradients"
<myles> Tavmjong: That deals with a focal point being on the circle
<myles> AmeliaBR: That's separate from talking about the focal radius being overlapping.
<myles> AmeliaBR: Whatever happens, if there is an error, that we have clear error behavior, or if we use one stop or the other, we have clear behavior. But turning the gradient transparent isn't consistent with anything.
<myles> krit: That is the annotation. Annotation 3, I see this comment. I don't see it anywhere else.
<myles> AmeliaBR: Maybe that note should be promoted?
<myles> krit: I thought annotations were going to be in the spec but not visible
<myles> Tavmjong: yeah
<myles> <general confusion>
<AmeliaBR> Scribenick: AmeliaBR
<AmeliaBR> Amelia: There are two notes. One mentions a change vs SVG 1.1 to conform with Canvas, the other mentions that there is still a difference compared to canvas (padding with solid color vs padding with transparent black) to keep consistency with SVG 1.1. But the question is whether normative text backs up those notes.
<AmeliaBR> Dirk: Ok, I think we need to review again & comment on the issue, maybe come back next week.
Tavmjong commented 5 years ago

A similar situation would be for a linear gradient if x1=x2 and y1=y2. In this case: Firefox shows the last stop color, Chrome shows the first stop color. Inkscape shows the average of the first and last stop colors. The SVG 2 spec says: If ‘x1’ = ‘x2’ and ‘y1’ = ‘y2’, then the area to be painted will be painted as a single color using the color and opacity of the last gradient stop. I would expect a similar rule to apply for radial gradients, thus I would expect the example to show yellow (the last stop color).

longsonr commented 5 years ago

In SVG 1.1 an r value of 0 meant use the last colour stop per https://www.w3.org/TR/SVG11/pservers.html#RadialGradients, that text has been removed but Firefox still implements it at the moment, that's why you get the last colour stop.

Tavmjong commented 5 years ago

Here is a test with various gradients: http://tavmjong.free.fr/SVG/SVG2_TESTS/gradient_animation.svg

css-meeting-bot commented 5 years ago

The SVG Working Group just discussed Radial gradients: "fully overlapping" vs "focal point on the edge" - which takes precedence?.

The full IRC log of that discussion <myles> Topic: Radial gradients: "fully overlapping" vs "focal point on the edge" - which takes precedence?
<myles> github: https://github.com/w3c/svgwg/issues/648
css-meeting-bot commented 5 years ago

The SVG Working Group just discussed Radial gradients: "fully overlapping" vs "focal point on the edge" - which takes precedence?, and agreed to the following:

The full IRC log of that discussion <myles> Topic: Radial gradients: "fully overlapping" vs "focal point on the edge" - which takes precedence?
<myles> Tavmjong: I just added a test file to the issue.
<myles> Tavmjong: Let's look at it.
<AmeliaBR> github: https://github.com/w3c/svgwg/issues/648
<krit> example: http://tavmjong.free.fr/SVG/SVG2_TESTS/gradient_animation.svg
<myles> Tavmjong: The top one shows what happens for a linear gradient when x1=x2 and y1=y2. According to the spec, you're supposed to use the last stop color. FF & Chrome seem to do that. For some reason, on an earlier test, Chrome wasn't doing that. Maybe I screwed up. Then you see an animation where I animation x1, so you can see what happens at the limit.
<myles> Tavmjong: On the right is repeat, and the left is the default
<myles> Tavmjong: The next row down is radial gradients. When you have the same values for focus and end, you get nothing.
<myles> Tavmjong: You can see an animation. When one gets bigger than the other, the gradient flips.
<myles> Tavmjong: The next row down is from SVG 1.1. The focal point on the edge. You can see differences in behavior between Firefox and Chrome. Firefox doesn't allow you to move the focal point outside the outer circle. Chrome does. Firefox draws outside the left, but Chrome doesn't.
<myles> The bottom row is if you have a focal ring moving across from left to right. There is some difference in behavior with the browsers.
<myles> s/The bottom/Tavmjong: The bottom/
<myles> krit: I don't see a test for when the rings are the same?
<myles> krit: there was another test for it in the issue
<myles> AmeliaBR: We know there's an inconsistency there. It's useful to break it apart and find which parts are inconsistent. THe result: Lots of different parts are inconsistent
<myles> krit: I'm not so sure about that. The inconsistency between Firefox and Chrome, it's because Firefox doesn't allow you to have a focal radius that's outside of the circle. Firefox just didn't adapt to SVG 2 yet.
<myles> Tavmjong: That's partially true. Chrome also doesn't draw outside. 3rd one down, 3rd one across.
<myles> Tavmjong: 3rd one down, 3rd one across should be identical between Chrome and Firefox. The focal point is right on the edge.
<myles> krit: Chrome doesn't repeat after the edge.
<myles> Tavmjong: it's supposed to repeat very very close together.
<myles> krit: Firefox moves the focal point inside of the circle.
<myles> krit: SVG 1.1 says that the focal point should be slightly within the circle, not on the circle.
<myles> AmeliaBR: Maybe I'm seeing different results than you are? OS-dependent renderings?
<myles> AmeliaBR: myles: on macOS, FF & Chrome agree on this one.
<myles> chris: They render similarly on Windows 10 too, but one puts the colors into sRGB and the other doesn't.
<myles> AmeliaBR: What is the most useful behavior? Is it reasonable for implementations to make it match?
<myles> AmeliaBR: As far as my personal preference, I think that anything that results in transparent regions when all the stops are opaque is wrong and inconsistent with the rest of SVG gradients. But I understand it's consistent with canvas gradients.
<myles> krit: SVG 1.1 avoided that by not allowing the focal point outside of the focal radius. However, it was decided a long time ago that we would move closer to canvas.
<myles> AmeliaBR: The SVG spec does say "except that you pad with a solid color". That is a difference from canvas. Is making that one adjustment too difficult for implementors?
<myles> Tavmjong: "average" color.
<myles> AmeliaBR: okay.
<myles> AmeliaBR: Depends on whether or not it's repeating or not.
<myles> Tavmjong: Exactly. The spec could be more clear here.
<myles> Tavmjong: On my screen on Chrome. It does do an average on linear repeating colors when X and Y are the same.
<myles> krit: On Chrome, that's true.
<myles> Tavmjong: that's what makes sense to me.
<myles> krit: Safari has blue there. Chrome has the average color. Firefox has green. Many choices!
<myles> AmeliaBR: The average color is the logical limit of infinite repeats when the length of the repeat gets smaller and smaller. Animations make this clear.
<myles> krit: Shouldn't the same apply to circle and focal radius being 0? And focal point is in the middle?
<myles> AmeliaBR: Yes. Any place where the logical geometry is infinite repeats of 0 lengths, then we should pick consistent results.
<myles> krit: That's inconsistent with other places where we use the last color. It seems we have to use the last color
<chris> I guess, oce we have tightened the spec, whether the implementations are willing to converge on the correct rendering, or whether we should put "fr" at risk
<myles> AmeliaBR: If it's a padded gradient, we use the last color. top left example with a zero-length vector
<myles> krit: What if we just don't specify it?
<myles> AmeliaBR: That's a cop out
<myles> AmeliaBR: If we can't get interop, the spec should warn authors about it.
<chris> Avoids the numerical instability so not really a cop-out
<myles> AmeliaBR: Can we get an agreement that nothing should be transparent?
<myles> AmeliaBR: And if you would do it, use the average color of the stops
<myles> krit: That won't work
<chris> agree average color makes the most sense as the repeats converge
<myles> Tavmjong: That won't work. If you look at the SVG 2 spec, there's an example
<myles> krit: At that point we have interoperability between Chrome and Safari and Canvas for all browsers.
<AmeliaBR> https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
<myles> Tavmjong: That's as if you take the circle, draw it, move it, draw it
<myles> krit: And firefox removes the restriction
<myles> Tavmjong: Everywhere but that case I would <missed>
<myles> chris: If we agree on that and put it in the spec, we could raise a bug on FF?
<myles> AmeliaBR: They're not the only one who's inconsistent
<myles> AmeliaBR: There are 2 issues
<myles> krit: chris: Let's separate the two issues
<myles> AmeliaBR: Issue 1) What to do with infinite repeats and using the average color, we'll keep the spec text there and make sure there's a bug on Firefox
<myles> Tavmjong: The first thing is a bug on Firefox for not supporting the focal point outside of the circle
<myles> AmeliaBR: Infinite repeats even with linear gradients. Tavmjong's example: top row 3rd over
<myles> krit: There we have inconsistency with all browsers.
<myles> chris: I did testing in Edge but there's no animation.
<myles> AmeliaBR: Edge shows solid blue instead of solid green or solid average
<myles> Tavmjong: That's wrong
<myles> krit: So should the color be blue, green, or average? Can we just open issues against any browsers that don't match the behavior? And assume that those would be fixed? Or keep it unspecified.
<myles> chris: We should clarify the spec, raise the issues, and if they're not fixed, change the spec back.
<myles> krit: ok
<myles> krit: Tavmjong and AmeliaBR and chris prefer average color
<myles> krit: Do we say how to compute the average color?
<myles> Tavmjong: Yes.
<myles> krit: Then we can resolve that way.
<myles> Tavmjong: The color space is the same in which the gradient is interpolated.
<myles> krit: Let's agree on average color for infinite repeating patterns
<chris> avg color is the correct result, if you assume infinite sub-pixel sampling
<myles> Tavmjong: spread-method="repeat". If there's no spread method, use the last color stop.
<myles> krit: So the average color is just for spread-method="repeat" and "reflect"?
<myles> Tavmjong: yes
<myles> RESOLUTION: For spread-method="repeat" and "reflect", infinitesimal repeats use the average color of the stops
<myles> RESOLVED: For spread-method="repeat" and "reflect", infinitesimal repeats use the average color of the stops
<myles> RESOLVED: for spread-method="pad" we use the last color stop
<myles> RESOLVED: for spread-method="pad" we use the last color stop for infinitesimal repeats
<myles> krit: So let's open an issue on Firefox about how they don't match the above resolutions.
<myles> Tavmjong: Okay.
<myles> AmeliaBR: The final issue is do we want to continue to push spec text that says always paint something even when the focal radius is doing weird things that in canvas would include transparent regions of the code?
<myles> s/code/cone/
<myles> Tavmjong: maybe
<myles> Tavmjong: If you look at the first figure under 14.2.3.2 it shows you the logical way of drawing outside the code with transparent black. That's what everyone does.
<myles> AmeliaBR: But then we have that second picture which shows it padded and the note that says we've got a difference to maintain compatibility with SVG 1.1. Do we want to stick with that spec text or match implementations, or ask implementations to match the spec text?
<myles> Tavmjong: Ask them to match the spec text
<myles> chris: Yes. Use the other one as a fallback position
<myles> AmeliaBR: Okay, let's make sure there are bugs.
<myles> krit: Any other things to discuss?
<myles> <silence>
<myles> AmeliaBR: I suppose it's just make sure there are bugs filed and we collect the links to the bugs in the issue on GitHub and then at some point we'll come back and decide if we need to recognize that we won't have conforming implementations and make this undefined or match reality
<myles> AmeliaBR: The issue should stay open until we get matching implementations. Can we get some tests?
<myles> krit: Should we add text about implementation feedback needed.
<myles> Tavmjong: Back to AmeliaBR's question about transparent black: I think there is an inconsistency between the two images.
<myles> AmeliaBR: The two figures in the spec?
<myles> Tavmjong: yeah.
<myles> Tavmjong: Cause if you move the circle in, in the top figure, so the two right edges edges just touch, then that cone becomes a half-plane.
<myles> krit: correct.
<myles> krit: We see that in Chrome and Firefox
<myles> AmeliaBR: It goes invisible because it can't define the geometry
<myles> Tavmjong: We could say that outside that becomes the average.
<myles> AmeliaBR: Do you want to make some figures of that case and what it could look like?
<myles> krit: I feel we'll get inconsistent again with canvas. We want to be more compatible with canvas, that's the initial reason for this change.
<myles> Tavmjong: The bottom right figure needs to be changed, then.
<myles> Tavmjong: The argument when we made that decision was if you move the focal point just inside the circle, we do fill the whole plane with color.
<myles> Tavmjong: So we were trying to be consistent with thtat
<myles> s/thtat/that/
<myles> AmeliaBR: Well, if we make sure there are bugs filed with implementors, we'll get some more opinions coming in hopefully.
<myles> Tavmjong: mhm.
<myles> Tavmjong: But the spec itself should be consistent.
<myles> AmeliaBR: Yeah.
<myles> krit: If you go back to the animation, you see the transparent left side in any browser other than Firefox (not sure about Edge)
<myles> AmeliaBR: Edge hasn't implemented the "fr" so it's not relevant.
<myles> Tavmjong: Edge is switching, right?
<myles> AmeliaBR: It will eventually be Chrome.
<myles> krit: Can we close the topic?
<myles> chris: yes
<myles> Tavmjong: yes
<myles> GitHub: https://github.com/w3c/svgwg/issues/642
<AmeliaBR> github: https://github.com/w3c/svgwg/issues/648
<myles> GitHub: https://github.com/w3c/svgwg/issues/648