Advanced Gradients for SVG

Adding a Mesh Patch Gradient.


Table of Contents

The need for advanced gradients.
Requirements
Types of Advanced Gradients
Mesh Gradients
Diffusion Curves
Why Coons Patch Mesh Gradients?
Proposed SVG Syntax
Defining a Coons Patch Mesh
Examples
Remaining Issues
Tensor Meshes
Different Corners or Paths at Patch Boundaries
Allow Additional Path Elements
Standalone Meshes
Conclusion
Bibliography

Advanced gradients are important where ever one desires color variations that blend smoothly along a curved path. This includes creating life-like vector art, 3D lighting effects on irregular surfaces, or fills that follow curved contours. It is possible to simulate advanced gradients by using a combination of linear and radial gradients, masking, and/or the Gaussian blur filter. This can be a complicated and tedious procedure. Never-the-less some artists have done so as illustrated by these drawings of a Sports Car and a Tea Cup. Using advanced gradients can result in quicker and better content creation as well as rendering.


The requirements for advanced gradients have been summarized in an SVG working group document.[SVGWGAdvGrad] That document focuses on mostly technical requirements. For this talk, I will stress these requirements:

  • Able to render life-like objects.

  • Easy for content creators to use.

  • Straight forward to implement.

Also desirable is the ability to play well with other graphics standards.

The most common type of advanced gradient is the mesh gradient. A mesh gradient is composed of a set of color points (stops) defined over a two-dimensional surface. The space between the points if then filled in via an algorithm using the color value at those points. Mesh gradients can be use to simulate a wide variety of gradients including conical gradients. A number of different types of mesh gradients can be found in use. Here are a few examples.

Creating a triangle mesh from just a set of points (stops) using Delauney triangulation seems quite attractive and thus was investigated. With Gourand shading the mesh had problems with discontinuities. Phong shading might provide better results but has not been tested.

There are a couple of problems with these kinds of meshes. Large numbers of small triangles are required to handle curved edges. While automated construction of these meshes seems to be quite practical from photographs[Swaminarayan2007], creation by an artist's hand seem less practical.

Diffusion curves use colors along a curve to define a gradient.[Orzan2008] Jasper van de Gronde will show an example of their use.[vandeGronde2011] Diffusion curves are a rather new idea and understanding their potential and problems still needs quite a bit of work.

Given the number of choices for advanced gradients, why choose Coons Patch meshes?

  • Can produce life-like drawings.

  • Easy conceptually to create using drawing programs as demonstrated in their use by Adobe Illustrator and CorelDraw.

  • Rendering method solved: PDF (poppler), Cairo (trunk). Already supported by a number of standards and programs: PDF, Scribus, Cairo. It is a mature technology.

  • Can simulate a wide variety of other gradients including a conical gradient.

Each “patch” requires:

  • Four Bézier paths (4 corner points, 8 “handle” points).

  • Four colors (defined at each corner).

Adjacent patches share two colors and one Bézier path. In PostScript/PDF, each patch can share one edge definition with the immediately preceding defined patch.

Existing SVG gradients:

	

<linearGradient x1="100" y1="100" x2="200" y2="200">
<stop offset="0" stop-color="#000000">
...
<stop offset="1" stop-color="#ff0000">
</linearGradient>

<radialGradient cx="100" cy="100" r="100" fx="150" fy="150">
<stop offset="0" stop-color="#000000">
...
<stop offset="1" stop-color="#ff0000">
</radialGradient>
	
      

Mesh gradients can be made to fit into the existing gradient structure with few caveats:

  1. Associate a <stop> to a patch side. A patch then consists of four <stop>s, not all of which need to be defined (see below).

  2. A mesh gradient could be used as a graphical element by itself as in the patch sides define a boundary. However, to fit into the current gradient structure, they must be used to paint another object. The SVG Working Group has requested that it be possible to use a mesh gradient as a stand-alone object or as a paint-server (if defined in the <defs> section).

A “rectangular” grid of patches seems to be the best way to go from looking at use cases. There are two advantages of preserving the rectangular structure in the syntax:

  • It aids content creation as it is easy to insert new rows and columns (or delete them).

  • It is possible to compact the data by sharing sides and colors between adjacent patches.

The rectangle structure of the grid in the discussion below with using terms such as left, right, above is conceptional only. The actual patches can be distorted into any desirable shape.


Bézier cubic curves are required for paths. Non-cubic Bézier paths are converted to Bézier cubic curves by the renderer. One side can be zero length to allow for a triangle mesh or a conical gradient.

After experimenting with rendering mesh gradients using a modified version of Inkscape, and feedback from Tab Atkins and others in the SVG working group, I have proposed the following syntax:

	
		 <meshGradient x="100" y="100" id="example"> <!-- x, y used for initial moveto. -->
		 <meshRow> <!-- No attributes, used only to define begin/end of row. -->
		 <meshPatch>
		 <stop path="c  25,-25  75, 25  100,0" stop-color="blue" />
		 <stop path="c  25, 25 -25, 75  0,100" stop-color="green" />
		 <stop path="c -25, 25 -75,-25 -100,0" stop-color="orange" />
		 <stop path="c -25,-25, 25,-75"        stop-color="red" /> <!-- Last point not needed (closed path). -->
		 </meshPatch>
		 <meshPatch>
		 <stop path="c  25,-25  75, 25  100,0" /> <!-- stop-color from previous patch. -->
		 <stop path="c  25, 25 -25, 75  0,100" stop-color="green" />
		 <stop path="c -25, 25 -75,-25"        stop-color="orange" /> <!-- Last point not needed (closed path). -->
		 <!-- Last path (left side) taken from right side of previous path (with points reversed). -->
		 </meshPatch>
		 ...
		 </meshRow>
		 <meshRow> <!-- New row. -->
		 <meshPatch>
		 <!-- First path (top side) taken from bottom path of patch above. -->
		 <stop path="c  25, 25 -25, 75  0,100" /> <!-- stop-color from patch above. -->
		 <stop path="c -25, 25 -75,-25 -100,0" stop-color="orange" />
		 <stop path="c -25,-25, 25,-75"        stop-color="red" /> <!-- Last point not needed (closed path). -->
		 </meshPatch>
		 <meshPatch>
		 <!-- First path (top side) taken from bottom path of patch above (with points reversed). -->
		 <stop path="c  25, 25 -25, 75  0,100" /> <!-- stop-color from patch above. -->
		 <stop path="c -25, 25 -75,-25"        stop-color="orange" /> <!-- Last point not needed (closed path). -->
		 <!-- Last path (left side) taken from right side of previous path (with points reversed). -->
		 </meshPatch>
		 ...
		 </meshRow>
		 ...
		 </meshGradient>
      

The following rules apply:

  • Allowed path elements are:

    • closepath: 'Z', 'z'.

    • lineto: 'L', 'l'

    • curveto (cubic): 'C', 'c'

  • A 'Z' or 'z' path closes the path to the nominal end point for a given patch.

    • For the patch in the first column in the first row this is the starting point.

    • For any other patch in the first column, this is the the lower-left corner of the patch directly above.

    • For any patch not in the first column, this is the the lower-right corner of the previous patch in the row.

  • Each 'L' or 'l' path consists of one point, using the last node of the previous path as the first node of the current path, except if the path is the last path in a patch, in which case no point is given.

  • Each 'C' or 'c' path consists of three points, using the last node of the previous path as the first node of the current path, except if the path is the last path in a patch, in which case it consists of only two points as the last point has already be given.

  • A missing path is equivalent to a 'Z' or 'z' path.

  • The stop-color and stop-opacity are assigned to the corner at the start of the path. This follows the Cairo conventions (which follows the PDF convention).

I am not an artist!

Done with hacked Inkscape program which at the moment lacks many important editing capabilities.






The SVG working group has endorsed the addition of Coons Patch Mesh gradients to the SVG standard. This will satisfy the need of content creators for a more complex shadings. The exact syntax for the mesh gradients is still being worked on. The proposed syntax has been tried out with a modified version of Inkscape and works quite well.