canvg icon indicating copy to clipboard operation
canvg copied to clipboard

Canvas and SVG render results are different

Open Corey600 opened this issue 6 years ago • 7 comments

Canvas and SVG render results are different, as follows:

canvg result: canvas

svg result: svg

svg code:

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" 
  xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="图层_1" x="0px" y="0px" viewBox="0 0 1000 600" enable-background="new 0 0 1000 600" xml:space="preserve" style="width: 583.333px; height: 350px;">
  <g id="c_105_" opacity="0.85">
    <g>
      <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="331.7825" y1="-749.6113" x2="553.6317" y2="-352.8736" gradientTransform="matrix(0.9996 2.833383e-02 2.833383e-02 -0.9996 106.1187 -138.3644)">
        <stop offset="0.2354" style="stop-color:#225CFF"/>
        <stop offset="0.4433" style="stop-color:#91AEFF"/>
        <stop offset="0.6243" style="stop-color:#225CFF"/>
        <stop offset="0.9118" style="stop-color:#225CFF"/>
      </linearGradient>
      <path fill="url(#SVGID_1_)" d="M569.5,310.5c0,0-82.9,105.6-154.3,109c-71.4,3.4-255.4-120.4-285.7-81    c-63.5,36,3.8,117.3,36,107.6c70.5-21.2,136.9-18,97.7,12c-39.3,30,35.9,29.1,74.3,28.4c38.4-0.7,79.7-43.4,152-12    c72.3,31.4,47.6,68.7,140,34.8s50.1-76.6,172-51.2c121.9,25.5,23.1,163,99.7,101.2c68.6-55.4,82.6-121.5,39.7-159.3    s-135.5-60.7-135.5-60.7L569.5,310.5z"/>
      <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="487.5439" y1="377.6481" x2="663.0073" y2="553.1114" gradientTransform="matrix(0.9871 -0.1599 -0.133 -0.8208 167.9462 844.796)">
        <stop offset="0" style="stop-color:#FF003A"/>
        <stop offset="1" style="stop-color:#FF837F"/>
      </linearGradient>
      <path fill="url(#SVGID_2_)" d="M480.7,306.8c-64.6,46.1-81.6,79.5-29.1,117.2c52.5,37.8,194.5,22.5,270.3-12.8    s153.5-92.6,232.4-63.7c0,0-103.1-52.7-219.3-72.5C618.7,255.2,542.1,263,480.7,306.8z"/>
    </g>
  </g>
  <g id="c_109_" opacity="0.85">
    <g>
      <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1721.9917" y1="-417.6683" x2="1473.6665" y2="-643.0983" gradientTransform="matrix(0.9997 -2.570367e-02 -2.210279e-02 -0.8596 -1346.933 -212.363)">
        <stop offset="0" style="stop-color:#FF4E00"/>
        <stop offset="1" style="stop-color:#FFB696"/>
      </linearGradient>
      <path fill="url(#SVGID_3_)" d="M236.9,86.4c0,0,54,41.1,25.4,96.4c-28.5,55.3-79.8,80.9-137.8,89.1c-57.9,8.3-111.4,20.3-60,28.8    c51.3,8.6,239.8,5.3,341.6-79.9c101.7-85.2,60.5-111.5,53-129.5S383,45.5,319.1,63C255.2,80.6,236.9,86.4,236.9,86.4z"/>
      <g>
        <path fill="#FFD833" d="M456,87.9c0,0-74.6-29.4-111.3,9.2c-41.6,43.8-60.3,102.8-122.2,108.4s-94.6-26.7-75.9-72.2     c18.8-45.4,78.1-86.7,159.9-94.4C388.3,31.3,433.2,59.8,456,87.9z"/>
      </g>
    </g>
  </g>
</svg>

Corey600 avatar Aug 20 '18 07:08 Corey600

yea it's kind of a regression from https://github.com/canvg/canvg/pull/602/files ...

i'm not treating the g element differently but it appears that the group opacity applies to the group, not to the individual elements. this present a challenge when working w/ html5 canvas as you can only have a global opacity.

for example:

<svg>
<g opacity="0.5">
<rect x="0" y="0" width="100" height="100" fill="red" />
<rect x="50" y="50" width="100" height="100" fill="green" />
</g>
</svg>

the rectangles should both be pale colored due to the 0.5 but the green should sit on top of the red. canvg unfortunately has an overlap area: screen shot 2018-08-20 at 7 59 47 pm

i think we'll need to change the handling of g to render in temporary canvas, then apply opacity and render in the main canvas for it to be truly working.

gabelerner avatar Aug 21 '18 01:08 gabelerner

@gabelerner Thanks for your help! Has it been released in version 1.5.2 ?

Corey600 avatar Aug 21 '18 02:08 Corey600

this issue hasn't been fixed until somebody (or me) writes code for it. i'm just documenting for now.

gabelerner avatar Aug 21 '18 02:08 gabelerner

Well, thank you all the same.

Corey600 avatar Aug 21 '18 12:08 Corey600

It sounds like we'd need to build an analog of getComputedStyle for SVG so they can inherit the opacity from parents g and we can treat groups as non-opaque.

@gabelerner I'm seeing that the gradientTransform for the first group is messing with the canvas cropping:

cropped

If I remove said property from the first group, the canvas doesn't crop (of course, the gradient is lost in the SVG element so this is just a comment)

not_cropped

It seems the logic for parsing a gradient with matrix type transform is affecting the calculated size of the output, which shouldn't be since the definition of the gradient is not an element by itself.

ffflabs avatar Aug 24 '18 11:08 ffflabs

@gabelerner another comment:

If we take just the second group (I'm adjusting its size and viewbox for the example)

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" 
  xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="图层_1" x="0px" y="0px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve" style="width: 300px; height: 300px;">
  
  <g id="c_109_" opacity="0.85">
    <g>
      <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1721.9917" y1="-417.6683" x2="1473.6665" y2="-643.0983" gradientTransform="matrix(0.9997 -2.570367e-02 -2.210279e-02 -0.8596 -1346.933 -212.363)">
        <stop offset="0" style="stop-color:#FF4E00"/>
        <stop offset="1" style="stop-color:#FFB696"/>
      </linearGradient>
      <path fill="url(#SVGID_3_)" d="M236.9,86.4c0,0,54,41.1,25.4,96.4c-28.5,55.3-79.8,80.9-137.8,89.1c-57.9,8.3-111.4,20.3-60,28.8    c51.3,8.6,239.8,5.3,341.6-79.9c101.7-85.2,60.5-111.5,53-129.5S383,45.5,319.1,63C255.2,80.6,236.9,86.4,236.9,86.4z"/>
      <g>
        <path fill="#FFD833" d="M456,87.9c0,0-74.6-29.4-111.3,9.2c-41.6,43.8-60.3,102.8-122.2,108.4s-94.6-26.7-75.9-72.2     c18.8-45.4,78.1-86.7,159.9-94.4C388.3,31.3,433.2,59.8,456,87.9z"/>
      </g>
    </g>
  </g>
</svg>

The problem can be seen as:

nested_groups

But if we unnest the groups, as in:

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" 
  xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="图层_1" x="0px" y="0px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve" style="width: 300px; height: 300px;">
  
  <g id="c_109_" opacity="0.85">
      <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1721.9917" y1="-417.6683" x2="1473.6665" y2="-643.0983" gradientTransform="matrix(0.9997 -2.570367e-02 -2.210279e-02 -0.8596 -1346.933 -212.363)">
        <stop offset="0" style="stop-color:#FF4E00"/>
        <stop offset="1" style="stop-color:#FFB696"/>
      </linearGradient>
      <path fill="url(#SVGID_3_)" d="M236.9,86.4c0,0,54,41.1,25.4,96.4c-28.5,55.3-79.8,80.9-137.8,89.1c-57.9,8.3-111.4,20.3-60,28.8    c51.3,8.6,239.8,5.3,341.6-79.9c101.7-85.2,60.5-111.5,53-129.5S383,45.5,319.1,63C255.2,80.6,236.9,86.4,236.9,86.4z"/>
  </g>
  
  <g>
        <path fill="#FFD833" d="M456,87.9c0,0-74.6-29.4-111.3,9.2c-41.6,43.8-60.3,102.8-122.2,108.4s-94.6-26.7-75.9-72.2     c18.8-45.4,78.1-86.7,159.9-94.4C388.3,31.3,433.2,59.8,456,87.9z"/>
  </g>
</svg>

The opacity of the background group doesn't affect the front path fill

unnested

Again, I don't know if unnesting groups would be of any help, since there are plenty scenarios where they should be nested indeed.

ffflabs avatar Aug 24 '18 11:08 ffflabs

Hi folks. Here's a separate reduction of a similar issue (I believe), involving just a single rect.

Entering this svg on the test page:

<svg>
<rect x="10" y="10" width="100" height="100" fill="#888" stroke-width="6" stroke="#888" opacity="0.5">
</rect>
</svg>

produces this output:

stroke-opacity-combines-with-fill

The stroke and fill appear to have their opacity applied separately, leading to the stroke/fill overlap being darker. Applying the opacity value to both together ought to produce a flat color, as on the right.

nowthis avatar Jan 02 '22 15:01 nowthis