10

Note: I can't use JavaScript, because this is for a CSS Zen Garden sort of challenge. Please do not suggest a JS library.

I have 2 ideas that I'm not making headway on:

  1. Use a SVG filter to just pixelate the dang image; I've been playing with <feMorphology operator="erode"/> and punching the contrast up after, but it looks bad.

  2. Filter the image to be smaller, then scale it up using CSS and image-rendering to be all blocky. The hard part is Step A; I can't find any filter operations that scale the input.

Am I missing something? How can I get a "pixelated" effect using an SVG filter?

1
  • 1
    I would suggest to add your codepen as a separate answer as it is quite good and people searching can find a good answer. Deleting my answer.
    – Nikos M.
    Commented Mar 30, 2020 at 16:58

2 Answers 2

16

You can pixelate images if you have the right "magic" displacementMap. Feel free to use the one referenced below (courtesy of Zoltan Fegyver).

Update: Changed the sample code to inline the displacementmap image as a data: URI (thanks for the code IllidanS4.)

The original answer had the displacementMap image hosted on a different domain. This used to work - but browsers implemented the new Filters security measures that disallow this. For production code today, you need the displacement map image served from the same domain as the source graphic's file or you need to inline the displacementMap.

Update 2: You may have to tweak the size of feImage and feGaussianBlur to avoid bugs in feTile that adds artifacts. For example - this seems to work better:

<feGaussianBlur stdDeviation="8" in="SourceGraphic" result="smoothed" />
 <feImage width="15.4" height="15.4" 

<svg x="0px" y="0px" width="810px" height="600px" viewBox="0 0 810 600" color-interpolation-filters="sRGB">
  <defs>
<filter id="pixelate" x="0%" y="0%" width="100%" height="100%">
  <!--Thanks to Zoltan Fegyver for figuring out pixelation and producing the awesome pixelation map. -->
  <feGaussianBlur stdDeviation="2" in="SourceGraphic" result="smoothed" />
  <feImage width="15" height="15" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWSURBVAgdY1ywgOEDAwKxgJhIgFQ+AP/vCNK2s+8LAAAAAElFTkSuQmCC" result="displacement-map" />
  <feTile in="displacement-map" result="pixelate-map" />
  <feDisplacementMap in="smoothed" in2="pixelate-map" xChannelSelector="R" yChannelSelector="G" scale="50" result="pre-final"/>
  <feComposite operator="in" in2="SourceGraphic"/>
</filter>
  </defs>

  <image filter="url(#pixelate)" width="810" height="600" preserveAspectRatio="xMidYMid meet" xlink:href="http://uploads2.wikiart.org/images/vincent-van-gogh/the-starry-night-1889(1).jpg"/>
</svg>

8
  • Hm. Works okay in WebKits, but it's just a blur in Firefox.
    – Tigt
    Commented May 26, 2016 at 5:00
  • 2
    That's because the displacementMap is using a cross-domain image. If you host the displacementMap image on the same domain as the source it should work just fine. (Firefox has implemented the filter security spec - webkit has not) Commented May 26, 2016 at 15:23
  • Oh, that's awesome; if you could put that in the answer for other people, I'd happily accept!
    – Tigt
    Commented May 26, 2016 at 17:54
  • Added to the answer Commented May 27, 2016 at 16:08
  • 2
    You can embed the map directly into the image with the URI data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAIAAAACDbGyAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAWSURBVAgdY1ywgOEDAwKxgJhIgFQ+AP/vCNK2s+8LAAAAAElFTkSuQmCC.
    – IS4
    Commented Nov 5, 2020 at 10:26
5

The filter in Michael Mullany's answer didn't work for me, instead I found this filter by Taylor Hunt:

<svg>
  <filter id="pixelate" x="0" y="0">
    <feFlood x="4" y="4" height="2" width="2"/>
    <feComposite width="10" height="10"/>
    <feTile result="a"/>
    <feComposite in="SourceGraphic" in2="a" operator="in"/>
    <feMorphology operator="dilate" radius="5"/>
  </filter>
</svg>

(use it in the same way as the other filter: By giving an image the attribute filter="url(#pixelate)")

In action in this CodePen: https://codepen.io/tigt/pen/aZYqrg

However, both these filters seem unable to handle SVGs where the drawing doesn't take up the entire viewBox.

3
  • 1
    feTile has bugs when it tries to tile content that is "too small" at certain magic numbers. You can fix it by tweaking the size of what you're trying to tile until it falls outside the magic number range. Please help by starring this issue: bugs.chromium.org/p/chromium/issues/… Commented Jul 28, 2022 at 15:09
  • 5
    The funny part about this answer is that I am Taylor Hunt. Obviously I have now upvoted it
    – Tigt
    Commented Jul 28, 2022 at 20:59
  • 2
    NO WAY! Small world, I guess :-) Can't upvote the question yet (not enough rep), but will do as soon as I can!
    – JLubberger
    Commented Jul 29, 2022 at 7:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.