dnfield / vector_graphics

BSD 3-Clause "New" or "Revised" License
89 stars 43 forks source link

Optimize masks #22

Open dnfield opened 2 years ago

dnfield commented 2 years ago

Masks should be optimized away. Today they have to be implemented with a saveLayer/RT switch, which is especially sad.

Consider this SVG:

<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path fill-rule="evenodd" clip-rule="evenodd" d="M15.094 17.092a.882.882 0 01-.623-1.503l2.656-2.66H4.28a.883.883 0 010-1.765h12.846L14.47 8.503a.88.88 0 011.245-1.245l4.611 4.611a.252.252 0 010 .354l-4.611 4.611a.876.876 0 01-.622.258z" fill="#424242" />
  <mask id="a" maskUnits="userSpaceOnUse" x="3" y="7" width="18" height="11">
    <path fill-rule="evenodd" clip-rule="evenodd" d="M15.094 17.092a.882.882 0 01-.623-1.503l2.656-2.66H4.28a.883.883 0 010-1.765h12.846L14.47 8.503a.88.88 0 011.245-1.245l4.611 4.611a.252.252 0 010 .354l-4.611 4.611a.876.876 0 01-.622.258z" fill="#fff" />
  </mask>
  <g mask="url(#a)">
    <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0h24v24.375H0V0z" fill="#fff" />
  </g>
</svg>

It is trivially expressed as a single path that should be fast to draw. We should be able to easily calculate everything here and just encode that path.

dnfield commented 2 years ago

To clarify:

<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <path d="M15.094 17.092a.882.882 0 01-.623-1.503l2.656-2.66H4.28a.883.883 0 010-1.765h12.846L14.47 8.503a.88.88 0 011.245-1.245l4.611 4.611a.252.252 0 010 .354l-4.611 4.611a.876.876 0 01-.622.258z" fill="#fff" />  
</svg>

Draws the same thing as the above SVG with no masks, and would involve no saveLayers.

dnfield commented 2 years ago

(Probably what happened is the designer drew the arrow, selected it, changed the color, and/or maybe used some kind of masking tool in the editor...?)

dnfield commented 2 years ago

Relevant part of spec: https://www.w3.org/TR/SVG11/masking.html#Masking

dnfield commented 2 years ago

MDN: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/mask

c-h-i-a-m-a-k-a-2 commented 2 years ago

Further Masking Optimizer improvement: Multi-path masks should be able to also be resolved.

Example:

<svg viewBox="-10 -10 120 120">
  <mask id="myMask">
    <rect x="0" y="0" width="100" height="100" fill="white" />
    <path d="M10,35 A20,20,0,0,1,50,35 A20,20,0,0,1,90,35 Q90,65,50,95 Q10,65,10,35 Z" fill="black" />
  </mask>
  <polygon points="-10,110 110,110 110,-10" fill="orange" />
  <circle cx="50" cy="50" r="50" mask="url(#myMask)" />
</svg>
c-h-i-a-m-a-k-a-2 commented 2 years ago

Further Masking Optimizer improvement: Paths that have the "stroke-width" attribute set should be able to be optimized.

Example:

<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  <mask id="a" maskUnits="userSpaceOnUse" x="3" y="7" width="18" height="11">
    <path fill-rule="evenodd" clip-rule="evenodd" d="M15.094 17.092a.882.882 0 01-.623-1.503l2.656-2.66H4.28a.883.883 0 010-1.765h12.846L14.47 8.503a.88.88 0 011.245-1.245l4.611 4.611a.252.252 0 010 .354l-4.611 4.611a.876.876 0 01-.622.258z" fill="#fff"  stroke-width="2"/>
  </mask>
  <g mask="url(#a)">
    <path fill-rule="evenodd" clip-rule="evenodd" d="M0 0h24v24.375H0V0z" fill="#fff" />
  </g>
</svg>