@effing/canvas 0.20.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +96 -47
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ function renderLottieFrame(ctx, animation, frame) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// src/jsx/draw/index.ts
|
|
24
|
-
import {
|
|
24
|
+
import { loadImage as loadImage3 } from "@napi-rs/canvas";
|
|
25
25
|
|
|
26
26
|
// src/jsx/language.ts
|
|
27
27
|
function isEmoji(char) {
|
|
@@ -752,6 +752,37 @@ async function drawImage(ctx, src, x, y, width, height, style, preloadedImage) {
|
|
|
752
752
|
}
|
|
753
753
|
}
|
|
754
754
|
|
|
755
|
+
// src/jsx/draw/offscreen.ts
|
|
756
|
+
import { createCanvas as createCanvas2 } from "@napi-rs/canvas";
|
|
757
|
+
var canvasPool = /* @__PURE__ */ new Map();
|
|
758
|
+
function acquireOffscreen(w, h) {
|
|
759
|
+
const key = `${w}x${h}`;
|
|
760
|
+
const stack = canvasPool.get(key);
|
|
761
|
+
if (stack) {
|
|
762
|
+
while (stack.length > 0) {
|
|
763
|
+
const ref = stack.pop();
|
|
764
|
+
const canvas2 = ref.deref();
|
|
765
|
+
if (canvas2) {
|
|
766
|
+
const ctx = canvas2.getContext("2d");
|
|
767
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
768
|
+
ctx.clearRect(0, 0, w, h);
|
|
769
|
+
return [canvas2, ctx];
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const canvas = createCanvas2(w, h);
|
|
774
|
+
return [canvas, canvas.getContext("2d")];
|
|
775
|
+
}
|
|
776
|
+
function releaseOffscreen(canvas) {
|
|
777
|
+
const key = `${canvas.width}x${canvas.height}`;
|
|
778
|
+
let stack = canvasPool.get(key);
|
|
779
|
+
if (!stack) {
|
|
780
|
+
stack = [];
|
|
781
|
+
canvasPool.set(key, stack);
|
|
782
|
+
}
|
|
783
|
+
stack.push(new WeakRef(canvas));
|
|
784
|
+
}
|
|
785
|
+
|
|
755
786
|
// src/jsx/draw/utils.ts
|
|
756
787
|
function parseCSSLength(value, referenceSize) {
|
|
757
788
|
if (value.endsWith("%")) return parseFloat(value) / 100 * referenceSize;
|
|
@@ -926,17 +957,19 @@ function mergeStyleIntoProps(props) {
|
|
|
926
957
|
function collectDefs(children) {
|
|
927
958
|
const clips = /* @__PURE__ */ new Map();
|
|
928
959
|
const gradients = /* @__PURE__ */ new Map();
|
|
960
|
+
const masks = /* @__PURE__ */ new Map();
|
|
929
961
|
for (const child of children) {
|
|
930
962
|
if (child.type !== "defs") continue;
|
|
931
963
|
for (const def of normalizeChildren(child)) {
|
|
932
964
|
const id = def.props.id;
|
|
933
965
|
if (!id) continue;
|
|
934
966
|
if (def.type === "clipPath") clips.set(id, normalizeChildren(def));
|
|
967
|
+
else if (def.type === "mask") masks.set(id, normalizeChildren(def));
|
|
935
968
|
else if (def.type === "radialGradient" || def.type === "linearGradient")
|
|
936
969
|
gradients.set(id, def);
|
|
937
970
|
}
|
|
938
971
|
}
|
|
939
|
-
return { clips, gradients };
|
|
972
|
+
return { clips, gradients, masks };
|
|
940
973
|
}
|
|
941
974
|
function parseUrlRef(value) {
|
|
942
975
|
if (typeof value !== "string") return void 0;
|
|
@@ -1226,13 +1259,23 @@ function buildPath(child, vbW, vbH) {
|
|
|
1226
1259
|
return p;
|
|
1227
1260
|
}
|
|
1228
1261
|
case "rect": {
|
|
1229
|
-
const
|
|
1230
|
-
const
|
|
1262
|
+
const x = svgLength(props.x, vbW);
|
|
1263
|
+
const y = svgLength(props.y, vbH);
|
|
1231
1264
|
const w = svgLength(props.width, vbW);
|
|
1232
1265
|
const h = svgLength(props.height, vbH);
|
|
1233
1266
|
if (w <= 0 || h <= 0) return void 0;
|
|
1267
|
+
const rxRaw = svgLength(props.rx, vbW, -1);
|
|
1268
|
+
const ryRaw = svgLength(props.ry, vbH, -1);
|
|
1269
|
+
let rx = rxRaw >= 0 ? rxRaw : ryRaw >= 0 ? ryRaw : 0;
|
|
1270
|
+
let ry = ryRaw >= 0 ? ryRaw : rx;
|
|
1271
|
+
rx = Math.min(rx, w / 2);
|
|
1272
|
+
ry = Math.min(ry, h / 2);
|
|
1234
1273
|
const p = new Path2D();
|
|
1235
|
-
|
|
1274
|
+
if (rx > 0 || ry > 0) {
|
|
1275
|
+
p.roundRect(x, y, w, h, [rx]);
|
|
1276
|
+
} else {
|
|
1277
|
+
p.rect(x, y, w, h);
|
|
1278
|
+
}
|
|
1236
1279
|
return p;
|
|
1237
1280
|
}
|
|
1238
1281
|
case "ellipse": {
|
|
@@ -1309,43 +1352,67 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
1309
1352
|
}
|
|
1310
1353
|
var EMPTY_DEFS = {
|
|
1311
1354
|
clips: /* @__PURE__ */ new Map(),
|
|
1312
|
-
gradients: /* @__PURE__ */ new Map()
|
|
1355
|
+
gradients: /* @__PURE__ */ new Map(),
|
|
1356
|
+
masks: /* @__PURE__ */ new Map()
|
|
1313
1357
|
};
|
|
1314
1358
|
function drawSvgChild(ctx, child, inheritedFill, color, defs = EMPTY_DEFS, vbW = 0, vbH = 0) {
|
|
1315
1359
|
const { type } = child;
|
|
1316
1360
|
const props = mergeStyleIntoProps(child.props);
|
|
1317
|
-
if (type === "defs" || type === "clipPath") return;
|
|
1361
|
+
if (type === "defs" || type === "clipPath" || type === "mask") return;
|
|
1318
1362
|
const clipRef = parseUrlRef(props.clipPath ?? props["clip-path"]);
|
|
1319
1363
|
const clipShapes = clipRef ? defs.clips.get(clipRef) : void 0;
|
|
1320
1364
|
const clipPath = clipShapes ? buildClipPath(clipShapes, vbW, vbH) : void 0;
|
|
1365
|
+
const maskRef = parseUrlRef(props.mask);
|
|
1366
|
+
const maskShapes = maskRef ? defs.masks.get(maskRef) : void 0;
|
|
1321
1367
|
if (clipPath) ctx.save();
|
|
1322
1368
|
if (clipPath) ctx.clip(clipPath);
|
|
1369
|
+
let elemCanvas;
|
|
1370
|
+
const targetCtx = maskShapes ? (() => {
|
|
1371
|
+
const [c, cCtx] = acquireOffscreen(Math.ceil(vbW), Math.ceil(vbH));
|
|
1372
|
+
elemCanvas = c;
|
|
1373
|
+
return cCtx;
|
|
1374
|
+
})() : ctx;
|
|
1323
1375
|
switch (type) {
|
|
1324
1376
|
case "path":
|
|
1325
|
-
drawPath(
|
|
1377
|
+
drawPath(targetCtx, props, inheritedFill, color, defs);
|
|
1326
1378
|
break;
|
|
1327
1379
|
case "circle":
|
|
1328
|
-
drawCircle(
|
|
1380
|
+
drawCircle(targetCtx, props, inheritedFill, color, defs, vbW, vbH);
|
|
1329
1381
|
break;
|
|
1330
1382
|
case "rect":
|
|
1331
|
-
drawSvgRect(
|
|
1383
|
+
drawSvgRect(targetCtx, props, inheritedFill, color, defs, vbW, vbH);
|
|
1332
1384
|
break;
|
|
1333
1385
|
case "line":
|
|
1334
|
-
drawLine(
|
|
1386
|
+
drawLine(targetCtx, props, color, defs, vbW, vbH);
|
|
1335
1387
|
break;
|
|
1336
1388
|
case "ellipse":
|
|
1337
|
-
drawEllipse(
|
|
1389
|
+
drawEllipse(targetCtx, props, inheritedFill, color, defs, vbW, vbH);
|
|
1338
1390
|
break;
|
|
1339
1391
|
case "polygon":
|
|
1340
|
-
drawPolygon(
|
|
1392
|
+
drawPolygon(targetCtx, props, inheritedFill, color, defs);
|
|
1341
1393
|
break;
|
|
1342
1394
|
case "polyline":
|
|
1343
|
-
drawPolyline(
|
|
1395
|
+
drawPolyline(targetCtx, props, inheritedFill, color, defs);
|
|
1344
1396
|
break;
|
|
1345
1397
|
case "g":
|
|
1346
|
-
drawGroup(
|
|
1398
|
+
drawGroup(targetCtx, child, inheritedFill, color, defs, vbW, vbH);
|
|
1347
1399
|
break;
|
|
1348
1400
|
}
|
|
1401
|
+
if (maskShapes && elemCanvas) {
|
|
1402
|
+
const [maskCanvas, maskCtx] = acquireOffscreen(
|
|
1403
|
+
Math.ceil(vbW),
|
|
1404
|
+
Math.ceil(vbH)
|
|
1405
|
+
);
|
|
1406
|
+
for (const shape of maskShapes) {
|
|
1407
|
+
drawSvgChild(maskCtx, shape, "white", "white", defs, vbW, vbH);
|
|
1408
|
+
}
|
|
1409
|
+
targetCtx.globalCompositeOperation = "destination-in";
|
|
1410
|
+
targetCtx.drawImage(maskCanvas, 0, 0);
|
|
1411
|
+
targetCtx.globalCompositeOperation = "source-over";
|
|
1412
|
+
ctx.drawImage(elemCanvas, 0, 0);
|
|
1413
|
+
releaseOffscreen(maskCanvas);
|
|
1414
|
+
releaseOffscreen(elemCanvas);
|
|
1415
|
+
}
|
|
1349
1416
|
if (clipPath) ctx.restore();
|
|
1350
1417
|
}
|
|
1351
1418
|
function drawPath(ctx, props, inheritedFill, color, defs) {
|
|
@@ -1366,14 +1433,24 @@ function drawCircle(ctx, props, inheritedFill, color, defs, vbW = 0, vbH = 0) {
|
|
|
1366
1433
|
applyFillAndStroke(ctx, props, path, inheritedFill, color, defs, bbox);
|
|
1367
1434
|
}
|
|
1368
1435
|
function drawSvgRect(ctx, props, inheritedFill, color, defs, vbW = 0, vbH = 0) {
|
|
1369
|
-
const
|
|
1370
|
-
const
|
|
1436
|
+
const x = svgLength(props.x, vbW);
|
|
1437
|
+
const y = svgLength(props.y, vbH);
|
|
1371
1438
|
const w = svgLength(props.width, vbW);
|
|
1372
1439
|
const h = svgLength(props.height, vbH);
|
|
1373
1440
|
if (w <= 0 || h <= 0) return;
|
|
1441
|
+
const rxRaw = svgLength(props.rx, vbW, -1);
|
|
1442
|
+
const ryRaw = svgLength(props.ry, vbH, -1);
|
|
1443
|
+
let rx = rxRaw >= 0 ? rxRaw : ryRaw >= 0 ? ryRaw : 0;
|
|
1444
|
+
let ry = ryRaw >= 0 ? ryRaw : rx;
|
|
1445
|
+
rx = Math.min(rx, w / 2);
|
|
1446
|
+
ry = Math.min(ry, h / 2);
|
|
1374
1447
|
const path = new Path2D();
|
|
1375
|
-
|
|
1376
|
-
|
|
1448
|
+
if (rx > 0 || ry > 0) {
|
|
1449
|
+
path.roundRect(x, y, w, h, [rx]);
|
|
1450
|
+
} else {
|
|
1451
|
+
path.rect(x, y, w, h);
|
|
1452
|
+
}
|
|
1453
|
+
const bbox = { x, y, width: w, height: h };
|
|
1377
1454
|
applyFillAndStroke(ctx, props, path, inheritedFill, color, defs, bbox);
|
|
1378
1455
|
}
|
|
1379
1456
|
function drawLine(ctx, props, color, defs, vbW = 0, vbH = 0) {
|
|
@@ -1776,34 +1853,6 @@ function drawTextDecoration(ctx, seg, offsetX, offsetY) {
|
|
|
1776
1853
|
}
|
|
1777
1854
|
|
|
1778
1855
|
// src/jsx/draw/index.ts
|
|
1779
|
-
var canvasPool = /* @__PURE__ */ new Map();
|
|
1780
|
-
function acquireOffscreen(w, h) {
|
|
1781
|
-
const key = `${w}x${h}`;
|
|
1782
|
-
const stack = canvasPool.get(key);
|
|
1783
|
-
if (stack) {
|
|
1784
|
-
while (stack.length > 0) {
|
|
1785
|
-
const ref = stack.pop();
|
|
1786
|
-
const canvas2 = ref.deref();
|
|
1787
|
-
if (canvas2) {
|
|
1788
|
-
const ctx = canvas2.getContext("2d");
|
|
1789
|
-
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1790
|
-
ctx.clearRect(0, 0, w, h);
|
|
1791
|
-
return [canvas2, ctx];
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
const canvas = createCanvas2(w, h);
|
|
1796
|
-
return [canvas, canvas.getContext("2d")];
|
|
1797
|
-
}
|
|
1798
|
-
function releaseOffscreen(canvas) {
|
|
1799
|
-
const key = `${canvas.width}x${canvas.height}`;
|
|
1800
|
-
let stack = canvasPool.get(key);
|
|
1801
|
-
if (!stack) {
|
|
1802
|
-
stack = [];
|
|
1803
|
-
canvasPool.set(key, stack);
|
|
1804
|
-
}
|
|
1805
|
-
stack.push(new WeakRef(canvas));
|
|
1806
|
-
}
|
|
1807
1856
|
async function drawNode(ctx, node, parentX, parentY, debug, emojiStyle) {
|
|
1808
1857
|
const x = parentX + node.x;
|
|
1809
1858
|
const y = parentY + node.y;
|