@@ -409,6 +409,8 @@ Q5.modules.canvas = ($, q) => {
409
409
}
410
410
if ( $ . _beginRender ) $ . _beginRender ( ) ;
411
411
412
+ c . mousePressed = ( cb ) => c . addEventListener ( 'mousedown' , cb ) ;
413
+
412
414
return rend ;
413
415
} ;
414
416
@@ -2607,6 +2609,8 @@ Q5.modules.dom = ($, q) => {
2607
2609
return el ;
2608
2610
} ;
2609
2611
2612
+ el . mousePressed = ( cb ) => el . addEventListener ( 'mousedown' , cb ) ;
2613
+
2610
2614
$ . _elements . push ( el ) ;
2611
2615
if ( $ . canvas ) $ . canvas . parentElement . append ( el ) ;
2612
2616
else document . body . append ( el ) ;
@@ -5147,9 +5151,7 @@ Q5.renderers.webgpu.drawing = ($, q) => {
5147
5151
const TAU = Math . PI * 2 ;
5148
5152
const HALF_PI = Math . PI / 2 ;
5149
5153
5150
- let drawingShader = Q5 . device . createShaderModule ( {
5151
- label : 'drawingShader' ,
5152
- code : `
5154
+ let drawingShaderCode = `
5153
5155
struct Uniforms {
5154
5156
halfWidth: f32,
5155
5157
halfHeight: f32
@@ -5168,12 +5170,17 @@ struct FragmentParams {
5168
5170
@group(0) @binding(1) var<storage> transforms: array<mat4x4<f32>>;
5169
5171
@group(0) @binding(2) var<storage> colors : array<vec4f>;
5170
5172
5171
- @vertex
5172
- fn vertexMain(v: VertexParams) -> FragmentParams {
5173
- var vert = vec4f(v.pos, 0.0, 1.0);
5174
- vert = transforms[i32(v.matrixIndex)] * vert;
5173
+ fn transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f {
5174
+ var vert = vec4f(pos, 0.0, 1.0);
5175
+ vert = transforms[i32(matrixIndex)] * vert;
5175
5176
vert.x /= uniforms.halfWidth;
5176
5177
vert.y /= uniforms.halfHeight;
5178
+ return vert;
5179
+ }
5180
+
5181
+ @vertex
5182
+ fn vertexMain(v: VertexParams) -> FragmentParams {
5183
+ var vert = transformVertex(v.pos, v.matrixIndex);
5177
5184
5178
5185
var f: FragmentParams;
5179
5186
f.position = vert;
@@ -5182,10 +5189,14 @@ fn vertexMain(v: VertexParams) -> FragmentParams {
5182
5189
}
5183
5190
5184
5191
@fragment
5185
- fn fragmentMain(@location(0) color: vec4f ) -> @location(0) vec4f {
5186
- return color;
5192
+ fn fragmentMain(f: FragmentParams ) -> @location(0) vec4f {
5193
+ return f. color;
5187
5194
}
5188
- `
5195
+ ` ;
5196
+
5197
+ let drawingShader = Q5 . device . createShaderModule ( {
5198
+ label : 'drawingShader' ,
5199
+ code : drawingShaderCode
5189
5200
} ) ;
5190
5201
5191
5202
let vertexBufferLayout = {
@@ -5786,13 +5797,13 @@ struct VertexParams {
5786
5797
@location(1) texCoord: vec2f,
5787
5798
@location(2) tintIndex: f32,
5788
5799
@location(3) matrixIndex: f32,
5789
- @location(4) globalAlpha : f32
5800
+ @location(4) imageAlpha : f32
5790
5801
}
5791
5802
struct FragmentParams {
5792
5803
@builtin(position) position: vec4f,
5793
5804
@location(0) texCoord: vec2f,
5794
- @location(1) tintIndex: f32 ,
5795
- @location(2) globalAlpha : f32
5805
+ @location(1) tintColor: vec4f ,
5806
+ @location(2) imageAlpha : f32
5796
5807
}
5797
5808
5798
5809
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@@ -5802,29 +5813,33 @@ struct FragmentParams {
5802
5813
@group(1) @binding(0) var samp: sampler;
5803
5814
@group(1) @binding(1) var texture: texture_2d<f32>;
5804
5815
5805
- @vertex
5806
- fn vertexMain(v: VertexParams) -> FragmentParams {
5807
- var vert = vec4f(v.pos, 0.0, 1.0);
5808
- vert = transforms[i32(v.matrixIndex)] * vert;
5816
+ fn transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f {
5817
+ var vert = vec4f(pos, 0.0, 1.0);
5818
+ vert = transforms[i32(matrixIndex)] * vert;
5809
5819
vert.x /= uniforms.halfWidth;
5810
5820
vert.y /= uniforms.halfHeight;
5821
+ return vert;
5822
+ }
5823
+
5824
+ @vertex
5825
+ fn vertexMain(v: VertexParams) -> FragmentParams {
5826
+ var vert = transformVertex(v.pos, v.matrixIndex);
5811
5827
5812
5828
var f: FragmentParams;
5813
5829
f.position = vert;
5814
5830
f.texCoord = v.texCoord;
5815
- f.tintIndex = v.tintIndex;
5816
- f.globalAlpha = v.globalAlpha ;
5831
+ f.tintColor = colors[i32( v.tintIndex)] ;
5832
+ f.imageAlpha = v.imageAlpha ;
5817
5833
return f;
5818
5834
}
5819
5835
5820
5836
@fragment
5821
5837
fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
5822
- let texColor = textureSample(texture, samp, f.texCoord);
5823
- let tintColor = colors[i32(f.tintIndex)];
5824
-
5825
- // Mix original and tinted colors using tint alpha as blend factor
5826
- let tinted = vec4f(texColor.rgb * tintColor.rgb, texColor.a * f.globalAlpha);
5827
- return mix(texColor, tinted, tintColor.a);
5838
+ let texColor = textureSample(texture, samp, f.texCoord);
5839
+
5840
+ // Mix original and tinted colors using tint alpha as blend factor
5841
+ let tinted = vec4f(texColor.rgb * f.tintColor.rgb, texColor.a * f.imageAlpha);
5842
+ return mix(texColor, tinted, f.tintColor.a);
5828
5843
}
5829
5844
` ;
5830
5845
@@ -5849,7 +5864,7 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
5849
5864
{ shaderLocation : 1 , offset : 8 , format : 'float32x2' } ,
5850
5865
{ shaderLocation : 2 , offset : 16 , format : 'float32' } , // tintIndex
5851
5866
{ shaderLocation : 3 , offset : 20 , format : 'float32' } , // matrixIndex
5852
- { shaderLocation : 4 , offset : 24 , format : 'float32' } // globalAlpha
5867
+ { shaderLocation : 4 , offset : 24 , format : 'float32' } // imageAlpha
5853
5868
]
5854
5869
} ;
5855
5870
@@ -6020,7 +6035,7 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
6020
6035
6021
6036
$ . imageMode = ( x ) => ( $ . _imageMode = x ) ;
6022
6037
6023
- const addVert = ( x , y , u , v , ci , ti , ga ) => {
6038
+ const addVert = ( x , y , u , v , ci , ti , ia ) => {
6024
6039
let s = vertexStack ,
6025
6040
i = vertIndex ;
6026
6041
s [ i ++ ] = x ;
@@ -6029,7 +6044,7 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
6029
6044
s [ i ++ ] = v ;
6030
6045
s [ i ++ ] = ci ;
6031
6046
s [ i ++ ] = ti ;
6032
- s [ i ++ ] = ga ;
6047
+ s [ i ++ ] = ia ;
6033
6048
vertIndex = i ;
6034
6049
} ;
6035
6050
@@ -6085,12 +6100,12 @@ fn fragmentMain(f: FragmentParams) -> @location(0) vec4f {
6085
6100
v1 = ( sy + sh ) / h ,
6086
6101
ti = $ . _matrixIndex ,
6087
6102
ci = $ . _tint ,
6088
- ga = $ . _globalAlpha ;
6103
+ ia = $ . _imageAlpha ;
6089
6104
6090
- addVert ( l , t , u0 , v0 , ci , ti , ga ) ;
6091
- addVert ( r , t , u1 , v0 , ci , ti , ga ) ;
6092
- addVert ( l , b , u0 , v1 , ci , ti , ga ) ;
6093
- addVert ( r , b , u1 , v1 , ci , ti , ga ) ;
6105
+ addVert ( l , t , u0 , v0 , ci , ti , ia ) ;
6106
+ addVert ( r , t , u1 , v0 , ci , ti , ia ) ;
6107
+ addVert ( l , b , u0 , v1 , ci , ti , ia ) ;
6108
+ addVert ( r , b , u1 , v1 , ci , ti , ia ) ;
6094
6109
6095
6110
if ( ! isVideo ) {
6096
6111
$ . drawStack . push ( 1 , img . textureIndex ) ;
@@ -6214,9 +6229,7 @@ Q5.DILATE = 6;
6214
6229
Q5 . ERODE = 7 ;
6215
6230
Q5 . BLUR = 8 ;
6216
6231
Q5 . renderers . webgpu . text = ( $ , q ) => {
6217
- let textShader = Q5 . device . createShaderModule ( {
6218
- label : 'MSDF text shader' ,
6219
- code : `
6232
+ let textShaderCode = `
6220
6233
struct Uniforms {
6221
6234
halfWidth: f32,
6222
6235
halfHeight: f32
@@ -6228,7 +6241,9 @@ struct VertexParams {
6228
6241
struct FragmentParams {
6229
6242
@builtin(position) position : vec4f,
6230
6243
@location(0) texCoord : vec2f,
6231
- @location(1) fillColor : vec4f
6244
+ @location(1) fillColor : vec4f,
6245
+ @location(2) strokeColor : vec4f,
6246
+ @location(3) strokeWeight : f32
6232
6247
}
6233
6248
struct Char {
6234
6249
texOffset: vec2f,
@@ -6241,7 +6256,8 @@ struct Text {
6241
6256
scale: f32,
6242
6257
matrixIndex: f32,
6243
6258
fillIndex: f32,
6244
- strokeIndex: f32
6259
+ strokeIndex: f32,
6260
+ strokeWeight: f32
6245
6261
}
6246
6262
6247
6263
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@@ -6257,52 +6273,73 @@ struct Text {
6257
6273
6258
6274
const quad = array(vec2f(0, -1), vec2f(1, -1), vec2f(0, 0), vec2f(1, 0));
6259
6275
6276
+ fn sampleMsdf(texCoord: vec2f) -> f32 {
6277
+ let c = textureSample(fontTexture, fontSampler, texCoord);
6278
+ return max(min(c.r, c.g), min(max(c.r, c.g), c.b));
6279
+ }
6280
+
6281
+ fn transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f {
6282
+ var vert = vec4f(pos, 0.0, 1.0);
6283
+ vert = transforms[i32(matrixIndex)] * vert;
6284
+ vert.x /= uniforms.halfWidth;
6285
+ vert.y /= uniforms.halfHeight;
6286
+ return vert;
6287
+ }
6288
+
6260
6289
@vertex
6261
6290
fn vertexMain(v : VertexParams) -> FragmentParams {
6262
6291
let char = textChars[v.instance];
6263
-
6264
6292
let text = textMetadata[i32(char.w)];
6265
-
6266
6293
let fontChar = fontChars[i32(char.z)];
6267
6294
6268
6295
let charPos = ((quad[v.vertex] * fontChar.size + char.xy + fontChar.offset) * text.scale) + text.pos;
6269
6296
6270
- var vert = vec4f(charPos, 0.0, 1.0);
6271
- vert = transforms[i32(text.matrixIndex)] * vert;
6272
- vert.x /= uniforms.halfWidth;
6273
- vert.y /= uniforms.halfHeight;
6297
+ var vert = transformVertex(charPos, text.matrixIndex);
6274
6298
6275
6299
var f : FragmentParams;
6276
6300
f.position = vert;
6277
6301
f.texCoord = (quad[v.vertex] * vec2f(1, -1)) * fontChar.texExtent + fontChar.texOffset;
6278
6302
f.fillColor = colors[i32(text.fillIndex)];
6303
+ f.strokeColor = colors[i32(text.strokeIndex)];
6304
+ f.strokeWeight = text.strokeWeight;
6279
6305
return f;
6280
6306
}
6281
6307
6282
- fn sampleMsdf(texCoord: vec2f) -> f32 {
6283
- let c = textureSample(fontTexture, fontSampler, texCoord);
6284
- return max(min(c.r, c.g), min(max(c.r, c.g), c.b));
6285
- }
6286
-
6287
6308
@fragment
6288
6309
fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
6289
- // pxRange (AKA distanceRange) comes from the msdfgen tool,
6290
- // uses the default which is 4.
6291
6310
let pxRange = 4.0;
6292
6311
let sz = vec2f(textureDimensions(fontTexture, 0));
6293
- let dx = sz.x* length(vec2f(dpdxFine(f.texCoord.x), dpdyFine(f.texCoord.x)));
6294
- let dy = sz.y* length(vec2f(dpdxFine(f.texCoord.y), dpdyFine(f.texCoord.y)));
6312
+ let dx = sz.x * length(vec2f(dpdxFine(f.texCoord.x), dpdyFine(f.texCoord.x)));
6313
+ let dy = sz.y * length(vec2f(dpdxFine(f.texCoord.y), dpdyFine(f.texCoord.y)));
6295
6314
let toPixels = pxRange * inverseSqrt(dx * dx + dy * dy);
6296
6315
let sigDist = sampleMsdf(f.texCoord) - 0.5;
6297
6316
let pxDist = sigDist * toPixels;
6298
6317
let edgeWidth = 0.5;
6299
- let alpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
6300
- if (alpha < 0.001) {
6318
+
6319
+ if (f.strokeWeight == 0.0) {
6320
+ let fillAlpha = smoothstep(-edgeWidth, edgeWidth, pxDist);
6321
+ var color = vec4f(f.fillColor.rgb, f.fillColor.a * fillAlpha);
6322
+ if (color.a < 0.01) {
6323
+ discard;
6324
+ }
6325
+ return color;
6326
+ }
6327
+
6328
+ let halfStroke = f.strokeWeight / 2.0;
6329
+ let fillAlpha = smoothstep(-edgeWidth, edgeWidth, pxDist - halfStroke);
6330
+ let strokeAlpha = smoothstep(-edgeWidth, edgeWidth, pxDist + halfStroke);
6331
+ var color = mix(f.strokeColor, f.fillColor, fillAlpha);
6332
+ color = vec4f(color.rgb, color.a * strokeAlpha);
6333
+ if (color.a < 0.01) {
6301
6334
discard;
6302
6335
}
6303
- return vec4f(f.fillColor.rgb, f.fillColor.a * alpha) ;
6336
+ return color ;
6304
6337
}
6305
- `
6338
+ ` ;
6339
+
6340
+ let textShader = Q5 . device . createShaderModule ( {
6341
+ label : 'textShader' ,
6342
+ code : textShaderCode
6306
6343
} ) ;
6307
6344
6308
6345
let textBindGroupLayout = Q5 . device . createBindGroupLayout ( {
@@ -6701,6 +6738,8 @@ fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
6701
6738
txt [ 3 ] = $ . _matrixIndex ;
6702
6739
txt [ 4 ] = $ . _fillSet ? $ . _fill : 0 ;
6703
6740
txt [ 5 ] = $ . _stroke ;
6741
+ txt [ 6 ] = $ . _strokeSet ? $ . _strokeWeight : 0 ;
6742
+ txt [ 7 ] = 0 ; // padding
6704
6743
6705
6744
textStack . push ( txt ) ;
6706
6745
$ . drawStack . push ( 3 , measurements . printedCharCount , $ . _font . index ) ;
@@ -6777,7 +6816,7 @@ fn fragmentMain(f : FragmentParams) -> @location(0) vec4f {
6777
6816
charBuffer . unmap ( ) ;
6778
6817
6779
6818
// calculate total buffer size for metadata
6780
- let totalMetadataSize = textStack . length * 6 * 4 ;
6819
+ let totalMetadataSize = textStack . length * 8 * 4 ;
6781
6820
6782
6821
// create a single buffer for all metadata
6783
6822
let textBuffer = Q5 . device . createBuffer ( {
0 commit comments