0

I'd like to show an image and apply a mask on top of that that would make the entire image darker, except for a number of regions that would keep the original content of the image. Something like using a mask with holes in it.

I've been trying a lot of options, but I couldn't make it work. That's the closest I've got:

<image id="image1" xlink:href="image.png" height="100%" width="100%" />
<g if="image1mask" >
    <circle id="image1maskhole" cx="160" cy="120" r="80" fill="black" fill-opacity="100%" />
    <rect id="image1maskbase" x="0" y="0" width="100%" height="100%" fill="rgb(100, 100, 100)" fill-opacity="30%" comp-op="dst-out"/>
</g>

However, the circle is filled (black) in the mask. I'd like it to be totally transparent. I tried using white instead, but the result is also wrong. I also tried playing with the fill-opacity values, but I couldn't make it work either.

I'm testing on chrome, but I'm going to run it in an embedded browser that doesn't handle masks very well. That's why I'm avoiding this possible solution (that works perfectly on chrome):

<defs>
  <mask id="image1mask" >
      <rect x="0" y="0" width="100%" height="100%" fill="white" fill-opacity="30%" />
  <polygon points="30,30, 80,30, 50,120, 80,210, 30,210" fill="white" />
      <circle cx="160" cy="120" r="80" fill="white" />
  <rect x="250" y="30" width="50" height="180" fill="white" />
  </mask>
</defs>
<image id="image1" xlink:href="image.png" height="100%" width="100%" mask="url(#image1mask)" />

Can anyone help, please?

Thanks very much.

---- Possible solution:

I found a way to do that. It is actually a workaround, and I'm a bit concerned about performance. I'm embedding this into a JavaFX2 application. And unfortunelly it doesn't work very well. The mask will actually be interpreted as a rectangle that encloses all items of mask1 in it. So back to the beginning.

    <svg class="svg-graphic" width="320" height="240" >
    <defs>
        <clipPath id="mask1">
            <polygon points="30,30, 80,30, 50,120, 80,210, 30,210" fill="white" />
            <circle cx="160" cy="120" r="80" fill="white" />
            <rect x="250" y="30" width="50" height="180" fill="white" />
        </clipPath>
    </defs>

    <image id="backimage" xlink:href="image.png" height="100%" width="100%" style="opacity:0.3" />
    <image id="imagemask" xlink:href="image.png" height="100%" width="100%" clip-path="url(#mask1)"/>
</svg>

-- Another possible solution: This one works in the embedded browser.

The only drawback is that it uses a lot of "fake" images. One for the background (shaded) and one for each hole in the mask (non shaded parts). Once again, performance may suffer.

    <svg class="svg-graphic" width="320" height="240" >
    <image id="image1back" xlink:href="image.png" height="100%" width="100%" style="opacity:0.3" />
    <g id="mask4" visibility="visible">
        <defs>
            <clipPath id="mask4def-1"><polygon points="30,30, 80,30, 50,120, 80,210, 30,210" /></clipPath>
            <clipPath id="mask4def-2"><circle cx="160" cy="120" r="50" /></clipPath>
            <clipPath id="mask4def-3"><rect x="250" y="30" width="50" height="180" /></clipPath>
        </defs>
        <image xlink:href="image.png" height="100%" width="100%" clip-path="url(#mask4def-1)"/>
        <image xlink:href="image.png" height="100%" width="100%" clip-path="url(#mask4def-2)"/>
        <image xlink:href="image.png" height="100%" width="100%" clip-path="url(#mask4def-3)"/>
    </g>
</svg>

It will result in the same result as the Masking approach I give in above (that won't work in my embedded browser). Does anyone knows whether it is a problematic approach or not? Anyone with better ideas?

Marcus
  • 1,675
  • 3
  • 18
  • 29

3 Answers3

1

Try something like this:

<defs>
  <mask id="image1mask" >
    <g>
      <rect id="image1maskbase" x="0" y="0" width="100%" height="100%" fill="rgb(100, 100, 100)" fill-opacity="30%" />
      <circle id="image1maskhole" cx="160" cy="120" r="80" fill="black" />
    </g>
  </mask>
</defs>

<image id="image1" xlink:href="image.png" height="100%" width="100%" mask="url(#imagemask)" />
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • Thanks for your answer. Please check the edit I just made for a working solution following your method. It works on chrome, however it won't work well in the embedded brower I'm working with. I have to find another solution without using masks. – Marcus Mar 25 '14 at 16:51
  • I found a way to create a mask just like the one provided in my example, except that it is transparent where it is shaded and shaded where it is transparent. I need the opposit. I used geometric forms with fill opacity set. However I can't use compositing techniques to get what I want. – Marcus Mar 25 '14 at 16:53
1

I am now a little unclear which bits you want shaded and which bits you want clear :/

I am going to assume you want the "KOI" shape clear and the rest dark. Here is a solution that does that without using masks.

<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="320" height="240" >
    <defs>
        <clipPath id="mask1">
            <polygon points="30,30, 80,30, 50,120, 80,210, 30,210"/>
            <circle cx="160" cy="120" r="80" />
            <rect x="250" y="30" width="50" height="180" />
        </clipPath>
    </defs>

    <image xlink:href="http://placepuppy.it/320/240" height="100%" width="100%" />
    <rect height="100%" width="100%" fill="black" opacity="0.6" />
    <image xlink:href="http://placepuppy.it/320/240" height="100%" width="100%" clip-path="url(#mask1)"/>
</svg>

Demo here

There is also a solution that only needs one <image> element, but for that you will need to combine the "KOI" shape into a single <path> element.

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • This is exactly what I want to to do. I actualy managed to do is almost the same way as you did, but it won't work in the embedded browser I have to use. So I can't do it this way. The only way I could make it work was the second possible solution I describe in my first post (editted). Thanks very much for your answer. That gave me the basic idea to solve the issue. – Marcus Mar 26 '14 at 13:18
0

Both solutions provided by BigBadaboom work very well in Chrome. However, I have to use the JavaFX2 embedded browser and those solutions won't work very well. As far as I understood, it doesn't manage masks very well. Another limitation is related to multiple items in a clippath. The browser will actualy understand them as a single rectangle bigger enought to hold all items inside it.

The only way I could make it work in the embedded browser was doing superposition of images. I started with the base image entirely shaded. Then I add one image, with a clip-path for each highlighted part I desire.

Up to now, performance is acceptable. However I wouldn't suggest anyone to follow this approach when using normal browsers. It will probably be slower if you use it many times in a single page.

<svg class="svg-graphic" width="320" height="240">http://placepuppy.it/320/240
    <image id="image1back" xlink:href="http://placepuppy.it/320/240" height="100%" width="100%" />
    <rect height="100%" width="100%" fill="black" style="opacity:0.6" />
    <g id="mask4" visibility="visible">
        <defs>
            <clipPath id="mask4def-1">
                <polygon points="30,30, 80,30, 50,120, 80,210, 30,210" />
            </clipPath>
            <clipPath id="mask4def-2">
                <circle cx="160" cy="120" r="50" />
            </clipPath>
            <clipPath id="mask4def-3">
                <rect x="250" y="30" width="50" height="180" />
            </clipPath>
        </defs>
        <image xlink:href="http://placepuppy.it/320/240" height="100%" width="100%" clip-path="url(#mask4def-1)" />
        <image xlink:href="http://placepuppy.it/320/240" height="100%" width="100%" clip-path="url(#mask4def-2)" />
        <image xlink:href="http://placepuppy.it/320/240" height="100%" width="100%" clip-path="url(#mask4def-3)" />
    </g>
</svg>

Demo here

Marcus
  • 1,675
  • 3
  • 18
  • 29