Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SVG transparency with multiple gradients

I am trying to find out the optimal model for 4-way gradient filling. My latest model is this fiddle:

<svg height="360" width="400" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
    <clipPath id="C">
      <path d="M 100 200 L 300 58 L 400 250 L 300 341 Z" />
    </clipPath>
    <radialGradient id="G1" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
      <stop offset="0%"   style="stop-color:rgb(255,0,0);    stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(128,128,64); stop-opacity:0" />
    </radialGradient>
    <radialGradient id="G2" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
      <stop offset="0%"   style="stop-color:rgb(0,255,0);    stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(128,128,64); stop-opacity:0" />
    </radialGradient>
    <radialGradient id="G3" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
      <stop offset="0%"   style="stop-color:rgb(0,0,255);    stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(128,128,64); stop-opacity:0" />
    </radialGradient>
    <radialGradient id="G4" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
      <stop offset="0%"   style="stop-color:rgb(255,255,0);  stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(128,128,64); stop-opacity:0" />
    </radialGradient>
  </defs>
  <ellipse cx="100" cy="200" rx="200" ry="200" clip-path="url(#C)" fill="url(#G1)" />
  <ellipse cx="300" cy="58"  rx="200" ry="200" clip-path="url(#C)" fill="url(#G2)" />
  <ellipse cx="400" cy="250" rx="200" ry="200" clip-path="url(#C)" fill="url(#G3)" />
  <ellipse cx="300" cy="341" rx="200" ry="200" clip-path="url(#C)" fill="url(#G4)" />
</svg>

which comes with result:

enter image description here

But when I change order of <ellipse> elements (see this fiddle):

  <ellipse cx="100" cy="200" rx="200" ry="200" clip-path="url(#C)" fill="url(#G1)" />
  <ellipse cx="300" cy="58"  rx="200" ry="200" clip-path="url(#C)" fill="url(#G2)" />
  <ellipse cx="300" cy="341" rx="200" ry="200" clip-path="url(#C)" fill="url(#G4)" />
  <ellipse cx="400" cy="250" rx="200" ry="200" clip-path="url(#C)" fill="url(#G3)" />

result changes to:

enter image description here

Which means transparency is not calculated same way. I would like to have transparency calculated always with same results, regardless of order of the elements. What is a solution for this issue?

like image 869
Ωmega Avatar asked Jun 17 '12 16:06

Ωmega


1 Answers

At any given point on that surface, the final color is computed by taking into account all the elements that cover that point, in their stacking order. There is always a stacking order in SVG, you can't have all the gradients taken as a whole.

A true 4-way gradient must have at each point a color that mixes in an amount of each color equal to the percentage of the distance from the color source to the shape edge at that specific point. But that can't be obtained with stacked layers, and here's why:

Assuming that you have layers with a uniform transition from color to transparency, and that the layer order is red, green, blue, yellow, with red at the bottom and yellow on top, then a point right in the middle will have roughly

 50% yellow + 50% of the color below

which becomes

(50% yellow + 50%(50% blue + 50% of the color below))
->
(50% yellow + 50%(50% blue + 50%(50%green + 50%(50% red +50% background)))) 
->
(50%yellow + 25%blue + 12.5%green + 6.25%red)

which is far from a 50% of everything mix.

You could try to adjust how abruptly each layer becomes transparent, knowing that the final transparency isn't given by that layer's transparency alone, but also by how opaque the layers above it are. So, if you want to obtain 25% of each layer in the middle, then you'd have to solve the equation:

(y% yellow + (100-y)%(b% blue + (100-b)%(g% green + (100-g)%(r% red))))
=
(25% yellow + 25% blue + 25% green + 25% red)

While this equation is fairly simple to solve (y=25, b=33.3, g=50, r=100), the equation must stand at each point on the surface, and I doubt that you can get a valid answer with simple radial or linear gradients.

like image 104
Sergiu Dumitriu Avatar answered Oct 07 '22 18:10

Sergiu Dumitriu