q5 2.4.4 → 2.4.10
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/package.json +1 -1
- package/q5.d.ts +1425 -0
- package/q5.js +402 -226
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +29 -20
- package/src/q5-2d-image.js +1 -0
- package/src/q5-2d-text.js +188 -142
- package/src/q5-canvas.js +2 -2
- package/src/q5-vector.js +3 -3
- package/src/q5-webgpu-canvas.js +103 -45
- package/src/q5-webgpu-drawing.js +11 -0
- package/src/q5-webgpu-image.js +32 -4
- package/src/q5-webgpu-text.js +30 -7
- package/src/readme.md +3 -11
package/q5.js
CHANGED
|
@@ -554,12 +554,12 @@ Q5.modules.canvas = ($, q) => {
|
|
|
554
554
|
];
|
|
555
555
|
$._styles = [];
|
|
556
556
|
|
|
557
|
-
$.
|
|
557
|
+
$.pushStyles = () => {
|
|
558
558
|
let styles = {};
|
|
559
559
|
for (let s of $._styleNames) styles[s] = $[s];
|
|
560
560
|
$._styles.push(styles);
|
|
561
561
|
};
|
|
562
|
-
$.
|
|
562
|
+
$.popStyles = () => {
|
|
563
563
|
let styles = $._styles.pop();
|
|
564
564
|
for (let s of $._styleNames) $[s] = styles[s];
|
|
565
565
|
};
|
|
@@ -596,6 +596,13 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
596
596
|
return c;
|
|
597
597
|
};
|
|
598
598
|
|
|
599
|
+
$.clear = () => {
|
|
600
|
+
$.ctx.save();
|
|
601
|
+
$.ctx.resetTransform();
|
|
602
|
+
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
603
|
+
$.ctx.restore();
|
|
604
|
+
};
|
|
605
|
+
|
|
599
606
|
if ($._scope == 'image') return;
|
|
600
607
|
|
|
601
608
|
$._resizeCanvas = (w, h) => {
|
|
@@ -605,17 +612,21 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
605
612
|
}
|
|
606
613
|
delete t.canvas;
|
|
607
614
|
|
|
608
|
-
let o
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
615
|
+
let o;
|
|
616
|
+
if ($.frameCount > 1) {
|
|
617
|
+
o = new $._OffscreenCanvas(c.width, c.height);
|
|
618
|
+
o.w = c.w;
|
|
619
|
+
o.h = c.h;
|
|
620
|
+
let oCtx = o.getContext('2d');
|
|
621
|
+
oCtx.drawImage(c, 0, 0);
|
|
622
|
+
}
|
|
613
623
|
|
|
614
624
|
$._setCanvasSize(w, h);
|
|
615
625
|
|
|
616
626
|
for (let prop in t) $.ctx[prop] = t[prop];
|
|
617
627
|
$.scale($._pixelDensity);
|
|
618
|
-
|
|
628
|
+
|
|
629
|
+
if (o) $.ctx.drawImage(o, 0, 0, o.w, o.h);
|
|
619
630
|
};
|
|
620
631
|
|
|
621
632
|
$.fill = function (c) {
|
|
@@ -628,7 +639,7 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
628
639
|
}
|
|
629
640
|
if (c.a <= 0) return ($._doFill = false);
|
|
630
641
|
}
|
|
631
|
-
$.ctx.fillStyle = c.toString();
|
|
642
|
+
$.ctx.fillStyle = $._fill = c.toString();
|
|
632
643
|
};
|
|
633
644
|
$.noFill = () => ($._doFill = false);
|
|
634
645
|
$.stroke = function (c) {
|
|
@@ -641,20 +652,16 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
641
652
|
}
|
|
642
653
|
if (c.a <= 0) return ($._doStroke = false);
|
|
643
654
|
}
|
|
644
|
-
$.ctx.strokeStyle = c.toString();
|
|
655
|
+
$.ctx.strokeStyle = $._stroke = c.toString();
|
|
645
656
|
};
|
|
646
657
|
$.strokeWeight = (n) => {
|
|
647
658
|
if (!n) $._doStroke = false;
|
|
648
659
|
if ($._da) n *= $._da;
|
|
649
|
-
$.ctx.lineWidth = n || 0.0001;
|
|
660
|
+
$.ctx.lineWidth = $._strokeWeight = n || 0.0001;
|
|
650
661
|
};
|
|
651
662
|
$.noStroke = () => ($._doStroke = false);
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
$.ctx.resetTransform();
|
|
655
|
-
$.ctx.clearRect(0, 0, $.canvas.width, $.canvas.height);
|
|
656
|
-
$.ctx.restore();
|
|
657
|
-
};
|
|
663
|
+
|
|
664
|
+
$.opacity = (a) => ($.ctx.globalAlpha = a);
|
|
658
665
|
|
|
659
666
|
// DRAWING MATRIX
|
|
660
667
|
|
|
@@ -673,7 +680,6 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
673
680
|
y ??= x;
|
|
674
681
|
$.ctx.scale(x, y);
|
|
675
682
|
};
|
|
676
|
-
$.opacity = (a) => ($.ctx.globalAlpha = a);
|
|
677
683
|
$.applyMatrix = (a, b, c, d, e, f) => $.ctx.transform(a, b, c, d, e, f);
|
|
678
684
|
$.shearX = (ang) => $.ctx.transform(1, 0, $.tan(ang), 1, 0, 0);
|
|
679
685
|
$.shearY = (ang) => $.ctx.transform(1, $.tan(ang), 0, 1, 0, 0);
|
|
@@ -682,13 +688,16 @@ Q5.renderers.q2d.canvas = ($, q) => {
|
|
|
682
688
|
$.scale($._pixelDensity);
|
|
683
689
|
};
|
|
684
690
|
|
|
685
|
-
$.
|
|
691
|
+
$.pushMatrix = () => $.ctx.save();
|
|
692
|
+
$.popMatrix = () => $.ctx.restore();
|
|
693
|
+
|
|
694
|
+
$.push = () => {
|
|
686
695
|
$.ctx.save();
|
|
687
|
-
$.
|
|
696
|
+
$.pushStyles();
|
|
688
697
|
};
|
|
689
|
-
$.pop =
|
|
698
|
+
$.pop = () => {
|
|
690
699
|
$.ctx.restore();
|
|
691
|
-
$.
|
|
700
|
+
$.popStyles();
|
|
692
701
|
};
|
|
693
702
|
|
|
694
703
|
$.createCapture = (x) => {
|
|
@@ -1158,6 +1167,7 @@ Q5.renderers.q2d.image = ($, q) => {
|
|
|
1158
1167
|
for (let m of ['canvas', 'image', 'soft_filters']) {
|
|
1159
1168
|
if (r[m]) r[m]($, $);
|
|
1160
1169
|
}
|
|
1170
|
+
$._pixelDensity = opt.pixelDensity || 1;
|
|
1161
1171
|
$.createCanvas(w, h, opt);
|
|
1162
1172
|
delete $.createCanvas;
|
|
1163
1173
|
$._loop = false;
|
|
@@ -1412,11 +1422,23 @@ Q5.DILATE = 6;
|
|
|
1412
1422
|
Q5.ERODE = 7;
|
|
1413
1423
|
Q5.BLUR = 8;
|
|
1414
1424
|
Q5.renderers.q2d.text = ($, q) => {
|
|
1415
|
-
$.
|
|
1416
|
-
$.
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1425
|
+
$._textAlign = 'left';
|
|
1426
|
+
$._textBaseline = 'alphabetic';
|
|
1427
|
+
|
|
1428
|
+
let font = 'sans-serif',
|
|
1429
|
+
tSize = 12,
|
|
1430
|
+
leading = 15,
|
|
1431
|
+
leadDiff = 3,
|
|
1432
|
+
emphasis = 'normal',
|
|
1433
|
+
fontMod = false,
|
|
1434
|
+
styleHash = 0,
|
|
1435
|
+
styleHashes = [],
|
|
1436
|
+
useCache = false,
|
|
1437
|
+
genTextImage = false,
|
|
1438
|
+
cacheSize = 0,
|
|
1439
|
+
cacheMax = 12000;
|
|
1440
|
+
|
|
1441
|
+
let cache = ($._textCache = {});
|
|
1420
1442
|
|
|
1421
1443
|
$.loadFont = (url, cb) => {
|
|
1422
1444
|
q._preloadCount++;
|
|
@@ -1429,180 +1451,214 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
1429
1451
|
});
|
|
1430
1452
|
return name;
|
|
1431
1453
|
};
|
|
1432
|
-
|
|
1454
|
+
|
|
1455
|
+
$.textFont = (x) => {
|
|
1456
|
+
font = x;
|
|
1457
|
+
fontMod = true;
|
|
1458
|
+
styleHash = -1;
|
|
1459
|
+
};
|
|
1433
1460
|
$.textSize = (x) => {
|
|
1434
|
-
if (x === undefined) return
|
|
1461
|
+
if (x === undefined) return tSize;
|
|
1435
1462
|
if ($._da) x *= $._da;
|
|
1436
|
-
|
|
1463
|
+
tSize = x;
|
|
1464
|
+
fontMod = true;
|
|
1465
|
+
styleHash = -1;
|
|
1437
1466
|
if (!$._leadingSet) {
|
|
1438
|
-
|
|
1439
|
-
|
|
1467
|
+
leading = x * 1.25;
|
|
1468
|
+
leadDiff = leading - x;
|
|
1440
1469
|
}
|
|
1441
1470
|
};
|
|
1471
|
+
$.textStyle = (x) => {
|
|
1472
|
+
emphasis = x;
|
|
1473
|
+
fontMod = true;
|
|
1474
|
+
styleHash = -1;
|
|
1475
|
+
};
|
|
1442
1476
|
$.textLeading = (x) => {
|
|
1443
|
-
if (x === undefined) return
|
|
1477
|
+
if (x === undefined) return leading;
|
|
1444
1478
|
if ($._da) x *= $._da;
|
|
1445
|
-
|
|
1446
|
-
|
|
1479
|
+
leading = x;
|
|
1480
|
+
leadDiff = x - tSize;
|
|
1447
1481
|
$._leadingSet = true;
|
|
1482
|
+
styleHash = -1;
|
|
1448
1483
|
};
|
|
1449
|
-
$.textStyle = (x) => ($._textStyle = x);
|
|
1450
1484
|
$.textAlign = (horiz, vert) => {
|
|
1451
|
-
$.ctx.textAlign = horiz;
|
|
1485
|
+
$.ctx.textAlign = $._textAlign = horiz;
|
|
1452
1486
|
if (vert) {
|
|
1453
|
-
$.ctx.textBaseline = vert == $.CENTER ? 'middle' : vert;
|
|
1487
|
+
$.ctx.textBaseline = $._textBaseline = vert == $.CENTER ? 'middle' : vert;
|
|
1454
1488
|
}
|
|
1489
|
+
styleHash = -1;
|
|
1455
1490
|
};
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
$.ctx.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
|
|
1462
|
-
return $.ctx.measureText(str).actualBoundingBoxAscent;
|
|
1463
|
-
};
|
|
1464
|
-
$.textDescent = (str) => {
|
|
1465
|
-
$.ctx.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
|
|
1466
|
-
return $.ctx.measureText(str).actualBoundingBoxDescent;
|
|
1467
|
-
};
|
|
1491
|
+
|
|
1492
|
+
$.textWidth = (str) => $.ctx.measureText(str).width;
|
|
1493
|
+
$.textAscent = (str) => $.ctx.measureText(str).actualBoundingBoxAscent;
|
|
1494
|
+
$.textDescent = (str) => $.ctx.measureText(str).actualBoundingBoxDescent;
|
|
1495
|
+
|
|
1468
1496
|
$.textFill = $.fill;
|
|
1469
1497
|
$.textStroke = $.stroke;
|
|
1470
1498
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
set(k, v) {
|
|
1478
|
-
v.lastAccessed = Date.now();
|
|
1479
|
-
super.set(k, v);
|
|
1480
|
-
if (this.size > this.maxSize) this.gc();
|
|
1481
|
-
}
|
|
1482
|
-
get(k) {
|
|
1483
|
-
const v = super.get(k);
|
|
1484
|
-
if (v) v.lastAccessed = Date.now();
|
|
1485
|
-
return v;
|
|
1486
|
-
}
|
|
1487
|
-
gc() {
|
|
1488
|
-
let t = Infinity;
|
|
1489
|
-
let oldest;
|
|
1490
|
-
let i = 0;
|
|
1491
|
-
for (const [k, v] of this.entries()) {
|
|
1492
|
-
if (v.lastAccessed < t) {
|
|
1493
|
-
t = v.lastAccessed;
|
|
1494
|
-
oldest = i;
|
|
1495
|
-
}
|
|
1496
|
-
i++;
|
|
1497
|
-
}
|
|
1498
|
-
i = oldest;
|
|
1499
|
-
for (const k of this.keys()) {
|
|
1500
|
-
if (i == 0) {
|
|
1501
|
-
oldest = k;
|
|
1502
|
-
break;
|
|
1503
|
-
}
|
|
1504
|
-
i--;
|
|
1505
|
-
}
|
|
1506
|
-
this.delete(oldest);
|
|
1499
|
+
let updateStyleHash = () => {
|
|
1500
|
+
let styleString = font + tSize + emphasis + leading;
|
|
1501
|
+
|
|
1502
|
+
let hash = 5381;
|
|
1503
|
+
for (let i = 0; i < styleString.length; i++) {
|
|
1504
|
+
hash = (hash * 33) ^ styleString.charCodeAt(i);
|
|
1507
1505
|
}
|
|
1506
|
+
styleHash = hash >>> 0;
|
|
1508
1507
|
};
|
|
1509
|
-
|
|
1510
|
-
$.textCache = (
|
|
1511
|
-
if (maxSize)
|
|
1512
|
-
if (
|
|
1513
|
-
return
|
|
1514
|
-
};
|
|
1515
|
-
$._genTextImageKey = (str, w, h) => {
|
|
1516
|
-
return (
|
|
1517
|
-
str.slice(0, 200) +
|
|
1518
|
-
$._textStyle +
|
|
1519
|
-
$._textSize +
|
|
1520
|
-
$._textFont +
|
|
1521
|
-
($._doFill ? $.ctx.fillStyle : '') +
|
|
1522
|
-
'_' +
|
|
1523
|
-
($._doStroke && $._strokeSet ? $.ctx.lineWidth + $.ctx.strokeStyle + '_' : '') +
|
|
1524
|
-
(w || '') +
|
|
1525
|
-
(h ? 'x' + h : '')
|
|
1526
|
-
);
|
|
1508
|
+
|
|
1509
|
+
$.textCache = (enable, maxSize) => {
|
|
1510
|
+
if (maxSize) cacheMax = maxSize;
|
|
1511
|
+
if (enable !== undefined) useCache = enable;
|
|
1512
|
+
return useCache;
|
|
1527
1513
|
};
|
|
1528
1514
|
$.createTextImage = (str, w, h) => {
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
$._textCache = true;
|
|
1534
|
-
$._genTextImage = true;
|
|
1535
|
-
$.text(str, 0, 0, w, h);
|
|
1536
|
-
$._genTextImage = false;
|
|
1537
|
-
$._textCache = og;
|
|
1538
|
-
return $._tic.get(k);
|
|
1515
|
+
genTextImage = true;
|
|
1516
|
+
img = $.text(str, 0, 0, w, h);
|
|
1517
|
+
genTextImage = false;
|
|
1518
|
+
return img;
|
|
1539
1519
|
};
|
|
1520
|
+
|
|
1521
|
+
let lines = [];
|
|
1540
1522
|
$.text = (str, x, y, w, h) => {
|
|
1541
|
-
if (str === undefined) return;
|
|
1523
|
+
if (str === undefined || (!$._doFill && !$._doStroke)) return;
|
|
1542
1524
|
str = str.toString();
|
|
1543
1525
|
if ($._da) {
|
|
1544
1526
|
x *= $._da;
|
|
1545
1527
|
y *= $._da;
|
|
1546
1528
|
}
|
|
1547
|
-
|
|
1548
|
-
let
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1529
|
+
let ctx = $.ctx;
|
|
1530
|
+
let img, tX, tY;
|
|
1531
|
+
|
|
1532
|
+
if (fontMod) {
|
|
1533
|
+
ctx.font = `${emphasis} ${tSize}px ${font}`;
|
|
1534
|
+
fontMod = false;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
if (useCache || genTextImage) {
|
|
1538
|
+
if (styleHash == -1) updateStyleHash();
|
|
1539
|
+
|
|
1540
|
+
img = cache[str];
|
|
1541
|
+
if (img) img = img[styleHash];
|
|
1542
|
+
|
|
1543
|
+
if (img) {
|
|
1544
|
+
if (img._fill == $._fill && img._stroke == $._stroke && img._strokeWeight == $._strokeWeight) {
|
|
1545
|
+
if (genTextImage) return img;
|
|
1546
|
+
return $.textImage(img, x, y);
|
|
1547
|
+
} else img.clear();
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
if (str.indexOf('\n') == -1) lines[0] = str;
|
|
1552
|
+
else lines = str.split('\n');
|
|
1553
|
+
|
|
1554
|
+
if (w) {
|
|
1555
|
+
let wrapped = [];
|
|
1556
|
+
for (let line of lines) {
|
|
1557
|
+
let i = 0;
|
|
1558
|
+
|
|
1559
|
+
while (i < line.length) {
|
|
1560
|
+
let max = i + w;
|
|
1561
|
+
if (max >= line.length) {
|
|
1562
|
+
wrapped.push(line.slice(i));
|
|
1563
|
+
break;
|
|
1564
|
+
}
|
|
1565
|
+
let end = line.lastIndexOf(' ', max);
|
|
1566
|
+
if (end === -1 || end < i) {
|
|
1567
|
+
end = max;
|
|
1568
|
+
}
|
|
1569
|
+
wrapped.push(line.slice(i, end));
|
|
1570
|
+
i = end;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
lines = wrapped;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
if (!useCache && !genTextImage) {
|
|
1577
|
+
tX = x;
|
|
1578
|
+
tY = y;
|
|
1555
1579
|
} else {
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1580
|
+
tX = 0;
|
|
1581
|
+
tY = leading * lines.length;
|
|
1582
|
+
|
|
1583
|
+
if (!img) {
|
|
1584
|
+
let measure = ctx.measureText(' ');
|
|
1585
|
+
let ascent = measure.fontBoundingBoxAscent;
|
|
1586
|
+
let descent = measure.fontBoundingBoxDescent;
|
|
1587
|
+
h ??= tY + descent;
|
|
1588
|
+
|
|
1589
|
+
img = $.createImage.call($, Math.ceil(ctx.measureText(str).width), Math.ceil(h), {
|
|
1590
|
+
pixelDensity: $._pixelDensity
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
img._ascent = ascent;
|
|
1594
|
+
img._descent = descent;
|
|
1595
|
+
img._top = descent + leadDiff;
|
|
1596
|
+
img._middle = img._top + ascent * 0.5;
|
|
1597
|
+
img._bottom = img._top + ascent;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
img._fill = $._fill;
|
|
1601
|
+
img._stroke = $._stroke;
|
|
1602
|
+
img._strokeWeight = $._strokeWeight;
|
|
1603
|
+
img.modified = true;
|
|
1604
|
+
|
|
1605
|
+
ctx = img.ctx;
|
|
1606
|
+
|
|
1607
|
+
ctx.font = $.ctx.font;
|
|
1608
|
+
ctx.fillStyle = $._fill;
|
|
1609
|
+
ctx.strokeStyle = $._stroke;
|
|
1610
|
+
ctx.lineWidth = $.ctx.lineWidth;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
let ogFill;
|
|
1614
|
+
if (!$._fillSet) {
|
|
1615
|
+
ogFill = ctx.fillStyle;
|
|
1616
|
+
ctx.fillStyle = 'black';
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
for (let line of lines) {
|
|
1620
|
+
if ($._doStroke && $._strokeSet) ctx.strokeText(line, tX, tY);
|
|
1621
|
+
if ($._doFill) ctx.fillText(line, tX, tY);
|
|
1622
|
+
tY += leading;
|
|
1623
|
+
if (tY > h) break;
|
|
1624
|
+
}
|
|
1625
|
+
lines.length = 0;
|
|
1626
|
+
|
|
1627
|
+
if (!$._fillSet) ctx.fillStyle = ogFill;
|
|
1628
|
+
|
|
1629
|
+
if (useCache || genTextImage) {
|
|
1630
|
+
styleHashes.push(styleHash);
|
|
1631
|
+
(cache[str] ??= {})[styleHash] = img;
|
|
1632
|
+
|
|
1633
|
+
cacheSize++;
|
|
1634
|
+
if (cacheSize > cacheMax) {
|
|
1635
|
+
let half = Math.ceil(cacheSize / 2);
|
|
1636
|
+
let hashes = styleHashes.splice(0, half);
|
|
1637
|
+
for (let s in cache) {
|
|
1638
|
+
s = cache[s];
|
|
1639
|
+
for (let h of hashes) delete s[h];
|
|
1640
|
+
}
|
|
1641
|
+
cacheSize -= half;
|
|
1561
1642
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
c.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
|
|
1566
|
-
let lines = str.split('\n');
|
|
1567
|
-
if (useCache) {
|
|
1568
|
-
cX = 0;
|
|
1569
|
-
cY = $._textLeading * lines.length;
|
|
1570
|
-
let m = c.measureText(' ');
|
|
1571
|
-
_ascent = m.fontBoundingBoxAscent;
|
|
1572
|
-
_descent = m.fontBoundingBoxDescent;
|
|
1573
|
-
h ??= cY + _descent;
|
|
1574
|
-
tg.resizeCanvas(Math.ceil(c.measureText(str).width), Math.ceil(h));
|
|
1575
|
-
|
|
1576
|
-
c.fillStyle = $.ctx.fillStyle;
|
|
1577
|
-
c.strokeStyle = $.ctx.strokeStyle;
|
|
1578
|
-
c.lineWidth = $.ctx.lineWidth;
|
|
1579
|
-
}
|
|
1580
|
-
let f = c.fillStyle;
|
|
1581
|
-
if (!$._fillSet) c.fillStyle = 'black';
|
|
1582
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1583
|
-
if ($._doStroke && $._strokeSet) c.strokeText(lines[i], cX, cY);
|
|
1584
|
-
if ($._doFill) c.fillText(lines[i], cX, cY);
|
|
1585
|
-
cY += $._textLeading;
|
|
1586
|
-
if (cY > h) break;
|
|
1587
|
-
}
|
|
1588
|
-
if (!$._fillSet) c.fillStyle = f;
|
|
1589
|
-
if (useCache) {
|
|
1590
|
-
ti = tg;
|
|
1591
|
-
ti._ascent = _ascent;
|
|
1592
|
-
ti._descent = _descent;
|
|
1593
|
-
$._tic.set(k, ti);
|
|
1594
|
-
if (!$._genTextImage) $.textImage(ti, x, y);
|
|
1643
|
+
|
|
1644
|
+
if (genTextImage) return img;
|
|
1645
|
+
$.textImage(img, x, y);
|
|
1595
1646
|
}
|
|
1596
1647
|
};
|
|
1597
1648
|
$.textImage = (img, x, y) => {
|
|
1598
1649
|
let og = $._imageMode;
|
|
1599
1650
|
$._imageMode = 'corner';
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
if (
|
|
1603
|
-
if (
|
|
1604
|
-
|
|
1605
|
-
|
|
1651
|
+
|
|
1652
|
+
let ta = $._textAlign;
|
|
1653
|
+
if (ta == 'center') x -= img.canvas.hw;
|
|
1654
|
+
else if (ta == 'right') x -= img.width;
|
|
1655
|
+
|
|
1656
|
+
let bl = $._textBaseline;
|
|
1657
|
+
if (bl == 'alphabetic') y -= leading;
|
|
1658
|
+
else if (bl == 'middle') y -= img._middle;
|
|
1659
|
+
else if (bl == 'bottom') y -= img._bottom;
|
|
1660
|
+
else if (bl == 'top') y -= img._top;
|
|
1661
|
+
|
|
1606
1662
|
$.image(img, x, y);
|
|
1607
1663
|
$._imageMode = og;
|
|
1608
1664
|
};
|
|
@@ -2850,9 +2906,9 @@ Q5.Vector = class {
|
|
|
2850
2906
|
return this._$.atan2(this.y, this.x);
|
|
2851
2907
|
}
|
|
2852
2908
|
setHeading(ang) {
|
|
2853
|
-
let mag = this.mag();
|
|
2854
|
-
this.x = mag * this._$.cos(ang);
|
|
2855
|
-
this.y = mag * this._$.sin(ang);
|
|
2909
|
+
let mag = this.mag();
|
|
2910
|
+
this.x = mag * this._$.cos(ang);
|
|
2911
|
+
this.y = mag * this._$.sin(ang);
|
|
2856
2912
|
return this;
|
|
2857
2913
|
}
|
|
2858
2914
|
rotate(ang) {
|
|
@@ -3044,8 +3100,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3044
3100
|
$._createCanvas = (w, h, opt) => {
|
|
3045
3101
|
q.ctx = q.drawingContext = c.getContext('webgpu');
|
|
3046
3102
|
|
|
3047
|
-
opt.format
|
|
3048
|
-
opt.device
|
|
3103
|
+
opt.format ??= navigator.gpu.getPreferredCanvasFormat();
|
|
3104
|
+
opt.device ??= Q5.device;
|
|
3049
3105
|
|
|
3050
3106
|
$.ctx.configure(opt);
|
|
3051
3107
|
|
|
@@ -3070,6 +3126,37 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3070
3126
|
$._setCanvasSize(w, h);
|
|
3071
3127
|
};
|
|
3072
3128
|
|
|
3129
|
+
// current color index, used to associate a vertex with a color
|
|
3130
|
+
let colorIndex = 0;
|
|
3131
|
+
const addColor = (r, g, b, a = 1) => {
|
|
3132
|
+
if (typeof r == 'string') r = $.color(r);
|
|
3133
|
+
else if (b == undefined) {
|
|
3134
|
+
// grayscale mode `fill(1, 0.5)`
|
|
3135
|
+
a = g ?? 1;
|
|
3136
|
+
g = b = r;
|
|
3137
|
+
}
|
|
3138
|
+
if (r._q5Color) colorsStack.push(r.r, r.g, r.b, r.a);
|
|
3139
|
+
else colorsStack.push(r, g, b, a);
|
|
3140
|
+
colorIndex++;
|
|
3141
|
+
};
|
|
3142
|
+
|
|
3143
|
+
$.fill = (r, g, b, a) => {
|
|
3144
|
+
addColor(r, g, b, a);
|
|
3145
|
+
$._doFill = true;
|
|
3146
|
+
$._fillIndex = colorIndex;
|
|
3147
|
+
};
|
|
3148
|
+
$.stroke = (r, g, b, a) => {
|
|
3149
|
+
addColor(r, g, b, a);
|
|
3150
|
+
$._doStroke = true;
|
|
3151
|
+
$._strokeIndex = colorIndex;
|
|
3152
|
+
};
|
|
3153
|
+
|
|
3154
|
+
$.noFill = () => ($._doFill = false);
|
|
3155
|
+
$.noStroke = () => ($._doStroke = false);
|
|
3156
|
+
|
|
3157
|
+
$._strokeWeight = 1;
|
|
3158
|
+
$.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
|
|
3159
|
+
|
|
3073
3160
|
$.resetMatrix = () => {
|
|
3074
3161
|
// Initialize the transformation matrix as 4x4 identity matrix
|
|
3075
3162
|
|
|
@@ -3093,24 +3180,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3093
3180
|
// Stack to keep track of transformation matrix indexes
|
|
3094
3181
|
$._transformIndexStack = [];
|
|
3095
3182
|
|
|
3096
|
-
$.push = $.pushMatrix = () => {
|
|
3097
|
-
// Push the current matrix index onto the stack
|
|
3098
|
-
$._transformIndexStack.push($._transformIndex);
|
|
3099
|
-
$._pushStyles();
|
|
3100
|
-
};
|
|
3101
|
-
|
|
3102
|
-
$.pop = $.popMatrix = () => {
|
|
3103
|
-
if (!$._transformIndexStack.length) {
|
|
3104
|
-
return console.warn('Matrix index stack is empty!');
|
|
3105
|
-
}
|
|
3106
|
-
// Pop the last matrix index from the stack and set it as the current matrix index
|
|
3107
|
-
let idx = $._transformIndexStack.pop();
|
|
3108
|
-
$._matrix = $.transformStates[idx].slice();
|
|
3109
|
-
$._transformIndex = idx;
|
|
3110
|
-
$._matrixDirty = false;
|
|
3111
|
-
$._popStyles();
|
|
3112
|
-
};
|
|
3113
|
-
|
|
3114
3183
|
$.translate = (x, y, z) => {
|
|
3115
3184
|
if (!x && !y && !z) return;
|
|
3116
3185
|
// Update the translation values
|
|
@@ -3156,6 +3225,57 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3156
3225
|
$._matrixDirty = true;
|
|
3157
3226
|
};
|
|
3158
3227
|
|
|
3228
|
+
$.shearX = (ang) => {
|
|
3229
|
+
if (!ang) return;
|
|
3230
|
+
if ($._angleMode) ang *= $._DEGTORAD;
|
|
3231
|
+
|
|
3232
|
+
let tanAng = Math.tan(ang);
|
|
3233
|
+
|
|
3234
|
+
let m0 = $._matrix[0],
|
|
3235
|
+
m1 = $._matrix[1],
|
|
3236
|
+
m4 = $._matrix[4],
|
|
3237
|
+
m5 = $._matrix[5];
|
|
3238
|
+
|
|
3239
|
+
$._matrix[0] = m0 + m4 * tanAng;
|
|
3240
|
+
$._matrix[1] = m1 + m5 * tanAng;
|
|
3241
|
+
|
|
3242
|
+
$._matrixDirty = true;
|
|
3243
|
+
};
|
|
3244
|
+
|
|
3245
|
+
$.shearY = (ang) => {
|
|
3246
|
+
if (!ang) return;
|
|
3247
|
+
if ($._angleMode) ang *= $._DEGTORAD;
|
|
3248
|
+
|
|
3249
|
+
let tanAng = Math.tan(ang);
|
|
3250
|
+
|
|
3251
|
+
let m0 = $._matrix[0],
|
|
3252
|
+
m1 = $._matrix[1],
|
|
3253
|
+
m4 = $._matrix[4],
|
|
3254
|
+
m5 = $._matrix[5];
|
|
3255
|
+
|
|
3256
|
+
$._matrix[4] = m4 + m0 * tanAng;
|
|
3257
|
+
$._matrix[5] = m5 + m1 * tanAng;
|
|
3258
|
+
|
|
3259
|
+
$._matrixDirty = true;
|
|
3260
|
+
};
|
|
3261
|
+
|
|
3262
|
+
$.applyMatrix = (...args) => {
|
|
3263
|
+
let m;
|
|
3264
|
+
if (args.length == 1) m = args[0];
|
|
3265
|
+
else m = args;
|
|
3266
|
+
|
|
3267
|
+
if (m.length == 9) {
|
|
3268
|
+
// Convert 3x3 matrix to 4x4 matrix
|
|
3269
|
+
m = [m[0], m[1], 0, m[2], m[3], m[4], 0, m[5], 0, 0, 1, 0, m[6], m[7], 0, m[8]];
|
|
3270
|
+
} else if (m.length != 16) {
|
|
3271
|
+
throw new Error('Matrix must be a 3x3 or 4x4 array.');
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
// Overwrite the current transformation matrix
|
|
3275
|
+
$._matrix = m.slice();
|
|
3276
|
+
$._matrixDirty = true;
|
|
3277
|
+
};
|
|
3278
|
+
|
|
3159
3279
|
// Function to save the current matrix state if dirty
|
|
3160
3280
|
$._saveMatrix = () => {
|
|
3161
3281
|
$.transformStates.push($._matrix.slice());
|
|
@@ -3163,37 +3283,31 @@ Q5.renderers.webgpu.canvas = ($, q) => {
|
|
|
3163
3283
|
$._matrixDirty = false;
|
|
3164
3284
|
};
|
|
3165
3285
|
|
|
3166
|
-
// current
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3286
|
+
// Push the current matrix index onto the stack
|
|
3287
|
+
$.pushMatrix = () => {
|
|
3288
|
+
if ($._matrixDirty) $._saveMatrix();
|
|
3289
|
+
$._transformIndexStack.push($._transformIndex);
|
|
3290
|
+
};
|
|
3291
|
+
$.popMatrix = () => {
|
|
3292
|
+
if (!$._transformIndexStack.length) {
|
|
3293
|
+
return console.warn('Matrix index stack is empty!');
|
|
3174
3294
|
}
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3295
|
+
// Pop the last matrix index from the stack and set it as the current matrix index
|
|
3296
|
+
let idx = $._transformIndexStack.pop();
|
|
3297
|
+
$._matrix = $.transformStates[idx].slice();
|
|
3298
|
+
$._transformIndex = idx;
|
|
3299
|
+
$._matrixDirty = false;
|
|
3178
3300
|
};
|
|
3179
3301
|
|
|
3180
|
-
$.
|
|
3181
|
-
|
|
3182
|
-
$.
|
|
3183
|
-
$._fillIndex = colorIndex;
|
|
3302
|
+
$.push = () => {
|
|
3303
|
+
$.pushMatrix();
|
|
3304
|
+
$.pushStyles();
|
|
3184
3305
|
};
|
|
3185
|
-
$.
|
|
3186
|
-
|
|
3187
|
-
$.
|
|
3188
|
-
$._strokeIndex = colorIndex;
|
|
3306
|
+
$.pop = () => {
|
|
3307
|
+
$.popMatrix();
|
|
3308
|
+
$.popStyles();
|
|
3189
3309
|
};
|
|
3190
3310
|
|
|
3191
|
-
$.noFill = () => ($._doFill = false);
|
|
3192
|
-
$.noStroke = () => ($._doStroke = false);
|
|
3193
|
-
|
|
3194
|
-
$._strokeWeight = 1;
|
|
3195
|
-
$.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
|
|
3196
|
-
|
|
3197
3311
|
$._calcBox = (x, y, w, h, mode) => {
|
|
3198
3312
|
let hw = w / 2;
|
|
3199
3313
|
let hh = h / 2;
|
|
@@ -3553,6 +3667,15 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3553
3667
|
$.endShape(1);
|
|
3554
3668
|
};
|
|
3555
3669
|
|
|
3670
|
+
$.quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
3671
|
+
$.beginShape();
|
|
3672
|
+
$.vertex(x1, y1);
|
|
3673
|
+
$.vertex(x2, y2);
|
|
3674
|
+
$.vertex(x3, y3);
|
|
3675
|
+
$.vertex(x4, y4);
|
|
3676
|
+
$.endShape(1);
|
|
3677
|
+
};
|
|
3678
|
+
|
|
3556
3679
|
$.rectMode = (x) => ($._rectMode = x);
|
|
3557
3680
|
|
|
3558
3681
|
$.rect = (x, y, w, h) => {
|
|
@@ -3574,6 +3697,8 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
|
|
|
3574
3697
|
drawStack.push(0, 6);
|
|
3575
3698
|
};
|
|
3576
3699
|
|
|
3700
|
+
$.square = (x, y, s) => $.rect(x, y, s, s);
|
|
3701
|
+
|
|
3577
3702
|
$.point = (x, y) => {
|
|
3578
3703
|
colorIndex = $._strokeIndex;
|
|
3579
3704
|
let sw = $._strokeWeight;
|
|
@@ -3839,20 +3964,30 @@ fn fragmentMain(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
|
|
|
3839
3964
|
minFilter: 'linear'
|
|
3840
3965
|
});
|
|
3841
3966
|
|
|
3967
|
+
let MAX_TEXTURES = 12000;
|
|
3968
|
+
|
|
3969
|
+
$._textures = [];
|
|
3970
|
+
let tIdx = 0;
|
|
3971
|
+
|
|
3842
3972
|
$._createTexture = (img) => {
|
|
3843
3973
|
if (img.canvas) img = img.canvas;
|
|
3844
3974
|
|
|
3845
3975
|
let textureSize = [img.width, img.height, 1];
|
|
3846
3976
|
|
|
3847
|
-
|
|
3977
|
+
let texture = Q5.device.createTexture({
|
|
3848
3978
|
size: textureSize,
|
|
3849
3979
|
format: 'bgra8unorm',
|
|
3850
3980
|
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
|
|
3851
3981
|
});
|
|
3852
3982
|
|
|
3853
|
-
Q5.device.queue.copyExternalImageToTexture(
|
|
3983
|
+
Q5.device.queue.copyExternalImageToTexture(
|
|
3984
|
+
{ source: img },
|
|
3985
|
+
{ texture, colorSpace: $.canvas.colorSpace },
|
|
3986
|
+
textureSize
|
|
3987
|
+
);
|
|
3854
3988
|
|
|
3855
|
-
|
|
3989
|
+
$._textures[tIdx] = texture;
|
|
3990
|
+
img.textureIndex = tIdx;
|
|
3856
3991
|
|
|
3857
3992
|
const textureBindGroup = Q5.device.createBindGroup({
|
|
3858
3993
|
layout: textureLayout,
|
|
@@ -3861,7 +3996,16 @@ fn fragmentMain(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
|
|
|
3861
3996
|
{ binding: 1, resource: texture.createView() }
|
|
3862
3997
|
]
|
|
3863
3998
|
});
|
|
3864
|
-
$._textureBindGroups
|
|
3999
|
+
$._textureBindGroups[tIdx] = textureBindGroup;
|
|
4000
|
+
|
|
4001
|
+
tIdx = (tIdx + 1) % MAX_TEXTURES;
|
|
4002
|
+
|
|
4003
|
+
// If the texture array is full, destroy the oldest texture
|
|
4004
|
+
if ($._textures[tIdx]) {
|
|
4005
|
+
$._textures[tIdx].destroy();
|
|
4006
|
+
delete $._textures[tIdx];
|
|
4007
|
+
delete $._textureBindGroups[tIdx];
|
|
4008
|
+
}
|
|
3865
4009
|
};
|
|
3866
4010
|
|
|
3867
4011
|
$.loadImage = $.loadTexture = (src) => {
|
|
@@ -3925,6 +4069,15 @@ fn fragmentMain(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
|
|
|
3925
4069
|
verticesStack.length = 0;
|
|
3926
4070
|
});
|
|
3927
4071
|
};
|
|
4072
|
+
|
|
4073
|
+
Q5.THRESHOLD = 1;
|
|
4074
|
+
Q5.GRAY = 2;
|
|
4075
|
+
Q5.OPAQUE = 3;
|
|
4076
|
+
Q5.INVERT = 4;
|
|
4077
|
+
Q5.POSTERIZE = 5;
|
|
4078
|
+
Q5.DILATE = 6;
|
|
4079
|
+
Q5.ERODE = 7;
|
|
4080
|
+
Q5.BLUR = 8;
|
|
3928
4081
|
Q5.renderers.webgpu.text = ($, q) => {
|
|
3929
4082
|
let t = $.createGraphics(1, 1);
|
|
3930
4083
|
t.pixelDensity($._pixelDensity);
|
|
@@ -3936,6 +4089,8 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
3936
4089
|
q._preloadCount--;
|
|
3937
4090
|
});
|
|
3938
4091
|
};
|
|
4092
|
+
|
|
4093
|
+
// directly add these text setting functions to the webgpu renderer
|
|
3939
4094
|
$.textFont = t.textFont;
|
|
3940
4095
|
$.textSize = t.textSize;
|
|
3941
4096
|
$.textLeading = t.textLeading;
|
|
@@ -3951,7 +4106,20 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
3951
4106
|
$.text = (str, x, y, w, h) => {
|
|
3952
4107
|
let img = t.createTextImage(str, w, h);
|
|
3953
4108
|
|
|
3954
|
-
if (img.canvas.textureIndex
|
|
4109
|
+
if (img.canvas.textureIndex === undefined) {
|
|
4110
|
+
$._createTexture(img);
|
|
4111
|
+
} else if (img.modified) {
|
|
4112
|
+
let cnv = img.canvas;
|
|
4113
|
+
let textureSize = [cnv.width, cnv.height, 1];
|
|
4114
|
+
let texture = $._textures[cnv.textureIndex];
|
|
4115
|
+
|
|
4116
|
+
Q5.device.queue.copyExternalImageToTexture(
|
|
4117
|
+
{ source: cnv },
|
|
4118
|
+
{ texture, colorSpace: $.canvas.colorSpace },
|
|
4119
|
+
textureSize
|
|
4120
|
+
);
|
|
4121
|
+
img.modified = false;
|
|
4122
|
+
}
|
|
3955
4123
|
|
|
3956
4124
|
$.textImage(img, x, y);
|
|
3957
4125
|
};
|
|
@@ -3959,12 +4127,20 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
3959
4127
|
$.createTextImage = t.createTextImage;
|
|
3960
4128
|
|
|
3961
4129
|
$.textImage = (img, x, y) => {
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
else if (
|
|
4130
|
+
let og = $._imageMode;
|
|
4131
|
+
$._imageMode = 'corner';
|
|
4132
|
+
|
|
4133
|
+
let ta = t._textAlign;
|
|
4134
|
+
if (ta == 'center') x -= img.canvas.hw;
|
|
4135
|
+
else if (ta == 'right') x -= img.width;
|
|
4136
|
+
|
|
4137
|
+
let bl = t._textBaseline;
|
|
4138
|
+
if (bl == 'alphabetic') y -= t._textLeading;
|
|
4139
|
+
else if (bl == 'middle') y -= img._middle;
|
|
4140
|
+
else if (bl == 'bottom') y -= img._bottom;
|
|
4141
|
+
else if (bl == 'top') y -= img._top;
|
|
4142
|
+
|
|
3968
4143
|
$.image(img, x, y);
|
|
4144
|
+
$._imageMode = og;
|
|
3969
4145
|
};
|
|
3970
4146
|
};
|