q5 2.4.5 → 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.js +230 -152
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +10 -10
- package/src/q5-2d-text.js +164 -128
- package/src/q5-webgpu-canvas.js +2 -2
- package/src/q5-webgpu-image.js +23 -4
- package/src/q5-webgpu-text.js +30 -7
- package/src/readme.md +2 -10
package/src/q5-2d-text.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
Q5.renderers.q2d.text = ($, q) => {
|
|
2
|
-
$.
|
|
3
|
-
$.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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,156 +28,162 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
16
28
|
});
|
|
17
29
|
return name;
|
|
18
30
|
};
|
|
19
|
-
|
|
31
|
+
|
|
32
|
+
$.textFont = (x) => {
|
|
33
|
+
font = x;
|
|
34
|
+
fontMod = true;
|
|
35
|
+
styleHash = -1;
|
|
36
|
+
};
|
|
20
37
|
$.textSize = (x) => {
|
|
21
|
-
if (x === undefined) return
|
|
38
|
+
if (x === undefined) return tSize;
|
|
22
39
|
if ($._da) x *= $._da;
|
|
23
|
-
|
|
40
|
+
tSize = x;
|
|
41
|
+
fontMod = true;
|
|
42
|
+
styleHash = -1;
|
|
24
43
|
if (!$._leadingSet) {
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
54
|
+
if (x === undefined) return leading;
|
|
31
55
|
if ($._da) x *= $._da;
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
97
|
-
$.textCache = (
|
|
98
|
-
if (maxSize)
|
|
99
|
-
if (
|
|
100
|
-
return
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
100
|
if (str === undefined || (!$._doFill && !$._doStroke)) return;
|
|
129
101
|
str = str.toString();
|
|
130
|
-
let lines = str.split('\n');
|
|
131
102
|
if ($._da) {
|
|
132
103
|
x *= $._da;
|
|
133
104
|
y *= $._da;
|
|
134
105
|
}
|
|
135
106
|
let ctx = $.ctx;
|
|
136
|
-
|
|
107
|
+
let img, tX, tY;
|
|
137
108
|
|
|
138
|
-
|
|
109
|
+
if (fontMod) {
|
|
110
|
+
ctx.font = `${emphasis} ${tSize}px ${font}`;
|
|
111
|
+
fontMod = false;
|
|
112
|
+
}
|
|
139
113
|
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
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
|
+
}
|
|
143
126
|
}
|
|
144
127
|
|
|
145
|
-
if (
|
|
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) {
|
|
146
154
|
tX = x;
|
|
147
155
|
tY = y;
|
|
148
156
|
} else {
|
|
149
|
-
cacheKey = $._genTextImageKey(str, w, h);
|
|
150
|
-
img = $._tic.get(cacheKey);
|
|
151
|
-
if (img && !$._genTextImage) return $.textImage(img, x, y);
|
|
152
|
-
|
|
153
157
|
tX = 0;
|
|
154
|
-
tY =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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;
|
|
159
165
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
img._fill = $._fill;
|
|
178
|
+
img._stroke = $._stroke;
|
|
179
|
+
img._strokeWeight = $._strokeWeight;
|
|
180
|
+
img.modified = true;
|
|
163
181
|
|
|
164
182
|
ctx = img.ctx;
|
|
165
183
|
|
|
166
184
|
ctx.font = $.ctx.font;
|
|
167
|
-
ctx.fillStyle = $.
|
|
168
|
-
ctx.strokeStyle = $.
|
|
185
|
+
ctx.fillStyle = $._fill;
|
|
186
|
+
ctx.strokeStyle = $._stroke;
|
|
169
187
|
ctx.lineWidth = $.ctx.lineWidth;
|
|
170
188
|
}
|
|
171
189
|
|
|
@@ -175,31 +193,49 @@ Q5.renderers.q2d.text = ($, q) => {
|
|
|
175
193
|
ctx.fillStyle = 'black';
|
|
176
194
|
}
|
|
177
195
|
|
|
178
|
-
for (let
|
|
179
|
-
if ($._doStroke && $._strokeSet) ctx.strokeText(
|
|
180
|
-
if ($._doFill) ctx.fillText(
|
|
181
|
-
tY +=
|
|
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;
|
|
182
200
|
if (tY > h) break;
|
|
183
201
|
}
|
|
202
|
+
lines.length = 0;
|
|
184
203
|
|
|
185
204
|
if (!$._fillSet) ctx.fillStyle = ogFill;
|
|
186
205
|
|
|
187
|
-
if (useCache) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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);
|
|
192
223
|
}
|
|
193
224
|
};
|
|
194
225
|
$.textImage = (img, x, y) => {
|
|
195
226
|
let og = $._imageMode;
|
|
196
227
|
$._imageMode = 'corner';
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
|
|
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
|
+
|
|
203
239
|
$.image(img, x, y);
|
|
204
240
|
$._imageMode = og;
|
|
205
241
|
};
|
package/src/q5-webgpu-canvas.js
CHANGED
|
@@ -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
|
|
64
|
-
opt.device
|
|
63
|
+
opt.format ??= navigator.gpu.getPreferredCanvasFormat();
|
|
64
|
+
opt.device ??= Q5.device;
|
|
65
65
|
|
|
66
66
|
$.ctx.configure(opt);
|
|
67
67
|
|
package/src/q5-webgpu-image.js
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
137
|
+
Q5.device.queue.copyExternalImageToTexture(
|
|
138
|
+
{ source: img },
|
|
139
|
+
{ texture, colorSpace: $.canvas.colorSpace },
|
|
140
|
+
textureSize
|
|
141
|
+
);
|
|
133
142
|
|
|
134
|
-
|
|
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
|
|
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) => {
|
package/src/q5-webgpu-text.js
CHANGED
|
@@ -9,6 +9,8 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
9
9
|
q._preloadCount--;
|
|
10
10
|
});
|
|
11
11
|
};
|
|
12
|
+
|
|
13
|
+
// directly add these text setting functions to the webgpu renderer
|
|
12
14
|
$.textFont = t.textFont;
|
|
13
15
|
$.textSize = t.textSize;
|
|
14
16
|
$.textLeading = t.textLeading;
|
|
@@ -24,7 +26,20 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
24
26
|
$.text = (str, x, y, w, h) => {
|
|
25
27
|
let img = t.createTextImage(str, w, h);
|
|
26
28
|
|
|
27
|
-
if (img.canvas.textureIndex
|
|
29
|
+
if (img.canvas.textureIndex === undefined) {
|
|
30
|
+
$._createTexture(img);
|
|
31
|
+
} else if (img.modified) {
|
|
32
|
+
let cnv = img.canvas;
|
|
33
|
+
let textureSize = [cnv.width, cnv.height, 1];
|
|
34
|
+
let texture = $._textures[cnv.textureIndex];
|
|
35
|
+
|
|
36
|
+
Q5.device.queue.copyExternalImageToTexture(
|
|
37
|
+
{ source: cnv },
|
|
38
|
+
{ texture, colorSpace: $.canvas.colorSpace },
|
|
39
|
+
textureSize
|
|
40
|
+
);
|
|
41
|
+
img.modified = false;
|
|
42
|
+
}
|
|
28
43
|
|
|
29
44
|
$.textImage(img, x, y);
|
|
30
45
|
};
|
|
@@ -32,12 +47,20 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
32
47
|
$.createTextImage = t.createTextImage;
|
|
33
48
|
|
|
34
49
|
$.textImage = (img, x, y) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
else if (
|
|
50
|
+
let og = $._imageMode;
|
|
51
|
+
$._imageMode = 'corner';
|
|
52
|
+
|
|
53
|
+
let ta = t._textAlign;
|
|
54
|
+
if (ta == 'center') x -= img.canvas.hw;
|
|
55
|
+
else if (ta == 'right') x -= img.width;
|
|
56
|
+
|
|
57
|
+
let bl = t._textBaseline;
|
|
58
|
+
if (bl == 'alphabetic') y -= t._textLeading;
|
|
59
|
+
else if (bl == 'middle') y -= img._middle;
|
|
60
|
+
else if (bl == 'bottom') y -= img._bottom;
|
|
61
|
+
else if (bl == 'top') y -= img._top;
|
|
62
|
+
|
|
41
63
|
$.image(img, x, y);
|
|
64
|
+
$._imageMode = og;
|
|
42
65
|
};
|
|
43
66
|
};
|
package/src/readme.md
CHANGED
|
@@ -105,7 +105,7 @@ Image based features in this module require the q5-2d-image module.
|
|
|
105
105
|
|
|
106
106
|
`textImage(img, x, y)` displays text images, complying with the user's text position settings instead of their image position settings. The idea is that text will appear in the same place as it would if it were drawn with the `text` function.
|
|
107
107
|
|
|
108
|
-
`textCache(bool, maxSize)` enables or disables text caching.
|
|
108
|
+
`textCache(bool, maxSize)` enables or disables text caching.
|
|
109
109
|
|
|
110
110
|
## webgpu-canvas
|
|
111
111
|
|
|
@@ -181,17 +181,9 @@ Implemented functions:
|
|
|
181
181
|
|
|
182
182
|
> Use `textFill` and `textStroke` to set text colors.
|
|
183
183
|
|
|
184
|
-
WebGPU (and WebGL) don't have fast HTML5 based text rasterization functionality, like Canvas2D does.
|
|
185
|
-
|
|
186
|
-
In p5.js WebGL mode, text is drawn directly to the canvas. This is a complex task, since letters have intricate geometry: thus many triangles must be used to render text at high resolution. Unless a user wants to render a lot of text, the performance cost is actually negligible. Yet since p5.js depends on opentype.js for this, which is 528kb (171kb minified), a different approach was needed to keep q5 lightweight.
|
|
187
|
-
|
|
188
184
|
Internally, q5's WebGPU renderer uses a q5 graphics object to draw text to a Canvas2D canvas via `createTextImage`, then converts that canvas to a WebGPU texture. Each texture is cached, so it doesn't have to be recreated every frame that users want to display the same text.
|
|
189
185
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
For typical use cases, this is a great trade-off!
|
|
193
|
-
|
|
194
|
-
Complete implementation of text rendering in WebGPU.
|
|
186
|
+
Implemented functions:
|
|
195
187
|
|
|
196
188
|
`loadFont`,`textFont`, `textSize`, `textLeading`, `textStyle`, `textAlign`, `textWidth`, `textAscent`, `textDescent`, `textFill`, `textStroke`, `text`
|
|
197
189
|
|