1

I'm trying to recreate the look of an iOS app icon using SVG, but with an outline in case the icon is white on a white background.

Example:

<svg width="76" height="76">
  <defs>
    <mask id="myMask">
      <rect fill="#fff" rx="15" ry="15" width="76" height="76"/>
    </mask>
  </defs>
  <rect id="border" mask="url(#myMask)" fill="#000" x="0" y="0" width="76" height="76" />
  <rect id="image" mask="url(#myMask)" fill="#fff" x="1" y="1" width="74" height="74" />
</svg>

(https://jsfiddle.net/d4ngtuqa/1/)

To do this, I'm rendering a filled rectangle behind an image rendered 2x2 pixels smaller (or another rect in my simplified example) and then applying an SVG mask to both layers.

However, when I do this, the rounded corners of the border render lighter than the rest of the border. Is this a rendering bug or something? Is there an alternate approach that could avoid this?

dwieme
  • 318
  • 2
  • 9
  • It's just antialiasing, it's quite normal and not a bug. Why not just draw a white stroked rounded rect without any fill at all. – Robert Longson Jan 21 '18 at 18:22

1 Answers1

1

You don't need to mask both the image and the border. Just mask the image, then draw a 1px black border on top of it.

<svg width="76" height="76">
  <defs>
    <mask id="myMask">
      <rect fill="#fff" rx="15" ry="15" width="76" height="76"/>
    </mask>
  </defs>
  <rect id="image" mask="url(#myMask)" fill="#fff" x="0" y="0" width="76" height="76" />
  <rect id="border" fill="none" stroke="#000" stroke-width="1" x="0.5" y="0.5" width="75" height="75" rx="15" ry="15" />
</svg>

Note that, in order to make the 1px border as neat and clean as possible, we use coordinates for the rect that align to a half-pixel (0.5 and 75.5), so that the line falls cleanly within a line of pixels.

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • The half pixel coordinates did the trick! Didn't think to do that. Thank you! – dwieme Jan 22 '18 at 08:45
  • This post helped me understand a little better: https://stackoverflow.com/questions/19558454/how-to-handle-svg-pixel-snapping "By default, integer coordinates map to the intersections of the pixel squares. So a width-1 horizontal/vertical line is centered on the boundary between pixel rows and extends half way into the pixels on either side." – dwieme Jan 22 '18 at 18:17