3

I try to understand how SVG programs (like browsers) draw a shape by the given paths. I struggle to understand how a path is drawn and one clips part of a shape.

For example, consider the letter Å, and A with a top ring. The SVG code is like

<svg viewBox="0 0 1800 1800" xmlns="http://www.w3.org/2000/svg">
<path 
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M426 822q0 -47 -24 -71.5t-80 -24.5q-104 0 -104 96q0 46 25.5 71t78.5 25q56 0 80 -25t24 -71z
M319 515h-1l-85 -264h169z
M374 822q0 51 -52 51q-50 0 -50 -51q0 -49 50 -49q52 0 52 49z
" />
</svg>

JSFIDDLE

  • First line: draws the body of an A.
  • Second line: draws a top circle.
  • Third line: clips a triangle from the first line.
  • Fourth line: clips a small circle from the second line.

My question is: how do SVG programs understand to draw a shape by the second line, but clip a shape from an existing shape?

Obviously the answer is: if the path is within another path, it clips otherwise it draws.

There are two reasons that I think this not the whole picture:

  1. It needs huge calculations to find if a path is within another path.
  2. The order of lines is not important (the clipping path does not necessarily come after the drawing path).

This is, of course, not limited to SVG, as other vector formats such as EPS does the same.

To add a pragmatic perspective, please read the question as: how can we parse (in any programming language) the above d element to find out which path is drawing (black) and which is clipping (white) out of the four paths given in the above SVG?

Googlebot
  • 15,159
  • 44
  • 133
  • 229

3 Answers3

3

Broadly speaking, you don't parse the paths at all.

Instead you 'scan convert' each path to a series of rectangles, at the current resolution. Each reactangle may be as little as one pixel high, and there may be more than one rectangle at a given y value.

Do this for both the path to be filled or stroked and the path to apply as a clip, then intersect the series of rectangles. This is, obviously, a much simpler task. Then fill the rectangles.

This does, of course, produce a result which is only correct at a given resolution. Change the resolution and you get a different series of rectangles. However it produces the correct output at a decent speed. Intersecting two arbitrary paths to produce a new arbitrary path is obviously a much more complex task, and for the purpose of drawing the result, not one we need to perform.

KenS
  • 30,202
  • 3
  • 34
  • 51
  • The idea of scan conversion is very helpful, but still, I do not get the order independence. At the given resolution, the program find the pixels which are within the first path. Then, scan convert the second path: if the pixels are inside the previous path(s), it will remove the pixels, otherwise, will add them. In this case, the order of paths should be important. – Googlebot Jun 09 '20 at 20:17
  • Yes, the order of paths is important. A path is clipped by the clipping path in force at the time it is rendered. If you later update the clipping path it does not alter the rendered path. The new clip only applies to subsequent operations. This is absolutely true for EPS and I would assume it is true for SVG as well. – KenS Jun 10 '20 at 06:56
2

In the next examples I'm using the path for the letter A in your example.

In the first svg element the letter A is drawn from right to left while the hole of is drawn in the opposite dirrection: you get the "clipping".

In the second example I've reversed the part that is drawing the hole. Now this part is drawn in the same direction as the main part of the letter A. Now you won't get the "clipping"

In the third example I'm using the reversed path as before but I'm adding fill-rule="evenodd" Now the hole is clipped since the "fill-rule attribute is a presentation attribute defining the algorithm to use to determine the inside part of a shape".

svg{width:30%;border:solid}
<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path 
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319 515h-1l-85 -264h169z
" />
</svg>
<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path 
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319,515L402,251L233,251L318,515L319,515z
" />
</svg>

<svg viewBox="-40 -40 900 900" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="
M484 0l-42 137h-245l-47 -137h-147l237 664h161l234 -664h-151z
M319,515L402,251L233,251L318,515L319,515z
" />
</svg>
enxaneta
  • 31,608
  • 5
  • 29
  • 42
  • I got your point about the impact of the direction. However, I did not get the general algorithm for drawing the shapes. Is it really dictated by the direction? Then, what is the standard for it? – Googlebot Jun 09 '20 at 20:26
1

Whether a given path is "filled" or "clipped" depends on the "winding" algorithm being used, which is determined by the SVG fill-rule property, which defaults to nonzero.

For the nonzero mode:

This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and then examining the places where a segment of the shape crosses the ray. Starting with a count of zero, add one each time a path segment crosses the ray from left to right and subtract one each time a path segment crosses the ray from right to left. After counting the crossings, if the result is zero then the point is outside the path. Otherwise, it is inside. The following drawing illustrates the nonzero rule:

a visualization of the nonzero rule

The nonzero default is why you'll often hear that rotational direction is important, because clockwise creates fills and counterclockwise creates clips. (This is how it works in GeoJSON as well.)

For the evenodd mode:

This rule determines the "insideness" of a point on the canvas by drawing a ray from that point to infinity in any direction and counting the number of path segments from the given shape that the ray crosses. If this number is odd, the point is inside; if even, the point is outside. The following drawing illustrates the evenodd rule:

a visualization of the evenodd rule

The evenodd mode is a simpler to understand for basic shapes with holes, but not as flexible for clipping arbitrary chunks out that may not be completely isolated as islands.

Here's a great article called Understanding the SVG fill-rule Property that explains it further with code examples.

Ian Storm Taylor
  • 8,520
  • 12
  • 55
  • 72