q5 2.21.5 → 2.22.2

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/q5.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * q5.js
3
- * @version 2.21
3
+ * @version 2.22
4
4
  * @author quinton-ashley, Tezumie, and LingDong-
5
5
  * @license LGPL-3.0
6
6
  * @class Q5
@@ -41,7 +41,8 @@ function Q5(scope, parent, renderer) {
41
41
 
42
42
  $.canvas = $.ctx = $.drawingContext = null;
43
43
  $.pixels = [];
44
- let looper = null;
44
+ let looper = null,
45
+ useRAF = true;
45
46
 
46
47
  $.frameCount = 0;
47
48
  $.deltaTime = 16;
@@ -83,13 +84,22 @@ function Q5(scope, parent, renderer) {
83
84
  $._didResize = false;
84
85
  }
85
86
 
86
- if ($._loop) looper = raf($._draw);
87
- else if ($.frameCount && !$._redraw) return;
87
+ if ($._loop) {
88
+ if (useRAF) looper = raf($._draw);
89
+ else {
90
+ let nextTS = ts + $._targetFrameDuration;
91
+ let frameDelay = nextTS - performance.now();
92
+ while (frameDelay < 0) frameDelay += $._targetFrameDuration;
93
+ log(frameDelay);
94
+ looper = setTimeout(() => $._draw(nextTS), frameDelay);
95
+ }
96
+ } else if ($.frameCount && !$._redraw) return;
88
97
 
89
- if (looper && $.frameCount) {
90
- let time_since_last = ts - $._lastFrameTime;
91
- if (time_since_last < $._targetFrameDuration - 4) return;
98
+ if ($.frameCount && useRAF) {
99
+ let timeSinceLast = ts - $._lastFrameTime;
100
+ if (timeSinceLast < $._targetFrameDuration - 4) return;
92
101
  }
102
+
93
103
  q.deltaTime = ts - $._lastFrameTime;
94
104
  $._frameRate = 1000 / $.deltaTime;
95
105
  q.frameCount++;
@@ -100,7 +110,6 @@ function Q5(scope, parent, renderer) {
100
110
  try {
101
111
  $.draw();
102
112
  } catch (e) {
103
- if (!Q5.disableFriendlyErrors && $._askAI) $._askAI(e);
104
113
  if (!Q5.errorTolerant) $.noLoop();
105
114
  throw e;
106
115
  }
@@ -117,6 +126,10 @@ function Q5(scope, parent, renderer) {
117
126
  };
118
127
  $.noLoop = () => {
119
128
  $._loop = false;
129
+ if (looper) {
130
+ if (useRAF) cancelAnimationFrame(looper);
131
+ else clearTimeout(looper);
132
+ }
120
133
  looper = null;
121
134
  };
122
135
  $.loop = () => {
@@ -140,6 +153,14 @@ function Q5(scope, parent, renderer) {
140
153
  if (hz) {
141
154
  $._targetFrameRate = hz;
142
155
  $._targetFrameDuration = 1000 / hz;
156
+
157
+ if ($._loop && $._setupDone && looper != null) {
158
+ if (useRAF) cancelAnimationFrame(looper);
159
+ else clearTimeout(looper);
160
+ looper = null;
161
+ }
162
+ useRAF = hz <= 60;
163
+ setTimeout(() => $._draw(), $._targetFrameDuration);
143
164
  }
144
165
  return $._frameRate;
145
166
  };
@@ -243,20 +264,13 @@ function Q5(scope, parent, renderer) {
243
264
  for (let k of userFns) {
244
265
  if (!t[k]) $[k] = () => {};
245
266
  else if ($._isGlobal) {
246
- $[k] = (event) => {
247
- try {
248
- return t[k](event);
249
- } catch (e) {
250
- if ($._askAI) $._askAI(e);
251
- throw e;
252
- }
253
- };
267
+ $[k] = (event) => t[k](event);
254
268
  }
255
269
  }
256
270
 
257
271
  async function _setup() {
258
272
  $._startDone = true;
259
- if ($._preloadCount > 0) return raf(_setup);
273
+ if ($._preloadCount > 0 || $._g?._preloadCount > 0) return raf(_setup);
260
274
  millisStart = performance.now();
261
275
  await $.setup();
262
276
  $._setupDone = true;
@@ -314,7 +328,7 @@ function createCanvas(w, h, opt) {
314
328
  }
315
329
  }
316
330
 
317
- Q5.version = Q5.VERSION = '2.21';
331
+ Q5.version = Q5.VERSION = '2.22';
318
332
 
319
333
  if (typeof document == 'object') {
320
334
  document.addEventListener('DOMContentLoaded', () => {
@@ -407,7 +421,6 @@ Q5.modules.canvas = ($, q) => {
407
421
  if ($._hooks) {
408
422
  for (let m of $._hooks.postCanvas) m();
409
423
  }
410
- if ($._beginRender) $._beginRender();
411
424
 
412
425
  if ($._addEventMethods) $._addEventMethods(c);
413
426
 
@@ -514,7 +527,7 @@ Q5.modules.canvas = ($, q) => {
514
527
  $.pixelDensity = (v) => {
515
528
  if (!v || v == $._pixelDensity) return $._pixelDensity;
516
529
  $._pixelDensity = v;
517
- $._setCanvasSize(c.w, c.h);
530
+ $._resizeCanvas(c.w, c.h);
518
531
  return v;
519
532
  };
520
533
 
@@ -688,6 +701,22 @@ Q5.renderers.c2d.canvas = ($, q) => {
688
701
 
689
702
  if ($._scope == 'image') return;
690
703
 
704
+ $.background = function (c) {
705
+ $.ctx.save();
706
+ $.ctx.resetTransform();
707
+ $.ctx.globalAlpha = 1;
708
+ if (c.canvas) $.image(c, 0, 0, $.canvas.width, $.canvas.height);
709
+ else {
710
+ if (Q5.Color && !c._q5Color) {
711
+ if (typeof c != 'string') c = $.color(...arguments);
712
+ else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
713
+ }
714
+ $.ctx.fillStyle = c.toString();
715
+ $.ctx.fillRect(0, 0, $.canvas.width, $.canvas.height);
716
+ }
717
+ $.ctx.restore();
718
+ };
719
+
691
720
  $._resizeCanvas = (w, h) => {
692
721
  let t = {};
693
722
  for (let prop in $.ctx) {
@@ -868,22 +897,6 @@ Q5.renderers.c2d.shapes = ($) => {
868
897
 
869
898
  // DRAWING
870
899
 
871
- $.background = function (c) {
872
- $.ctx.save();
873
- $.ctx.resetTransform();
874
- $.ctx.globalAlpha = 1;
875
- if (c.canvas) $.image(c, 0, 0, $.canvas.width, $.canvas.height);
876
- else {
877
- if (Q5.Color && !c._q5Color) {
878
- if (typeof c != 'string') c = $.color(...arguments);
879
- else if ($._namedColors[c]) c = $.color(...$._namedColors[c]);
880
- }
881
- $.ctx.fillStyle = c.toString();
882
- $.ctx.fillRect(0, 0, $.canvas.width, $.canvas.height);
883
- }
884
- $.ctx.restore();
885
- };
886
-
887
900
  $.line = (x0, y0, x1, y1) => {
888
901
  if ($._doStroke) {
889
902
  $._da && ((x0 *= $._da), (y0 *= $._da), (x1 *= $._da), (y1 *= $._da));
@@ -1217,8 +1230,8 @@ Q5.renderers.c2d.shapes = ($) => {
1217
1230
  $.erase = function (fillAlpha = 255, strokeAlpha = 255) {
1218
1231
  $.ctx.save();
1219
1232
  $.ctx.globalCompositeOperation = 'destination-out';
1220
- $.ctx.fillStyle = `rgba(0, 0, 0, ${fillAlpha / 255})`;
1221
- $.ctx.strokeStyle = `rgba(0, 0, 0, ${strokeAlpha / 255})`;
1233
+ $.ctx.fillStyle = `rgb(0 0 0 / ${fillAlpha / 255})`;
1234
+ $.ctx.strokeStyle = `rgb(0 0 0 / ${strokeAlpha / 255})`;
1222
1235
  };
1223
1236
 
1224
1237
  $.noErase = function () {
@@ -1868,7 +1881,7 @@ Q5.renderers.c2d.text = ($, q) => {
1868
1881
 
1869
1882
  $.createTextImage = (str, w, h) => {
1870
1883
  genTextImage = true;
1871
- img = $.text(str, 0, 0, w, h);
1884
+ let img = $.text(str, 0, 0, w, h);
1872
1885
  genTextImage = false;
1873
1886
  return img;
1874
1887
  };
@@ -2095,21 +2108,29 @@ Q5.modules.ai = ($) => {
2095
2108
  };
2096
2109
  Q5.modules.color = ($, q) => {
2097
2110
  $.RGB = $.RGBA = $._colorMode = 'rgb';
2098
- $.SRGB = 'srgb';
2111
+ $.HSL = 'hsl';
2112
+ $.HSB = 'hsb';
2099
2113
  $.OKLCH = 'oklch';
2100
2114
 
2101
- $.colorMode = (mode, format) => {
2115
+ $.SRGB = 'srgb';
2116
+ $.DISPLAY_P3 = 'display-p3';
2117
+
2118
+ $.colorMode = (mode, format, gamut) => {
2102
2119
  $._colorMode = mode;
2103
- let srgb = $.canvas.colorSpace == 'srgb' || mode == 'srgb';
2120
+ let srgb = $.canvas.colorSpace == 'srgb' || gamut == 'srgb';
2104
2121
  format ??= srgb ? 'integer' : 'float';
2105
2122
  $._colorFormat = format == 'float' || format == 1 ? 1 : 255;
2106
2123
  if (mode == 'oklch') {
2107
2124
  q.Color = Q5.ColorOKLCH;
2125
+ } else if (mode == 'hsl') {
2126
+ q.Color = srgb ? Q5.ColorHSL : Q5.ColorHSL_P3;
2127
+ } else if (mode == 'hsb') {
2128
+ q.Color = srgb ? Q5.ColorHSB : Q5.ColorHSB_P3;
2108
2129
  } else {
2109
2130
  if ($._colorFormat == 255) {
2110
- q.Color = srgb ? Q5.ColorRGBA_8 : Q5.ColorRGBA_P3_8;
2131
+ q.Color = srgb ? Q5.ColorRGB_8 : Q5.ColorRGB_P3_8;
2111
2132
  } else {
2112
- q.Color = srgb ? Q5.ColorRGBA : Q5.ColorRGBA_P3;
2133
+ q.Color = srgb ? Q5.ColorRGB : Q5.ColorRGB_P3;
2113
2134
  }
2114
2135
  $._colorMode = 'rgb';
2115
2136
  }
@@ -2203,7 +2224,8 @@ Q5.modules.color = ($, q) => {
2203
2224
 
2204
2225
  $.lightness = (c) => {
2205
2226
  if (c.l) return c.l;
2206
- return ((0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * 100) / 255;
2227
+ let l = (0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * 100;
2228
+ return $._colorFormat == 255 ? l / 255 : l;
2207
2229
  };
2208
2230
  $.hue = (c) => {
2209
2231
  if (c.h) return c.h;
@@ -2248,6 +2270,12 @@ Q5.Color = class {
2248
2270
  constructor() {
2249
2271
  this._q5Color = true;
2250
2272
  }
2273
+ get alpha() {
2274
+ return this.a;
2275
+ }
2276
+ set alpha(v) {
2277
+ this.a = v;
2278
+ }
2251
2279
  };
2252
2280
 
2253
2281
  Q5.ColorOKLCH = class extends Q5.Color {
@@ -2289,15 +2317,9 @@ Q5.ColorOKLCH = class extends Q5.Color {
2289
2317
  set hue(v) {
2290
2318
  this.h = v;
2291
2319
  }
2292
- get alpha() {
2293
- return this.a;
2294
- }
2295
- set alpha(v) {
2296
- this.a = v;
2297
- }
2298
2320
  };
2299
2321
 
2300
- Q5.ColorRGBA = class extends Q5.Color {
2322
+ Q5.ColorRGB = class extends Q5.Color {
2301
2323
  constructor(r, g, b, a) {
2302
2324
  super();
2303
2325
  this.r = r;
@@ -2317,6 +2339,7 @@ Q5.ColorRGBA = class extends Q5.Color {
2317
2339
  toString() {
2318
2340
  return `color(srgb ${this.r} ${this.g} ${this.b} / ${this.a})`;
2319
2341
  }
2342
+
2320
2343
  get red() {
2321
2344
  return this.r;
2322
2345
  }
@@ -2335,22 +2358,16 @@ Q5.ColorRGBA = class extends Q5.Color {
2335
2358
  set blue(v) {
2336
2359
  this.b = v;
2337
2360
  }
2338
- get alpha() {
2339
- return this.a;
2340
- }
2341
- set alpha(v) {
2342
- this.a = v;
2343
- }
2344
2361
  };
2345
2362
 
2346
- Q5.ColorRGBA_P3 = class extends Q5.ColorRGBA {
2363
+ Q5.ColorRGB_P3 = class extends Q5.ColorRGB {
2347
2364
  toString() {
2348
2365
  return `color(display-p3 ${this.r} ${this.g} ${this.b} / ${this.a})`;
2349
2366
  }
2350
2367
  };
2351
2368
 
2352
- // legacy 8-bit (0-255) integer color format
2353
- Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
2369
+ // legacy 8-bit (0-255) integer color format, srgb color space
2370
+ Q5.ColorRGB_8 = class extends Q5.ColorRGB {
2354
2371
  constructor(r, g, b, a) {
2355
2372
  super(r, g, b, a ?? 255);
2356
2373
  }
@@ -2373,7 +2390,7 @@ Q5.ColorRGBA_8 = class extends Q5.ColorRGBA {
2373
2390
  };
2374
2391
 
2375
2392
  // p3 10-bit color in integer color format, for backwards compatibility
2376
- Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
2393
+ Q5.ColorRGB_P3_8 = class extends Q5.ColorRGB_8 {
2377
2394
  constructor(r, g, b, a) {
2378
2395
  super(r, g, b, a ?? 255);
2379
2396
  this._edited = true;
@@ -2418,6 +2435,159 @@ Q5.ColorRGBA_P3_8 = class extends Q5.ColorRGBA {
2418
2435
  return this._css;
2419
2436
  }
2420
2437
  };
2438
+
2439
+ Q5.ColorHSL = class extends Q5.Color {
2440
+ constructor(h, s, l, a) {
2441
+ super();
2442
+ this.h = h;
2443
+ this.s = s;
2444
+ this.l = l;
2445
+ this.a = a ?? 1;
2446
+ }
2447
+ get levels() {
2448
+ return [this.h, this.s, this.l, this.a];
2449
+ }
2450
+ equals(c) {
2451
+ return c && this.h == c.h && this.s == c.s && this.l == c.l && this.a == c.a;
2452
+ }
2453
+ isSameColor(c) {
2454
+ return c && this.h == c.h && this.s == c.s && this.l == c.l;
2455
+ }
2456
+ toString() {
2457
+ return `hsl(${this.h} ${this.s} ${this.l} / ${this.a})`;
2458
+ }
2459
+
2460
+ get hue() {
2461
+ return this.h;
2462
+ }
2463
+ set hue(v) {
2464
+ this.h = v;
2465
+ }
2466
+ get saturation() {
2467
+ return this.s;
2468
+ }
2469
+ set saturation(v) {
2470
+ this.s = v;
2471
+ }
2472
+ get lightness() {
2473
+ return this.l;
2474
+ }
2475
+ set lightness(v) {
2476
+ this.l = v;
2477
+ }
2478
+ };
2479
+
2480
+ Q5.ColorHSL_P3 = class extends Q5.ColorHSL {
2481
+ toString() {
2482
+ let o = Q5.HSLtoRGB(this.h, this.s, this.l);
2483
+ return `color(display-p3 ${o.join(' ')} / ${this.a})`;
2484
+ }
2485
+ };
2486
+
2487
+ Q5.ColorHSB = class extends Q5.ColorHSL {
2488
+ constructor(h, s, b, a) {
2489
+ super(h, s, b, a);
2490
+ delete this.l;
2491
+ this.b = b;
2492
+ }
2493
+ get levels() {
2494
+ return [this.h, this.s, this.b, this.a];
2495
+ }
2496
+ equals(c) {
2497
+ return c && this.h == c.h && this.s == c.s && this.b == c.b && this.a == c.a;
2498
+ }
2499
+ isSameColor(c) {
2500
+ return c && this.h == c.h && this.s == c.s && this.b == c.b;
2501
+ }
2502
+ toString() {
2503
+ let o = Q5.HSBtoHSL(this.h, this.s, this.b);
2504
+ return `hsl(${o.join(' ')} / ${this.a})`;
2505
+ }
2506
+
2507
+ get v() {
2508
+ return this.b;
2509
+ }
2510
+ set v(v) {
2511
+ this.b = v;
2512
+ }
2513
+ get brightness() {
2514
+ return this.b;
2515
+ }
2516
+ set brightness(v) {
2517
+ this.b = v;
2518
+ }
2519
+ get value() {
2520
+ return this.b;
2521
+ }
2522
+ set value(v) {
2523
+ this.b = v;
2524
+ }
2525
+ };
2526
+
2527
+ Q5.ColorHSB_P3 = class extends Q5.ColorHSB {
2528
+ toString() {
2529
+ let o = Q5.HSLtoRGB(...Q5.HSBtoHSL(this.h, this.s, this.b));
2530
+ return `color(display-p3 ${o.join(' ')} / ${this.a})`;
2531
+ }
2532
+ };
2533
+
2534
+ Q5.HSLtoRGB = (h, s, l) => {
2535
+ l /= 100;
2536
+ let m = (s / 100) * Math.min(l, 1 - l);
2537
+ let f = (n, k = (n + h / 30) % 12) => l - m * Math.max(Math.min(k - 3, 9 - k, 1), -1);
2538
+ return [f(0), f(8), f(4)];
2539
+ };
2540
+
2541
+ Q5.HSBtoHSL = (h, s, v, l = v * (1 - s / 200)) => [h, !l || l == 100 ? 0 : ((v - l) / Math.min(l, 100 - l)) * 100, l];
2542
+
2543
+ {
2544
+ const multiplyMatrices = (A, B) => [
2545
+ A[0] * B[0] + A[1] * B[1] + A[2] * B[2],
2546
+ A[3] * B[0] + A[4] * B[1] + A[5] * B[2],
2547
+ A[6] * B[0] + A[7] * B[1] + A[8] * B[2]
2548
+ ];
2549
+
2550
+ const oklch2oklab = (l, c, h) => [
2551
+ l,
2552
+ isNaN(h) ? 0 : c * Math.cos((h * Math.PI) / 180),
2553
+ isNaN(h) ? 0 : c * Math.sin((h * Math.PI) / 180)
2554
+ ];
2555
+
2556
+ const srgbLinear2rgb = (rgb) =>
2557
+ rgb.map((c) =>
2558
+ Math.max(
2559
+ 0,
2560
+ Math.min(1, Math.abs(c) > 0.0031308 ? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055) : 12.92 * c)
2561
+ )
2562
+ );
2563
+
2564
+ const oklab2xyz = (lab) => {
2565
+ const LMSg = multiplyMatrices(
2566
+ [
2567
+ 1, 0.3963377773761749, 0.2158037573099136, 1, -0.1055613458156586, -0.0638541728258133, 1, -0.0894841775298119,
2568
+ -1.2914855480194092
2569
+ ],
2570
+ lab
2571
+ );
2572
+ return multiplyMatrices(
2573
+ [
2574
+ 1.2268798758459243, -0.5578149944602171, 0.2813910456659647, -0.0405757452148008, 1.112286803280317,
2575
+ -0.0717110580655164, -0.0763729366746601, -0.4214933324022432, 1.5869240198367816
2576
+ ],
2577
+ LMSg.map((val) => val ** 3)
2578
+ );
2579
+ };
2580
+ const xyz2rgbLinear = (xyz) =>
2581
+ multiplyMatrices(
2582
+ [
2583
+ 3.2409699419045226, -1.537383177570094, -0.4986107602930034, -0.9692436362808796, 1.8759675015077202,
2584
+ 0.04155505740717559, 0.05563007969699366, -0.20397695888897652, 1.0569715142428786
2585
+ ],
2586
+ xyz
2587
+ );
2588
+
2589
+ Q5.OKLCHtoRGB = (l, c, h) => srgbLinear2rgb(xyz2rgbLinear(oklab2xyz(oklch2oklab(l, c, h))));
2590
+ }
2421
2591
  Q5.modules.display = ($) => {
2422
2592
  if (!$.canvas || $._scope == 'graphics') return;
2423
2593
 
@@ -4556,9 +4726,11 @@ struct Q5 {
4556
4726
  $._pipelineConfigs = [];
4557
4727
  $._pipelines = [];
4558
4728
  $._buffers = [];
4729
+ $._prevFramePL = 0;
4559
4730
  $._framePL = 0;
4560
4731
 
4561
4732
  // local variables used for slightly better performance
4733
+
4562
4734
  // stores pipeline shifts and vertex counts/image indices
4563
4735
  let drawStack = ($.drawStack = []);
4564
4736
 
@@ -4595,7 +4767,7 @@ struct Q5 {
4595
4767
  $.bindGroupLayouts = [mainLayout];
4596
4768
 
4597
4769
  let uniformBuffer = Q5.device.createBuffer({
4598
- size: 64, // Size of four floats
4770
+ size: 64,
4599
4771
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
4600
4772
  });
4601
4773
 
@@ -4695,7 +4867,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
4695
4867
  fragment: {
4696
4868
  module: frameShader,
4697
4869
  entryPoint: 'fragMain',
4698
- targets: [{ format, blend: $.blendConfigs.normal }]
4870
+ targets: [{ format }]
4699
4871
  },
4700
4872
  primitive: { topology: 'triangle-strip' },
4701
4873
  multisample: { count: 4 }
@@ -4723,27 +4895,24 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
4723
4895
  createMainView();
4724
4896
  };
4725
4897
 
4726
- $.pixelDensity = (v) => {
4727
- if (!v || v == $._pixelDensity) return $._pixelDensity;
4728
- $._pixelDensity = v;
4729
- $._setCanvasSize(c.w, c.h);
4730
- createMainView();
4731
- return v;
4732
- };
4733
-
4734
- // current color index, used to associate a vertex with a color
4735
4898
  let addColor = (r, g, b, a = 1) => {
4736
- if (typeof r == 'string') r = $.color(r);
4737
- else if (b == undefined) {
4899
+ if (typeof r == 'string' || $._colorMode != 'rgb') {
4900
+ r = $.color(r, g, b, a);
4901
+ } else if (b == undefined) {
4738
4902
  // grayscale mode `fill(1, 0.5)`
4739
4903
  a = g ?? 1;
4740
4904
  g = b = r;
4741
4905
  }
4742
4906
  if (r._q5Color) {
4743
- a = r.a;
4744
- b = r.b;
4745
- g = r.g;
4746
- r = r.r;
4907
+ let c = r;
4908
+ if (c.r) ({ r, g, b, a } = c);
4909
+ else {
4910
+ a = c.a;
4911
+ if (c.c != undefined) c = Q5.OKLCHtoRGB(c.l, c.c, c.h);
4912
+ else if (c.l != undefined) c = Q5.HSLtoRGB(c.h, c.s, c.l);
4913
+ else c = Q5.HSLtoRGB(...Q5.HSBtoHSL(c.h, c.s, c.b));
4914
+ [r, g, b] = c;
4915
+ }
4747
4916
  }
4748
4917
 
4749
4918
  let cs = colorStack,
@@ -4776,7 +4945,6 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
4776
4945
  $._tint = colorIndex;
4777
4946
  };
4778
4947
  $.opacity = (a) => ($._globalAlpha = a);
4779
-
4780
4948
  $.noFill = () => ($._doFill = false);
4781
4949
  $.noStroke = () => ($._doStroke = false);
4782
4950
  $.noTint = () => ($._tint = 1);
@@ -4784,6 +4952,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
4784
4952
  $._strokeWeight = 1;
4785
4953
  $._hsw = 0.5;
4786
4954
  $._scaledSW = 1;
4955
+
4787
4956
  $.strokeWeight = (v) => {
4788
4957
  v = Math.abs(v);
4789
4958
  $._strokeWeight = v;
@@ -4934,7 +5103,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
4934
5103
  $._matrixDirty = true;
4935
5104
  };
4936
5105
 
4937
- // function to save the current matrix state if dirty
5106
+ // saves the current matrix state
4938
5107
  $._saveMatrix = () => {
4939
5108
  transforms.set(matrix, matrices.length * MATRIX_SIZE);
4940
5109
  $._matrixIndex = matrices.length;
@@ -5066,13 +5235,28 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5066
5235
  };
5067
5236
 
5068
5237
  let shouldClear;
5238
+
5069
5239
  $.clear = () => {
5070
5240
  shouldClear = true;
5071
5241
  };
5072
5242
 
5073
- $._beginRender = () => {
5074
- if (encoder) return;
5243
+ $.background = (r, g, b, a) => {
5244
+ $.push();
5245
+ $.resetMatrix();
5246
+ if (r.canvas) {
5247
+ let img = r;
5248
+ $._imageMode = 'corner';
5249
+ $.image(img, -c.hw, -c.hh, c.w, c.h);
5250
+ } else {
5251
+ $._rectMode = 'corner';
5252
+ $.fill(r, g, b, a);
5253
+ $._doStroke = false;
5254
+ $.rect(-c.hw, -c.hh, c.w, c.h);
5255
+ }
5256
+ $.pop();
5257
+ };
5075
5258
 
5259
+ $._beginRender = () => {
5076
5260
  // swap the frame textures
5077
5261
  const temp = frameA;
5078
5262
  frameA = frameB;
@@ -5103,7 +5287,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5103
5287
  });
5104
5288
 
5105
5289
  if (!shouldClear) {
5106
- pass.setPipeline($._pipelines[0]);
5290
+ pass.setPipeline($._pipelines[$._prevFramePL]);
5107
5291
  pass.setBindGroup(0, frameBindGroup);
5108
5292
  pass.draw(4);
5109
5293
  }
@@ -5120,6 +5304,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5120
5304
  new Float32Array(transformBuffer.getMappedRange()).set(transforms.slice(0, matrices.length * MATRIX_SIZE));
5121
5305
  transformBuffer.unmap();
5122
5306
 
5307
+ $._buffers.push(transformBuffer);
5308
+
5123
5309
  let colorsBuffer = Q5.device.createBuffer({
5124
5310
  size: colorStackIndex * 4,
5125
5311
  usage: GPUBufferUsage.STORAGE,
@@ -5129,6 +5315,8 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5129
5315
  new Float32Array(colorsBuffer.getMappedRange()).set(colorStack.slice(0, colorStackIndex));
5130
5316
  colorsBuffer.unmap();
5131
5317
 
5318
+ $._buffers.push(colorsBuffer);
5319
+
5132
5320
  $._uniforms = [
5133
5321
  $.width,
5134
5322
  $.height,
@@ -5189,7 +5377,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5189
5377
  pass.setBindGroup(1, $._textureBindGroups[v]);
5190
5378
  pass.draw(4, 1, imageVertOffset);
5191
5379
  imageVertOffset += 4;
5192
- } else if (curPipelineIndex == 1 || curPipelineIndex >= 1000) {
5380
+ } else {
5193
5381
  // draw a shape
5194
5382
  // v is the number of vertices
5195
5383
  pass.draw(v, 1, drawVertOffset);
@@ -5198,7 +5386,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5198
5386
  }
5199
5387
  };
5200
5388
 
5201
- $._finishRender = () => {
5389
+ $._finishRender = async () => {
5202
5390
  pass.end();
5203
5391
 
5204
5392
  pass = encoder.beginRenderPass({
@@ -5228,6 +5416,7 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5228
5416
  pass.end();
5229
5417
 
5230
5418
  Q5.device.queue.submit([encoder.finish()]);
5419
+ $._pass = pass = encoder = null;
5231
5420
 
5232
5421
  // destroy buffers
5233
5422
  Q5.device.queue.onSubmittedWorkDone().then(() => {
@@ -5235,8 +5424,6 @@ fn fragMain(f: FragParams ) -> @location(0) vec4f {
5235
5424
  $._buffers = [];
5236
5425
  });
5237
5426
 
5238
- $._pass = pass = encoder = null;
5239
-
5240
5427
  // clear the stacks for the next frame
5241
5428
  drawStack.splice(0, drawStack.length);
5242
5429
  colorIndex = 1;
@@ -5886,22 +6073,6 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
5886
6073
  $.endShape(true);
5887
6074
  };
5888
6075
 
5889
- $.background = (r, g, b, a) => {
5890
- $.push();
5891
- $.resetMatrix();
5892
- if (r.canvas) {
5893
- let img = r;
5894
- $._imageMode = 'corner';
5895
- $.image(img, -c.hw, -c.hh, c.w, c.h);
5896
- } else {
5897
- $._rectMode = 'corner';
5898
- $.fill(r, g, b, a);
5899
- $._doStroke = false;
5900
- $.rect(-c.hw, -c.hh, c.w, c.h);
5901
- }
5902
- $.pop();
5903
- };
5904
-
5905
6076
  $._hooks.preRender.push(() => {
5906
6077
  $._pass.setPipeline($._pipelines[1]);
5907
6078
 
@@ -6231,9 +6402,11 @@ fn fragMain(f: FragParams) -> @location(0) vec4f {
6231
6402
 
6232
6403
  $.createGraphics = (w, h, opt) => {
6233
6404
  let g = _createGraphics(w, h, opt);
6234
- $._addTexture(g, g._frameA);
6235
- $._addTexture(g, g._frameB);
6236
- g._beginRender();
6405
+ if (g.canvas.renderer == 'webgpu') {
6406
+ $._addTexture(g, g._frameA);
6407
+ $._addTexture(g, g._frameB);
6408
+ g._beginRender();
6409
+ }
6237
6410
  return g;
6238
6411
  };
6239
6412
 
@@ -6720,6 +6893,7 @@ fn fragMain(f : FragParams) -> @location(0) vec4f {
6720
6893
  leadPercent = 1.25;
6721
6894
 
6722
6895
  $.textFont = (fontName) => {
6896
+ if (!fontName) return $._font;
6723
6897
  if (typeof fontName != 'string') fontName = fontName.family;
6724
6898
  let font = fonts[fontName];
6725
6899
  if (font) $._font = font;
@@ -7049,6 +7223,7 @@ Q5.renderers.webgpu.shaders = ($) => {
7049
7223
 
7050
7224
  let pl = plCounters[type];
7051
7225
  $._pipelines[pl] = Q5.device.createRenderPipeline(config);
7226
+ $._pipelines[pl].shader = shader;
7052
7227
  shader.pipelineIndex = pl;
7053
7228
 
7054
7229
  plCounters[type]++;
@@ -7063,15 +7238,17 @@ Q5.renderers.webgpu.shaders = ($) => {
7063
7238
  $.createTextShader = (code) => $._createShader(code, 'text');
7064
7239
 
7065
7240
  $.shader = (shader) => {
7066
- $['_' + shader.type + 'PL'] = shader.pipelineIndex;
7241
+ if (shader.applyBeforeDraw) $._prevFramePL = shader.pipelineIndex;
7242
+ else $['_' + shader.type + 'PL'] = shader.pipelineIndex;
7067
7243
  };
7068
7244
 
7069
7245
  $.resetShader = (type = 'shapes') => {
7246
+ if (type == 'frame') $._prevFramePL = 0;
7070
7247
  $['_' + type + 'PL'] = pipelineTypes.indexOf(type);
7071
7248
  };
7072
7249
 
7073
7250
  $.resetShaders = () => {
7074
- $._framePL = 0;
7251
+ $._prevFramePL = $._framePL = 0;
7075
7252
  $._shapesPL = 1;
7076
7253
  $._imagePL = 2;
7077
7254
  $._videoPL = 3;