@effing/canvas 0.21.1 → 0.22.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 +340 -90
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1041,6 +1041,7 @@ function drawBoxShadow(ctx, x, y, width, height, boxShadow, borderRadius) {
|
|
|
1041
1041
|
|
|
1042
1042
|
// src/jsx/draw/svg.ts
|
|
1043
1043
|
import { Path2D } from "@napi-rs/canvas";
|
|
1044
|
+
import parseCssColor from "parse-css-color";
|
|
1044
1045
|
function resolveCurrentColor(value, color) {
|
|
1045
1046
|
if (value?.toLowerCase() === "currentcolor") return color;
|
|
1046
1047
|
return value;
|
|
@@ -1054,18 +1055,24 @@ function collectDefs(children) {
|
|
|
1054
1055
|
const clips = /* @__PURE__ */ new Map();
|
|
1055
1056
|
const gradients = /* @__PURE__ */ new Map();
|
|
1056
1057
|
const masks = /* @__PURE__ */ new Map();
|
|
1058
|
+
const filters = /* @__PURE__ */ new Map();
|
|
1059
|
+
function insertDef(def) {
|
|
1060
|
+
const id = def.props.id;
|
|
1061
|
+
if (!id) return;
|
|
1062
|
+
if (def.type === "clipPath") clips.set(id, normalizeChildren(def));
|
|
1063
|
+
else if (def.type === "mask") masks.set(id, normalizeChildren(def));
|
|
1064
|
+
else if (def.type === "filter") filters.set(id, def);
|
|
1065
|
+
else if (def.type === "radialGradient" || def.type === "linearGradient")
|
|
1066
|
+
gradients.set(id, def);
|
|
1067
|
+
}
|
|
1057
1068
|
for (const child of children) {
|
|
1058
|
-
if (child.type
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
if (def.type === "clipPath") clips.set(id, normalizeChildren(def));
|
|
1063
|
-
else if (def.type === "mask") masks.set(id, normalizeChildren(def));
|
|
1064
|
-
else if (def.type === "radialGradient" || def.type === "linearGradient")
|
|
1065
|
-
gradients.set(id, def);
|
|
1069
|
+
if (child.type === "defs") {
|
|
1070
|
+
for (const def of normalizeChildren(child)) insertDef(def);
|
|
1071
|
+
} else {
|
|
1072
|
+
insertDef(child);
|
|
1066
1073
|
}
|
|
1067
1074
|
}
|
|
1068
|
-
return { clips, gradients, masks };
|
|
1075
|
+
return { clips, gradients, masks, filters };
|
|
1069
1076
|
}
|
|
1070
1077
|
function parseUrlRef(value) {
|
|
1071
1078
|
if (typeof value !== "string") return void 0;
|
|
@@ -1092,10 +1099,10 @@ function parseFrac(value, fallback) {
|
|
|
1092
1099
|
}
|
|
1093
1100
|
function applyOpacity(color, opacity) {
|
|
1094
1101
|
if (opacity >= 1) return color;
|
|
1095
|
-
const
|
|
1096
|
-
if (
|
|
1097
|
-
|
|
1098
|
-
return
|
|
1102
|
+
const parsed = parseCssColor(color);
|
|
1103
|
+
if (!parsed) return color;
|
|
1104
|
+
const [r, g, b] = parsed.values;
|
|
1105
|
+
return `rgba(${r}, ${g}, ${b}, ${parsed.alpha * opacity})`;
|
|
1099
1106
|
}
|
|
1100
1107
|
function addGradientStops(gradient, def) {
|
|
1101
1108
|
for (const stop of normalizeChildren(def)) {
|
|
@@ -1432,7 +1439,14 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
1432
1439
|
}
|
|
1433
1440
|
const color = node.style.color ?? "black";
|
|
1434
1441
|
const merged = mergeStyleIntoProps(node.props);
|
|
1435
|
-
const
|
|
1442
|
+
const inherited = {
|
|
1443
|
+
fill: resolveCurrentColor(merged.fill, color) ?? "black",
|
|
1444
|
+
stroke: resolveCurrentColor(merged.stroke, color),
|
|
1445
|
+
strokeWidth: merged.strokeWidth ?? merged["stroke-width"],
|
|
1446
|
+
strokeLinecap: merged.strokeLinecap ?? merged["stroke-linecap"],
|
|
1447
|
+
strokeLinejoin: merged.strokeLinejoin ?? merged["stroke-linejoin"],
|
|
1448
|
+
strokeOpacity: merged.strokeOpacity ?? merged["stroke-opacity"]
|
|
1449
|
+
};
|
|
1436
1450
|
const children = node.props.children;
|
|
1437
1451
|
if (children != null) {
|
|
1438
1452
|
const childArray = Array.isArray(children) ? children : [children];
|
|
@@ -1441,7 +1455,7 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
1441
1455
|
);
|
|
1442
1456
|
const defs = collectDefs(svgChildren);
|
|
1443
1457
|
for (const child of svgChildren) {
|
|
1444
|
-
drawSvgChild(ctx, child,
|
|
1458
|
+
drawSvgChild(ctx, child, inherited, color, defs, vbW, vbH);
|
|
1445
1459
|
}
|
|
1446
1460
|
}
|
|
1447
1461
|
ctx.restore();
|
|
@@ -1449,76 +1463,254 @@ function drawSvgContainer(ctx, node, x, y, width, height) {
|
|
|
1449
1463
|
var EMPTY_DEFS = {
|
|
1450
1464
|
clips: /* @__PURE__ */ new Map(),
|
|
1451
1465
|
gradients: /* @__PURE__ */ new Map(),
|
|
1452
|
-
masks: /* @__PURE__ */ new Map()
|
|
1466
|
+
masks: /* @__PURE__ */ new Map(),
|
|
1467
|
+
filters: /* @__PURE__ */ new Map()
|
|
1453
1468
|
};
|
|
1454
|
-
function
|
|
1469
|
+
function applyFeOffset(input, dx, dy, w, h) {
|
|
1470
|
+
const [canvas, ctx] = acquireOffscreen(w, h);
|
|
1471
|
+
ctx.drawImage(input, dx, dy);
|
|
1472
|
+
return canvas;
|
|
1473
|
+
}
|
|
1474
|
+
function applyFeGaussianBlur(input, stdDeviation, w, h) {
|
|
1475
|
+
const [canvas, ctx] = acquireOffscreen(w, h);
|
|
1476
|
+
ctx.filter = `blur(${stdDeviation}px)`;
|
|
1477
|
+
ctx.drawImage(input, 0, 0);
|
|
1478
|
+
ctx.filter = "none";
|
|
1479
|
+
return canvas;
|
|
1480
|
+
}
|
|
1481
|
+
function applyFeColorMatrix(input, type, values, w, h) {
|
|
1482
|
+
const [canvas, ctx] = acquireOffscreen(w, h);
|
|
1483
|
+
ctx.drawImage(input, 0, 0);
|
|
1484
|
+
if (type === "matrix" && values) {
|
|
1485
|
+
const m = values.trim().split(/[\s,]+/).map(Number);
|
|
1486
|
+
if (m.length >= 20) {
|
|
1487
|
+
const imageData = ctx.getImageData(0, 0, w, h);
|
|
1488
|
+
const d = imageData.data;
|
|
1489
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
1490
|
+
const r = d[i];
|
|
1491
|
+
const g = d[i + 1];
|
|
1492
|
+
const b = d[i + 2];
|
|
1493
|
+
const a = d[i + 3];
|
|
1494
|
+
d[i] = Math.min(
|
|
1495
|
+
255,
|
|
1496
|
+
Math.max(
|
|
1497
|
+
0,
|
|
1498
|
+
m[0] * r + m[1] * g + m[2] * b + m[3] * a + m[4] * 255
|
|
1499
|
+
)
|
|
1500
|
+
);
|
|
1501
|
+
d[i + 1] = Math.min(
|
|
1502
|
+
255,
|
|
1503
|
+
Math.max(
|
|
1504
|
+
0,
|
|
1505
|
+
m[5] * r + m[6] * g + m[7] * b + m[8] * a + m[9] * 255
|
|
1506
|
+
)
|
|
1507
|
+
);
|
|
1508
|
+
d[i + 2] = Math.min(
|
|
1509
|
+
255,
|
|
1510
|
+
Math.max(
|
|
1511
|
+
0,
|
|
1512
|
+
m[10] * r + m[11] * g + m[12] * b + m[13] * a + m[14] * 255
|
|
1513
|
+
)
|
|
1514
|
+
);
|
|
1515
|
+
d[i + 3] = Math.min(
|
|
1516
|
+
255,
|
|
1517
|
+
Math.max(
|
|
1518
|
+
0,
|
|
1519
|
+
m[15] * r + m[16] * g + m[17] * b + m[18] * a + m[19] * 255
|
|
1520
|
+
)
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
ctx.putImageData(imageData, 0, 0);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return canvas;
|
|
1527
|
+
}
|
|
1528
|
+
function applyFeBlend(in1, in2, mode, w, h) {
|
|
1529
|
+
const modeMap = {
|
|
1530
|
+
normal: "source-over",
|
|
1531
|
+
multiply: "multiply",
|
|
1532
|
+
screen: "screen",
|
|
1533
|
+
darken: "darken",
|
|
1534
|
+
lighten: "lighten",
|
|
1535
|
+
overlay: "overlay"
|
|
1536
|
+
};
|
|
1537
|
+
const [canvas, ctx] = acquireOffscreen(w, h);
|
|
1538
|
+
ctx.drawImage(in2, 0, 0);
|
|
1539
|
+
ctx.globalCompositeOperation = modeMap[mode] ?? "source-over";
|
|
1540
|
+
ctx.drawImage(in1, 0, 0);
|
|
1541
|
+
ctx.globalCompositeOperation = "source-over";
|
|
1542
|
+
return canvas;
|
|
1543
|
+
}
|
|
1544
|
+
function extractSourceAlpha(input, w, h) {
|
|
1545
|
+
const [canvas, ctx] = acquireOffscreen(w, h);
|
|
1546
|
+
ctx.fillStyle = "black";
|
|
1547
|
+
ctx.fillRect(0, 0, w, h);
|
|
1548
|
+
ctx.globalCompositeOperation = "destination-in";
|
|
1549
|
+
ctx.drawImage(input, 0, 0);
|
|
1550
|
+
ctx.globalCompositeOperation = "source-over";
|
|
1551
|
+
return canvas;
|
|
1552
|
+
}
|
|
1553
|
+
function applyFilter(filterDef, sourceGraphic, w, h) {
|
|
1554
|
+
const buffers = /* @__PURE__ */ new Map();
|
|
1555
|
+
buffers.set("SourceGraphic", sourceGraphic);
|
|
1556
|
+
let sourceAlpha;
|
|
1557
|
+
const getSourceAlpha = () => {
|
|
1558
|
+
if (!sourceAlpha) {
|
|
1559
|
+
sourceAlpha = extractSourceAlpha(sourceGraphic, w, h);
|
|
1560
|
+
buffers.set("SourceAlpha", sourceAlpha);
|
|
1561
|
+
}
|
|
1562
|
+
return sourceAlpha;
|
|
1563
|
+
};
|
|
1564
|
+
const children = normalizeChildren(filterDef);
|
|
1565
|
+
let lastResult = sourceGraphic;
|
|
1566
|
+
for (const prim of children) {
|
|
1567
|
+
const p = prim.props;
|
|
1568
|
+
const inName = p.in ?? "";
|
|
1569
|
+
const resolve = (name) => {
|
|
1570
|
+
if (name === "SourceAlpha") return getSourceAlpha();
|
|
1571
|
+
return buffers.get(name) ?? lastResult;
|
|
1572
|
+
};
|
|
1573
|
+
const input = inName ? resolve(inName) : lastResult;
|
|
1574
|
+
let output;
|
|
1575
|
+
switch (prim.type) {
|
|
1576
|
+
case "feOffset":
|
|
1577
|
+
output = applyFeOffset(
|
|
1578
|
+
input,
|
|
1579
|
+
Number(p.dx ?? 0),
|
|
1580
|
+
Number(p.dy ?? 0),
|
|
1581
|
+
w,
|
|
1582
|
+
h
|
|
1583
|
+
);
|
|
1584
|
+
break;
|
|
1585
|
+
case "feGaussianBlur": {
|
|
1586
|
+
const std = Number(p.stdDeviation ?? 0);
|
|
1587
|
+
output = std > 0 ? applyFeGaussianBlur(input, std, w, h) : input;
|
|
1588
|
+
break;
|
|
1589
|
+
}
|
|
1590
|
+
case "feColorMatrix":
|
|
1591
|
+
output = applyFeColorMatrix(
|
|
1592
|
+
input,
|
|
1593
|
+
p.type ?? "matrix",
|
|
1594
|
+
p.values,
|
|
1595
|
+
w,
|
|
1596
|
+
h
|
|
1597
|
+
);
|
|
1598
|
+
break;
|
|
1599
|
+
case "feBlend": {
|
|
1600
|
+
const in2Name = p.in2 ?? "";
|
|
1601
|
+
const in2 = in2Name ? resolve(in2Name) : lastResult;
|
|
1602
|
+
output = applyFeBlend(input, in2, p.mode ?? "normal", w, h);
|
|
1603
|
+
break;
|
|
1604
|
+
}
|
|
1605
|
+
default:
|
|
1606
|
+
output = input;
|
|
1607
|
+
break;
|
|
1608
|
+
}
|
|
1609
|
+
const resultName = p.result;
|
|
1610
|
+
if (resultName) buffers.set(resultName, output);
|
|
1611
|
+
lastResult = output;
|
|
1612
|
+
}
|
|
1613
|
+
return lastResult;
|
|
1614
|
+
}
|
|
1615
|
+
function drawSvgChild(ctx, child, inherited, color, defs = EMPTY_DEFS, vbW = 0, vbH = 0) {
|
|
1455
1616
|
const { type } = child;
|
|
1456
1617
|
const props = mergeStyleIntoProps(child.props);
|
|
1457
|
-
if (type === "defs" || type === "clipPath" || type === "mask")
|
|
1618
|
+
if (type === "defs" || type === "clipPath" || type === "mask" || type === "filter")
|
|
1619
|
+
return;
|
|
1620
|
+
const transform = type !== "g" ? props.transform : void 0;
|
|
1621
|
+
if (transform) {
|
|
1622
|
+
ctx.save();
|
|
1623
|
+
applySvgTransform(ctx, transform);
|
|
1624
|
+
}
|
|
1458
1625
|
const clipRef = parseUrlRef(props.clipPath ?? props["clip-path"]);
|
|
1459
1626
|
const clipShapes = clipRef ? defs.clips.get(clipRef) : void 0;
|
|
1460
1627
|
const clipPath = clipShapes ? buildClipPath(clipShapes, vbW, vbH) : void 0;
|
|
1461
1628
|
const maskRef = parseUrlRef(props.mask);
|
|
1462
1629
|
const maskShapes = maskRef ? defs.masks.get(maskRef) : void 0;
|
|
1630
|
+
const filterRef = parseUrlRef(props.filter);
|
|
1631
|
+
const filterDef = filterRef ? defs.filters.get(filterRef) : void 0;
|
|
1463
1632
|
if (clipPath) ctx.save();
|
|
1464
1633
|
if (clipPath) ctx.clip(clipPath);
|
|
1634
|
+
const needsOffscreen = maskShapes || filterDef;
|
|
1465
1635
|
let elemCanvas;
|
|
1466
|
-
const targetCtx =
|
|
1636
|
+
const targetCtx = needsOffscreen ? (() => {
|
|
1467
1637
|
const [c, cCtx] = acquireOffscreen(Math.ceil(vbW), Math.ceil(vbH));
|
|
1468
1638
|
elemCanvas = c;
|
|
1469
1639
|
return cCtx;
|
|
1470
1640
|
})() : ctx;
|
|
1471
1641
|
switch (type) {
|
|
1472
1642
|
case "path":
|
|
1473
|
-
drawPath(targetCtx, props,
|
|
1643
|
+
drawPath(targetCtx, props, inherited, color, defs);
|
|
1474
1644
|
break;
|
|
1475
1645
|
case "circle":
|
|
1476
|
-
drawCircle(targetCtx, props,
|
|
1646
|
+
drawCircle(targetCtx, props, inherited, color, defs, vbW, vbH);
|
|
1477
1647
|
break;
|
|
1478
1648
|
case "rect":
|
|
1479
|
-
drawSvgRect(targetCtx, props,
|
|
1649
|
+
drawSvgRect(targetCtx, props, inherited, color, defs, vbW, vbH);
|
|
1480
1650
|
break;
|
|
1481
1651
|
case "line":
|
|
1482
|
-
drawLine(targetCtx, props, color, defs, vbW, vbH);
|
|
1652
|
+
drawLine(targetCtx, props, inherited, color, defs, vbW, vbH);
|
|
1483
1653
|
break;
|
|
1484
1654
|
case "ellipse":
|
|
1485
|
-
drawEllipse(targetCtx, props,
|
|
1655
|
+
drawEllipse(targetCtx, props, inherited, color, defs, vbW, vbH);
|
|
1486
1656
|
break;
|
|
1487
1657
|
case "polygon":
|
|
1488
|
-
drawPolygon(targetCtx, props,
|
|
1658
|
+
drawPolygon(targetCtx, props, inherited, color, defs);
|
|
1489
1659
|
break;
|
|
1490
1660
|
case "polyline":
|
|
1491
|
-
drawPolyline(targetCtx, props,
|
|
1661
|
+
drawPolyline(targetCtx, props, inherited, color, defs);
|
|
1492
1662
|
break;
|
|
1493
1663
|
case "g":
|
|
1494
|
-
drawGroup(targetCtx, child,
|
|
1664
|
+
drawGroup(targetCtx, child, inherited, color, defs, vbW, vbH);
|
|
1495
1665
|
break;
|
|
1496
1666
|
}
|
|
1497
|
-
if (
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1667
|
+
if (needsOffscreen && elemCanvas) {
|
|
1668
|
+
if (maskShapes) {
|
|
1669
|
+
const [maskCanvas, maskCtx] = acquireOffscreen(
|
|
1670
|
+
Math.ceil(vbW),
|
|
1671
|
+
Math.ceil(vbH)
|
|
1672
|
+
);
|
|
1673
|
+
for (const shape of maskShapes) {
|
|
1674
|
+
drawSvgChild(
|
|
1675
|
+
maskCtx,
|
|
1676
|
+
shape,
|
|
1677
|
+
{ fill: "white" },
|
|
1678
|
+
"white",
|
|
1679
|
+
defs,
|
|
1680
|
+
vbW,
|
|
1681
|
+
vbH
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
targetCtx.globalCompositeOperation = "destination-in";
|
|
1685
|
+
targetCtx.drawImage(maskCanvas, 0, 0);
|
|
1686
|
+
targetCtx.globalCompositeOperation = "source-over";
|
|
1687
|
+
releaseOffscreen(maskCanvas);
|
|
1688
|
+
}
|
|
1689
|
+
if (filterDef) {
|
|
1690
|
+
const filteredCanvas = applyFilter(
|
|
1691
|
+
filterDef,
|
|
1692
|
+
elemCanvas,
|
|
1693
|
+
Math.ceil(vbW),
|
|
1694
|
+
Math.ceil(vbH)
|
|
1695
|
+
);
|
|
1696
|
+
ctx.drawImage(filteredCanvas, 0, 0);
|
|
1697
|
+
if (filteredCanvas !== elemCanvas) releaseOffscreen(filteredCanvas);
|
|
1698
|
+
} else {
|
|
1699
|
+
ctx.drawImage(elemCanvas, 0, 0);
|
|
1700
|
+
}
|
|
1510
1701
|
releaseOffscreen(elemCanvas);
|
|
1511
1702
|
}
|
|
1512
1703
|
if (clipPath) ctx.restore();
|
|
1704
|
+
if (transform) ctx.restore();
|
|
1513
1705
|
}
|
|
1514
|
-
function drawPath(ctx, props,
|
|
1706
|
+
function drawPath(ctx, props, inherited, color, defs) {
|
|
1515
1707
|
const d = props.d;
|
|
1516
1708
|
if (!d) return;
|
|
1517
1709
|
const path = new Path2D(d);
|
|
1518
1710
|
const bbox = pathBBox(d);
|
|
1519
|
-
applyFillAndStroke(ctx, props, path,
|
|
1711
|
+
applyFillAndStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1520
1712
|
}
|
|
1521
|
-
function drawCircle(ctx, props,
|
|
1713
|
+
function drawCircle(ctx, props, inherited, color, defs, vbW = 0, vbH = 0) {
|
|
1522
1714
|
const cx = svgLength(props.cx, vbW);
|
|
1523
1715
|
const cy = svgLength(props.cy, vbH);
|
|
1524
1716
|
const r = svgLength(props.r, Math.min(vbW, vbH));
|
|
@@ -1526,9 +1718,9 @@ function drawCircle(ctx, props, inheritedFill, color, defs, vbW = 0, vbH = 0) {
|
|
|
1526
1718
|
const path = new Path2D();
|
|
1527
1719
|
path.arc(cx, cy, r, 0, Math.PI * 2);
|
|
1528
1720
|
const bbox = { x: cx - r, y: cy - r, width: 2 * r, height: 2 * r };
|
|
1529
|
-
applyFillAndStroke(ctx, props, path,
|
|
1721
|
+
applyFillAndStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1530
1722
|
}
|
|
1531
|
-
function drawSvgRect(ctx, props,
|
|
1723
|
+
function drawSvgRect(ctx, props, inherited, color, defs, vbW = 0, vbH = 0) {
|
|
1532
1724
|
const x = svgLength(props.x, vbW);
|
|
1533
1725
|
const y = svgLength(props.y, vbH);
|
|
1534
1726
|
const w = svgLength(props.width, vbW);
|
|
@@ -1547,9 +1739,9 @@ function drawSvgRect(ctx, props, inheritedFill, color, defs, vbW = 0, vbH = 0) {
|
|
|
1547
1739
|
path.rect(x, y, w, h);
|
|
1548
1740
|
}
|
|
1549
1741
|
const bbox = { x, y, width: w, height: h };
|
|
1550
|
-
applyFillAndStroke(ctx, props, path,
|
|
1742
|
+
applyFillAndStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1551
1743
|
}
|
|
1552
|
-
function drawLine(ctx, props, color, defs, vbW = 0, vbH = 0) {
|
|
1744
|
+
function drawLine(ctx, props, inherited, color, defs, vbW = 0, vbH = 0) {
|
|
1553
1745
|
const x1 = svgLength(props.x1, vbW);
|
|
1554
1746
|
const y1 = svgLength(props.y1, vbH);
|
|
1555
1747
|
const x2 = svgLength(props.x2, vbW);
|
|
@@ -1563,9 +1755,9 @@ function drawLine(ctx, props, color, defs, vbW = 0, vbH = 0) {
|
|
|
1563
1755
|
width: Math.abs(x2 - x1),
|
|
1564
1756
|
height: Math.abs(y2 - y1)
|
|
1565
1757
|
};
|
|
1566
|
-
applyStroke(ctx, props, path, color, defs, bbox);
|
|
1758
|
+
applyStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1567
1759
|
}
|
|
1568
|
-
function drawEllipse(ctx, props,
|
|
1760
|
+
function drawEllipse(ctx, props, inherited, color, defs, vbW = 0, vbH = 0) {
|
|
1569
1761
|
const cx = svgLength(props.cx, vbW);
|
|
1570
1762
|
const cy = svgLength(props.cy, vbH);
|
|
1571
1763
|
const rx = svgLength(props.rx, vbW);
|
|
@@ -1579,9 +1771,9 @@ function drawEllipse(ctx, props, inheritedFill, color, defs, vbW = 0, vbH = 0) {
|
|
|
1579
1771
|
width: 2 * rx,
|
|
1580
1772
|
height: 2 * ry
|
|
1581
1773
|
};
|
|
1582
|
-
applyFillAndStroke(ctx, props, path,
|
|
1774
|
+
applyFillAndStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1583
1775
|
}
|
|
1584
|
-
function drawPolygon(ctx, props,
|
|
1776
|
+
function drawPolygon(ctx, props, inherited, color, defs) {
|
|
1585
1777
|
const points = parsePoints(props.points);
|
|
1586
1778
|
if (points.length < 2) return;
|
|
1587
1779
|
const path = new Path2D();
|
|
@@ -1591,9 +1783,9 @@ function drawPolygon(ctx, props, inheritedFill, color, defs) {
|
|
|
1591
1783
|
}
|
|
1592
1784
|
path.closePath();
|
|
1593
1785
|
const bbox = pointsBBox(points);
|
|
1594
|
-
applyFillAndStroke(ctx, props, path,
|
|
1786
|
+
applyFillAndStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1595
1787
|
}
|
|
1596
|
-
function drawPolyline(ctx, props,
|
|
1788
|
+
function drawPolyline(ctx, props, inherited, color, defs) {
|
|
1597
1789
|
const points = parsePoints(props.points);
|
|
1598
1790
|
if (points.length < 2) return;
|
|
1599
1791
|
const path = new Path2D();
|
|
@@ -1602,7 +1794,7 @@ function drawPolyline(ctx, props, inheritedFill, color, defs) {
|
|
|
1602
1794
|
path.lineTo(points[i][0], points[i][1]);
|
|
1603
1795
|
}
|
|
1604
1796
|
const bbox = pointsBBox(points);
|
|
1605
|
-
applyFillAndStroke(ctx, props, path,
|
|
1797
|
+
applyFillAndStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1606
1798
|
}
|
|
1607
1799
|
function applySvgTransform(ctx, transform) {
|
|
1608
1800
|
const re = /\b(translate|scale|rotate|skewX|skewY|matrix)\s*\(([^)]*)\)/g;
|
|
@@ -1665,11 +1857,18 @@ function applySvgTransform(ctx, transform) {
|
|
|
1665
1857
|
}
|
|
1666
1858
|
}
|
|
1667
1859
|
}
|
|
1668
|
-
function drawGroup(ctx, node,
|
|
1860
|
+
function drawGroup(ctx, node, inherited, color, defs = EMPTY_DEFS, vbW = 0, vbH = 0) {
|
|
1669
1861
|
const children = normalizeChildren(node);
|
|
1670
1862
|
if (children.length === 0) return;
|
|
1671
1863
|
const merged = mergeStyleIntoProps(node.props);
|
|
1672
|
-
const
|
|
1864
|
+
const groupInherited = {
|
|
1865
|
+
fill: resolveCurrentColor(merged.fill, color) ?? inherited.fill,
|
|
1866
|
+
stroke: resolveCurrentColor(merged.stroke, color) ?? inherited.stroke,
|
|
1867
|
+
strokeWidth: merged.strokeWidth ?? merged["stroke-width"] ?? inherited.strokeWidth,
|
|
1868
|
+
strokeLinecap: merged.strokeLinecap ?? merged["stroke-linecap"] ?? inherited.strokeLinecap,
|
|
1869
|
+
strokeLinejoin: merged.strokeLinejoin ?? merged["stroke-linejoin"] ?? inherited.strokeLinejoin,
|
|
1870
|
+
strokeOpacity: merged.strokeOpacity ?? merged["stroke-opacity"] ?? inherited.strokeOpacity
|
|
1871
|
+
};
|
|
1673
1872
|
const transform = merged.transform;
|
|
1674
1873
|
if (transform) {
|
|
1675
1874
|
ctx.save();
|
|
@@ -1677,7 +1876,7 @@ function drawGroup(ctx, node, inheritedFill, color, defs = EMPTY_DEFS, vbW = 0,
|
|
|
1677
1876
|
}
|
|
1678
1877
|
for (const child of children) {
|
|
1679
1878
|
if (child != null && typeof child === "object") {
|
|
1680
|
-
drawSvgChild(ctx, child,
|
|
1879
|
+
drawSvgChild(ctx, child, groupInherited, color, defs, vbW, vbH);
|
|
1681
1880
|
}
|
|
1682
1881
|
}
|
|
1683
1882
|
if (transform) {
|
|
@@ -1693,36 +1892,68 @@ function parsePoints(value) {
|
|
|
1693
1892
|
}
|
|
1694
1893
|
return result;
|
|
1695
1894
|
}
|
|
1696
|
-
function applyFillAndStroke(ctx, props, path,
|
|
1697
|
-
const fill = resolveCurrentColor(props.fill, color) ??
|
|
1895
|
+
function applyFillAndStroke(ctx, props, path, inherited, color, defs, bbox) {
|
|
1896
|
+
const fill = resolveCurrentColor(props.fill, color) ?? inherited.fill;
|
|
1698
1897
|
const fillRule = props.fillRule ?? props["fill-rule"];
|
|
1898
|
+
const fillOpacity = Number(props.fillOpacity ?? props["fill-opacity"] ?? 1);
|
|
1699
1899
|
const fillRef = parseUrlRef(fill);
|
|
1700
1900
|
if (fillRef) {
|
|
1701
1901
|
const gradientDef = defs.gradients.get(fillRef);
|
|
1702
1902
|
if (gradientDef) {
|
|
1703
|
-
|
|
1903
|
+
if (fillOpacity < 1) {
|
|
1904
|
+
ctx.save();
|
|
1905
|
+
ctx.globalAlpha *= fillOpacity;
|
|
1906
|
+
fillWithSvgGradient(
|
|
1907
|
+
ctx,
|
|
1908
|
+
gradientDef,
|
|
1909
|
+
bbox,
|
|
1910
|
+
path,
|
|
1911
|
+
fillRule ?? "nonzero"
|
|
1912
|
+
);
|
|
1913
|
+
ctx.restore();
|
|
1914
|
+
} else {
|
|
1915
|
+
fillWithSvgGradient(
|
|
1916
|
+
ctx,
|
|
1917
|
+
gradientDef,
|
|
1918
|
+
bbox,
|
|
1919
|
+
path,
|
|
1920
|
+
fillRule ?? "nonzero"
|
|
1921
|
+
);
|
|
1922
|
+
}
|
|
1704
1923
|
}
|
|
1705
1924
|
} else if (fill !== "none") {
|
|
1706
|
-
ctx.fillStyle = fill;
|
|
1925
|
+
ctx.fillStyle = applyOpacity(fill, fillOpacity);
|
|
1707
1926
|
ctx.fill(path, fillRule ?? "nonzero");
|
|
1708
1927
|
}
|
|
1709
|
-
applyStroke(ctx, props, path, color, defs, bbox);
|
|
1928
|
+
applyStroke(ctx, props, path, inherited, color, defs, bbox);
|
|
1710
1929
|
}
|
|
1711
|
-
function applyStroke(ctx, props, path, color, defs, bbox) {
|
|
1712
|
-
const stroke = resolveCurrentColor(props.stroke, color);
|
|
1930
|
+
function applyStroke(ctx, props, path, inherited, color, defs, bbox) {
|
|
1931
|
+
const stroke = resolveCurrentColor(props.stroke, color) ?? inherited.stroke;
|
|
1713
1932
|
if (!stroke || stroke === "none") return;
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1933
|
+
const strokeOpacity = Number(
|
|
1934
|
+
props.strokeOpacity ?? props["stroke-opacity"] ?? inherited.strokeOpacity ?? 1
|
|
1935
|
+
);
|
|
1936
|
+
ctx.lineWidth = Number(
|
|
1937
|
+
props.strokeWidth ?? props["stroke-width"] ?? inherited.strokeWidth ?? 1
|
|
1938
|
+
);
|
|
1939
|
+
ctx.lineCap = props.strokeLinecap ?? props["stroke-linecap"] ?? inherited.strokeLinecap ?? "butt";
|
|
1940
|
+
ctx.lineJoin = props.strokeLinejoin ?? props["stroke-linejoin"] ?? inherited.strokeLinejoin ?? "miter";
|
|
1717
1941
|
const strokeRef = parseUrlRef(stroke);
|
|
1718
1942
|
if (strokeRef) {
|
|
1719
1943
|
const gradientDef = defs.gradients.get(strokeRef);
|
|
1720
1944
|
if (gradientDef) {
|
|
1721
|
-
|
|
1945
|
+
if (strokeOpacity < 1) {
|
|
1946
|
+
ctx.save();
|
|
1947
|
+
ctx.globalAlpha *= strokeOpacity;
|
|
1948
|
+
strokeWithSvgGradient(ctx, gradientDef, bbox, path);
|
|
1949
|
+
ctx.restore();
|
|
1950
|
+
} else {
|
|
1951
|
+
strokeWithSvgGradient(ctx, gradientDef, bbox, path);
|
|
1952
|
+
}
|
|
1722
1953
|
return;
|
|
1723
1954
|
}
|
|
1724
1955
|
}
|
|
1725
|
-
ctx.strokeStyle = stroke;
|
|
1956
|
+
ctx.strokeStyle = applyOpacity(stroke, strokeOpacity);
|
|
1726
1957
|
ctx.stroke(path);
|
|
1727
1958
|
}
|
|
1728
1959
|
|
|
@@ -2177,10 +2408,14 @@ async function drawNodeCore(ctx, node, parentX, parentY, offsetX, offsetY, debug
|
|
|
2177
2408
|
const paddingLeft = resolveBoxValue(style.paddingLeft, width);
|
|
2178
2409
|
const paddingRight = resolveBoxValue(style.paddingRight, width);
|
|
2179
2410
|
const paddingBottom = resolveBoxValue(style.paddingBottom, width);
|
|
2180
|
-
const
|
|
2181
|
-
const
|
|
2182
|
-
const
|
|
2183
|
-
const
|
|
2411
|
+
const borderTopW = resolveBoxValue(style.borderTopWidth, width);
|
|
2412
|
+
const borderLeftW = resolveBoxValue(style.borderLeftWidth, width);
|
|
2413
|
+
const borderRightW = resolveBoxValue(style.borderRightWidth, width);
|
|
2414
|
+
const borderBottomW = resolveBoxValue(style.borderBottomWidth, width);
|
|
2415
|
+
const imgX = x + paddingLeft + borderLeftW;
|
|
2416
|
+
const imgY = y + paddingTop + borderTopW;
|
|
2417
|
+
const imgW = width - paddingLeft - paddingRight - borderLeftW - borderRightW;
|
|
2418
|
+
const imgH = height - paddingTop - paddingBottom - borderTopW - borderBottomW;
|
|
2184
2419
|
if (!isClipped) {
|
|
2185
2420
|
const borderRadius = getBorderRadiusFromStyle(style, width, height);
|
|
2186
2421
|
if (hasRadius(borderRadius)) {
|
|
@@ -2421,23 +2656,32 @@ function expandStyle(raw, fontFamilies) {
|
|
|
2421
2656
|
}
|
|
2422
2657
|
if (style.flex !== void 0) {
|
|
2423
2658
|
const val = String(style.flex);
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
if (
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2659
|
+
if (val === "none") {
|
|
2660
|
+
if (style.flexGrow === void 0) style.flexGrow = 0;
|
|
2661
|
+
if (style.flexShrink === void 0) style.flexShrink = 0;
|
|
2662
|
+
if (style.flexBasis === void 0) style.flexBasis = "auto";
|
|
2663
|
+
} else {
|
|
2664
|
+
const parts = val.split(/\s+/);
|
|
2665
|
+
if (parts.length === 1) {
|
|
2666
|
+
const n = parseFloat(parts[0]);
|
|
2667
|
+
if (!isNaN(n)) {
|
|
2668
|
+
if (style.flexGrow === void 0) style.flexGrow = n;
|
|
2669
|
+
if (style.flexShrink === void 0) style.flexShrink = 1;
|
|
2670
|
+
if (style.flexBasis === void 0) style.flexBasis = 0;
|
|
2671
|
+
}
|
|
2672
|
+
} else if (parts.length === 2) {
|
|
2673
|
+
if (style.flexGrow === void 0)
|
|
2674
|
+
style.flexGrow = parseFloat(parts[0]);
|
|
2675
|
+
if (style.flexShrink === void 0)
|
|
2676
|
+
style.flexShrink = parseFloat(parts[1]);
|
|
2677
|
+
} else if (parts.length >= 3) {
|
|
2678
|
+
if (style.flexGrow === void 0)
|
|
2679
|
+
style.flexGrow = parseFloat(parts[0]);
|
|
2680
|
+
if (style.flexShrink === void 0)
|
|
2681
|
+
style.flexShrink = parseFloat(parts[1]);
|
|
2682
|
+
if (style.flexBasis === void 0)
|
|
2683
|
+
style.flexBasis = parseValue(parts[2]);
|
|
2431
2684
|
}
|
|
2432
|
-
} else if (parts.length === 2) {
|
|
2433
|
-
if (style.flexGrow === void 0) style.flexGrow = parseFloat(parts[0]);
|
|
2434
|
-
if (style.flexShrink === void 0)
|
|
2435
|
-
style.flexShrink = parseFloat(parts[1]);
|
|
2436
|
-
} else if (parts.length >= 3) {
|
|
2437
|
-
if (style.flexGrow === void 0) style.flexGrow = parseFloat(parts[0]);
|
|
2438
|
-
if (style.flexShrink === void 0)
|
|
2439
|
-
style.flexShrink = parseFloat(parts[1]);
|
|
2440
|
-
if (style.flexBasis === void 0) style.flexBasis = parseValue(parts[2]);
|
|
2441
2685
|
}
|
|
2442
2686
|
delete style.flex;
|
|
2443
2687
|
}
|
|
@@ -3049,9 +3293,15 @@ async function buildNode(element, parentStyle, yogaNode, viewportWidth, viewport
|
|
|
3049
3293
|
emojiEnabled
|
|
3050
3294
|
);
|
|
3051
3295
|
childYogaNode.setMeasureFunc(measureFunc);
|
|
3052
|
-
|
|
3296
|
+
const jc = style.justifyContent;
|
|
3297
|
+
if (!jc || jc === "flex-start") {
|
|
3298
|
+
childYogaNode.setFlexGrow(1);
|
|
3299
|
+
}
|
|
3053
3300
|
childYogaNode.setFlexShrink(1);
|
|
3054
3301
|
yogaNode.insertChild(childYogaNode, 0);
|
|
3302
|
+
if (style.flexShrink === void 0) {
|
|
3303
|
+
yogaNode.setFlexShrink(1);
|
|
3304
|
+
}
|
|
3055
3305
|
return {
|
|
3056
3306
|
type: tagName,
|
|
3057
3307
|
style,
|