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/src/q5-2d-text.js CHANGED
@@ -1,9 +1,21 @@
1
1
  Q5.renderers.q2d.text = ($, q) => {
2
- $._textFont = 'sans-serif';
3
- $._textSize = 12;
4
- $._textLeading = 15;
5
- $._textLeadDiff = 3;
6
- $._textStyle = 'normal';
2
+ $._textAlign = 'left';
3
+ $._textBaseline = 'alphabetic';
4
+
5
+ let font = 'sans-serif',
6
+ tSize = 12,
7
+ leading = 15,
8
+ leadDiff = 3,
9
+ emphasis = 'normal',
10
+ fontMod = false,
11
+ styleHash = 0,
12
+ styleHashes = [],
13
+ useCache = false,
14
+ genTextImage = false,
15
+ cacheSize = 0,
16
+ cacheMax = 12000;
17
+
18
+ let cache = ($._textCache = {});
7
19
 
8
20
  $.loadFont = (url, cb) => {
9
21
  q._preloadCount++;
@@ -16,180 +28,214 @@ Q5.renderers.q2d.text = ($, q) => {
16
28
  });
17
29
  return name;
18
30
  };
19
- $.textFont = (x) => ($._textFont = x);
31
+
32
+ $.textFont = (x) => {
33
+ font = x;
34
+ fontMod = true;
35
+ styleHash = -1;
36
+ };
20
37
  $.textSize = (x) => {
21
- if (x === undefined) return $._textSize;
38
+ if (x === undefined) return tSize;
22
39
  if ($._da) x *= $._da;
23
- $._textSize = x;
40
+ tSize = x;
41
+ fontMod = true;
42
+ styleHash = -1;
24
43
  if (!$._leadingSet) {
25
- $._textLeading = x * 1.25;
26
- $._textLeadDiff = $._textLeading - x;
44
+ leading = x * 1.25;
45
+ leadDiff = leading - x;
27
46
  }
28
47
  };
48
+ $.textStyle = (x) => {
49
+ emphasis = x;
50
+ fontMod = true;
51
+ styleHash = -1;
52
+ };
29
53
  $.textLeading = (x) => {
30
- if (x === undefined) return $._textLeading;
54
+ if (x === undefined) return leading;
31
55
  if ($._da) x *= $._da;
32
- $._textLeading = x;
33
- $._textLeadDiff = x - $._textSize;
56
+ leading = x;
57
+ leadDiff = x - tSize;
34
58
  $._leadingSet = true;
59
+ styleHash = -1;
35
60
  };
36
- $.textStyle = (x) => ($._textStyle = x);
37
61
  $.textAlign = (horiz, vert) => {
38
- $.ctx.textAlign = horiz;
62
+ $.ctx.textAlign = $._textAlign = horiz;
39
63
  if (vert) {
40
- $.ctx.textBaseline = vert == $.CENTER ? 'middle' : vert;
64
+ $.ctx.textBaseline = $._textBaseline = vert == $.CENTER ? 'middle' : vert;
41
65
  }
66
+ styleHash = -1;
42
67
  };
43
- $.textWidth = (str) => {
44
- $.ctx.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
45
- return $.ctx.measureText(str).width;
46
- };
47
- $.textAscent = (str) => {
48
- $.ctx.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
49
- return $.ctx.measureText(str).actualBoundingBoxAscent;
50
- };
51
- $.textDescent = (str) => {
52
- $.ctx.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
53
- return $.ctx.measureText(str).actualBoundingBoxDescent;
54
- };
68
+
69
+ $.textWidth = (str) => $.ctx.measureText(str).width;
70
+ $.textAscent = (str) => $.ctx.measureText(str).actualBoundingBoxAscent;
71
+ $.textDescent = (str) => $.ctx.measureText(str).actualBoundingBoxDescent;
72
+
55
73
  $.textFill = $.fill;
56
74
  $.textStroke = $.stroke;
57
75
 
58
- $._textCache = !!Q5.Image;
59
- $._TimedCache = class extends Map {
60
- constructor() {
61
- super();
62
- this.maxSize = 500;
63
- }
64
- set(k, v) {
65
- v.lastAccessed = Date.now();
66
- super.set(k, v);
67
- if (this.size > this.maxSize) this.gc();
68
- }
69
- get(k) {
70
- const v = super.get(k);
71
- if (v) v.lastAccessed = Date.now();
72
- return v;
73
- }
74
- gc() {
75
- let t = Infinity;
76
- let oldest;
77
- let i = 0;
78
- for (const [k, v] of this.entries()) {
79
- if (v.lastAccessed < t) {
80
- t = v.lastAccessed;
81
- oldest = i;
82
- }
83
- i++;
84
- }
85
- i = oldest;
86
- for (const k of this.keys()) {
87
- if (i == 0) {
88
- oldest = k;
89
- break;
90
- }
91
- i--;
92
- }
93
- this.delete(oldest);
76
+ let updateStyleHash = () => {
77
+ let styleString = font + tSize + emphasis + leading;
78
+
79
+ let hash = 5381;
80
+ for (let i = 0; i < styleString.length; i++) {
81
+ hash = (hash * 33) ^ styleString.charCodeAt(i);
94
82
  }
83
+ styleHash = hash >>> 0;
95
84
  };
96
- $._tic = new $._TimedCache();
97
- $.textCache = (b, maxSize) => {
98
- if (maxSize) $._tic.maxSize = maxSize;
99
- if (b !== undefined) $._textCache = b;
100
- return $._textCache;
101
- };
102
- $._genTextImageKey = (str, w, h) => {
103
- return (
104
- str.slice(0, 200) +
105
- $._textStyle +
106
- $._textSize +
107
- $._textFont +
108
- ($._doFill ? $.ctx.fillStyle : '') +
109
- '_' +
110
- ($._doStroke && $._strokeSet ? $.ctx.lineWidth + $.ctx.strokeStyle + '_' : '') +
111
- (w || '') +
112
- (h ? 'x' + h : '')
113
- );
85
+
86
+ $.textCache = (enable, maxSize) => {
87
+ if (maxSize) cacheMax = maxSize;
88
+ if (enable !== undefined) useCache = enable;
89
+ return useCache;
114
90
  };
115
91
  $.createTextImage = (str, w, h) => {
116
- let k = $._genTextImageKey(str, w, h);
117
- if ($._tic.get(k)) return $._tic.get(k);
118
-
119
- let og = $._textCache;
120
- $._textCache = true;
121
- $._genTextImage = true;
122
- $.text(str, 0, 0, w, h);
123
- $._genTextImage = false;
124
- $._textCache = og;
125
- return $._tic.get(k);
92
+ genTextImage = true;
93
+ img = $.text(str, 0, 0, w, h);
94
+ genTextImage = false;
95
+ return img;
126
96
  };
97
+
98
+ let lines = [];
127
99
  $.text = (str, x, y, w, h) => {
128
- if (str === undefined) return;
100
+ if (str === undefined || (!$._doFill && !$._doStroke)) return;
129
101
  str = str.toString();
130
102
  if ($._da) {
131
103
  x *= $._da;
132
104
  y *= $._da;
133
105
  }
134
- if (!$._doFill && !$._doStroke) return;
135
- let c, ti, tg, k, cX, cY, _ascent, _descent;
136
- let t = $.ctx.getTransform();
137
- let useCache = $._genTextImage || ($._textCache && (t.b != 0 || t.c != 0));
138
- if (!useCache) {
139
- c = $.ctx;
140
- cX = x;
141
- cY = y;
106
+ let ctx = $.ctx;
107
+ let img, tX, tY;
108
+
109
+ if (fontMod) {
110
+ ctx.font = `${emphasis} ${tSize}px ${font}`;
111
+ fontMod = false;
112
+ }
113
+
114
+ if (useCache || genTextImage) {
115
+ if (styleHash == -1) updateStyleHash();
116
+
117
+ img = cache[str];
118
+ if (img) img = img[styleHash];
119
+
120
+ if (img) {
121
+ if (img._fill == $._fill && img._stroke == $._stroke && img._strokeWeight == $._strokeWeight) {
122
+ if (genTextImage) return img;
123
+ return $.textImage(img, x, y);
124
+ } else img.clear();
125
+ }
126
+ }
127
+
128
+ if (str.indexOf('\n') == -1) lines[0] = str;
129
+ else lines = str.split('\n');
130
+
131
+ if (w) {
132
+ let wrapped = [];
133
+ for (let line of lines) {
134
+ let i = 0;
135
+
136
+ while (i < line.length) {
137
+ let max = i + w;
138
+ if (max >= line.length) {
139
+ wrapped.push(line.slice(i));
140
+ break;
141
+ }
142
+ let end = line.lastIndexOf(' ', max);
143
+ if (end === -1 || end < i) {
144
+ end = max;
145
+ }
146
+ wrapped.push(line.slice(i, end));
147
+ i = end;
148
+ }
149
+ }
150
+ lines = wrapped;
151
+ }
152
+
153
+ if (!useCache && !genTextImage) {
154
+ tX = x;
155
+ tY = y;
142
156
  } else {
143
- k = $._genTextImageKey(str, w, h);
144
- ti = $._tic.get(k);
145
- if (ti && !$._genTextImage) {
146
- $.textImage(ti, x, y);
147
- return;
157
+ tX = 0;
158
+ tY = leading * lines.length;
159
+
160
+ if (!img) {
161
+ let measure = ctx.measureText(' ');
162
+ let ascent = measure.fontBoundingBoxAscent;
163
+ let descent = measure.fontBoundingBoxDescent;
164
+ h ??= tY + descent;
165
+
166
+ img = $.createImage.call($, Math.ceil(ctx.measureText(str).width), Math.ceil(h), {
167
+ pixelDensity: $._pixelDensity
168
+ });
169
+
170
+ img._ascent = ascent;
171
+ img._descent = descent;
172
+ img._top = descent + leadDiff;
173
+ img._middle = img._top + ascent * 0.5;
174
+ img._bottom = img._top + ascent;
148
175
  }
149
- tg = $.createGraphics.call($, 1, 1);
150
- c = tg.ctx;
176
+
177
+ img._fill = $._fill;
178
+ img._stroke = $._stroke;
179
+ img._strokeWeight = $._strokeWeight;
180
+ img.modified = true;
181
+
182
+ ctx = img.ctx;
183
+
184
+ ctx.font = $.ctx.font;
185
+ ctx.fillStyle = $._fill;
186
+ ctx.strokeStyle = $._stroke;
187
+ ctx.lineWidth = $.ctx.lineWidth;
151
188
  }
152
- c.font = `${$._textStyle} ${$._textSize}px ${$._textFont}`;
153
- let lines = str.split('\n');
154
- if (useCache) {
155
- cX = 0;
156
- cY = $._textLeading * lines.length;
157
- let m = c.measureText(' ');
158
- _ascent = m.fontBoundingBoxAscent;
159
- _descent = m.fontBoundingBoxDescent;
160
- h ??= cY + _descent;
161
- tg.resizeCanvas(Math.ceil(c.measureText(str).width), Math.ceil(h));
162
-
163
- c.fillStyle = $.ctx.fillStyle;
164
- c.strokeStyle = $.ctx.strokeStyle;
165
- c.lineWidth = $.ctx.lineWidth;
189
+
190
+ let ogFill;
191
+ if (!$._fillSet) {
192
+ ogFill = ctx.fillStyle;
193
+ ctx.fillStyle = 'black';
166
194
  }
167
- let f = c.fillStyle;
168
- if (!$._fillSet) c.fillStyle = 'black';
169
- for (let i = 0; i < lines.length; i++) {
170
- if ($._doStroke && $._strokeSet) c.strokeText(lines[i], cX, cY);
171
- if ($._doFill) c.fillText(lines[i], cX, cY);
172
- cY += $._textLeading;
173
- if (cY > h) break;
195
+
196
+ for (let line of lines) {
197
+ if ($._doStroke && $._strokeSet) ctx.strokeText(line, tX, tY);
198
+ if ($._doFill) ctx.fillText(line, tX, tY);
199
+ tY += leading;
200
+ if (tY > h) break;
174
201
  }
175
- if (!$._fillSet) c.fillStyle = f;
176
- if (useCache) {
177
- ti = tg;
178
- ti._ascent = _ascent;
179
- ti._descent = _descent;
180
- $._tic.set(k, ti);
181
- if (!$._genTextImage) $.textImage(ti, x, y);
202
+ lines.length = 0;
203
+
204
+ if (!$._fillSet) ctx.fillStyle = ogFill;
205
+
206
+ if (useCache || genTextImage) {
207
+ styleHashes.push(styleHash);
208
+ (cache[str] ??= {})[styleHash] = img;
209
+
210
+ cacheSize++;
211
+ if (cacheSize > cacheMax) {
212
+ let half = Math.ceil(cacheSize / 2);
213
+ let hashes = styleHashes.splice(0, half);
214
+ for (let s in cache) {
215
+ s = cache[s];
216
+ for (let h of hashes) delete s[h];
217
+ }
218
+ cacheSize -= half;
219
+ }
220
+
221
+ if (genTextImage) return img;
222
+ $.textImage(img, x, y);
182
223
  }
183
224
  };
184
225
  $.textImage = (img, x, y) => {
185
226
  let og = $._imageMode;
186
227
  $._imageMode = 'corner';
187
- if ($.ctx.textAlign == 'center') x -= img.width * 0.5;
188
- else if ($.ctx.textAlign == 'right') x -= img.width;
189
- if ($.ctx.textBaseline == 'alphabetic') y -= $._textLeading;
190
- if ($.ctx.textBaseline == 'middle') y -= img._descent + img._ascent * 0.5 + $._textLeadDiff;
191
- else if ($.ctx.textBaseline == 'bottom') y -= img._ascent + img._descent + $._textLeadDiff;
192
- else if ($.ctx.textBaseline == 'top') y -= img._descent + $._textLeadDiff;
228
+
229
+ let ta = $._textAlign;
230
+ if (ta == 'center') x -= img.canvas.hw;
231
+ else if (ta == 'right') x -= img.width;
232
+
233
+ let bl = $._textBaseline;
234
+ if (bl == 'alphabetic') y -= leading;
235
+ else if (bl == 'middle') y -= img._middle;
236
+ else if (bl == 'bottom') y -= img._bottom;
237
+ else if (bl == 'top') y -= img._top;
238
+
193
239
  $.image(img, x, y);
194
240
  $._imageMode = og;
195
241
  };
package/src/q5-canvas.js CHANGED
@@ -272,12 +272,12 @@ Q5.modules.canvas = ($, q) => {
272
272
  ];
273
273
  $._styles = [];
274
274
 
275
- $._pushStyles = () => {
275
+ $.pushStyles = () => {
276
276
  let styles = {};
277
277
  for (let s of $._styleNames) styles[s] = $[s];
278
278
  $._styles.push(styles);
279
279
  };
280
- $._popStyles = () => {
280
+ $.popStyles = () => {
281
281
  let styles = $._styles.pop();
282
282
  for (let s of $._styleNames) $[s] = styles[s];
283
283
  };
package/src/q5-vector.js CHANGED
@@ -138,9 +138,9 @@ Q5.Vector = class {
138
138
  return this._$.atan2(this.y, this.x);
139
139
  }
140
140
  setHeading(ang) {
141
- let mag = this.mag(); // Calculate the magnitude of the vector
142
- this.x = mag * this._$.cos(ang); // Set the new x component
143
- this.y = mag * this._$.sin(ang); // Set the new y component
141
+ let mag = this.mag();
142
+ this.x = mag * this._$.cos(ang);
143
+ this.y = mag * this._$.sin(ang);
144
144
  return this;
145
145
  }
146
146
  rotate(ang) {
@@ -60,8 +60,8 @@ Q5.renderers.webgpu.canvas = ($, q) => {
60
60
  $._createCanvas = (w, h, opt) => {
61
61
  q.ctx = q.drawingContext = c.getContext('webgpu');
62
62
 
63
- opt.format = navigator.gpu.getPreferredCanvasFormat();
64
- opt.device = Q5.device;
63
+ opt.format ??= navigator.gpu.getPreferredCanvasFormat();
64
+ opt.device ??= Q5.device;
65
65
 
66
66
  $.ctx.configure(opt);
67
67
 
@@ -86,6 +86,37 @@ Q5.renderers.webgpu.canvas = ($, q) => {
86
86
  $._setCanvasSize(w, h);
87
87
  };
88
88
 
89
+ // current color index, used to associate a vertex with a color
90
+ let colorIndex = 0;
91
+ const addColor = (r, g, b, a = 1) => {
92
+ if (typeof r == 'string') r = $.color(r);
93
+ else if (b == undefined) {
94
+ // grayscale mode `fill(1, 0.5)`
95
+ a = g ?? 1;
96
+ g = b = r;
97
+ }
98
+ if (r._q5Color) colorsStack.push(r.r, r.g, r.b, r.a);
99
+ else colorsStack.push(r, g, b, a);
100
+ colorIndex++;
101
+ };
102
+
103
+ $.fill = (r, g, b, a) => {
104
+ addColor(r, g, b, a);
105
+ $._doFill = true;
106
+ $._fillIndex = colorIndex;
107
+ };
108
+ $.stroke = (r, g, b, a) => {
109
+ addColor(r, g, b, a);
110
+ $._doStroke = true;
111
+ $._strokeIndex = colorIndex;
112
+ };
113
+
114
+ $.noFill = () => ($._doFill = false);
115
+ $.noStroke = () => ($._doStroke = false);
116
+
117
+ $._strokeWeight = 1;
118
+ $.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
119
+
89
120
  $.resetMatrix = () => {
90
121
  // Initialize the transformation matrix as 4x4 identity matrix
91
122
 
@@ -109,24 +140,6 @@ Q5.renderers.webgpu.canvas = ($, q) => {
109
140
  // Stack to keep track of transformation matrix indexes
110
141
  $._transformIndexStack = [];
111
142
 
112
- $.push = $.pushMatrix = () => {
113
- // Push the current matrix index onto the stack
114
- $._transformIndexStack.push($._transformIndex);
115
- $._pushStyles();
116
- };
117
-
118
- $.pop = $.popMatrix = () => {
119
- if (!$._transformIndexStack.length) {
120
- return console.warn('Matrix index stack is empty!');
121
- }
122
- // Pop the last matrix index from the stack and set it as the current matrix index
123
- let idx = $._transformIndexStack.pop();
124
- $._matrix = $.transformStates[idx].slice();
125
- $._transformIndex = idx;
126
- $._matrixDirty = false;
127
- $._popStyles();
128
- };
129
-
130
143
  $.translate = (x, y, z) => {
131
144
  if (!x && !y && !z) return;
132
145
  // Update the translation values
@@ -172,6 +185,57 @@ Q5.renderers.webgpu.canvas = ($, q) => {
172
185
  $._matrixDirty = true;
173
186
  };
174
187
 
188
+ $.shearX = (ang) => {
189
+ if (!ang) return;
190
+ if ($._angleMode) ang *= $._DEGTORAD;
191
+
192
+ let tanAng = Math.tan(ang);
193
+
194
+ let m0 = $._matrix[0],
195
+ m1 = $._matrix[1],
196
+ m4 = $._matrix[4],
197
+ m5 = $._matrix[5];
198
+
199
+ $._matrix[0] = m0 + m4 * tanAng;
200
+ $._matrix[1] = m1 + m5 * tanAng;
201
+
202
+ $._matrixDirty = true;
203
+ };
204
+
205
+ $.shearY = (ang) => {
206
+ if (!ang) return;
207
+ if ($._angleMode) ang *= $._DEGTORAD;
208
+
209
+ let tanAng = Math.tan(ang);
210
+
211
+ let m0 = $._matrix[0],
212
+ m1 = $._matrix[1],
213
+ m4 = $._matrix[4],
214
+ m5 = $._matrix[5];
215
+
216
+ $._matrix[4] = m4 + m0 * tanAng;
217
+ $._matrix[5] = m5 + m1 * tanAng;
218
+
219
+ $._matrixDirty = true;
220
+ };
221
+
222
+ $.applyMatrix = (...args) => {
223
+ let m;
224
+ if (args.length == 1) m = args[0];
225
+ else m = args;
226
+
227
+ if (m.length == 9) {
228
+ // Convert 3x3 matrix to 4x4 matrix
229
+ 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]];
230
+ } else if (m.length != 16) {
231
+ throw new Error('Matrix must be a 3x3 or 4x4 array.');
232
+ }
233
+
234
+ // Overwrite the current transformation matrix
235
+ $._matrix = m.slice();
236
+ $._matrixDirty = true;
237
+ };
238
+
175
239
  // Function to save the current matrix state if dirty
176
240
  $._saveMatrix = () => {
177
241
  $.transformStates.push($._matrix.slice());
@@ -179,37 +243,31 @@ Q5.renderers.webgpu.canvas = ($, q) => {
179
243
  $._matrixDirty = false;
180
244
  };
181
245
 
182
- // current color index, used to associate a vertex with a color
183
- let colorIndex = 0;
184
- const addColor = (r, g, b, a = 1) => {
185
- if (typeof r == 'string') r = $.color(r);
186
- else if (b == undefined) {
187
- // grayscale mode `fill(1, 0.5)`
188
- a = g ?? 1;
189
- g = b = r;
246
+ // Push the current matrix index onto the stack
247
+ $.pushMatrix = () => {
248
+ if ($._matrixDirty) $._saveMatrix();
249
+ $._transformIndexStack.push($._transformIndex);
250
+ };
251
+ $.popMatrix = () => {
252
+ if (!$._transformIndexStack.length) {
253
+ return console.warn('Matrix index stack is empty!');
190
254
  }
191
- if (r._q5Color) colorsStack.push(r.r, r.g, r.b, r.a);
192
- else colorsStack.push(r, g, b, a);
193
- colorIndex++;
255
+ // Pop the last matrix index from the stack and set it as the current matrix index
256
+ let idx = $._transformIndexStack.pop();
257
+ $._matrix = $.transformStates[idx].slice();
258
+ $._transformIndex = idx;
259
+ $._matrixDirty = false;
194
260
  };
195
261
 
196
- $.fill = (r, g, b, a) => {
197
- addColor(r, g, b, a);
198
- $._doFill = true;
199
- $._fillIndex = colorIndex;
262
+ $.push = () => {
263
+ $.pushMatrix();
264
+ $.pushStyles();
200
265
  };
201
- $.stroke = (r, g, b, a) => {
202
- addColor(r, g, b, a);
203
- $._doStroke = true;
204
- $._strokeIndex = colorIndex;
266
+ $.pop = () => {
267
+ $.popMatrix();
268
+ $.popStyles();
205
269
  };
206
270
 
207
- $.noFill = () => ($._doFill = false);
208
- $.noStroke = () => ($._doStroke = false);
209
-
210
- $._strokeWeight = 1;
211
- $.strokeWeight = (v) => ($._strokeWeight = Math.abs(v));
212
-
213
271
  $._calcBox = (x, y, w, h, mode) => {
214
272
  let hw = w / 2;
215
273
  let hh = h / 2;
@@ -224,6 +224,15 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
224
224
  $.endShape(1);
225
225
  };
226
226
 
227
+ $.quad = (x1, y1, x2, y2, x3, y3, x4, y4) => {
228
+ $.beginShape();
229
+ $.vertex(x1, y1);
230
+ $.vertex(x2, y2);
231
+ $.vertex(x3, y3);
232
+ $.vertex(x4, y4);
233
+ $.endShape(1);
234
+ };
235
+
227
236
  $.rectMode = (x) => ($._rectMode = x);
228
237
 
229
238
  $.rect = (x, y, w, h) => {
@@ -245,6 +254,8 @@ fn fragmentMain(@location(1) colorIndex: f32) -> @location(0) vec4<f32> {
245
254
  drawStack.push(0, 6);
246
255
  };
247
256
 
257
+ $.square = (x, y, s) => $.rect(x, y, s, s);
258
+
248
259
  $.point = (x, y) => {
249
260
  colorIndex = $._strokeIndex;
250
261
  let sw = $._strokeWeight;
@@ -118,20 +118,30 @@ fn fragmentMain(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
118
118
  minFilter: 'linear'
119
119
  });
120
120
 
121
+ let MAX_TEXTURES = 12000;
122
+
123
+ $._textures = [];
124
+ let tIdx = 0;
125
+
121
126
  $._createTexture = (img) => {
122
127
  if (img.canvas) img = img.canvas;
123
128
 
124
129
  let textureSize = [img.width, img.height, 1];
125
130
 
126
- const texture = Q5.device.createTexture({
131
+ let texture = Q5.device.createTexture({
127
132
  size: textureSize,
128
133
  format: 'bgra8unorm',
129
134
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
130
135
  });
131
136
 
132
- Q5.device.queue.copyExternalImageToTexture({ source: img }, { texture }, textureSize);
137
+ Q5.device.queue.copyExternalImageToTexture(
138
+ { source: img },
139
+ { texture, colorSpace: $.canvas.colorSpace },
140
+ textureSize
141
+ );
133
142
 
134
- img.textureIndex = $._textureBindGroups.length;
143
+ $._textures[tIdx] = texture;
144
+ img.textureIndex = tIdx;
135
145
 
136
146
  const textureBindGroup = Q5.device.createBindGroup({
137
147
  layout: textureLayout,
@@ -140,7 +150,16 @@ fn fragmentMain(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
140
150
  { binding: 1, resource: texture.createView() }
141
151
  ]
142
152
  });
143
- $._textureBindGroups.push(textureBindGroup);
153
+ $._textureBindGroups[tIdx] = textureBindGroup;
154
+
155
+ tIdx = (tIdx + 1) % MAX_TEXTURES;
156
+
157
+ // If the texture array is full, destroy the oldest texture
158
+ if ($._textures[tIdx]) {
159
+ $._textures[tIdx].destroy();
160
+ delete $._textures[tIdx];
161
+ delete $._textureBindGroups[tIdx];
162
+ }
144
163
  };
145
164
 
146
165
  $.loadImage = $.loadTexture = (src) => {
@@ -204,3 +223,12 @@ fn fragmentMain(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> {
204
223
  verticesStack.length = 0;
205
224
  });
206
225
  };
226
+
227
+ Q5.THRESHOLD = 1;
228
+ Q5.GRAY = 2;
229
+ Q5.OPAQUE = 3;
230
+ Q5.INVERT = 4;
231
+ Q5.POSTERIZE = 5;
232
+ Q5.DILATE = 6;
233
+ Q5.ERODE = 7;
234
+ Q5.BLUR = 8;