q5 2.13.1 → 2.13.3
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 +169 -204
- package/q5.min.js +1 -1
- package/src/q5-2d-canvas.js +1 -0
- package/src/q5-core.js +3 -0
- package/src/q5-webgpu-canvas.js +51 -82
- package/src/q5-webgpu-drawing.js +20 -28
- package/src/q5-webgpu-image.js +49 -47
- package/src/q5-webgpu-text.js +45 -47
package/src/q5-webgpu-text.js
CHANGED
|
@@ -2,14 +2,15 @@ Q5.renderers.webgpu.text = ($, q) => {
|
|
|
2
2
|
let textShader = Q5.device.createShaderModule({
|
|
3
3
|
label: 'MSDF text shader',
|
|
4
4
|
code: `
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
struct Uniforms {
|
|
6
|
+
halfWidth: f32,
|
|
7
|
+
halfHeight: f32
|
|
8
|
+
}
|
|
9
|
+
struct VertexParams {
|
|
9
10
|
@builtin(vertex_index) vertex : u32,
|
|
10
11
|
@builtin(instance_index) instance : u32
|
|
11
12
|
}
|
|
12
|
-
struct
|
|
13
|
+
struct FragmentParams {
|
|
13
14
|
@builtin(position) position : vec4f,
|
|
14
15
|
@location(0) texCoord : vec2f,
|
|
15
16
|
@location(1) fillColor : vec4f
|
|
@@ -27,43 +28,40 @@ struct Text {
|
|
|
27
28
|
fillIndex: f32,
|
|
28
29
|
strokeIndex: f32
|
|
29
30
|
}
|
|
30
|
-
struct Uniforms {
|
|
31
|
-
halfWidth: f32,
|
|
32
|
-
halfHeight: f32
|
|
33
|
-
}
|
|
34
31
|
|
|
35
32
|
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
36
33
|
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
|
|
34
|
+
@group(0) @binding(2) var<storage> colors : array<vec4f>;
|
|
37
35
|
|
|
38
|
-
@group(1) @binding(0) var
|
|
36
|
+
@group(1) @binding(0) var fontTexture: texture_2d<f32>;
|
|
37
|
+
@group(1) @binding(1) var fontSampler: sampler;
|
|
38
|
+
@group(1) @binding(2) var<storage> fontChars: array<Char>;
|
|
39
39
|
|
|
40
|
-
@group(2) @binding(0) var
|
|
41
|
-
@group(2) @binding(1) var
|
|
42
|
-
@group(2) @binding(2) var<storage> fontChars: array<Char>;
|
|
40
|
+
@group(2) @binding(0) var<storage> textChars: array<vec4f>;
|
|
41
|
+
@group(2) @binding(1) var<storage> textMetadata: array<Text>;
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
@group(3) @binding(1) var<storage> textMetadata: array<Text>;
|
|
43
|
+
const quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
|
|
46
44
|
|
|
47
45
|
@vertex
|
|
48
|
-
fn vertexMain(
|
|
49
|
-
let char = textChars[
|
|
46
|
+
fn vertexMain(v : VertexParams) -> FragmentParams {
|
|
47
|
+
let char = textChars[v.instance];
|
|
50
48
|
|
|
51
49
|
let text = textMetadata[i32(char.w)];
|
|
52
50
|
|
|
53
51
|
let fontChar = fontChars[i32(char.z)];
|
|
54
52
|
|
|
55
|
-
let charPos = ((
|
|
53
|
+
let charPos = ((quad[v.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
|
|
56
54
|
|
|
57
55
|
var vert = vec4f(charPos, 0.0, 1.0);
|
|
58
56
|
vert = transforms[i32(text.matrixIndex)] * vert;
|
|
59
57
|
vert.x /= uniforms.halfWidth;
|
|
60
58
|
vert.y /= uniforms.halfHeight;
|
|
61
59
|
|
|
62
|
-
var
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return
|
|
60
|
+
var f : FragmentParams;
|
|
61
|
+
f.position = vert;
|
|
62
|
+
f.texCoord = (quad[v.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
|
|
63
|
+
f.fillColor = colors[i32(text.fillIndex)];
|
|
64
|
+
return f;
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
fn sampleMsdf(texCoord: vec2f) -> f32 {
|
|
@@ -72,22 +70,22 @@ fn sampleMsdf(texCoord: vec2f) -> f32 {
|
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
@fragment
|
|
75
|
-
fn fragmentMain(
|
|
73
|
+
fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
|
|
76
74
|
// pxRange (AKA distanceRange) comes from the msdfgen tool,
|
|
77
75
|
// uses the default which is 4.
|
|
78
76
|
let pxRange = 4.0;
|
|
79
77
|
let sz = vec2f(textureDimensions(fontTexture, 0));
|
|
80
|
-
let dx = sz.x*length(vec2f(dpdxFine(
|
|
81
|
-
let dy = sz.y*length(vec2f(dpdxFine(
|
|
78
|
+
let dx = sz.x*length(vec2f(dpdxFine(f.texCoord.x), dpdyFine(f.texCoord.x)));
|
|
79
|
+
let dy = sz.y*length(vec2f(dpdxFine(f.texCoord.y), dpdyFine(f.texCoord.y)));
|
|
82
80
|
let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);
|
|
83
|
-
let sigDist = sampleMsdf(
|
|
81
|
+
let sigDist = sampleMsdf(f.texCoord) - 0.5;
|
|
84
82
|
let pxDist = sigDist * toPixels;
|
|
85
83
|
let edgeWidth = 0.5;
|
|
86
84
|
let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
|
|
87
85
|
if (alpha < 0.001) {
|
|
88
86
|
discard;
|
|
89
87
|
}
|
|
90
|
-
return vec4f(
|
|
88
|
+
return vec4f(f.fillColor.rgb, f.fillColor.a * alpha);
|
|
91
89
|
}
|
|
92
90
|
`
|
|
93
91
|
});
|
|
@@ -321,8 +319,8 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
321
319
|
if (vert) $._textBaseline = vert;
|
|
322
320
|
};
|
|
323
321
|
|
|
324
|
-
|
|
325
|
-
|
|
322
|
+
let charStack = [],
|
|
323
|
+
textStack = [];
|
|
326
324
|
|
|
327
325
|
let measureText = (font, text, charCallback) => {
|
|
328
326
|
let maxWidth = 0,
|
|
@@ -419,7 +417,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
419
417
|
|
|
420
418
|
let ta = $._textAlign,
|
|
421
419
|
tb = $._textBaseline,
|
|
422
|
-
textIndex =
|
|
420
|
+
textIndex = textStack.length,
|
|
423
421
|
o = 0, // offset
|
|
424
422
|
measurements;
|
|
425
423
|
|
|
@@ -459,20 +457,20 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
459
457
|
o += 4;
|
|
460
458
|
});
|
|
461
459
|
}
|
|
462
|
-
|
|
460
|
+
charStack.push(charsData);
|
|
463
461
|
|
|
464
|
-
let
|
|
462
|
+
let txt = [];
|
|
465
463
|
|
|
466
464
|
if ($._matrixDirty) $._saveMatrix();
|
|
467
465
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
466
|
+
txt[0] = x;
|
|
467
|
+
txt[1] = -y;
|
|
468
|
+
txt[2] = $._textSize / 44;
|
|
469
|
+
txt[3] = $._matrixIndex;
|
|
470
|
+
txt[4] = $._fillSet ? $._fill : 0;
|
|
471
|
+
txt[5] = $._stroke;
|
|
474
472
|
|
|
475
|
-
|
|
473
|
+
textStack.push(txt);
|
|
476
474
|
$.drawStack.push(2, measurements.printedCharCount, $._font.index);
|
|
477
475
|
};
|
|
478
476
|
|
|
@@ -533,11 +531,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
533
531
|
};
|
|
534
532
|
|
|
535
533
|
$._hooks.preRender.push(() => {
|
|
536
|
-
if (
|
|
534
|
+
if (!charStack.length) return;
|
|
537
535
|
|
|
538
536
|
// calculate total buffer size for text data
|
|
539
537
|
let totalTextSize = 0;
|
|
540
|
-
for (let charsData of
|
|
538
|
+
for (let charsData of charStack) {
|
|
541
539
|
totalTextSize += charsData.length * 4;
|
|
542
540
|
}
|
|
543
541
|
|
|
@@ -549,11 +547,11 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
549
547
|
});
|
|
550
548
|
|
|
551
549
|
// copy all the text data into the buffer
|
|
552
|
-
new Float32Array(charBuffer.getMappedRange()).set(
|
|
550
|
+
new Float32Array(charBuffer.getMappedRange()).set(charStack.flat());
|
|
553
551
|
charBuffer.unmap();
|
|
554
552
|
|
|
555
553
|
// calculate total buffer size for metadata
|
|
556
|
-
let totalMetadataSize =
|
|
554
|
+
let totalMetadataSize = textStack.length * 6 * 4;
|
|
557
555
|
|
|
558
556
|
// create a single buffer for all metadata
|
|
559
557
|
let textBuffer = Q5.device.createBuffer({
|
|
@@ -564,7 +562,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
564
562
|
});
|
|
565
563
|
|
|
566
564
|
// copy all metadata into the buffer
|
|
567
|
-
new Float32Array(textBuffer.getMappedRange()).set(
|
|
565
|
+
new Float32Array(textBuffer.getMappedRange()).set(textStack.flat());
|
|
568
566
|
textBuffer.unmap();
|
|
569
567
|
|
|
570
568
|
// create a single bind group for the text buffer and metadata buffer
|
|
@@ -579,7 +577,7 @@ fn fragmentMain(input : VertexOutput) -> @location(0) vec4f {
|
|
|
579
577
|
});
|
|
580
578
|
|
|
581
579
|
$._hooks.postRender.push(() => {
|
|
582
|
-
|
|
583
|
-
|
|
580
|
+
charStack.length = 0;
|
|
581
|
+
textStack.length = 0;
|
|
584
582
|
});
|
|
585
583
|
};
|