SVG - How to apply a gradient transform matrix to a linear gradient brush?

独自空忆成欢 提交于 2021-01-28 12:09:11

问题


I'm writing a SVG viewer application in c++ language. I actually face a transform issue in several SVG files that I cannot figure out.

Considering the following SVG file:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg9686" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="90mm" width="145mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 145 90" xmlns:dc="http://purl.org/dc/elements/1.1/">
 <defs id="defs9680">
  <linearGradient id="linearGradient6593-0" gradientUnits="userSpaceOnUse" x1="74.658" y1="-398.92" x2="75.519" y2="-485.7" gradientTransform="matrix(1.0069 0 0 1.19 1.4571 709.77)">
   <stop id="stop6595" stop-color="#be245a" offset="0"/>
   <stop id="stop6600" stop-color="#e46e6f" offset=".48408"/>
   <stop id="stop6597" stop-color="#f1a769" offset="1"/>
  </linearGradient>
 </defs>
 <g id="layer1" transform="translate(-7.631 -139.36)">
  <rect id="rect3690-4-2-09-4-2-8-0" height="90" width="145" y="139.36" x="7.631" fill="url(#linearGradient6593-0)"/>
 </g>
</svg>

This is basically a rectangle filled with a gradient brush, from orange to magenta. The rectangle size is 90x145, located at coordinates [0, 0] after all the transformations are applied.

If I well understood the theory, to draw the rectangle correctly, I should process the following steps:

  1. Calculate the gradient brush bounding box, given by the x1, y1, x2 and y2 values, in the local document coordinates system. This should be done by applying the given gradient transform matrix to the points computed from x1, y1, x2 and y2
  2. As the gradient units are declared to be "user space on use", calculate the brush from the linear gradient tag values and the transformed bounding box
  3. Transform the rectangle coordinates to fill, to also put it in the document coordinates system
  4. Using the previously created brush, draw the rectangle at his transformed coordinates

Applying the above described process, I expected to reach the following result:

But I get this result:

If I manually change all the values in the above source file, to remove all transformations and apply all the values in the document coordinates, the linear gradient is filled correctly in my rectangle. For that reason, I would be really thankful if somebody could explain to me

  1. What I'm doing wrong in my process?
  2. How I should calculate the linear gradient values?
  3. How should I apply the gradient matrix to the given values? (i.e. I expected that applying the matrix to the values should transform them in the document system coordinates, so the transformed values should roughly give x1 = 0, y1 = 0, x2 = 90 and y2 = 145 as result, but it's not the case)

NOTE a demonstration in mathematical form would be welcome


回答1:


First, using the term "bounding box" for the gradient I think is not helpfull. The four values x1, x2, y1, y2 describe a vector onto which the gradient stops are matched, and to which the gradient normal is perpendicular (before applying any transform). A "box" has no meaningfull relation to the gradient properties.

The gradient vector could be visualized as a line element

<line x1="74.658" y1="-398.92" x2="75.519" y2="-485.7" />

The first step is to apply gradientTransform="matrix(1.0069 0 0 1.19 1.4571 709.77)". The resulting line would be drawn as

<line x1="76.6302" y1="235.055" x2="77.4972" y2="131.787" />

(Since the transformation introduces no skew, the gradient normal is still perpendicular to that line.)

At this point, the gradient is applied to the rectangle in its local coordinate system

<rect width="145" height="90" x="7.631" y="139.36"/>
<line x1="76.6302" y1="235.055" x2="77.4972" y2="131.787" />

Only after that, the final transform="translate(-7.631 -139.36)" is applied to both the rectangle and the vector:

<rect width="145" height="90" x="7.631" y="139.36"/>
<line x1="70" y1="95.7" x2="69.87" y2="-7.57" />

Note that this would even be true if the transform had been applied to the rect directly, instead of an enclosing group. Applying the transform to an element is always the last operation to perform.

Where you went wrong, I think, is interpreting userSpaceOnUse as the final coordinate system after applying transforms to the rectangle. But what it is, is the coordinate system

in place at the time when the gradient element is referenced,

thus before further transformations.



来源:https://stackoverflow.com/questions/48358560/svg-how-to-apply-a-gradient-transform-matrix-to-a-linear-gradient-brush

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!