googlefonts / colr-gradients-spec

63 stars 8 forks source link

Clarifications for Sweep gradients needed #361

Open drott opened 2 years ago

drott commented 2 years ago

https://docs.microsoft.com/en-us/typography/opentype/spec/colr#sweep-gradients

For context, discussion in: https://github.com/BlackFoundryCom/black-renderer/issues/17

A sweep gradient is defined by a center point, starting and ending angles, and a color line. The angles are expressed in counter-clockwise degrees from the direction of the positive x-axis on the design grid.

The color line is aligned to a circular arc around the center point, with arbitrary radius, with stop offset 0 aligned with the starting angle, and stop offset 1 aligned with the ending angle. The color line progresses from the start angle to the end angle in the counter-clockwise direction; for example, if the start and end angles are both 0°, then stop offset 0.1 is at 36° counter-clockwise from the direction of the positive x-axis. For each position along the circular arc, from start to end in the counter-clockwise direction, a ray from the center outward is painted with the color of the color line at the point where the ray passes through the arc.

The color line may be defined using color stops outside the range [0, 1], and color stops outside the range [0, 1] can be used to interpolate color values within the range [0, 1], but only color values for the range [0, 1] are painted. If the specified color stops cover the entire [0, 1] range (or beyond), then the extend mode is not relevant and may be ignored. If the specified color stops do not cover the entire [0, 1] range, the extend mode is used to determine color values for the remainder of that range. For example, if a color line is specified with two color stops, red at stop offset 0.3 and yellow at stop offset 0.6, and the pad extend mode is specified, then the extend mode is used to derive color values from 0.0 to 0.3 (red), and from 0.6 to 1.0 (yellow).

I see two main issues:

For each position along the circular arc, from start to end in the counter-clockwise direction, a ray from the center outward is painted with the color of the color line at the point where the ray passes through the arc.

It's not painted only from "start to end" but it's painted for all rays of the circle from start to (start + 360) degrees. Start is aligned with color stop offset 0, end is aligned with color stop offset 1.

, but only color values for the range [0, 1] are painted.

I suggest to remove that or perhaps this whole paragraph? Do we need the definition of the [0,1] range?

The ColorLine rules for extend modes apply, but they do not talk about the [0,1] range, but the defined interval.

In this paragraph, instead we might want to say that the ColorLine extend modes apply for color stops outside the defined interval. Which means, going down to 0 (= start angle) if the ColorLine's first stop offset is > 0, or going up to 360 / ( endAngle - startAngle). What confused me here is: The [0,1] interval is not what is repeated along the circle, the defined interval of the ColorLine defines that.

We may also need to define what happens if the ColorLine stop points + start and end angles would be overlapping along all directions of rays from the center of the sweep outwards.

CC @justvanrossum

drott commented 2 years ago

Ambiguities arise if the extend mode for the ColorLine is pad and the sector from start angle to end angle of the sweep gradient crosses the 0/360 degree angle, for example 315° and 45°.

In that regard, it seems we need to be clearer on the drawing order and the mapping from angle to ColorLine coordinate.

For each position along the circular arc, from start to end in the counter-clockwise direction, a ray from the center outward is painted with the color of the color line at the point where the ray passes through the arc.

Does "start and end" here refer to 0 degres to 360 degrees starting from the positive horizontal x axis? If it does not and refers to start and end angle, then what happens at the other angles?

Using a ColorLine with extend mode pad, and color stop 0 red, 0.5 yellow, 1 blue, what do we paint in the following situation:

If "from start to end" in the spec means start drawing from 0 degrees (option 1 above) to 360 degrees, and stop offset 0 on the color line is aligned with 315°, and 1 is aligned with 45°, what do we paint at 0 degrees? What coordinate on the Color line doe 0 degrees refer to? Do we start with yellow at 0° fading to blue at 45°, then continue with blue until 360°, or do we start with red, draw almost a full circle, then fade from red to yellow from 315° to 0°?

drott commented 2 years ago

For code-archeology background, original discussions when adding PaintSweepGradient in #217 and #253.

drott commented 2 years ago

For defining where do we start and end drawing from, i.e. what is painted, and what is discarded as overlap, we need to also take into account the definitions of startAngle and endAngle being in F2DOT14 * 180.0f, so the only way precise way to define "full circle" means -2.0 to 0, i.e. -360 to 0.

drott commented 2 years ago

To summarize, we have at least the following issues:

1) The spec text is too strictly tied to the [0,1] interval. Spec text: "but only color values for the range [0, 1] are painted." conflicts with the definition of extend modes and ColorLine intervals wider than [0, 1] or narrower than [0,1]. 2) Overlap or drawing order is not covered accurately in the spec text. The text is not clear on where drawing should start and where it should end: Should it be from 0 to 360 degrees, or from startAngle to endAngle - and if the latter, what happens if such a sector is larger than 360? Or if it's less wide than 360° but the ColorLine stop positions are defined for outside [0, 1] which then need to be drawn? - This lack of drawing order definition also makes it hard to reason what should happen a sector is drawn across the 0° angle - for example from 340° to 20° or when the sector between endAngle and startAngle is wider than 360°. 3) The modulo rules "Start and end angle values can be outside the range [0, 360), and are converted to values within that range by applying a modulus operation." cause sudden discontinuities, which are not desirable. Think variations. 4) Spec text: "then the extend mode is not relevant and may be ignored. " is inconsistent with other gradient definitions and the behaviour and definition of ColorLine.

We've previously used the CSS and other W3C specs (Compositing) as a reference or blueprint. It helps implementors apply similar concepts or even reuse code.

The relevant sections are: https://drafts.csswg.org/css-images-4/#conic-gradient-syntax and in particular: https://drafts.csswg.org/css-images-4/#conic-color-stops

I suggest to do the following:

The CSS Images Lvl 4 spec describes this behaviour more intuitively in the following note:

Note: It may be more helpful to think of the gradient line as forming a spiral, where only the segment from 0deg to 360deg is rendered. This avoids any confusion about "overlap" when you have angles outside of the rendered region.

This helps understand how the above changes would make the behaviour more consistent with the other gradient types and the definition of ColorLine, and covers the open issues listed in the initial list of issues above.

As an additional visualisation: ColorLine intervals wider or narrower than or outside of [0, 1] can be normalized or scaled to a [0, 1] interval by scaling startAngle and endAngle accordingly. I.e. startAngle = 50, endAngle = 100 (sector angle 50°) for a defined interval of [-0.2, 1.2] is equivalent to startAngle = 50 - 0.2 50 = 40, endAngle = 50 + 1.2 50 = 110, and color stops scaled proportionally to [0, 1].

drott commented 2 years ago

For reference, updated test cases in https://github.com/googlefonts/color-fonts/pull/99/files - with example renderings in https://github.com/googlefonts/color-fonts/pull/99#issuecomment-1117357643 (to be used with caution, work-in-progress).

drott commented 2 years ago

Summary from virtual f2f (please correct me if anything from the meeting is inaccurately reflected):

drott commented 2 years ago

Draft in https://github.com/googlefonts/colr-gradients-spec/pull/362/files