Clean up the logic for <pattern> and update for SVG2 overflow=visible
The code in DrawingCtx.set_pattern()
is pretty convoluted; it is a more or less direct translation of the original C code, when I didn't understand how pattern transforms worked and everything was done by hand. Now we have much better machinery to do this in a cleaner fashion.
-
The code to figure out the transforms should basically look like this:
if have_pattern_viewBox {
Normalize (x, y, width, height) with respect to patternUnits/bbox.
Set up coordinate system from viewbox inside (x, y, width, height) with preserve_aspect_ratio
apply patternTransform
} else {
Normalize (x, y, width, height) with respect to patternUnits/bbox.
Set up coordinate system from patternContentUnits inside (x, y, width, height)
apply patternTransform
}
Then, about tiling the pattern:
Patterns are supposed to take a base tile defined by (x, y, width, height) and to repeat it infinitely so that it covers the ink extents of what is to be filled/stroked. The objectBoundingBox is just used to compute the pattern's coordinate system.
The current code is ugly, in part because to allow infinite tiling, it creates a Cairo surface and sets it as a pattern source with cairo::Extend::Repeat
. However, since surfaces must have integer sizes, it does some ugly scaling from the tile's size to a nearby integer size, and then changes the transform so that the final result is as if the tile were rendered at its exact non-integer size.
However, SVG2 tries to do something about overflow=visible for patterns, which was undefined in SVG1.1. Librsvg implements more or less the old behavior, where the pattern tile is clipped and repeated. SVG2 would prefer it so that that happens with the default overflow=hidden; but for overflow=visible there should be no clipping, and instead the contents of the pattern should be re-drawn entirely at each tile position.
See the associated issue for the spec on github and especially Tavmjong's comments about Inkscape's implementation of this.