Re: Complementary masking

Hi Paul,

At 2016-06-03Fri02:18:58+1200, Paul LeBeau sent:
> Hi J.R.
> It is possible to create an SVG that has uses two masks that are "complments" of one another and share a single path definition.

 That is very interesting, and useful for complementary clipping if either rendering is hairline-free or one is not concerned about hairlines. It achieves the same as the first of the 2 bullet points of my complementary clipping suggestion, but in a more elaborate (and less obvious) manner. (The other bullet point being to require resolution-agnostic rendering, i.e. hairline-free rendering.)

> Here's a simple example:
> <svg width="500" height="300">
>   <defs>
>     <path id="maskshape" d="M0 0 L200 0 C250 100 250 200 200 300 L0 300 Z"/>
>     <mask id="mask1">
>       <rect width="500" height="300" fill="black"/>
>       <use xlink:href="#maskshape" fill="white"/>
>     </mask>
>     <mask id="mask1-complement">
>       <rect width="500" height="300" fill="white"/>
>       <use xlink:href="#maskshape" fill="black"/>
>     </mask>
>   </defs>
>   <rect width="500" height="300" fill="darkorange" mask="url(#mask1)" />
>   <rect width="500" height="300" fill="darkslateblue" mask="url(#mask1-complement)" />
> </svg>
> https://jsfiddle.net/tukk4so2/

 Note that the first rectangle is unnecessary (https://jsfiddle.net/tukk4so2/4/ ) because the undrawn element colour is transparent black. In fact, the first mask is just a clip and can be done as normal with a clipPath element (https://jsfiddle.net/tukk4so2/5/ ):

<svg width="500" height="300">
  <defs>
    <clipPath id="clip1">
      <path id="clipshape" d="M0 0 L200 0 C250 100 250 200 200 300 L0 300 Z"/>
    </clipPath>
    <mask id="mask-clip1-complement">
      <rect width="500" height="300" fill="white"/>
      <use xlink:href="#clipshape" fill="black"/>
    </mask>
  </defs>
  <rect width="500" height="300" fill="darkorange" clip-path="url(#clip1)"/>
  <rect width="500" height="300" fill="darkslateblue" mask="url(#mask-clip1-complement)"/>
</svg>

With my proposed addition of ‘zero’ and ‘even’ insideness rules, the second rectangle would also be unnecessary and thus your example could be simplified to:

<svg width="500" height="300">
  <defs>
    <path id="clipshape" d="M0 0 L200 0 C250 100 250 200 200 300 L0 300 Z"/>
    <mask id="mask-clip1">
      <use xlink:href="#clipshape" fill="white"/>
    </mask>
    <mask id="mask-clip1-complement">
      <use xlink:href="#clipshape" fill="white" fill-rule="zero"/>
    </mask>
  </defs>
  <rect width="500" height="300" fill="darkorange" mask="url(#mask-clip1)"/>
  <rect width="500" height="300" fill="darkslateblue" mask="url(#mask-clip1-complement)"/>
</svg>

Combining these 2 simplifications… Ah, sorry, I realise that there's a mistake in my suggestion – I didn't realise that ‘clip-rule’ applied only to “graphics elements within a ‘clipPath’ element” and not to elements for which ‘clip-path’ applies to (https://www.w3.org/TR/SVG/masking#ClipRuleProperty ). In that case, 2 clipPath elements are still needed, not 1 as in my suggestion. Nonetheless, even with 2 clipPath elements, it is still a slight improvement:

<svg width="500" height="300">
  <defs>
    <clipPath id="clip1">
      <path id="clipshape" d="M0 0 L200 0 C250 100 250 200 200 300 L0 300 Z"/>
    </clipPath>
    <clipPath id="clip1-complement">
      <use xlink:href="#clipshape" clip-rule="zero"/>
    </clipPath>
  </defs>
  <rect width="500" height="300" fill="darkorange" clip-path="url(#clip1)"/>
  <rect width="500" height="300" fill="darkslateblue" clip-path="url(#clip1-complement)"/>
</svg>

 The reason that I renamed ‘maskshape’ to ‘clipshape’ is because all these variants only deal with the special-case of complementary clipping, even where masks are used to achieve this. They also are all subject to hairline artifacts due to the way that antialiasing being rendered is actually not exactly the clip special-case of masks. Thanks for demonstrating that complementary clipping is a special case of masking. I didn't notice that; I only noticed 2 of 3 of the following paths of specialisation (forming a sort of ‘parallelogram’ network, now with a crosslink revealed):
• complementary masking → complementary clipping → clipping;
• complementary masking → masking → clipping;
• complementary masking → masking → complementary clipping → clipping.

  clipping    <---    masking
      ^           ,'     ^
      |         ,'       |
      |       ,'         |
      |     L'           |
complementary <--- complementary
  clipping            masking

So with only clipping and masking available, complementary masking is still out-of-reach (ignoring filters, which are more general than all of this).
 For complementary masking (where both the original and the mask are added), there needs to be some additional container drawing element or attribute on an existing container element such as svg:g that makes its internal Z-order irrelevant and blends its children as addition before writing the result of the container to the canvas, rather than each being written over the last as defined at https://www.w3.org/TR/SVG/masking#SimpleAlphaBlending .
 Complementary clipping being a special-case of complementary masking, such a container element or property would also allow a workaround to the hairline artifacts of common rendering.

---- Hairlines ----
 The hairline is clearly visible in the example when the colours are the same or similar:

<svg width="500" height="300">
  <defs>
    <clipPath id="clip1">
      <path id="clipshape" d="M0 0 L200 0 C250 100 250 200 200 300 L0 300 Z"/>
    </clipPath>
    <mask id="mask-clip1-complement">
      <rect width="500" height="300" fill="white"/>
      <use xlink:href="#clipshape" fill="black"/>
    </mask>
  </defs>
  <rect width="500" height="300" fill="darkslateblue" clip-path="url(#clip1)"/>
  <rect width="500" height="300" fill="darkslateblue" mask="url(#mask-clip1-complement)"/>
</svg>
https://jsfiddle.net/tukk4so2/6/

And here's a demonstration of the translucency, with a third object behind of a contrasting colour:

<svg width="500" height="300">
  <defs>
    <clipPath id="clip1">
      <path id="clipshape" d="M0 0 L200 0 C250 100 250 200 200 300 L0 300 Z"/>
    </clipPath>
    <mask id="mask-clip1-complement">
      <rect width="500" height="300" fill="white"/>
      <use xlink:href="#clipshape" fill="black"/>
    </mask>
  </defs>
  <rect width="500" height="300" fill="red"/>
  <rect width="500" height="300" fill="black" clip-path="url(#clip1)"/>
  <rect width="500" height="300" fill="darkslateblue" mask="url(#mask-clip1-complement)"/>
</svg>
https://jsfiddle.net/tukk4so2/7/

 Seeing as the hairline issue is not specific to clipping, I've separated it into its own thread: ‘Hairline-free rendering – accurate antialiasing of a composition’ http://lists.w3.org/Archives/Public/www-svg/2016Jun/0004.html

Regards,
J. R. Haigh.

-- 
Sent from Claws Mail on NixOS.

Received on Saturday, 4 June 2016 22:56:56 UTC