Define the color of a SF symbols in drawcontext

I already know that I can define the color of a sfsymbles when add image to a stack by using tintcolor.
What I want to know is that how can I do the same in a drawcontext?

1 Like

I have looked for this as well but couldn’t find anything. I guess there is no way currently to do that. As an alternative I am using Unicode emojis.

So I haven’t needed this yet, but I took on the challenge and found a workaround. I’ve used the canvas element from the browser to decode the image from the SFSymbol and then just set the color values manually. After that I again used the canvas to convert it back to a PNG image. I’ve also annotated the function with JSDoc, so you get the correct types if you use an IDE. Just copy the function into your code to use it. I hope it is useful!

/**
 * source: https://talk.automators.fm/t/define-the-color-of-a-sf-symbols-in-drawcontext/9897/3
 * @param {Image} image The image from the SFSymbol
 * @param {Color} color The color it should be tinted with
 */
async function tintSFSymbol(image, color) {
  let html = `
  <img id="image" src="data:image/png;base64,${Data.fromPNG(image).toBase64String()}" />
  <canvas id="canvas"></canvas>
  `;
  
  let js = `
    let img = document.getElementById("image");
    let canvas = document.getElementById("canvas");
    let color = 0x${color.hex};

    canvas.width = img.width;
    canvas.height = img.height;
    let ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    let imgData = ctx.getImageData(0, 0, img.width, img.height);
    // ordered in RGBA format
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      // skip alpha channel
      if (i % 4 === 3) continue;
      // bit shift the color value to get the correct channel
      data[i] = (color >> (2 - i % 4) * 8) & 0xFF
    }
    ctx.putImageData(imgData, 0, 0);
    canvas.toDataURL("image/png").replace(/^data:image\\/png;base64,/, "");
  `;
  
  let wv = new WebView();
  await wv.loadHTML(html);
  let base64 = await wv.evaluateJavaScript(js);
  return Image.fromData(Data.fromBase64String(base64));
}

// example
// you don't need to copy this
let sym = SFSymbol.named("icloud").image;
let col = Color.orange();
let res = await tintSFSymbol(sym, col);
await QuickLook.present(sym)
QuickLook.present(res)
2 Likes