SVG filter: center a blended image - svg

I have an SVG filter that overlays an image with a spinner. This works fine, except I cannot figure out how to center the spinner on the image. The logical answer seems to be:
<filter id="loading" primitiveUnits="objectBoundingBox">
<feImage xlink:href="filters.svg#loading-overlay" x="50%" y="50%" result="spinner" />
<feOffset in="spinner" result="offsetSpinner" dx="-25px" dy="-25px" />
<feBlend in2="SourceGraphic" in="offsetSpinner" />
</filter>
But of course you can't use "-25px" as an argument to feOffset.
Anybody have a clever solution?
Fiddle: https://jsfiddle.net/pytmd8tr/
(As a matter of opinion - I think the way primitiveUnits works is really stupid. In particular, it makes no sense to specify things like the stdev of a blur as a % of the original image. Maybe I'm missing something obvious?)

Well you can use JavaScript to calculate the right %'s and insert them, but failing that - something like this should work. (Although feImage sizing & positioning can have bugs in IE10).
<filter id="loading" primitiveUnits="objectBoundingBox">
<feImage xlink:href="#loading-overlay" x="25%" y="25%" width="50%" height="50%" result="spinner" />
<feBlend in2="SourceGraphic" in="spinner" result="foo" />
</filter>

Related

Why can I only use SourceGraphic as in2 in a feDeformationMap?

If I use 'SourceGraphic' as in2 inside feDisplacementMap, things looks good. However, if I use anything else, the deformation is not applied.
Because of that, I have to define the deformation source first, and then apply the filter to the deformation source, which is quite counter-intuitive, at least for me.
For example, in the example below I have to define the circle and then apply the filter to the circle. Inside the filter I use feImage to load the kitten image. But that feels reverse to me. This works:
<svg>
<defs>
<filter id="displace">
<feImage href="https://placekitten.com/500/500" result="kitten" />
<feDisplacementMap
scale="10"
xChannelSelector="R"
yChannelSelector="R"
in="kitten"
in2="SourceGraphic"
/>
</filter>
</defs>
<circle cx="50" cy="50" r="50" fill="#f00" filter="url(#displace)" />
</svg>
As you can see, applying a displacement to a small part of an image becomes quite painful, as you have to first create the displacement map yourself, and it must have the same size as the image. Like this:
<g id="deformation-source" filter="url(#displace)">
<rect x="0" y="0" width={imageWidth} height={imageHeight} fill={neutralToDeformation} />
<circle cx="50" cy="50" r="50" fill="#f00" filter="url(#displace)" />
</g>
Also, applying 2 different deformations to the same image, requires you to put the objects that will cause the deformation, instad of doing something like this:
<image href="kitten-url" filter="url(#filter-1)" />
<image href="kitten-url" filter="url(#filter-2)" />
What I would like to be able to do is to load the kitten as an SVG image tag, and then apply the filter to that image, where I would load the circle inside a feImage making a reference to the object's id, which I would have previously defined inside the defs section. This doesn't work:
<svg>
<defs>
<circle id="displacement-source" cx="50" cy="50" r="50" fill="#f00" />
<filter id="displace">
<feImage href="displacement-source" result="displacement-source" />
<feDisplacementMap
scale="10"
xChannelSelector="R"
yChannelSelector="R"
in="SourceGraphic"
in2="displacement-source"
/>
</filter>
</defs>
<image href="https://placekitten.com/500/500" width="100" filter="url(#displace)" />
</svg>
I'm not sure if I'm the one that's thinking this backwards or if I'm missing something. What's going on with this?
You can define your displacement source within the feImage, but it has to be an complete image defined as an inline URI in order to be cross browser compatible.
<feImage width="500" height="500" xlink:href="data:image/svg+xml,%3Csvg width='247' height='34'etc. etc.
Please see this article for more detail on how to escape characters in the svg+xml format - as far as I can remember there are some gotchas vs. vanilla HTML escaping.
If you don't care about Firefox, you can use a fragment identifier instead of inlining a whole SVG.
<feImage width="500" height="500" xlink:href="#idOfElementYouWantToUse"/>
Also remember that there are cross-origin security rules on displacementMap sources - so you can't displace an image using a map from another domain (or vice versa - I'm a little hazy on which way the restrictions run).

IE11 filter attribute not supported

I tried the below code and noticed in IE11, the second circle did not appear at all. Once I removed the filter="url(#blurMe)" from the second circle tag, I was able to see the green second circle.
Is it correct that IE11 does not support this feature? Are there alternate ways of achieving this?
I looked at the documentation: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/filter and it showed unknown compatibility.
Thanks.
<svg width="230" height="120" xmlns="http://www.w3.org/2000/svg">
<filter id="blurMe">
<feGaussianBlur stdDeviation="5"/>
</filter>
<circle cx="60" cy="60" r="50" fill="green" />
<circle cx="170" cy="60" r="50" fill="green"
filter="url(#blurMe)" />
</svg>
Your code works fine on the IE 11 browser and I can see the 2 circles with your above-posted code.
Test code:
<!DOCTYPE html>
<html>
<head>
<title>Untitled Page</title>
</head>
<body>
<svg width="230" height="120" xmlns="http://www.w3.org/2000/svg">
<filter id="blurMe">
<feGaussianBlur stdDeviation="5"/>
</filter>
<circle cx="60" cy="60" r="50" fill="green" />
<circle cx="170" cy="60" r="50" fill="green"
filter="url(#blurMe)" />
</svg>
</body>
</html>
Output on the IE 11 browser:
Make sure that you are using the exact code that you had posted above.
Try to clear the cache and again try to test the issue.
If the issue persists then please try to provide more information about the issue like which exact version of the IE 11 browser and which OS build you are using for making this test? Is any other code available on the page?

Shared coordinates between elements in SVG

I would like to share coordinates between two or more elements.
My example in peudo-Code :
<point xml:id="myPoint" x="10" y="20" />
<circle point="url(#myPoint)" r="10" />
<circle point="url(#myPoint)" r="50" />
I've seen IRI references that have the best match with that I searched, but :
First, there is no nodes of type "point" existing.
Second, IRI references doesn't support any attributes.
With SVG format and specifications (no javascript code), Is there a way to have many elements which reference the same coordinates ?
Thus, I just have to manipulate the point "myPoint" to change the coordinate of all elements that reference it.
I hope my english isn't so bad and thanks for your answers.
Use a group <g> element to transform the origin. If you omit co-ordinates then objects will be drawn there.
<g transform="translate(10, 20)">
<circle r="10" />
<circle r="50" />
</g>

Putting coordinate location markers in an SVG

I'd like to create a list of locations markers on an svg graphic that I can call upon, using the id, to place content at dynamically.
what is the best way of achieving this?
I'm thinking of using an empty def like this:
<defs>
<g id="coord"></g>
</defs>
<use xlink:href="#coord" id="L1" x="10" y="10" />
<use xlink:href="#coord" id="L2" x="100" y="100" />
Is there another way of doing this?
Is there any visual editor that can be used to generate use elements and assign ids?
I think a simpler approach would be to use something like:
<circle id="L1" cx="10" cy="10" r="0" />
Without a radius (and assuming there is no stroke-width in effect), they won't be visible.
And if you put them in their own group or layer in your Inkscape file, you can temporarily give them all a stroke-width or non-zero radius if you want to edit them. Then reset them back when you save the final version.

Why are SVG filter effects blurry in Firefox on Mac OS?

It seems that SVG filter effects are rendered strangely in Firefox on Mac OS. I came across this issue when trying to implement a drop shadow for a shape with a 1px stroke. I did some pixel snapping in Javascript along with a 0.5px offset and this makes the 1px stroke render crisply as long as there is no drop shadow.
However, when I apply the drop shadow with a filter effect the shape is blurry, but only in Firefox on Mac OS (I've tried Firefox and Chrome on Mac OS and Linux).
Here's some sample code:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<!-- A trivial filter that shouldn't really do anything, to demonstrate a minimal case -->
<filter id="f1" x="-10%" y="-10%" width="120%" height="120%" filterRes="100">
<feMerge>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- A more representative example where this issue is annoying: a drop shadow -->
<filter id="f2" x="-10%" y="-10%" width="200%" height="200%" filterRes="100">
<feOffset result="offOut" in="SourceAlpha" dx="2" dy="2" />
<feGaussianBlur result="blurOut" in="offOut" stdDeviation="2" />
<feMerge>
<feMergeNode in="blurOut"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<!-- Translating by 0.5 px so that 1px borders are sharp -->
<g transform="translate(5.5,5.5)">
<rect width="20" height="20" rx="2" ry="2" stroke="black" stroke-width="1" fill="white" />
<rect x="30" width="20" height="20" rx="2" ry="2" stroke="black" stroke-width="1" fill="white" filter="url(#f1)" />
<rect x="60" width="20" height="20" rx="2" ry="2" stroke="black" stroke-width="1" fill="white" filter="url(#f2)" />
</g>
</svg>
The svg looks fine in Firefox on Linux: (sorry I can't post inline images as a new SO user)
http://i.imgur.com/czKP2.png
and the same in Chrome on Mac OS (I can only post 2 links as a new SO user, so no link to the screenshot, but it's essentially identical), but the rects with the filter effects applied are blurry in Firefox on Mac OS:
http://i.imgur.com/7WxI8.png
I've tried several tweaks to no avail, including:
Using feBlend or feComposite instead of feMerge
Bumping up filterRes to browser-destroyingly high values
Any ideas?
(Apologies for not including a working demo -- I attempted to create a jsfiddle but it doesn't really work properly because the pixel snapping Javascript code doesn't work when the SVGs are in a frame the way jsfiddle does things.)

Resources