CairoSVG icon indicating copy to clipboard operation
CairoSVG copied to clipboard

Masks use alpha instead of luminance

Open emrosenf opened this issue 8 years ago • 8 comments
trafficstars

When masking, the color values of all children are not being properly applied bitwise.

To illustrate, here is a simple example. There is a red rectangle at the base of the image with a masked blue rectangle on top of it. The mask has some padding, but is otherwise white, with a black rectangle in the center.

The white part of the mask mixed with blue is white (e.g. 0xFFF & 0x00F = 0x00F). The black part of the mask mixed with blue should be transparent (e.g. we can see the red square in the background). Therefore we should see effectively three rectangles: red background, blue on top, with red at the very center. In 2.0.2 we only see the first two rectangles. The part of the mask with fill #000 is being applied as if the fill were #fff.

Here is how CairoSVG renders this: mask3

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 300 300">
<rect width="100%" height="100%" fill="red"/>
<defs>
  <mask id="theMask">
    <rect x="20" y="20" width="260" height="260" fill="#FFF" />
    <rect x="100" y="100" width="100" height="100" fill="#000" />
  </mask>
</defs>
<rect width="100%" height="100%" fill="blue" mask="url(#theMask)"/>
</svg>

emrosenf avatar Apr 25 '17 00:04 emrosenf

@liZe Is this something that is easily fixable within CairoSVG, or is it a Cairo issue? If you could point me in the right direction, I could take a look.

emrosenf avatar May 01 '17 19:05 emrosenf

@emrosenf Sorry, I'm late :wink:.

The problem is known and explained in the tests:

SVG relies on the luminance and the alpha channel to apply masks, whereas Cairo only handles alpha. The only way to fix this is to find a "luminance-to-alpha" filter.

I've already spent some time trying to find a solution a long time ago, but the only reliable information I can find about this is an interesting mail archive from 2007. I'm afraid nothing changed in Cairo since this mail, maybe it's time to ask them how we could help :smile:.

liZe avatar May 02 '17 17:05 liZe

I have exactly the same problem. But I don't get how to use the luminance-to-alpha filter. @emrosenf @liZe Do you have a simple example of mask using luminance-to-alpha filter ? Thank you

microSoftware avatar May 07 '17 09:05 microSoftware

But I don't get how to use the luminance-to-alpha filter.

Nobody does, that's the problem :wink:. (Actually, there's probably no luminance-to-alpha filter yet.)

liZe avatar May 07 '17 09:05 liZe

librsvg uses cairo and it can render images that use luminance mask. rsvg-convert -f png -o cairosvgtest.png cairosvgtest.svg cairosvgtest Example that uses black to white gradient mask (source: Mozilla Contributors https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Clipping_and_masking)

<svg width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <linearGradient id="Gradient">
      <stop offset="0" stop-color="black" stop-opacity="1" />
      <stop offset="1" stop-color="white" stop-opacity="1" />
    </linearGradient>
    <mask id="Mask">
      <rect x="0" y="0" width="200" height="200" fill="url(#Gradient)"  />
    </mask>
  </defs>
  <rect x="0" y="0" width="200" height="200" fill="green" />
  <rect x="0" y="0" width="200" height="200" fill="red" mask="url(#Mask)" />
</svg>

Image rendered with cairosvg cairosvg -f png -o gradientmask.png gradientmask.svg gradientmask

Image rendered with librsvg rsvg-convert -f png -o rsvggradientmask.png gradientmask.svg rsvggradientmask

vqhqu avatar May 23 '18 13:05 vqhqu

librsvg uses cairo and it can render images that use luminance mask.

Really interesting.

According to librsvg's source, the filter is done using pixel matrices. When generating a PDF, the image is thus probably rasterized at some point (that's why the generated PDF file is much bigger with the filter than without).

My comment should have been:

Actually, there's probably no luminance-to-alpha vector filter in Cairo yet.

If anyone knows how filters work in Cairo and/or in librsvg, please help us :smile:!

liZe avatar May 23 '18 15:05 liZe

Hi all,

I came across a similar problem in a C project I'm working on (which is how I found this github issue) ... just wanted to say that you'll have to manipulate the pixel data directly, until such time as the Cairo developers deem it useful to be able to mask with luminance.

Specifically you'll have to call cairo_image_surface_get_data (or Python wrapper equivalent) to get the raw data pointer (on a surface of format CAIRO_FORMAT_ARGB32), and then iterate through all the 32-bit pixels, calculating luminance and transferring to the high 8 bits. Then you can mask with that surface as normal.

Here's a bit of code in InkScape which inspired my own C implementation: MaskLuminanceToAlpha

dewf avatar Aug 19 '18 05:08 dewf

@dewf Thanks a lot for your message.

Specifically you'll have to call cairo_image_surface_get_data

Thanks for the example. It will work for raster images (as it's a cairo_image_surface_* function), but as long as there's nothing in Cairo for that we can't do this for vector images.

liZe avatar Aug 19 '18 12:08 liZe