如何从PNG创建SDF-Icon(在Mapbox中使用)?

13

我的任务是更改Mapbox中图标图像的图标颜色。 Mapbox允许的唯一方法是使用sdf-icons(https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-symbol-icon-color)。

经过数小时的搜索,我找不到最简单的实现方法。我发现了一个npm模块https://www.npmjs.com/package/image-sdf,但是在将其命令用于png以将其转换为sdf并在地图上呈现之后,结果并不理想。

我正在使用的命令:

image-sdf cycle-initial.png --spread 5 --downscale 1 --color black > cycle.png

以下是cycle-initial.png(INPUT):

enter image description here

以下是cycle.png(OUTPUT):

enter image description here

但使用cycle.png作为图像源并不会产生最好的效果。

enter image description here

代码片段:

const img = new Image();
img.addEventListener('load', () => {
            this.mapInstance.addImage('circle-icon', img, { sdf: true });
        }, false);
img.src = cycle;

我请求如果有人的话,请帮助我看看是否有做错什么,或者是否有正确的方法来创建sdf-icon以正确呈现。

3个回答

32

经过多日的研究,我认为我已经找到了解决方法。此外,我想总结一下创建SDF图标的步骤。

首先,简单来说什么是SDF

SDF是一种优化用于显示调整大小、重新着色和旋转光栅图像的栅格格式。它们只有单一颜色。

用途: 这是我在MAPBOX中第一次与SDF互动。

原因是我们无法更改符号层中图标(如果是svg或普通光栅png)的颜色,您必须提供SDF(专用png)格式才能实现此目的。(https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#paint-symbol-icon-color

如何创建?

  1. Npm模块:https://www.npmjs.com/package/image-sdf 命令:
image-sdf input.png --spread 10 --downscale 1 --color black > output.png
  1. Imagemagick:https://imagemagick.org/script/download.php。ImageMagick可以通过一个命令来实现:
convert in.png -filter Jinc -resize 400% -threshold 30% \( +clone -negate -morphology Distance Euclidean -level 50%,-50% \) -morphology Distance Euclidean -compose Plus -composite -level 45%,55% -resize 25% out.png

更新

  1. 还有第三种方法可以通过使用Maki库的旧标签来实现,其中只有一个JS文件sdf-render.js可以将您的源SVG转换为SDF图标。 但请注意,这个标签(v0.5.0)使用的是旧版本的JSDOM,现在已不再支持,在执行此文件之前,您必须使用svgdom和@svgdotjs/svg.js模块转换此文件的代码。

更多细节: 在Mapbox上,sdf = true时图标周围出现框

我做错了什么?

答案:在对黑白光栅路径的png执行image-sdf命令之前(请注意:它必须是黑白光栅路径),将其调整大小(增加)到约40-50%。

就像我的情况一样,我调整了正常的光栅PNG,并且结果非常棒。看一看。 (请注意:我没有更改代码中的任何内容,并使用了相同的image-sdf命令)

enter image description here


5
感谢您抽出时间分享所学! - Steve Bennett
非常有帮助的问题和答案,谢谢。在面对相同的问题时,经过大量修改图像SDF参数和Mapbox样式规范参数后,我无法找到一个好的分辨率大小折中方案。但我找到了一个小技巧,将大小设置正确,然后在与图标相同颜色的地方添加了1像素的光晕。这个轮廓有助于保持图标的细节。 - Alex

2
我想分享一下我自己生成Mapbox的SDF符号图像的经验。
首先,我尝试了npm模块"image-sdf",但它似乎有些问题。生成的SDF中的值在输入图像的边缘上没有平滑过渡。(请参见this example image,左侧显示输入图像,右侧显示image-sdf -s 3生成的输出。在正确生成的SDF中,十字形状的轮廓不应该可见。)
接下来,我尝试了@Dolly提供的ImageMagick解决方案。然而,Dolly提供的命令没有生成Mapbox所需的精确格式。
Mapbox似乎只读取PNG的alpha通道,所以我们需要添加额外的命令将数据复制到alpha通道中。
Dolly的解决方案生成的SDF中,像素值为128充当了“零点”或阈值-形状外的像素值小于128,而形状内的像素值大于128。然而,Mapbox似乎使用192作为其阈值。我通过检查存储在Mapbox内部状态中的一些字体字形SDF,以及查看Mapbox的(令人愉快地未注释)GLSL文件来确认这一点。
以下是考虑到这些问题的ImageMagick命令行:

convert in.png -filter Jinc -resize 400% -threshold 30% \( +clone -negate -morphology Distance Euclidean -level 50%,-50% \) -morphology Distance Euclidean -compose Plus -composite -level 45%,55% -resize 25% -alpha copy -fill black -colorize 100 -channel alpha -fx "a+0.25" out.png

输出结果是令人愉快

请注意,输入图像应为纯二进制码位图 - 符号本身为白色,其他所有像素均为黑色,没有抗锯齿(无灰度)。

希望这对某人有用!


1

另一个需要探索的是给你的图像设置大小。我想使用一个svg作为我的标记图像,并将其更改为:

let img = new Image();
//to
let img = new Image(256, 256);

获得了更好的结果 -


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接