diff --git a/examples/basic.ts b/examples/basic.ts index 846558f..8a1219d 100644 --- a/examples/basic.ts +++ b/examples/basic.ts @@ -3,7 +3,7 @@ import { createCanvas } from "../mod.ts"; const canvas = createCanvas(200, 200); const ctx = canvas.getContext("2d"); -ctx.fillStyle = "red"; +ctx.fillStyle = "hsl(0, 100%, 50%)"; ctx.fillRect(10, 10, 200 - 20, 200 - 20); await Deno.writeFile("image.png", canvas.toBuffer()); diff --git a/src/color_util.ts b/src/color_util.ts new file mode 100644 index 0000000..f5a8f5d --- /dev/null +++ b/src/color_util.ts @@ -0,0 +1,34 @@ +function cap(num: number, min: number, max: number) { + return num < min ? min : num > max ? max : num; +} + +function hueToRgb(p: number, q: number, t: number) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; +} + +// https://stackoverflow.com/a/9493060/12101923 +export function hslToRgb(h: number, s: number, l: number) { + // Convert to 0-1 range + h = cap(h, 0, 360) / 360; + s = cap(s, 0, 100) / 100; + l = cap(l, 0, 100) / 100; + + let r, g, b; + + if (s == 0) { + r = g = b = l; + } else { + const q = l < 0.5 ? l * (1 + s) : l + s - l * s; + const p = 2 * l - q; + r = hueToRgb(p, q, h + 1 / 3); + g = hueToRgb(p, q, h); + b = hueToRgb(p, q, h - 1 / 3); + } + + return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; +} diff --git a/src/lib.js b/src/lib.js index 529dc8f..d47f2e4 100644 --- a/src/lib.js +++ b/src/lib.js @@ -1,9 +1,42 @@ import { decodeBase64, encodeBase64 } from "./base64.ts"; import { WASM_BASE64 } from "./wasm.js"; +import { hslToRgb } from "./color_util.ts"; let document = { getElementById: () => undefined }; let wasmBuff = decodeBase64(WASM_BASE64); +function maybeHSL(k) { + if (typeof k === "string") { + const match = k.match(/^hsla?\((\d+), *(\d+)%, *(\d+)%(, *([\d\.]+))?\)$/); + + if (match !== null) { + const h = Number(match[1]); + const s = Number(match[2]); + const l = Number(match[3]); + const a = k.startsWith("hsla") && match[5] ? Number(match[5]) : undefined; + + k = "rgb"; + if (a !== undefined) { + k += "a"; + } + k += "("; + + const [r, g, b] = hslToRgb(h, s, l); + k += r + ", "; + k += g + ", "; + k += b; + + if (a !== undefined) { + k += ", " + a; + } + + k += ")"; + } + } + + return k; +} + export var CanvasKitInit = (function () { var _scriptDir = typeof document !== "undefined" && document.currentScript ? document.currentScript.src @@ -2218,6 +2251,7 @@ export var CanvasKitInit = (function () { return f(this.fe) ? e(this.fe) : this.fe; }, set: function (k) { + k = maybeHSL(k); "string" === typeof k ? this.fe = h(k) : k.xe && (this.fe = k); }, }); @@ -2515,7 +2549,7 @@ export var CanvasKitInit = (function () { return e(this.Ne); }, set: function (k) { - this.Ne = h(k); + this.Ne = h(maybeHSL(k)); }, }); Object.defineProperty(this, "shadowOffsetX", { @@ -2542,7 +2576,7 @@ export var CanvasKitInit = (function () { return e(this.le); }, set: function (k) { - "string" === typeof k ? this.le = h(k) : k.xe && (this.le = k); + "string" === typeof k ? this.le = h(maybeHSL(k)) : k.xe && (this.le = k); }, }); this.arc = function (k, n, y, B, C, E) {