Re: [csswg-drafts] [css-images] Mesh gradient / Freeform gradient / 2D gradient (#7648)

Thanks for proposing this up @JoshTumath, and for your [summary article](https://www.joshtumath.uk/posts/2024-06-11-mesh-gradients-in-css/) bringing more attention. 

As others have mentioned, a mesh gradient proposal was [previously added to SVG](https://www.w3.org/TR/2016/CR-SVG2-20160915/pservers.html#MeshGradients) & was implemented in Inkscape. I have never liked that syntax. It is both far too verbose to write by hand and (IMO) simplified in ways that obscure the logical structure. I like the proposed array of 2D colour points much better.

But, the SVG proposal is a direct representation of the model used in PDF and Illustrator & implemented in many rendering engines.  Therefore, for ease of implementation, any CSS 2D gradient should be rendered in terms of an equivalent Coons-patch mesh gradient.

I assume that is true for the Adobe Illustrator "freeform" gradient. It would be helpful if someone from Adobe could confirm that & describe how the points are converted into a mesh. @svgeesus mentioned that it used Delauney triangulation, but are those triangles then somehow arranged into rows and columns? Does the "spread" factor affect the triangulation, or is it used like a colour hint to adjust the colour interpolation along the triangle edges, according to the relative weights of the spread factors at each vertex?

Before settling on a syntax, it is also worth considering the full potential of mesh gradients, so that CSS can be intentional about which subset it supports and why. Josh's blog post mentions that in addition to simple points, Adobe's "freeform" tool supports colour stops defined as curves or lines of solid colour. Should that also be supported?

The scope of the feature also influences the name. We shouldn't borrow Adobe's name if we're not going to fully represent their feature (and their feature set might change). If we might in future add a more mesh-like mesh gradient, we shouldn't use that name for the simple version. For the array of 2D colour stop points, I'd prefer a descriptive name like `points-gradient()` or `scatterplot-gradient()`. But if some of the colour stops are curves, then a more generic name might be better.

So, let's consider the potential scope. To quickly summarize how these Coons-patch mesh gradients work:

- Gradient colour stops are positioned at the intersections of set of row and column lines. 
- Each line can actually be a curve, or angled in any direction, so they might not end up being vertical and horizontal, but the row and column relationships still define how colours are interpolated along the line and in between them.
- In between the lines, there is very complex but clearly defined math for how the colours fill in that grid cell, which is called a patch.
- The patches (grid cells) can be all sorts of shapes, so long as each can be described by a boundary of four Bézier path components (straight lines or single curve commands) connecting four colour stop positions.
- Triangular patches can be created by making one of the four colour stops overlap with another, with a zero-length path in between.
- Radial and conic-gradient effects can be created by bending either the row or column lines around into a loop where the end points overlap the starting points.

The markup structure from the SVG proposal is inspired by HTML table markup: It defines rows of cells (patches). The columns are implicit, based on the number of patches per row. Each patch logically has four stop points, but some of them are implicitly borrowed from the previous column in the row or row in the column. Each stop point also defines the curve of a segment of one of the row/column lines reaching to the next point. Figure from the spec shows the order in which each patch edge is defined, skipping the implicit ones:

![A square-ish shape with wavy edges. It is bright red in the centre, with the red extending to the middle of the four wavy sides. The four corners are light blue. The rest is filled in with smoothly interpolated shades of purple. Arrows have been overlaid on the four quadrants of the shape: the top-left corner has an arrow doing a squarish loop from top left of the full shape to top middle, centre, left middle, then back to top left. The top-right and bottom-left arrows only do three-quarters of a loop to avoid overlapping the previous loop. The bottom-right arrow only covers two edges of that quadrant.](https://github.com/w3c/csswg-drafts/assets/9876129/7ec03efe-4f57-4451-8dcc-ac95312fbf37)

<details>
<summary>Markup for that gradient</summary>
<pre><code>&lt;meshgradient x="50" y="50" id="example"&gt; &lt;!-- x, y used for initial point in first patch. --&gt;
  &lt;meshrow&gt; &lt;!-- No attributes, used only to define begin/end of row. --&gt;
 &lt;meshpatch&gt;
   &lt;stop path="c  25,-25  75, 25  100,0" stop-color="lightblue" /&gt;
   &lt;stop path="c  25, 25 -25, 75  0,100" stop-color="purple" /&gt;
   &lt;stop path="c -25, 25 -75,-25 -100,0" stop-color="red" /&gt;
   &lt;stop path="c -25,-25, 25,-75"        stop-color="purple" /&gt; &lt;!-- Last point not needed (closed path). --&gt;
 &lt;/meshpatch&gt;
 &lt;meshpatch&gt;
   &lt;stop path="c  25,-25  75, 25  100,0" /&gt; &lt;!-- stop-color from previous patch. --&gt;
   &lt;stop path="c  25, 25 -25, 75  0,100" stop-color="lightblue" /&gt;
   &lt;stop path="c -25, 25 -75,-25"        stop-color="purple" /&gt; &lt;!-- Last point not needed (closed path). --&gt;
   &lt;!-- Last path (left side) taken from right side of previous path (with points reversed). --&gt;
 &lt;/meshpatch&gt;
  &lt;/meshrow&gt;
  &lt;meshrow&gt; &lt;!-- New row. --&gt;
 &lt;meshpatch&gt;
   &lt;!-- First path (top side) taken from bottom path of patch above. --&gt;
   &lt;stop path="c  25, 25 -25, 75  0,100" /&gt; &lt;!-- stop-color from patch above. --&gt;
   &lt;stop path="c -25, 25 -75,-25 -100,0" stop-color="purple" /&gt;
   &lt;stop path="c -25,-25, 25,-75"        stop-color="lightblue" /&gt; &lt;!-- Last point not needed (closed path). --&gt;
 &lt;/meshpatch&gt;
 &lt;meshpatch&gt;
   &lt;!-- First path (top side) taken from bottom path of patch above (with points reversed). --&gt;
   &lt;stop path="c  25, 25 -25, 75  0,100" /&gt; &lt;!-- stop-color from patch above. --&gt;
   &lt;stop path="c -25, 25 -75,-25"        stop-color="lightblue" /&gt; &lt;!-- Last point not needed (closed path). --&gt;
   &lt;!-- Last path (left side) taken from right side of previous path (with points reversed). --&gt;
 &lt;/meshpatch&gt;
  &lt;/meshrow&gt;
&lt;/meshgradient&gt;
  </code></pre>
</details>

I don't like that the mesh curves are defined piece-meal for each stop. I don't like that the number of stops and curves per patch varies according to which row and column it is in. I don't like that the point defined within the stop geometry (the end-point of the curve segment) is *not* the point that receives the colour defined by that stop. I don't like that if you want a more complex curve between stops (more than a single path segment) you need to split a row or column in two and figure out the in-between colour not just for that segment, but for all the others. I don't like that it doesn't include colour-hints or interpolation colour spaces to adjust the colour change along each curve segment (those could probably be added, but might require changes to rendering engines). I don't like that if you want to create a radial or conic effect, you may nonetheless need to duplicate stops and segments, with the segment curve reversed.

As I mentioned at the top: I really don't like this syntax! It's only really suitable as output from other software.

But I do like all the results you can get from editing the mesh-line structure in a visual editor! Here are some examples I created in Inkscape for a future-focused sidebar in the book [_Using SVG with CSS3 and HTML5_](https://oreillymedia.github.io/Using_SVG/ch12-fill-files/):
![Two shapes. The first is a dark blue circle surrounded by a thick ring of swirling glossy light blue colour, creating an effect something like an eye. The colour changes in the swirling ring are at times soft, at times sharp along a curved ridge. The second shape is a five-point star where each point has a symmetrical progression of colours from the centre to the tip, with crisp angles matching the ins and outs of the star points.](https://github.com/w3c/csswg-drafts/assets/9876129/db5e1a12-7cc6-44e6-8981-28c868aa0bbd)

-- 
GitHub Notification of comment by AmeliaBR
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/7648#issuecomment-2173861488 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 17 June 2024 16:37:16 UTC