Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying gradientTransform to linearGradient

Tags:

svg

I have trouble applying all transformations matrices in svg file. If file contains only paths, and without gradients - all works fine. But after adding and applying gradientTransform to linearGradient causes rendering errors.

Algorithm:

  1. multiply group and path matrices
  2. multiply path transform whith linked linearGradient gradientTransform
  3. apply transform to path
  4. apply gradientTransform to linearGradient

Input file:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="linearGradient3755">
      <stop offset="0" />
      <stop offset="1" stop-opacity="0" />
    </linearGradient>
    <linearGradient id="linearGradient3761" xlink:href="#linearGradient3755" x1="16.162441" y1="66.128159" x2="117.17769" y2="66.128159" gradientUnits="userSpaceOnUse" />
  </defs>
  <g transform="translate(0,-924.36218)">
    <g transform="matrix(0.95516166,-0.46694301,0.71994792,0.61949768,-706.90347,408.6637)">
      <path d="M 2.1428571,3 L 126.07143,3 L 126.07143,123 L 2.1428571,123 z" transform="translate(0,924.36218)" style="fill:#ff0000;fill-opacity:1;stroke:none" />
      <path d="M 16.162441,21.428905 L 117.17769,21.428905 L 117.17769,110.8274 L 16.162441,110.8274 z" transform="matrix(0.96592583,-0.25881905,0.25881905,0.96592583,-17.36888,938.82017)" style="fill:url(#linearGradient3761);fill-opacity:1;stroke:none" />
    </g>
  </g>
</svg>

Output file:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="linearGradient3755">
      <stop offset="0" />
      <stop offset="1" stop-opacity="0" />
    </linearGradient>
    <linearGradient id="linearGradient3761" xlink:href="#linearGradient3755" y1="95.70844949469" x1="26.6443734054997" y2="33.95075671356" x2="101.020294143975" gradientUnits="userSpaceOnUse" />
  </defs>
  <path fill="#f00" d="M -37.2023,57.8018 C -37.2023,57.8018 81.1699,-.0654 81.1699,-.0654 81.1699,-.0654 167.5631,74.2746 167.5631,74.2746 167.5631,74.2746 49.1909,132.1418 49.1909,132.1418 z" />
  <path fill="url(#linearGradient3761)" d="M -15.4903,74.3628 C -15.4903,74.3628 58.8856,12.6051 58.8856,12.6051 58.8856,12.6051 143.155,55.2964 143.155,55.2964 143.155,55.2964 68.7791,117.0541 68.7791,117.0541 z" />
</svg>

My question is why gradients are different, when vectors are equal? And how can I fix it?

like image 563
RazrFalcon Avatar asked Nov 02 '22 19:11

RazrFalcon


1 Answers

Your algorithm is correct as long as your transform matrix doesn't skew the gradient.

In your case, the final transformation that is applied to the gradient is

translate(0,-924.36218) matrix(0.95516166,-0.46694301,0.71994792,0.61949768,-706.90347,408.6637) matrix(0.96592583,-0.25881905,0.25881905,0.96592583,-17.36888,938.82017)

which is the same as

matrix(0.7363,-0.6113,0.9426,0.4775,-47.6376,74.0101)

which is equivalent to

translate(-47.6376, 74.0101) rotate(-39.7) scale(0.957, 0.9695) skewX(23.4337)

If the last skew(23.4337) weren't there, everything should work as you desired.

It's pretty hard, or more accurately I should say impossible, to achieve the skew effect without using gradientTransform attribute. According to the SVG 1.1 Specification:

gradientTransform contains the definition of an optional additional transformation from the gradient coordinate system onto the target coordinate system (i.e., userSpaceOnUse or objectBoundingBox). This allows for things such as skewing the gradient.

It is designed to achieve the skew effect.

If you are still curious why it's impossible, I've made a simple example shown in the image below.

an example explaining skew effect

On the left, we have a horizontal linear gradient applied to a square. If we skew the gradient on X axis for 20 degrees, the effect is rendered like a combination of 2 shapes:

  • a triangle on the left side whose fill is a solid color
  • the same square filled with the same gradient, but the square itself is skewed 20 degrees, and then clipped to its original form.

Now you can't simply use a line to describe the gradient, which is why we have to use gradientTransform attribute.

like image 181
YeHao Avatar answered Dec 02 '22 12:12

YeHao