Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarifications for Sweep gradients needed #361

Open
drott opened this issue Apr 20, 2022 · 7 comments
Open

Clarifications for Sweep gradients needed #361

drott opened this issue Apr 20, 2022 · 7 comments

Comments

@drott
Copy link
Collaborator

drott commented Apr 20, 2022

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

For context, discussion in: BlackFoundryCom/black-renderer#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
Copy link
Collaborator Author

drott commented Apr 21, 2022

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
Copy link
Collaborator Author

drott commented Apr 22, 2022

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

@drott
Copy link
Collaborator Author

drott commented May 2, 2022

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
Copy link
Collaborator Author

drott commented May 4, 2022

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:

  • Remove the notion of using a modulo operation to bring angle values into the 0-360° range, this ensure continuity, also under variations when color stops or angles are moved around. It also allows to render color stop intervals that are narrower or wider than [0, 1].
  • Keep aligning color stop offset zero with start angle, color stop offset 1 with end angle, but it's okay for start and end angle to be < 0° or > 360° - they do contribute to the rendering by "stretching" the gradient.
  • Define the drawn gradient as the gradient that is drawn for the projected color line between 0° and 360°. This means there's no need to draw more than one rotation, and overlap is handled correctly.
  • Because startAngle and endAngle are defined as F2DOT14 which allows values between -2 1.99993896484375 and our conversion definition is 180° in counter-clockwise degrees per 1.0 of value. a full circle from 0 degrees can only be defined as -360 to 0, and applying the modulo operation. Removing modulo, as explained above, I suggest to change the conversion to add an offset so that (startAngle + 1) * 180 or in other words startAngle * 180 + 180. That makes a value -1 0 degrees and +1 360 degrees, thus allowing even room for scaling startAngle and endAngle past 0 and past 360 on both sides. (Alternatively: Map -2 in F2DOT14 to 0° degrees, i.e. offset +2 before multiplication with 180°, or in other words +360°, but then there's no room for putting start angle < 0°).

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
Copy link
Collaborator Author

drott commented May 4, 2022

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

@drott
Copy link
Collaborator Author

drott commented May 4, 2022

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

  • We meet again in roughly a week, after reading more on CSS spec definition, looking at additional examples, experimenting with alternative resolution (clamping to 0,1, drawing wedges/sectors)
  • We tend to agree there's value in making this consistent with CSS, instead of defining sectoring. Trade-offs: With CSS there's a discontinuity at 0 degrees but a more consistent mapping of the color line and no restrictions or special handling of Color Lines needed. Whereas with the current definition, there's the surprising side effect when the start and end angle cross under variations ("pacman becoming small wedge").
  • There's an alternative resolution where we stay closer with the current definition, but clarify the note on what area of the drawing space is restricted, and explain the intention that sectors/wedges are the intention for all repeat modes if the start and end angle are not the same.

@drott
Copy link
Collaborator Author

drott commented May 25, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant