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!
Code
/**
* 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)
Edit: I’ve updated the code. You can find it further down: Define the color of a SF symbols in drawcontext - #11 by schl3ck