Open jtojnar opened 11 months ago
Never mind, it was black because the image was not a square. If I use rsvg-convert --page-width=16 --page-height=16 --width=16 --height=16 --keep-aspect-ratio --format=svg
to force the page size, it will render properly.
👍 Glad you figured it out. Awesome to see this being used this way!
Actually, the condition is a bit more specific 8×8, 16×16, 32×32 and even 32×64 or 16×32 works but not 24×24 or 48×48. And any dimensions seem to work with generator.javascript
so perhaps this is shader specific.
Hi, I am using your tool to generate SDF icons for MapLibre JS and it works great for some icons but for the following just renders as a black square:
I am downloading the icon as follows:
And then converting it to only use absolute
C
/M
path commands and normalizing it to a different threshold in lieu of #2:Full conversion code
```js import buildingUrl from "./icons/building.svg"; import firefighterUrl from "./icons/firefighter.svg"; import workerUrl from "./icons/fa-person-digging.svg"; // Needs to be used in browser since it relies on canvas API. import initSDFGenerator from "webgl-sdf-generator"; import parse from "parse-svg-path"; import abs from "abs-svg-path"; import normalize from "normalize-svg-path"; async function fetchSvgDom(url) { const svg = await (await fetch(url)).text(); const parser = new DOMParser(); return parser.parseFromString(svg, "image/svg+xml"); } function extractPathString(dom, url) { const svgRoot = dom.documentElement; let path = null; for (const child of svgRoot.childNodes) { if (child.nodeType !== Node.ELEMENT_NODE) { // Ignore other node types. // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType continue; } if (child.tagName !== "path") { throw new Error(`Found unsupported element type “${child.tagName}” in “${url}”.`); } if (path !== null) { throw new Error(`More than one *path* element is present in “${url}”.`); } path = child; } if (path === null) { throw new Error(`No *path* element is present in “${url}”.`); } if (!path.hasAttribute("d")) { throw new Error(`The *path* element lacks *d* attribute in “${url}”.`); } const pathStringAttr = path.getAttribute("d"); const viewBoxAttr = svgRoot.getAttribute("viewBox"); const widthAttr = svgRoot.getAttribute("width"); const heightAttr = svgRoot.getAttribute("height"); if ((widthAttr === null || heightAttr === null) && viewBoxAttr === null) { throw new Error(`The *svg* element in “${url}” lacks both *viewBox* and *width*/*height* attributes.`); } let width = widthAttr !== null ? parseInt(widthAttr, 10) : null; let height = heightAttr !== null ? parseInt(heightAttr, 10) : null; const viewBox = viewBoxAttr !== null ? viewBoxAttr.split(" ").map((c) => parseInt(c, 10)) : [0, 0, width, height]; if (width === null || height === null) { [, , width, height] = viewBox; } // webgl-sdf-generator does not support H, V, or S path commands. // Normalize them to C. const pathString = normalize(abs(parse(pathStringAttr))) .map((seg) => seg.join(" ")) .join(" "); return { viewBox, width, height, pathString, }; } // Converts an SVG path into a data supported by Map.addImage. async function buildIcon(generator, url) { const dom = await fetchSvgDom(url); const { viewBox, width, height, pathString } = extractPathString(dom, url); console.log(pathString) const exponent = 1; const maxDistance = 1; const sdfArray = generator.generate(width, height, pathString, viewBox, maxDistance, exponent); // StyleImage expects RGBA with SDF in alpha. const data = new Uint8ClampedArray(sdfArray.length * 4); sdfArray.forEach((value, i) => { const ix = 4 * i; data[ix + 0] = 0; data[ix + 1] = 0; data[ix + 2] = 0; // MapLibre expects the data in uint8 with zero distance value to be 192. // https://docs.mapbox.com/help/troubleshooting/using-recolorable-images-in-mapbox-maps/#mapbox-gl-js // Unfortunately, sdf-generator does not currently allow to configure this https://github.com/lojjic/webgl-sdf-generator/issues/2 // The transformation polynomial was obtained by passing `interpolate [(0,0), (127.5, 192), (255,255)]` to Wolfram Alpha. data[ix + 3] = Math.round(2.01176 * value - 0.0039677 * value ** 2); }); const idata = new ImageData(data, width, height); const canvas = document.createElement("canvas"); canvas.width = 400; canvas.height = 400; canvas.style.border = "1px solid"; const ctx = canvas.getContext("2d"); ctx.putImageData(idata, 0, 0); document.querySelector("#list-panel").appendChild(canvas); return idata; } export async function buildIcons() { const generator = initSDFGenerator(); const [buildingSymbol, firefighterSymbol, workerSymbol] = await Promise.all([ buildIcon(generator, buildingUrl), buildIcon(generator, firefighterUrl), buildIcon(generator, workerUrl), ]); return { buildingSymbol, firefighterSymbol, workerSymbol, }; } ```