three-text 0.4.11 → 0.5.0
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/LICENSE +5 -660
- package/LICENSE_THIRD_PARTY +15 -49
- package/README.md +265 -44
- package/dist/index.cjs +3424 -3450
- package/dist/index.d.ts +163 -9
- package/dist/index.js +3420 -3451
- package/dist/index.min.cjs +718 -676
- package/dist/index.min.js +721 -679
- package/dist/index.umd.js +3561 -3579
- package/dist/index.umd.min.js +803 -756
- package/dist/p5/index.cjs +2738 -5
- package/dist/p5/index.js +2738 -5
- package/dist/patterns/index.js +0 -4
- package/dist/slug/index.cjs +380 -0
- package/dist/slug/index.d.ts +62 -0
- package/dist/slug/index.js +374 -0
- package/dist/three/index.cjs +50 -35
- package/dist/three/index.js +50 -35
- package/dist/three/react.cjs +5 -2
- package/dist/three/react.d.ts +66 -120
- package/dist/three/react.js +6 -3
- package/dist/types/core/Text.d.ts +3 -10
- package/dist/types/core/cache/sharedCaches.d.ts +2 -1
- package/dist/types/core/shaping/DrawCallbacks.d.ts +11 -3
- package/dist/types/core/shaping/TextShaper.d.ts +1 -5
- package/dist/types/core/types.d.ts +84 -0
- package/dist/types/index.d.ts +7 -3
- package/dist/types/{core/cache → mesh}/GlyphContourCollector.d.ts +4 -4
- package/dist/types/{core/cache → mesh}/GlyphGeometryBuilder.d.ts +5 -5
- package/dist/types/mesh/MeshGeometryBuilder.d.ts +18 -0
- package/dist/types/{core → mesh}/geometry/BoundaryClusterer.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/Extruder.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/PathOptimizer.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/Polygonizer.d.ts +1 -1
- package/dist/types/{core → mesh}/geometry/Tessellator.d.ts +1 -1
- package/dist/types/react/utils.d.ts +2 -0
- package/dist/types/vector/GlyphOutlineCollector.d.ts +25 -0
- package/dist/types/vector/GlyphVectorGeometryBuilder.d.ts +26 -0
- package/dist/types/vector/LoopBlinnGeometry.d.ts +68 -0
- package/dist/types/vector/index.d.ts +29 -0
- package/dist/types/vector/loopBlinnTSL.d.ts +11 -0
- package/dist/types/vector/react.d.ts +24 -0
- package/dist/types/vector/webgl/index.d.ts +7 -0
- package/dist/types/vector/webgpu/index.d.ts +11 -0
- package/dist/vector/index.cjs +1458 -0
- package/dist/vector/index.d.ts +122 -0
- package/dist/vector/index.js +1434 -0
- package/dist/vector/react.cjs +153 -0
- package/dist/vector/react.d.ts +317 -0
- package/dist/vector/react.js +132 -0
- package/dist/vector/types/slug-lib/src/SlugPacker.d.ts +17 -0
- package/dist/vector/types/slug-lib/src/WebGL2Renderer.d.ts +21 -0
- package/dist/vector/types/slug-lib/src/WebGPURenderer.d.ts +16 -0
- package/dist/vector/types/slug-lib/src/index.d.ts +15 -0
- package/dist/vector/types/slug-lib/src/shaderStrings.d.ts +9 -0
- package/dist/vector/types/slug-lib/src/types.d.ts +34 -0
- package/dist/vector/types/src/core/types.d.ts +381 -0
- package/dist/vector/types/src/hyphenation/HyphenationPatternLoader.d.ts +2 -0
- package/dist/vector/types/src/hyphenation/index.d.ts +7 -0
- package/dist/vector/types/src/hyphenation/types.d.ts +6 -0
- package/dist/vector/types/src/utils/Cache.d.ts +14 -0
- package/dist/vector/types/src/utils/vectors.d.ts +75 -0
- package/dist/vector/types/src/vector/VectorDataBuilder.d.ts +30 -0
- package/dist/vector/types/src/vector/VectorThreeAdapter.d.ts +27 -0
- package/dist/vector/types/src/vector/index.d.ts +15 -0
- package/dist/vector/webgl/index.cjs +229 -0
- package/dist/vector/webgl/index.d.ts +53 -0
- package/dist/vector/webgl/index.js +227 -0
- package/dist/vector/webgpu/index.cjs +321 -0
- package/dist/vector/webgpu/index.d.ts +57 -0
- package/dist/vector/webgpu/index.js +319 -0
- package/dist/webgl-vector/index.cjs +243 -0
- package/dist/webgl-vector/index.d.ts +34 -0
- package/dist/webgl-vector/index.js +241 -0
- package/dist/webgpu-vector/index.cjs +336 -0
- package/dist/webgpu-vector/index.d.ts +38 -0
- package/dist/webgpu-vector/index.js +334 -0
- package/package.json +49 -4
- package/dist/patterns/cs.cjs +0 -14
- package/dist/patterns/cs.d.ts +0 -28
- package/dist/patterns/cs.js +0 -14
- package/dist/patterns/cs.umd.js +0 -14
- package/dist/patterns/id.cjs +0 -14
- package/dist/patterns/id.d.ts +0 -28
- package/dist/patterns/id.js +0 -14
- package/dist/patterns/id.umd.js +0 -14
- package/dist/patterns/mk.cjs +0 -14
- package/dist/patterns/mk.d.ts +0 -28
- package/dist/patterns/mk.js +0 -14
- package/dist/patterns/mk.umd.js +0 -14
- package/dist/patterns/sr-cyrl.cjs +0 -14
- package/dist/patterns/sr-cyrl.d.ts +0 -28
- package/dist/patterns/sr-cyrl.js +0 -14
- package/dist/patterns/sr-cyrl.umd.js +0 -14
package/dist/patterns/index.js
CHANGED
|
@@ -10,7 +10,6 @@ export const availablePatterns = [
|
|
|
10
10
|
'bg',
|
|
11
11
|
'bn',
|
|
12
12
|
'ca',
|
|
13
|
-
'cs',
|
|
14
13
|
'cy',
|
|
15
14
|
'da',
|
|
16
15
|
'de-1996',
|
|
@@ -34,7 +33,6 @@ export const availablePatterns = [
|
|
|
34
33
|
'hu',
|
|
35
34
|
'hy',
|
|
36
35
|
'ia',
|
|
37
|
-
'id',
|
|
38
36
|
'is',
|
|
39
37
|
'it',
|
|
40
38
|
'ka',
|
|
@@ -43,7 +41,6 @@ export const availablePatterns = [
|
|
|
43
41
|
'la',
|
|
44
42
|
'lt',
|
|
45
43
|
'lv',
|
|
46
|
-
'mk',
|
|
47
44
|
'ml',
|
|
48
45
|
'mn-cyrl',
|
|
49
46
|
'mr',
|
|
@@ -66,7 +63,6 @@ export const availablePatterns = [
|
|
|
66
63
|
'sk',
|
|
67
64
|
'sl',
|
|
68
65
|
'sq',
|
|
69
|
-
'sr-cyrl',
|
|
70
66
|
'sv',
|
|
71
67
|
'ta',
|
|
72
68
|
'te',
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vertGLSL = "#version 300 es\n// ===================================================\n// GLSL 300 es port of the reference Slug vertex shader.\n// Original HLSL by Eric Lengyel, MIT License, Copyright 2017.\n// See: https://jcgt.org/published/0006/02/02/\n// ===================================================\n\nprecision highp float;\nprecision highp int;\n\n// Per-vertex attributes (5 x vec4, matching Slug reference layout)\nlayout(location = 0) in vec4 a_pos; // .xy = object-space position, .zw = outward normal\nlayout(location = 1) in vec4 a_tex; // .xy = em-space sample coords, .z = packed glyph loc, .w = packed band max + flags\nlayout(location = 2) in vec4 a_jac; // inverse Jacobian (2x2): (j00, j01, j10, j11)\nlayout(location = 3) in vec4 a_bnd; // (bandScaleX, bandScaleY, bandOffsetX, bandOffsetY)\nlayout(location = 4) in vec4 a_col; // vertex color RGBA\n\nuniform mat4 slug_matrix; // MVP matrix (rows as vec4s)\nuniform vec2 slug_viewport; // viewport dimensions in pixels\n\nout vec4 v_color;\nout vec2 v_texcoord;\nflat out vec4 v_banding;\nflat out ivec4 v_glyph;\n\nvoid SlugUnpack(vec4 tex, vec4 bnd, out vec4 vbnd, out ivec4 vgly) {\n uvec2 g = floatBitsToUint(tex.zw);\n vgly = ivec4(g.x & 0xFFFFu, g.x >> 16u, g.y & 0xFFFFu, g.y >> 16u);\n vbnd = bnd;\n}\n\nvec2 SlugDilate(vec4 pos, vec4 tex, vec4 jac, vec4 m0, vec4 m1, vec4 m3, vec2 dim, out vec2 vpos) {\n vec2 n = normalize(pos.zw);\n float s = dot(m3.xy, pos.xy) + m3.w;\n float t = dot(m3.xy, n);\n\n float u = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n float v = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n float s2 = s * s;\n float st = s * t;\n float uv = u * u + v * v;\n vec2 d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n vpos = pos.xy + d;\n return vec2(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n}\n\nvoid main() {\n vec2 p;\n\n // Dynamic dilation: expand quad by a pixel to prevent edge clipping.\n v_texcoord = SlugDilate(a_pos, a_tex, a_jac,\n slug_matrix[0], slug_matrix[1], slug_matrix[3],\n slug_viewport, p);\n\n // MVP transform on dilated position.\n gl_Position.x = p.x * slug_matrix[0].x + p.y * slug_matrix[0].y + slug_matrix[0].w;\n gl_Position.y = p.x * slug_matrix[1].x + p.y * slug_matrix[1].y + slug_matrix[1].w;\n gl_Position.z = p.x * slug_matrix[2].x + p.y * slug_matrix[2].y + slug_matrix[2].w;\n gl_Position.w = p.x * slug_matrix[3].x + p.y * slug_matrix[3].y + slug_matrix[3].w;\n\n SlugUnpack(a_tex, a_bnd, v_banding, v_glyph);\n v_color = a_col;\n}\n";
|
|
4
|
+
|
|
5
|
+
var fragGLSL = "#version 300 es\n// ===================================================\n// GLSL 300 es port of the reference Slug pixel shader.\n// Original HLSL by Eric Lengyel, MIT License, Copyright 2017.\n// See: https://jcgt.org/published/0006/02/02/\n// ===================================================\n\nprecision highp float;\nprecision highp int;\n\n#define kLogBandTextureWidth 12\n\nin vec4 v_color;\nin vec2 v_texcoord;\nflat in vec4 v_banding;\nflat in ivec4 v_glyph;\n\nuniform sampler2D curveTexture; // RGBA32F control points\nuniform highp usampler2D bandTexture; // RGBA32UI band data\n\nlayout(location = 0) out vec4 outColor;\n\nuint CalcRootCode(float y1, float y2, float y3) {\n uint i1 = floatBitsToUint(y1) >> 31u;\n uint i2 = floatBitsToUint(y2) >> 30u;\n uint i3 = floatBitsToUint(y3) >> 29u;\n\n uint shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nvec2 SolveHorizPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.y;\n float rb = 0.5 / b.y;\n\n float d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n float t1 = (b.y - d) * ra;\n float t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) t1 = t2 = p12.y * rb;\n\n return vec2((a.x * t1 - b.x * 2.0) * t1 + p12.x, (a.x * t2 - b.x * 2.0) * t2 + p12.x);\n}\n\nvec2 SolveVertPoly(vec4 p12, vec2 p3) {\n vec2 a = p12.xy - p12.zw * 2.0 + p3;\n vec2 b = p12.xy - p12.zw;\n float ra = 1.0 / a.x;\n float rb = 0.5 / b.x;\n\n float d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n float t1 = (b.x - d) * ra;\n float t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) t1 = t2 = p12.x * rb;\n\n return vec2((a.y * t1 - b.y * 2.0) * t1 + p12.y, (a.y * t2 - b.y * 2.0) * t2 + p12.y);\n}\n\nivec2 CalcBandLoc(ivec2 glyphLoc, uint offset) {\n ivec2 bandLoc = ivec2(glyphLoc.x + int(offset), glyphLoc.y);\n bandLoc.y += bandLoc.x >> kLogBandTextureWidth;\n bandLoc.x &= (1 << kLogBandTextureWidth) - 1;\n return bandLoc;\n}\n\nfloat CalcCoverage(float xcov, float ycov, float xwgt, float ywgt, int flags) {\n float coverage = max(abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0), min(abs(xcov), abs(ycov)));\n\n#if defined(SLUG_EVENODD)\n if ((flags & 0x1000) == 0) {\n#endif\n coverage = clamp(coverage, 0.0, 1.0);\n#if defined(SLUG_EVENODD)\n } else {\n coverage = 1.0 - abs(1.0 - fract(coverage * 0.5) * 2.0);\n }\n#endif\n\n#if defined(SLUG_WEIGHT)\n coverage = sqrt(coverage);\n#endif\n\n return coverage;\n}\n\nfloat SlugRender(vec2 renderCoord, vec4 bandTransform, ivec4 glyphData) {\n int curveIndex;\n\n vec2 emsPerPixel = fwidth(renderCoord);\n vec2 pixelsPerEm = 1.0 / emsPerPixel;\n\n ivec2 bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n ivec2 bandIndex = clamp(ivec2(renderCoord * bandTransform.xy + bandTransform.zw), ivec2(0, 0), bandMax);\n ivec2 glyphLoc = glyphData.xy;\n\n float xcov = 0.0;\n float xwgt = 0.0;\n\n uvec2 hbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n ivec2 hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (curveIndex = 0; curveIndex < int(hbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(hbandLoc.x + curveIndex, hbandLoc.y), 0).xy);\n\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break;\n\n uint code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n vec2 r = SolveHorizPoly(p12, p3) * pixelsPerEm.x;\n\n if ((code & 1u) != 0u) {\n xcov += clamp(r.x + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n xcov -= clamp(r.y + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n float ycov = 0.0;\n float ywgt = 0.0;\n\n uvec2 vbandData = texelFetch(bandTexture, ivec2(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n ivec2 vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (curveIndex = 0; curveIndex < int(vbandData.x); curveIndex++) {\n ivec2 curveLoc = ivec2(texelFetch(bandTexture, ivec2(vbandLoc.x + curveIndex, vbandLoc.y), 0).xy);\n vec4 p12 = texelFetch(curveTexture, curveLoc, 0) - vec4(renderCoord, renderCoord);\n vec2 p3 = texelFetch(curveTexture, ivec2(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break;\n\n uint code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n vec2 r = SolveVertPoly(p12, p3) * pixelsPerEm.y;\n\n if ((code & 1u) != 0u) {\n ycov -= clamp(r.x + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n\n if (code > 1u) {\n ycov += clamp(r.y + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n return CalcCoverage(xcov, ycov, xwgt, ywgt, glyphData.w);\n}\n\nvoid main() {\n float coverage = SlugRender(v_texcoord, v_banding, v_glyph);\n outColor = v_color * coverage;\n}\n";
|
|
6
|
+
|
|
7
|
+
var vertWGSL = "// ===================================================\n// WGSL port of the reference Slug vertex shader.\n// Original HLSL by Eric Lengyel, MIT License, Copyright 2017.\n// See: https://jcgt.org/published/0006/02/02/\n// ===================================================\n\nstruct Uniforms {\n slug_matrix: mat4x4<f32>,\n slug_viewport: vec2<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n\nstruct VertexInput {\n @location(0) a_pos: vec4<f32>,\n @location(1) a_tex: vec4<f32>,\n @location(2) a_jac: vec4<f32>,\n @location(3) a_bnd: vec4<f32>,\n @location(4) a_col: vec4<f32>,\n};\n\nstruct VertexOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) color: vec4<f32>,\n @location(1) texcoord: vec2<f32>,\n @location(2) @interpolate(flat) banding: vec4<f32>,\n @location(3) @interpolate(flat) glyph: vec4<i32>,\n};\n\nfn SlugUnpack(tex: vec4<f32>, bnd: vec4<f32>) -> VertexOutput {\n var out: VertexOutput;\n let g = bitcast<vec2<u32>>(tex.zw);\n out.glyph = vec4<i32>(\n i32(g.x & 0xFFFFu),\n i32(g.x >> 16u),\n i32(g.y & 0xFFFFu),\n i32(g.y >> 16u)\n );\n out.banding = bnd;\n return out;\n}\n\nfn SlugDilate(\n pos: vec4<f32>, tex: vec4<f32>, jac: vec4<f32>,\n m0: vec4<f32>, m1: vec4<f32>, m3: vec4<f32>,\n dim: vec2<f32>\n) -> vec4<f32> {\n // Returns vec4(dilated_pos.xy, new_texcoord.xy)\n let n = normalize(pos.zw);\n let s = dot(m3.xy, pos.xy) + m3.w;\n let t = dot(m3.xy, n);\n\n let u_val = (s * dot(m0.xy, n) - t * (dot(m0.xy, pos.xy) + m0.w)) * dim.x;\n let v_val = (s * dot(m1.xy, n) - t * (dot(m1.xy, pos.xy) + m1.w)) * dim.y;\n\n let s2 = s * s;\n let st = s * t;\n let uv = u_val * u_val + v_val * v_val;\n let d = pos.zw * (s2 * (st + sqrt(uv)) / (uv - st * st));\n\n let vpos = pos.xy + d;\n let vtex = vec2<f32>(tex.x + dot(d, jac.xy), tex.y + dot(d, jac.zw));\n return vec4<f32>(vpos, vtex);\n}\n\n@vertex\nfn vs_main(in: VertexInput) -> VertexOutput {\n let dilated = SlugDilate(\n in.a_pos, in.a_tex, in.a_jac,\n u.slug_matrix[0], u.slug_matrix[1], u.slug_matrix[3],\n u.slug_viewport\n );\n\n let p = dilated.xy;\n var out = SlugUnpack(in.a_tex, in.a_bnd);\n\n out.position = vec4<f32>(\n p.x * u.slug_matrix[0].x + p.y * u.slug_matrix[0].y + u.slug_matrix[0].w,\n p.x * u.slug_matrix[1].x + p.y * u.slug_matrix[1].y + u.slug_matrix[1].w,\n p.x * u.slug_matrix[2].x + p.y * u.slug_matrix[2].y + u.slug_matrix[2].w,\n p.x * u.slug_matrix[3].x + p.y * u.slug_matrix[3].y + u.slug_matrix[3].w\n );\n\n out.texcoord = dilated.zw;\n out.color = in.a_col;\n return out;\n}\n";
|
|
8
|
+
|
|
9
|
+
var fragWGSL = "// ===================================================\n// WGSL port of the reference Slug pixel shader.\n// Original HLSL by Eric Lengyel, MIT License, Copyright 2017.\n// See: https://jcgt.org/published/0006/02/02/\n// ===================================================\n\nconst kLogBandTextureWidth: u32 = 12u;\n\n@group(0) @binding(1) var curveTexture: texture_2d<f32>;\n@group(0) @binding(2) var bandTexture: texture_2d<u32>;\n\nstruct FragmentInput {\n @location(0) color: vec4<f32>,\n @location(1) texcoord: vec2<f32>,\n @location(2) @interpolate(flat) banding: vec4<f32>,\n @location(3) @interpolate(flat) glyph: vec4<i32>,\n};\n\nfn CalcRootCode(y1: f32, y2: f32, y3: f32) -> u32 {\n let i1 = bitcast<u32>(y1) >> 31u;\n let i2 = bitcast<u32>(y2) >> 30u;\n let i3 = bitcast<u32>(y3) >> 29u;\n\n var shift = (i2 & 2u) | (i1 & ~2u);\n shift = (i3 & 4u) | (shift & ~4u);\n\n return (0x2E74u >> shift) & 0x0101u;\n}\n\nfn SolveHorizPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.y;\n let rb = 0.5 / b.y;\n\n let d = sqrt(max(b.y * b.y - a.y * p12.y, 0.0));\n var t1 = (b.y - d) * ra;\n var t2 = (b.y + d) * ra;\n\n if (abs(a.y) < 1.0 / 65536.0) {\n t1 = p12.y * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.x * t1 - b.x * 2.0) * t1 + p12.x,\n (a.x * t2 - b.x * 2.0) * t2 + p12.x\n );\n}\n\nfn SolveVertPoly(p12: vec4<f32>, p3: vec2<f32>) -> vec2<f32> {\n let a = p12.xy - p12.zw * 2.0 + p3;\n let b = p12.xy - p12.zw;\n let ra = 1.0 / a.x;\n let rb = 0.5 / b.x;\n\n let d = sqrt(max(b.x * b.x - a.x * p12.x, 0.0));\n var t1 = (b.x - d) * ra;\n var t2 = (b.x + d) * ra;\n\n if (abs(a.x) < 1.0 / 65536.0) {\n t1 = p12.x * rb;\n t2 = t1;\n }\n\n return vec2<f32>(\n (a.y * t1 - b.y * 2.0) * t1 + p12.y,\n (a.y * t2 - b.y * 2.0) * t2 + p12.y\n );\n}\n\nfn CalcBandLoc(glyphLoc: vec2<i32>, offset: u32) -> vec2<i32> {\n var bandLoc = vec2<i32>(glyphLoc.x + i32(offset), glyphLoc.y);\n bandLoc.y += bandLoc.x >> i32(kLogBandTextureWidth);\n bandLoc.x &= (1 << i32(kLogBandTextureWidth)) - 1;\n return bandLoc;\n}\n\nfn CalcCoverage(xcov: f32, ycov: f32, xwgt: f32, ywgt: f32) -> f32 {\n var coverage = max(\n abs(xcov * xwgt + ycov * ywgt) / max(xwgt + ywgt, 1.0 / 65536.0),\n min(abs(xcov), abs(ycov))\n );\n coverage = clamp(coverage, 0.0, 1.0);\n return coverage;\n}\n\nfn SlugRender(renderCoord: vec2<f32>, bandTransform: vec4<f32>, glyphData: vec4<i32>) -> f32 {\n let emsPerPixel = fwidth(renderCoord);\n let pixelsPerEm = 1.0 / emsPerPixel;\n\n var bandMax = glyphData.zw;\n bandMax.y &= 0x00FF;\n\n let bandIndex = clamp(\n vec2<i32>(renderCoord * bandTransform.xy + bandTransform.zw),\n vec2<i32>(0, 0),\n bandMax\n );\n let glyphLoc = glyphData.xy;\n\n var xcov = 0.0;\n var xwgt = 0.0;\n\n let hbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandIndex.y, glyphLoc.y), 0).xy;\n let hbandLoc = CalcBandLoc(glyphLoc, hbandData.y);\n\n for (var ci = 0; ci < i32(hbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(hbandLoc.x + ci, hbandLoc.y), 0).xy);\n\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) { break; }\n\n let code = CalcRootCode(p12.y, p12.w, p3.y);\n if (code != 0u) {\n let r = SolveHorizPoly(p12, p3) * pixelsPerEm.x;\n\n if ((code & 1u) != 0u) {\n xcov += clamp(r.x + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n if (code > 1u) {\n xcov -= clamp(r.y + 0.5, 0.0, 1.0);\n xwgt = max(xwgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n var ycov = 0.0;\n var ywgt = 0.0;\n\n let vbandData = textureLoad(bandTexture, vec2<i32>(glyphLoc.x + bandMax.y + 1 + bandIndex.x, glyphLoc.y), 0).xy;\n let vbandLoc = CalcBandLoc(glyphLoc, vbandData.y);\n\n for (var ci = 0; ci < i32(vbandData.x); ci++) {\n let curveLoc = vec2<i32>(textureLoad(bandTexture, vec2<i32>(vbandLoc.x + ci, vbandLoc.y), 0).xy);\n let p12 = textureLoad(curveTexture, curveLoc, 0) - vec4<f32>(renderCoord, renderCoord);\n let p3 = textureLoad(curveTexture, vec2<i32>(curveLoc.x + 1, curveLoc.y), 0).xy - renderCoord;\n\n if (max(max(p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) { break; }\n\n let code = CalcRootCode(p12.x, p12.z, p3.x);\n if (code != 0u) {\n let r = SolveVertPoly(p12, p3) * pixelsPerEm.y;\n\n if ((code & 1u) != 0u) {\n ycov -= clamp(r.x + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.x) * 2.0, 0.0, 1.0));\n }\n if (code > 1u) {\n ycov += clamp(r.y + 0.5, 0.0, 1.0);\n ywgt = max(ywgt, clamp(1.0 - abs(r.y) * 2.0, 0.0, 1.0));\n }\n }\n }\n\n return CalcCoverage(xcov, ycov, xwgt, ywgt);\n}\n\n@fragment\nfn fs_main(in: FragmentInput) -> @location(0) vec4<f32> {\n let coverage = SlugRender(in.texcoord, in.banding, in.glyph);\n return in.color * coverage;\n}\n";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Slug shader source re-exports.
|
|
13
|
+
* The .glsl/.wgsl files are the single source of truth — imported as strings
|
|
14
|
+
* at build time via the glslPlugin in rollup.config.js.
|
|
15
|
+
*/
|
|
16
|
+
// @ts-ignore — resolved by rollup glslPlugin
|
|
17
|
+
const vertexShaderGLSL300 = vertGLSL;
|
|
18
|
+
const fragmentShaderGLSL300 = fragGLSL;
|
|
19
|
+
const vertexShaderWGSL = vertWGSL;
|
|
20
|
+
const fragmentShaderWGSL = fragWGSL;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* CPU-side data packer for the Slug algorithm.
|
|
24
|
+
* Faithful to Eric Lengyel's reference layout (MIT License, 2017).
|
|
25
|
+
*
|
|
26
|
+
* Takes generic quadratic Bezier shapes (not text-specific) and produces
|
|
27
|
+
* GPU-ready packed textures + vertex attribute buffers.
|
|
28
|
+
*/
|
|
29
|
+
const TEX_WIDTH = 4096;
|
|
30
|
+
const LOG_TEX_WIDTH = 12;
|
|
31
|
+
// Float ↔ Uint32 reinterpretation helpers
|
|
32
|
+
const _f32 = new Float32Array(1);
|
|
33
|
+
const _u32 = new Uint32Array(_f32.buffer);
|
|
34
|
+
function uintAsFloat(u) {
|
|
35
|
+
_u32[0] = u;
|
|
36
|
+
return _f32[0];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Pack an array of shapes into Slug's GPU data layout.
|
|
40
|
+
*
|
|
41
|
+
* Each shape is a closed region defined by quadratic Bezier curves.
|
|
42
|
+
* Returns textures + vertex buffers ready for GPU upload.
|
|
43
|
+
*/
|
|
44
|
+
function packSlugData(shapes, options) {
|
|
45
|
+
const bandCount = options?.bandCount ?? 16;
|
|
46
|
+
const evenOdd = options?.evenOdd ?? false;
|
|
47
|
+
// Phase 1: Pack all curves into curveTexture
|
|
48
|
+
const allCurves = [];
|
|
49
|
+
// Estimate max texels needed
|
|
50
|
+
let totalCurves = 0;
|
|
51
|
+
for (const shape of shapes) {
|
|
52
|
+
totalCurves += shape.curves.length;
|
|
53
|
+
}
|
|
54
|
+
const curveTexHeight = Math.ceil((totalCurves * 2) / TEX_WIDTH) + 1;
|
|
55
|
+
const curveData = new Float32Array(TEX_WIDTH * curveTexHeight * 4);
|
|
56
|
+
let curveX = 0;
|
|
57
|
+
let curveY = 0;
|
|
58
|
+
for (const shape of shapes) {
|
|
59
|
+
const entries = [];
|
|
60
|
+
for (const curve of shape.curves) {
|
|
61
|
+
// Don't let a curve span across row boundary (needs 2 consecutive texels)
|
|
62
|
+
if (curveX >= TEX_WIDTH - 1) {
|
|
63
|
+
curveX = 0;
|
|
64
|
+
curveY++;
|
|
65
|
+
}
|
|
66
|
+
const base = (curveY * TEX_WIDTH + curveX) * 4;
|
|
67
|
+
curveData[base + 0] = curve.p1[0];
|
|
68
|
+
curveData[base + 1] = curve.p1[1];
|
|
69
|
+
curveData[base + 2] = curve.p2[0];
|
|
70
|
+
curveData[base + 3] = curve.p2[1];
|
|
71
|
+
const base2 = base + 4;
|
|
72
|
+
curveData[base2 + 0] = curve.p3[0];
|
|
73
|
+
curveData[base2 + 1] = curve.p3[1];
|
|
74
|
+
const minX = Math.min(curve.p1[0], curve.p2[0], curve.p3[0]);
|
|
75
|
+
const minY = Math.min(curve.p1[1], curve.p2[1], curve.p3[1]);
|
|
76
|
+
const maxX = Math.max(curve.p1[0], curve.p2[0], curve.p3[0]);
|
|
77
|
+
const maxY = Math.max(curve.p1[1], curve.p2[1], curve.p3[1]);
|
|
78
|
+
entries.push({
|
|
79
|
+
p1x: curve.p1[0], p1y: curve.p1[1],
|
|
80
|
+
p2x: curve.p2[0], p2y: curve.p2[1],
|
|
81
|
+
p3x: curve.p3[0], p3y: curve.p3[1],
|
|
82
|
+
minX, minY, maxX, maxY,
|
|
83
|
+
curveTexX: curveX,
|
|
84
|
+
curveTexY: curveY
|
|
85
|
+
});
|
|
86
|
+
curveX += 2;
|
|
87
|
+
}
|
|
88
|
+
allCurves.push(entries);
|
|
89
|
+
}
|
|
90
|
+
const actualCurveTexHeight = curveY + 1;
|
|
91
|
+
// Phase 2: Build band data for each shape and pack into bandTexture
|
|
92
|
+
// Layout per shape in bandTexture (relative to glyphLoc):
|
|
93
|
+
// [0 .. hBandMax] : h-band headers
|
|
94
|
+
// [hBandMax+1 .. hBandMax+1+vBandMax] : v-band headers
|
|
95
|
+
// [hBandMax+vBandMax+2 .. ] : curve index lists
|
|
96
|
+
// First pass: compute total band texels needed
|
|
97
|
+
const shapeBandData = [];
|
|
98
|
+
let totalBandTexels = 0;
|
|
99
|
+
for (let si = 0; si < shapes.length; si++) {
|
|
100
|
+
const shape = shapes[si];
|
|
101
|
+
const curves = allCurves[si];
|
|
102
|
+
if (curves.length === 0) {
|
|
103
|
+
shapeBandData.push({
|
|
104
|
+
hBands: [], vBands: [], hLists: [], vLists: [],
|
|
105
|
+
totalTexels: 0, bandMaxX: 0, bandMaxY: 0
|
|
106
|
+
});
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const [bMinX, bMinY, bMaxX, bMaxY] = shape.bounds;
|
|
110
|
+
const w = bMaxX - bMinX;
|
|
111
|
+
const h = bMaxY - bMinY;
|
|
112
|
+
const hBandCount = Math.min(bandCount, 255); // max 255 (fits in 8 bits)
|
|
113
|
+
const vBandCount = Math.min(bandCount, 255);
|
|
114
|
+
const bandMaxY = hBandCount - 1;
|
|
115
|
+
const bandMaxX = vBandCount - 1;
|
|
116
|
+
// Build horizontal bands (partition y-axis)
|
|
117
|
+
const hBands = [];
|
|
118
|
+
const hLists = [];
|
|
119
|
+
const bandH = h / hBandCount;
|
|
120
|
+
for (let bi = 0; bi < hBandCount; bi++) {
|
|
121
|
+
const bandMinY = bMinY + bi * bandH;
|
|
122
|
+
const bandMaxYCoord = bandMinY + bandH;
|
|
123
|
+
// Collect curves whose y-range overlaps this band
|
|
124
|
+
const list = [];
|
|
125
|
+
for (const c of curves) {
|
|
126
|
+
if (c.maxY >= bandMinY && c.minY <= bandMaxYCoord) {
|
|
127
|
+
list.push({ curve: c, sortKey: c.maxX });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Sort by descending max-x for early exit
|
|
131
|
+
list.sort((a, b) => b.sortKey - a.sortKey);
|
|
132
|
+
const flatList = [];
|
|
133
|
+
for (const item of list) {
|
|
134
|
+
flatList.push(item.curve.curveTexX, item.curve.curveTexY);
|
|
135
|
+
}
|
|
136
|
+
hBands.push({ curveCount: list.length, listOffset: 0 });
|
|
137
|
+
hLists.push(flatList);
|
|
138
|
+
}
|
|
139
|
+
// Build vertical bands (partition x-axis)
|
|
140
|
+
const vBands = [];
|
|
141
|
+
const vLists = [];
|
|
142
|
+
const bandW = w / vBandCount;
|
|
143
|
+
for (let bi = 0; bi < vBandCount; bi++) {
|
|
144
|
+
const bandMinX = bMinX + bi * bandW;
|
|
145
|
+
const bandMaxXCoord = bandMinX + bandW;
|
|
146
|
+
const list = [];
|
|
147
|
+
for (const c of curves) {
|
|
148
|
+
if (c.maxX >= bandMinX && c.minX <= bandMaxXCoord) {
|
|
149
|
+
list.push({ curve: c, sortKey: c.maxY });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Sort by descending max-y for early exit
|
|
153
|
+
list.sort((a, b) => b.sortKey - a.sortKey);
|
|
154
|
+
const flatList = [];
|
|
155
|
+
for (const item of list) {
|
|
156
|
+
flatList.push(item.curve.curveTexX, item.curve.curveTexY);
|
|
157
|
+
}
|
|
158
|
+
vBands.push({ curveCount: list.length, listOffset: 0 });
|
|
159
|
+
vLists.push(flatList);
|
|
160
|
+
}
|
|
161
|
+
// Total texels for this shape: band headers + curve lists
|
|
162
|
+
const headerTexels = hBandCount + vBandCount;
|
|
163
|
+
let listTexels = 0;
|
|
164
|
+
for (const l of hLists)
|
|
165
|
+
listTexels += l.length / 2;
|
|
166
|
+
for (const l of vLists)
|
|
167
|
+
listTexels += l.length / 2;
|
|
168
|
+
const total = headerTexels + listTexels;
|
|
169
|
+
shapeBandData.push({
|
|
170
|
+
hBands, vBands, hLists, vLists,
|
|
171
|
+
totalTexels: total, bandMaxX, bandMaxY
|
|
172
|
+
});
|
|
173
|
+
totalBandTexels += total;
|
|
174
|
+
}
|
|
175
|
+
// Allocate bandTexture
|
|
176
|
+
const bandTexHeight = Math.max(1, Math.ceil(totalBandTexels / TEX_WIDTH) + shapes.length);
|
|
177
|
+
const bandData = new Uint32Array(TEX_WIDTH * bandTexHeight * 4);
|
|
178
|
+
// Pack band data per shape
|
|
179
|
+
let bandX = 0;
|
|
180
|
+
let bandY = 0;
|
|
181
|
+
const glyphLocs = [];
|
|
182
|
+
for (let si = 0; si < shapes.length; si++) {
|
|
183
|
+
const sd = shapeBandData[si];
|
|
184
|
+
if (sd.totalTexels === 0) {
|
|
185
|
+
glyphLocs.push({ x: 0, y: 0 });
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
// Ensure glyph data doesn't start too close to row end
|
|
189
|
+
// (need at least headerTexels contiguous... actually wrapping is handled by CalcBandLoc)
|
|
190
|
+
// But the initial band header reads don't use CalcBandLoc, so glyphLoc.x + bandMax.y + 1 + bandMaxX
|
|
191
|
+
// must be reachable. CalcBandLoc handles wrapping for curve lists.
|
|
192
|
+
// To be safe, start each glyph at the beginning of a row if remaining space is tight.
|
|
193
|
+
const minContiguous = sd.hBands.length + sd.vBands.length;
|
|
194
|
+
if (bandX + minContiguous > TEX_WIDTH) {
|
|
195
|
+
bandX = 0;
|
|
196
|
+
bandY++;
|
|
197
|
+
}
|
|
198
|
+
const glyphLocX = bandX;
|
|
199
|
+
const glyphLocY = bandY;
|
|
200
|
+
glyphLocs.push({ x: glyphLocX, y: glyphLocY });
|
|
201
|
+
// Curve lists start after all headers
|
|
202
|
+
let listStartOffset = sd.hBands.length + sd.vBands.length;
|
|
203
|
+
// Assign list offsets for h-bands
|
|
204
|
+
for (let bi = 0; bi < sd.hBands.length; bi++) {
|
|
205
|
+
sd.hBands[bi].listOffset = listStartOffset;
|
|
206
|
+
listStartOffset += sd.hLists[bi].length / 2;
|
|
207
|
+
}
|
|
208
|
+
// Assign list offsets for v-bands
|
|
209
|
+
for (let bi = 0; bi < sd.vBands.length; bi++) {
|
|
210
|
+
sd.vBands[bi].listOffset = listStartOffset;
|
|
211
|
+
listStartOffset += sd.vLists[bi].length / 2;
|
|
212
|
+
}
|
|
213
|
+
// Write h-band headers
|
|
214
|
+
for (let bi = 0; bi < sd.hBands.length; bi++) {
|
|
215
|
+
const tx = glyphLocX + bi;
|
|
216
|
+
const ty = glyphLocY;
|
|
217
|
+
const idx = (ty * TEX_WIDTH + tx) * 4;
|
|
218
|
+
bandData[idx + 0] = sd.hBands[bi].curveCount;
|
|
219
|
+
bandData[idx + 1] = sd.hBands[bi].listOffset;
|
|
220
|
+
bandData[idx + 2] = 0;
|
|
221
|
+
bandData[idx + 3] = 0;
|
|
222
|
+
}
|
|
223
|
+
// Write v-band headers (after h-bands)
|
|
224
|
+
const vBandStart = glyphLocX + sd.hBands.length;
|
|
225
|
+
for (let bi = 0; bi < sd.vBands.length; bi++) {
|
|
226
|
+
const tx = vBandStart + bi;
|
|
227
|
+
const ty = glyphLocY;
|
|
228
|
+
const idx = (ty * TEX_WIDTH + tx) * 4;
|
|
229
|
+
bandData[idx + 0] = sd.vBands[bi].curveCount;
|
|
230
|
+
bandData[idx + 1] = sd.vBands[bi].listOffset;
|
|
231
|
+
bandData[idx + 2] = 0;
|
|
232
|
+
bandData[idx + 3] = 0;
|
|
233
|
+
}
|
|
234
|
+
// Write curve lists using CalcBandLoc-style wrapping
|
|
235
|
+
const writeBandLoc = (offset) => {
|
|
236
|
+
let bx = glyphLocX + offset;
|
|
237
|
+
let by = glyphLocY;
|
|
238
|
+
by += bx >> LOG_TEX_WIDTH;
|
|
239
|
+
bx &= (1 << LOG_TEX_WIDTH) - 1;
|
|
240
|
+
return { x: bx, y: by };
|
|
241
|
+
};
|
|
242
|
+
// Write h-band curve lists
|
|
243
|
+
for (let bi = 0; bi < sd.hBands.length; bi++) {
|
|
244
|
+
const list = sd.hLists[bi];
|
|
245
|
+
const baseOffset = sd.hBands[bi].listOffset;
|
|
246
|
+
for (let ci = 0; ci < list.length; ci += 2) {
|
|
247
|
+
const loc = writeBandLoc(baseOffset + ci / 2);
|
|
248
|
+
const idx = (loc.y * TEX_WIDTH + loc.x) * 4;
|
|
249
|
+
bandData[idx + 0] = list[ci]; // curveTexX
|
|
250
|
+
bandData[idx + 1] = list[ci + 1]; // curveTexY
|
|
251
|
+
bandData[idx + 2] = 0;
|
|
252
|
+
bandData[idx + 3] = 0;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Write v-band curve lists
|
|
256
|
+
for (let bi = 0; bi < sd.vBands.length; bi++) {
|
|
257
|
+
const list = sd.vLists[bi];
|
|
258
|
+
const baseOffset = sd.vBands[bi].listOffset;
|
|
259
|
+
for (let ci = 0; ci < list.length; ci += 2) {
|
|
260
|
+
const loc = writeBandLoc(baseOffset + ci / 2);
|
|
261
|
+
const idx = (loc.y * TEX_WIDTH + loc.x) * 4;
|
|
262
|
+
bandData[idx + 0] = list[ci];
|
|
263
|
+
bandData[idx + 1] = list[ci + 1];
|
|
264
|
+
bandData[idx + 2] = 0;
|
|
265
|
+
bandData[idx + 3] = 0;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Advance band cursor past this shape's data
|
|
269
|
+
const totalForShape = listStartOffset;
|
|
270
|
+
const endLoc = writeBandLoc(totalForShape);
|
|
271
|
+
bandX = endLoc.x;
|
|
272
|
+
bandY = endLoc.y;
|
|
273
|
+
}
|
|
274
|
+
const actualBandTexHeight = bandY + 1;
|
|
275
|
+
// Phase 3: Build vertex attributes
|
|
276
|
+
// 5 attribs x 4 floats x 4 vertices per shape = 80 floats per shape
|
|
277
|
+
const FLOATS_PER_VERTEX = 20; // 5 attribs * 4 components
|
|
278
|
+
const VERTS_PER_SHAPE = 4;
|
|
279
|
+
const vertices = new Float32Array(shapes.length * VERTS_PER_SHAPE * FLOATS_PER_VERTEX);
|
|
280
|
+
const indices = new Uint16Array(shapes.length * 6);
|
|
281
|
+
// Corner normals (outward-pointing, un-normalized — SlugDilate normalizes)
|
|
282
|
+
const cornerNormals = [
|
|
283
|
+
[-1, -1], // bottom-left
|
|
284
|
+
[1, -1], // bottom-right
|
|
285
|
+
[1, 1], // top-right
|
|
286
|
+
[-1, 1], // top-left
|
|
287
|
+
];
|
|
288
|
+
for (let si = 0; si < shapes.length; si++) {
|
|
289
|
+
const shape = shapes[si];
|
|
290
|
+
const sd = shapeBandData[si];
|
|
291
|
+
const glyph = glyphLocs[si];
|
|
292
|
+
const [bMinX, bMinY, bMaxX, bMaxY] = shape.bounds;
|
|
293
|
+
const w = bMaxX - bMinX;
|
|
294
|
+
const h = bMaxY - bMinY;
|
|
295
|
+
// Corner positions in object-space
|
|
296
|
+
const corners = [
|
|
297
|
+
[bMinX, bMinY],
|
|
298
|
+
[bMaxX, bMinY],
|
|
299
|
+
[bMaxX, bMaxY],
|
|
300
|
+
[bMinX, bMaxY],
|
|
301
|
+
];
|
|
302
|
+
// Em-space sample coords at corners (same as object-space for 1:1 mapping)
|
|
303
|
+
const emCorners = [
|
|
304
|
+
[bMinX, bMinY],
|
|
305
|
+
[bMaxX, bMinY],
|
|
306
|
+
[bMaxX, bMaxY],
|
|
307
|
+
[bMinX, bMaxY],
|
|
308
|
+
];
|
|
309
|
+
// Pack tex.z: glyph location in band texture
|
|
310
|
+
const texZ = uintAsFloat((glyph.x & 0xFFFF) | ((glyph.y & 0xFFFF) << 16));
|
|
311
|
+
// Pack tex.w: band max + flags
|
|
312
|
+
let texWBits = (sd.bandMaxX & 0xFF) | ((sd.bandMaxY & 0xFF) << 16);
|
|
313
|
+
if (evenOdd)
|
|
314
|
+
texWBits |= 0x10000000; // E flag at bit 28
|
|
315
|
+
const texW = uintAsFloat(texWBits);
|
|
316
|
+
// Band transform: scale and offset to map em-coords to band indices
|
|
317
|
+
const bandScaleX = w > 0 ? sd.vBands.length / w : 0;
|
|
318
|
+
const bandScaleY = h > 0 ? sd.hBands.length / h : 0;
|
|
319
|
+
const bandOffsetX = -bMinX * bandScaleX;
|
|
320
|
+
const bandOffsetY = -bMinY * bandScaleY;
|
|
321
|
+
for (let vi = 0; vi < 4; vi++) {
|
|
322
|
+
const base = (si * 4 + vi) * FLOATS_PER_VERTEX;
|
|
323
|
+
// pos: .xy = position, .zw = normal
|
|
324
|
+
vertices[base + 0] = corners[vi][0];
|
|
325
|
+
vertices[base + 1] = corners[vi][1];
|
|
326
|
+
vertices[base + 2] = cornerNormals[vi][0];
|
|
327
|
+
vertices[base + 3] = cornerNormals[vi][1];
|
|
328
|
+
// tex: .xy = em-space coords, .z = packed glyph loc, .w = packed band max
|
|
329
|
+
vertices[base + 4] = emCorners[vi][0];
|
|
330
|
+
vertices[base + 5] = emCorners[vi][1];
|
|
331
|
+
vertices[base + 6] = texZ;
|
|
332
|
+
vertices[base + 7] = texW;
|
|
333
|
+
// jac: identity Jacobian (em-space = object-space)
|
|
334
|
+
vertices[base + 8] = 1.0;
|
|
335
|
+
vertices[base + 9] = 0.0;
|
|
336
|
+
vertices[base + 10] = 0.0;
|
|
337
|
+
vertices[base + 11] = 1.0;
|
|
338
|
+
// bnd: band scale and offset
|
|
339
|
+
vertices[base + 12] = bandScaleX;
|
|
340
|
+
vertices[base + 13] = bandScaleY;
|
|
341
|
+
vertices[base + 14] = bandOffsetX;
|
|
342
|
+
vertices[base + 15] = bandOffsetY;
|
|
343
|
+
// col: white with full alpha (caller overrides via uniform or attribute)
|
|
344
|
+
vertices[base + 16] = 1.0;
|
|
345
|
+
vertices[base + 17] = 1.0;
|
|
346
|
+
vertices[base + 18] = 1.0;
|
|
347
|
+
vertices[base + 19] = 1.0;
|
|
348
|
+
}
|
|
349
|
+
// Indices: two triangles per quad
|
|
350
|
+
const vBase = si * 4;
|
|
351
|
+
const iBase = si * 6;
|
|
352
|
+
indices[iBase + 0] = vBase + 0;
|
|
353
|
+
indices[iBase + 1] = vBase + 1;
|
|
354
|
+
indices[iBase + 2] = vBase + 2;
|
|
355
|
+
indices[iBase + 3] = vBase + 0;
|
|
356
|
+
indices[iBase + 4] = vBase + 2;
|
|
357
|
+
indices[iBase + 5] = vBase + 3;
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
curveTexture: {
|
|
361
|
+
data: curveData.subarray(0, TEX_WIDTH * actualCurveTexHeight * 4),
|
|
362
|
+
width: TEX_WIDTH,
|
|
363
|
+
height: actualCurveTexHeight
|
|
364
|
+
},
|
|
365
|
+
bandTexture: {
|
|
366
|
+
data: bandData.subarray(0, TEX_WIDTH * actualBandTexHeight * 4),
|
|
367
|
+
width: TEX_WIDTH,
|
|
368
|
+
height: actualBandTexHeight
|
|
369
|
+
},
|
|
370
|
+
vertices,
|
|
371
|
+
indices,
|
|
372
|
+
shapeCount: shapes.length
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
exports.fragmentShaderGLSL300 = fragmentShaderGLSL300;
|
|
377
|
+
exports.fragmentShaderWGSL = fragmentShaderWGSL;
|
|
378
|
+
exports.packSlugData = packSlugData;
|
|
379
|
+
exports.vertexShaderGLSL300 = vertexShaderGLSL300;
|
|
380
|
+
exports.vertexShaderWGSL = vertexShaderWGSL;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure Slug algorithm types — no dependency on three-text internals.
|
|
3
|
+
* Based on Eric Lengyel's reference implementation (MIT License, 2017).
|
|
4
|
+
*/
|
|
5
|
+
interface QuadCurve {
|
|
6
|
+
p1: [number, number];
|
|
7
|
+
p2: [number, number];
|
|
8
|
+
p3: [number, number];
|
|
9
|
+
}
|
|
10
|
+
interface SlugShape {
|
|
11
|
+
curves: QuadCurve[];
|
|
12
|
+
bounds: [number, number, number, number];
|
|
13
|
+
}
|
|
14
|
+
interface SlugPackedTexture {
|
|
15
|
+
data: Float32Array | Uint32Array;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}
|
|
19
|
+
interface SlugGPUData {
|
|
20
|
+
curveTexture: SlugPackedTexture & {
|
|
21
|
+
data: Float32Array;
|
|
22
|
+
};
|
|
23
|
+
bandTexture: SlugPackedTexture & {
|
|
24
|
+
data: Uint32Array;
|
|
25
|
+
};
|
|
26
|
+
/** 5 attribs x 4 components x 4 verts per shape, tightly packed per-shape */
|
|
27
|
+
vertices: Float32Array;
|
|
28
|
+
indices: Uint16Array;
|
|
29
|
+
shapeCount: number;
|
|
30
|
+
}
|
|
31
|
+
interface SlugPackOptions {
|
|
32
|
+
bandCount?: number;
|
|
33
|
+
evenOdd?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Slug shader source re-exports.
|
|
38
|
+
* The .glsl/.wgsl files are the single source of truth — imported as strings
|
|
39
|
+
* at build time via the glslPlugin in rollup.config.js.
|
|
40
|
+
*/
|
|
41
|
+
declare const vertexShaderGLSL300: string;
|
|
42
|
+
declare const fragmentShaderGLSL300: string;
|
|
43
|
+
declare const vertexShaderWGSL: string;
|
|
44
|
+
declare const fragmentShaderWGSL: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* CPU-side data packer for the Slug algorithm.
|
|
48
|
+
* Faithful to Eric Lengyel's reference layout (MIT License, 2017).
|
|
49
|
+
*
|
|
50
|
+
* Takes generic quadratic Bezier shapes (not text-specific) and produces
|
|
51
|
+
* GPU-ready packed textures + vertex attribute buffers.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Pack an array of shapes into Slug's GPU data layout.
|
|
56
|
+
*
|
|
57
|
+
* Each shape is a closed region defined by quadratic Bezier curves.
|
|
58
|
+
* Returns textures + vertex buffers ready for GPU upload.
|
|
59
|
+
*/
|
|
60
|
+
declare function packSlugData(shapes: SlugShape[], options?: SlugPackOptions): SlugGPUData;
|
|
61
|
+
|
|
62
|
+
export { QuadCurve, SlugGPUData, SlugPackOptions, SlugPackedTexture, SlugShape, fragmentShaderGLSL300, fragmentShaderWGSL, packSlugData, vertexShaderGLSL300, vertexShaderWGSL };
|