@solidtv/renderer 1.5.4 → 1.5.5

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.
Files changed (65) hide show
  1. package/dist/exports/canvas-shaders.d.ts +1 -0
  2. package/dist/exports/canvas-shaders.js +1 -0
  3. package/dist/exports/canvas-shaders.js.map +1 -1
  4. package/dist/exports/index.d.ts +1 -0
  5. package/dist/exports/index.js +1 -0
  6. package/dist/exports/index.js.map +1 -1
  7. package/dist/exports/webgl-shaders.d.ts +1 -0
  8. package/dist/exports/webgl-shaders.js +1 -0
  9. package/dist/exports/webgl-shaders.js.map +1 -1
  10. package/dist/src/core/CoreShaderManager.js +16 -2
  11. package/dist/src/core/CoreShaderManager.js.map +1 -1
  12. package/dist/src/core/lib/WebGlContextWrapper.d.ts +13 -0
  13. package/dist/src/core/lib/WebGlContextWrapper.js +15 -0
  14. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  15. package/dist/src/core/renderers/canvas/CanvasRenderer.d.ts +10 -0
  16. package/dist/src/core/renderers/canvas/CanvasRenderer.js +19 -0
  17. package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
  18. package/dist/src/core/renderers/webgl/WebGlRenderer.js +14 -1
  19. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  20. package/dist/src/core/shaders/canvas/EdgeFade.d.ts +10 -0
  21. package/dist/src/core/shaders/canvas/EdgeFade.js +92 -0
  22. package/dist/src/core/shaders/canvas/EdgeFade.js.map +1 -0
  23. package/dist/src/core/shaders/canvas/LinearGradient.js +1 -18
  24. package/dist/src/core/shaders/canvas/LinearGradient.js.map +1 -1
  25. package/dist/src/core/shaders/templates/EdgeFadeTemplate.d.ts +32 -0
  26. package/dist/src/core/shaders/templates/EdgeFadeTemplate.js +9 -0
  27. package/dist/src/core/shaders/templates/EdgeFadeTemplate.js.map +1 -0
  28. package/dist/src/core/shaders/webgl/EdgeFade.d.ts +9 -0
  29. package/dist/src/core/shaders/webgl/EdgeFade.js +74 -0
  30. package/dist/src/core/shaders/webgl/EdgeFade.js.map +1 -0
  31. package/dist/src/core/shaders/webgl/LinearGradient.js +31 -51
  32. package/dist/src/core/shaders/webgl/LinearGradient.js.map +1 -1
  33. package/dist/src/core/shaders/webgl/RadialGradient.js +2 -2
  34. package/dist/src/core/shaders/webgl/RadialProgress.js +7 -0
  35. package/dist/src/core/shaders/webgl/RadialProgress.js.map +1 -1
  36. package/dist/src/core/text-rendering/SdfFontHandler.d.ts +1 -0
  37. package/dist/src/core/text-rendering/SdfFontHandler.js +88 -51
  38. package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
  39. package/dist/src/core/textures/ImageTexture.js +2 -1
  40. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  41. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  42. package/exports/canvas-shaders.ts +1 -0
  43. package/exports/index.ts +1 -0
  44. package/exports/webgl-shaders.ts +1 -0
  45. package/package.json +1 -1
  46. package/src/core/CoreShaderManager.contextLoss.test.ts +72 -0
  47. package/src/core/CoreShaderManager.ts +15 -2
  48. package/src/core/lib/WebGlContextWrapper.ts +16 -0
  49. package/src/core/renderers/canvas/CanvasRenderer.ts +20 -0
  50. package/src/core/renderers/webgl/WebGlRenderer.contextLoss.test.ts +49 -0
  51. package/src/core/renderers/webgl/WebGlRenderer.ts +13 -1
  52. package/src/core/shaders/canvas/EdgeFade.ts +105 -0
  53. package/src/core/shaders/canvas/LinearGradient.test.ts +45 -0
  54. package/src/core/shaders/canvas/LinearGradient.ts +1 -18
  55. package/src/core/shaders/templates/EdgeFadeTemplate.test.ts +35 -0
  56. package/src/core/shaders/templates/EdgeFadeTemplate.ts +41 -0
  57. package/src/core/shaders/webgl/EdgeFade.ts +84 -0
  58. package/src/core/shaders/webgl/LinearGradient.test.ts +70 -0
  59. package/src/core/shaders/webgl/LinearGradient.ts +36 -51
  60. package/src/core/shaders/webgl/RadialGradient.ts +2 -2
  61. package/src/core/shaders/webgl/RadialProgress.ts +7 -0
  62. package/src/core/text-rendering/SdfFontHandler.ts +77 -30
  63. package/src/core/text-rendering/tests/SdfFontHandler.test.ts +181 -35
  64. package/src/core/textures/ImageTexture.test.ts +91 -0
  65. package/src/core/textures/ImageTexture.ts +4 -1
@@ -0,0 +1,92 @@
1
+ import { EdgeFadeTemplate, } from '../templates/EdgeFadeTemplate.js';
2
+ // Shared scratch canvas, grown on demand and reused across all EdgeFade nodes.
3
+ // Rendering is single-threaded and non-reentrant so one scratch is enough.
4
+ let scratchCanvas = null;
5
+ let scratchCtx = null;
6
+ /**
7
+ * Canvas2D implementation of {@link EdgeFade}: the node's content is drawn
8
+ * into an offscreen canvas, the edge ramps are erased with destination-out
9
+ * gradients, and the result is composited onto the main canvas. This keeps
10
+ * the background behind the node intact, which a destination-out pass on the
11
+ * main canvas could not.
12
+ */
13
+ export const EdgeFade = {
14
+ props: EdgeFadeTemplate.props,
15
+ render(ctx, node, renderContext) {
16
+ const props = this.props;
17
+ const left = props.left;
18
+ const top = props.top;
19
+ const right = props.right;
20
+ const bottom = props.bottom;
21
+ if (left <= 0 && top <= 0 && right <= 0 && bottom <= 0) {
22
+ renderContext();
23
+ return;
24
+ }
25
+ const w = node.props.w;
26
+ const h = node.props.h;
27
+ if (w <= 0 || h <= 0) {
28
+ return;
29
+ }
30
+ const pr = this.stage.pixelRatio;
31
+ const sw = w * pr;
32
+ const sh = h * pr;
33
+ if (scratchCanvas === null) {
34
+ scratchCanvas = document.createElement('canvas');
35
+ scratchCtx = scratchCanvas.getContext('2d');
36
+ }
37
+ const sctx = scratchCtx;
38
+ if (scratchCanvas.width < sw || scratchCanvas.height < sh) {
39
+ // Growing the canvas implicitly clears it
40
+ scratchCanvas.width = Math.ceil(sw);
41
+ scratchCanvas.height = Math.ceil(sh);
42
+ }
43
+ else {
44
+ sctx.setTransform(1, 0, 0, 1, 0, 0);
45
+ sctx.clearRect(0, 0, sw, sh);
46
+ }
47
+ // Draw the node's content at the scratch origin
48
+ const tx = node.globalTransform.tx;
49
+ const ty = node.globalTransform.ty;
50
+ sctx.setTransform(pr, 0, 0, pr, -tx * pr, -ty * pr);
51
+ this.stage.renderer.renderNodeContent(node, sctx);
52
+ sctx.setTransform(1, 0, 0, 1, 0, 0);
53
+ // Erase each edge with a linear ramp. Sequential destination-out passes
54
+ // multiply: dst *= (1 - g), matching the WebGL ramp product.
55
+ sctx.globalCompositeOperation = 'destination-out';
56
+ if (left > 0) {
57
+ const d = left * pr;
58
+ const g = sctx.createLinearGradient(0, 0, d, 0);
59
+ g.addColorStop(0, 'rgba(0,0,0,1)');
60
+ g.addColorStop(1, 'rgba(0,0,0,0)');
61
+ sctx.fillStyle = g;
62
+ sctx.fillRect(0, 0, d, sh);
63
+ }
64
+ if (top > 0) {
65
+ const d = top * pr;
66
+ const g = sctx.createLinearGradient(0, 0, 0, d);
67
+ g.addColorStop(0, 'rgba(0,0,0,1)');
68
+ g.addColorStop(1, 'rgba(0,0,0,0)');
69
+ sctx.fillStyle = g;
70
+ sctx.fillRect(0, 0, sw, d);
71
+ }
72
+ if (right > 0) {
73
+ const d = right * pr;
74
+ const g = sctx.createLinearGradient(sw - d, 0, sw, 0);
75
+ g.addColorStop(0, 'rgba(0,0,0,0)');
76
+ g.addColorStop(1, 'rgba(0,0,0,1)');
77
+ sctx.fillStyle = g;
78
+ sctx.fillRect(sw - d, 0, d, sh);
79
+ }
80
+ if (bottom > 0) {
81
+ const d = bottom * pr;
82
+ const g = sctx.createLinearGradient(0, sh - d, 0, sh);
83
+ g.addColorStop(0, 'rgba(0,0,0,0)');
84
+ g.addColorStop(1, 'rgba(0,0,0,1)');
85
+ sctx.fillStyle = g;
86
+ sctx.fillRect(0, sh - d, sw, d);
87
+ }
88
+ sctx.globalCompositeOperation = 'source-over';
89
+ ctx.drawImage(scratchCanvas, 0, 0, sw, sh, tx, ty, w, h);
90
+ },
91
+ };
92
+ //# sourceMappingURL=EdgeFade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeFade.js","sourceRoot":"","sources":["../../../../../src/core/shaders/canvas/EdgeFade.ts"],"names":[],"mappings":"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"}
@@ -13,24 +13,7 @@ export const LinearGradient = {
13
13
  y0: line * Math.sin(angle) + nHeight * 0.5,
14
14
  x1: line * Math.cos(angle + Math.PI) + nWidth * 0.5,
15
15
  y1: line * Math.sin(angle + Math.PI) + nHeight * 0.5,
16
- colors: this.props.colors.map((value, i, arr) => {
17
- const alpha = (value >>> 24) & 0xff;
18
- if (alpha === 0) {
19
- let nearestRGB = value & 0x00ffffff;
20
- for (let step = 1; step < arr.length; step++) {
21
- if (i - step >= 0 && ((arr[i - step] >>> 24) & 0xff) > 0) {
22
- nearestRGB = arr[i - step] & 0x00ffffff;
23
- break;
24
- }
25
- if (i + step < arr.length && ((arr[i + step] >>> 24) & 0xff) > 0) {
26
- nearestRGB = arr[i + step] & 0x00ffffff;
27
- break;
28
- }
29
- }
30
- value = (value & 0xff000000) | nearestRGB;
31
- }
32
- return this.toColorString(value);
33
- }),
16
+ colors: this.props.colors.map((value) => this.toColorString(value)),
34
17
  };
35
18
  },
36
19
  render(ctx, node, renderContext) {
@@ -1 +1 @@
1
- {"version":3,"file":"LinearGradient.js","sourceRoot":"","sources":["../../../../../src/core/shaders/canvas/LinearGradient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LinearGradient.js","sourceRoot":"","sources":["../../../../../src/core/shaders/canvas/LinearGradient.ts"],"names":[],"mappings":"AACA,OAAO,EACL,sBAAsB,GAEvB,MAAM,wCAAwC,CAAC;AAUhD,MAAM,CAAC,MAAM,cAAc,GAGvB;IACF,KAAK,EAAE,sBAAsB,CAAC,KAAK;IACnC,MAAM,CAAC,IAAI;QACT,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;QACvB,MAAM,IAAI,GACR,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YACtC,GAAG,CAAC;QAEN,IAAI,CAAC,QAAQ,GAAG;YACd,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,GAAG;YACzC,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,GAAG;YAC1C,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,GAAG,GAAG;YACnD,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,OAAO,GAAG,GAAG;YACpD,MAAM,EAAE,IAAI,CAAC,KAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;SACrE,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,aAAa;QAC7B,aAAa,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAwC,CAAC;QAC/D,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,eAAgB,CAAC;QACzC,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5B,MAAM,QAAQ,GAAG,GAAG,CAAC,oBAAoB,CACvC,EAAE,GAAG,QAAQ,CAAC,EAAE,EAChB,EAAE,GAAG,QAAQ,CAAC,EAAE,EAChB,EAAE,GAAG,QAAQ,CAAC,EAAE,EAChB,EAAE,GAAG,QAAQ,CAAC,EAAE,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAC,KAAK,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;QAC/C,CAAC;QACD,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC;QACzB,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,CAAC;CACF,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { CoreShaderType } from '../../renderers/CoreShaderNode.js';
2
+ /**
3
+ * Properties of the {@link EdgeFade} shader
4
+ */
5
+ export interface EdgeFadeProps {
6
+ /**
7
+ * Fade distance in pixels from the left edge. Alpha ramps 0 → 1 over this
8
+ * distance. 0 disables the fade for this edge.
9
+ *
10
+ * @default 0
11
+ */
12
+ left: number;
13
+ /**
14
+ * Fade distance in pixels from the top edge.
15
+ *
16
+ * @default 0
17
+ */
18
+ top: number;
19
+ /**
20
+ * Fade distance in pixels from the right edge.
21
+ *
22
+ * @default 0
23
+ */
24
+ right: number;
25
+ /**
26
+ * Fade distance in pixels from the bottom edge.
27
+ *
28
+ * @default 0
29
+ */
30
+ bottom: number;
31
+ }
32
+ export declare const EdgeFadeTemplate: CoreShaderType<EdgeFadeProps>;
@@ -0,0 +1,9 @@
1
+ export const EdgeFadeTemplate = {
2
+ props: {
3
+ left: 0,
4
+ top: 0,
5
+ right: 0,
6
+ bottom: 0,
7
+ },
8
+ };
9
+ //# sourceMappingURL=EdgeFadeTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeFadeTemplate.js","sourceRoot":"","sources":["../../../../../src/core/shaders/templates/EdgeFadeTemplate.ts"],"names":[],"mappings":"AAiCA,MAAM,CAAC,MAAM,gBAAgB,GAAkC;IAC7D,KAAK,EAAE;QACL,IAAI,EAAE,CAAC;QACP,GAAG,EAAE,CAAC;QACN,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC;KACV;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { WebGlShaderType } from '../../renderers/webgl/WebGlShaderNode.js';
2
+ import { type EdgeFadeProps } from '../templates/EdgeFadeTemplate.js';
3
+ /**
4
+ * Multiplies the node's alpha by a linear ramp inward from each edge with a
5
+ * non-zero fade distance, revealing whatever is rendered behind the node.
6
+ * Unlike {@link LinearGradient} this masks the texture's own alpha instead of
7
+ * blending a gradient color over its RGB.
8
+ */
9
+ export declare const EdgeFade: WebGlShaderType<EdgeFadeProps>;
@@ -0,0 +1,74 @@
1
+ import { EdgeFadeTemplate, } from '../templates/EdgeFadeTemplate.js';
2
+ /**
3
+ * Multiplies the node's alpha by a linear ramp inward from each edge with a
4
+ * non-zero fade distance, revealing whatever is rendered behind the node.
5
+ * Unlike {@link LinearGradient} this masks the texture's own alpha instead of
6
+ * blending a gradient color over its RGB.
7
+ */
8
+ export const EdgeFade = {
9
+ props: EdgeFadeTemplate.props,
10
+ update() {
11
+ const props = this.props;
12
+ // Reciprocals are uploaded so the fragment shader needs no division and
13
+ // no zero-guard branch: 1e6 saturates clamp(px * recip) to 1.0 within a
14
+ // fraction of a pixel, which is exactly "no fade on this edge".
15
+ this.uniform4f('u_fadeRecip', props.left > 0 ? 1 / props.left : 1e6, props.top > 0 ? 1 / props.top : 1e6, props.right > 0 ? 1 / props.right : 1e6, props.bottom > 0 ? 1 / props.bottom : 1e6);
16
+ },
17
+ vertex: `
18
+ # ifdef GL_FRAGMENT_PRECISION_HIGH
19
+ precision highp float;
20
+ # else
21
+ precision mediump float;
22
+ # endif
23
+
24
+ attribute vec2 a_position;
25
+ attribute vec2 a_textureCoords;
26
+ attribute vec4 a_color;
27
+ attribute vec2 a_nodeCoords;
28
+
29
+ uniform vec2 u_resolution;
30
+ uniform float u_pixelRatio;
31
+
32
+ varying vec4 v_color;
33
+ varying vec2 v_textureCoords;
34
+ varying vec2 v_nodeCoords;
35
+
36
+ void main() {
37
+ vec2 normalized = a_position * u_pixelRatio / u_resolution;
38
+ vec2 zero_two = normalized * 2.0;
39
+ vec2 clip_space = zero_two - 1.0;
40
+
41
+ v_color = a_color;
42
+ v_textureCoords = a_textureCoords;
43
+ v_nodeCoords = a_nodeCoords;
44
+
45
+ gl_Position = vec4(clip_space * vec2(1.0, -1.0), 0, 1);
46
+ }
47
+ `,
48
+ fragment: `
49
+ # ifdef GL_FRAGMENT_PRECISION_HIGH
50
+ precision highp float;
51
+ # else
52
+ precision mediump float;
53
+ # endif
54
+
55
+ uniform vec2 u_dimensions;
56
+ uniform sampler2D u_texture;
57
+ uniform vec4 u_fadeRecip;
58
+
59
+ varying vec4 v_color;
60
+ varying vec2 v_textureCoords;
61
+ varying vec2 v_nodeCoords;
62
+
63
+ void main() {
64
+ vec4 color = v_color * texture2D(u_texture, v_textureCoords);
65
+ vec2 px = v_nodeCoords * u_dimensions;
66
+ float fade = clamp(px.x * u_fadeRecip.x, 0.0, 1.0)
67
+ * clamp(px.y * u_fadeRecip.y, 0.0, 1.0)
68
+ * clamp((u_dimensions.x - px.x) * u_fadeRecip.z, 0.0, 1.0)
69
+ * clamp((u_dimensions.y - px.y) * u_fadeRecip.w, 0.0, 1.0);
70
+ gl_FragColor = color * fade;
71
+ }
72
+ `,
73
+ };
74
+ //# sourceMappingURL=EdgeFade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EdgeFade.js","sourceRoot":"","sources":["../../../../../src/core/shaders/webgl/EdgeFade.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,GAEjB,MAAM,kCAAkC,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAmC;IACtD,KAAK,EAAE,gBAAgB,CAAC,KAAK;IAC7B,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAC;QAC1B,wEAAwE;QACxE,wEAAwE;QACxE,gEAAgE;QAChE,IAAI,CAAC,SAAS,CACZ,aAAa,EACb,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EACrC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EACnC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EACvC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAC1C,CAAC;IACJ,CAAC;IACD,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BP;IACD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;GAwBT;CACF,CAAC"}
@@ -2,9 +2,29 @@ import { getNormalizedRgbaComponents } from '../../lib/utils.js';
2
2
  import { LinearGradientTemplate, } from '../templates/LinearGradientTemplate.js';
3
3
  export const LinearGradient = {
4
4
  props: LinearGradientTemplate.props,
5
- update() {
5
+ update(node) {
6
6
  const props = this.props;
7
- this.uniform1f('u_angle', props.angle - (Math.PI / 180) * 90);
7
+ // The gradient distance is an affine function of the node-local texture
8
+ // coordinates, so it reduces to `dist = dot(v_textureCoords, a) + b`.
9
+ // `a`/`b` depend only on the angle and node dimensions (both are part of
10
+ // the value-key), so we compute them once on the CPU here instead of
11
+ // recomputing the trig per fragment on the GPU.
12
+ const angle = props.angle - (Math.PI / 180) * 90;
13
+ const c = Math.cos(angle);
14
+ const s = Math.sin(angle);
15
+ const w = node.w;
16
+ const h = node.h;
17
+ const lineDist = Math.abs(w * c) + Math.abs(h * s);
18
+ // Gradient axis (from -> to), gradVec = -lineDist * (c, s)
19
+ const gx = -lineDist * c;
20
+ const gy = -lineDist * s;
21
+ const gg = gx * gx + gy * gy;
22
+ const invGG = gg > 0 ? 1 / gg : 0;
23
+ // Gradient origin: f = lineDist * 0.5 * (c, s) + dimensions * 0.5
24
+ const fx = lineDist * 0.5 * c + w * 0.5;
25
+ const fy = lineDist * 0.5 * s + h * 0.5;
26
+ this.uniform2f('u_grad_a', w * gx * invGG, h * gy * invGG);
27
+ this.uniform1f('u_grad_b', -(fx * gx + fy * gy) * invGG);
8
28
  this.uniform1fv('u_stops', new Float32Array(props.stops));
9
29
  const colors = [];
10
30
  for (let i = 0; i < props.colors.length; i++) {
@@ -16,51 +36,6 @@ export const LinearGradient = {
16
36
  getCacheMarkers(props) {
17
37
  return `colors:${props.colors.length}`;
18
38
  },
19
- vertex: `
20
- # ifdef GL_FRAGMENT_PRECISION_HIGH
21
- precision highp float;
22
- # else
23
- precision mediump float;
24
- # endif
25
-
26
- attribute vec2 a_position;
27
- attribute vec2 a_textureCoords;
28
- attribute vec4 a_color;
29
-
30
- uniform vec2 u_resolution;
31
- uniform float u_pixelRatio;
32
- uniform vec2 u_dimensions;
33
- uniform float u_angle;
34
-
35
- varying vec4 v_color;
36
- varying vec2 v_textureCoords;
37
- varying float v_dist;
38
-
39
- const float PI = 3.14159265359;
40
-
41
- vec2 calcPoint(float d, float angle) {
42
- return d * vec2(cos(angle), sin(angle)) + (u_dimensions * 0.5);
43
- }
44
-
45
- void main() {
46
- vec2 normalized = a_position * u_pixelRatio / u_resolution;
47
- vec2 zero_two = normalized * 2.0;
48
- vec2 clip_space = zero_two - 1.0;
49
-
50
- gl_Position = vec4(clip_space * vec2(1.0, -1.0), 0, 1);
51
-
52
- v_color = a_color;
53
- v_textureCoords = a_textureCoords;
54
-
55
- float a = u_angle;
56
- float lineDist = abs(u_dimensions.x * cos(a)) + abs(u_dimensions.y * sin(a));
57
- vec2 f = calcPoint(lineDist * 0.5, a);
58
- vec2 t = calcPoint(lineDist * 0.5, a + PI);
59
- vec2 gradVec = t - f;
60
- float dist = dot(a_textureCoords * u_dimensions - f, gradVec) / dot(gradVec, gradVec);
61
- v_dist = dist;
62
- }
63
- `,
64
39
  fragment(renderer, props) {
65
40
  return `
66
41
  # ifdef GL_FRAGMENT_PRECISION_HIGH
@@ -72,13 +47,17 @@ export const LinearGradient = {
72
47
  #define MAX_STOPS ${props.colors.length}
73
48
  #define LAST_STOP ${props.colors.length - 1}
74
49
 
50
+ uniform float u_alpha;
51
+
75
52
  uniform sampler2D u_texture;
53
+
54
+ uniform vec2 u_grad_a;
55
+ uniform float u_grad_b;
76
56
  uniform float u_stops[MAX_STOPS];
77
57
  uniform vec4 u_colors[MAX_STOPS];
78
58
 
79
59
  varying vec4 v_color;
80
60
  varying vec2 v_textureCoords;
81
- varying float v_dist;
82
61
 
83
62
  vec4 getGradientColor(float dist) {
84
63
  dist = clamp(dist, 0.0, 1.0);
@@ -104,9 +83,10 @@ export const LinearGradient = {
104
83
 
105
84
  void main() {
106
85
  vec4 color = texture2D(u_texture, v_textureCoords) * v_color;
107
- vec4 colorOut = getGradientColor(v_dist);
108
- vec3 blendedRGB = mix(color.rgb, colorOut.rgb, clamp(colorOut.a, 0.0, 1.0));
109
- gl_FragColor = vec4(blendedRGB, color.a);
86
+ float dist = dot(v_textureCoords, u_grad_a) + u_grad_b;
87
+ vec4 colorOut = getGradientColor(dist);
88
+ color = mix(color, colorOut, clamp(colorOut.a, 0.0, 1.0));
89
+ gl_FragColor = color * u_alpha;
110
90
  }
111
91
  `;
112
92
  },
@@ -1 +1 @@
1
- {"version":3,"file":"LinearGradient.js","sourceRoot":"","sources":["../../../../../src/core/shaders/webgl/LinearGradient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EACL,sBAAsB,GAEvB,MAAM,wCAAwC,CAAC;AAIhD,MAAM,CAAC,MAAM,cAAc,GAAyC;IAClE,KAAK,EAAE,sBAAsB,CAAC,KAAK;IACnC,MAAM;QACJ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAC;QAC1B,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,2BAA2B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;YAC3D,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,eAAe,CAAC,KAA0B;QACxC,OAAO,UAAU,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;IACzC,CAAC;IACD,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CP;IACD,QAAQ,CAAC,QAAuB,EAAE,KAA0B;QAC1D,OAAO;;;;;;;wBAOa,KAAK,CAAC,MAAM,CAAC,MAAM;wBACnB,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsC5C,CAAC;IACF,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"LinearGradient.js","sourceRoot":"","sources":["../../../../../src/core/shaders/webgl/LinearGradient.ts"],"names":[],"mappings":"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"}
@@ -72,8 +72,8 @@ export const RadialGradient = {
72
72
  float dist = length((point - u_projection) / u_size);
73
73
 
74
74
  vec4 colorOut = getGradientColor(dist);
75
- vec3 blendedRGB = mix(color.rgb, colorOut.rgb, clamp(colorOut.a, 0.0, 1.0));
76
- gl_FragColor = vec4(blendedRGB, color.a);
75
+ color = mix(color, colorOut, clamp(colorOut.a, 0.0, 1.0));
76
+ gl_FragColor = color * u_alpha;
77
77
  }
78
78
  `;
79
79
  },
@@ -152,6 +152,13 @@ export const RadialProgress = {
152
152
  layer = fillPM * fillCoverage;
153
153
  #endif
154
154
 
155
+ // Apply node opacity to the introduced ring/track colors. They come
156
+ // from u_colors / u_trackColor and do not carry worldAlpha, so without
157
+ // this a fading RadialProgress node would keep its ring fully opaque.
158
+ // \`base\` already includes worldAlpha (via v_color), so it is left as-is
159
+ // to avoid double-applying. Scaling a premultiplied layer is valid.
160
+ layer *= u_alpha;
161
+
155
162
  // Premultiplied "over": out = src + dst*(1 - src.a). The output stays
156
163
  // visible on a fully-transparent \`base\` because layer brings its own alpha.
157
164
  float la = clamp(layer.a, 0.0, 1.0);
@@ -1 +1 @@
1
- {"version":3,"file":"RadialProgress.js","sourceRoot":"","sources":["../../../../../src/core/shaders/webgl/RadialProgress.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RadialProgress.js","sourceRoot":"","sources":["../../../../../src/core/shaders/webgl/RadialProgress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EACL,sBAAsB,GAEvB,MAAM,wCAAwC,CAAC;AAIhD,MAAM,CAAC,MAAM,cAAc,GAAyC;IAClE,KAAK,EAAE,sBAAsB,CAAC,KAAK;IACnC,IAAI,EAAE,IAAI;IACV,MAAM,CAAC,IAAc;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAC;QAE1B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC;QACtE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;QAE5D,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACvD,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACnC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAE1D,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,2BAA2B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;YAC3D,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,CAAE,EAAE,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;QAEtD,MAAM,SAAS,GAAG,2BAA2B,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChE,IAAI,CAAC,SAAS,CACZ,cAAc,EACd,SAAS,CAAC,CAAC,CAAE,EACb,SAAS,CAAC,CAAC,CAAE,EACb,SAAS,CAAC,CAAC,CAAE,EACb,SAAS,CAAC,CAAC,CAAE,CACd,CAAC;IACJ,CAAC;IACD,eAAe,CAAC,KAA0B;QACxC,OAAO,UAAU,KAAK,CAAC,MAAM,CAAC,MAAM,QAAQ,KAAK,CAAC,GAAG,UACnD,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC/B,EAAE,CAAC;IACL,CAAC;IACD,QAAQ,CAAC,QAAuB,EAAE,KAA0B;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAClD,OAAO;;;;;;;0BAOe,QAAQ;0BACR,QAAQ,GAAG,CAAC;0BACZ,KAAK,CAAC,GAAG;0BACT,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8HnD,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -88,6 +88,7 @@ export interface SdfFont {
88
88
  metrics: FontMetrics;
89
89
  maxCharHeight: number;
90
90
  }
91
+ export declare const MAX_FONT_LOAD_RETRIES = 3;
91
92
  /**
92
93
  * Check if the SDF font handler can render a font
93
94
  * @param {TrProps} trProps - Text rendering properties
@@ -2,6 +2,11 @@ import { UpdateType } from '../CoreNode.js';
2
2
  import { hasZeroWidthSpace } from './Utils.js';
3
3
  import { normalizeFontMetrics } from './TextLayoutEngine.js';
4
4
  import { isProductionEnvironment } from '../../utils.js';
5
+ // Number of times a failed font load is automatically retried before
6
+ // loadFont() finally rejects. Counts reloads *after* the initial attempt
7
+ // (matching the `maxRetryCount` = retries convention used for textures), so
8
+ // the load is attempted up to MAX_FONT_LOAD_RETRIES + 1 times in total.
9
+ export const MAX_FONT_LOAD_RETRIES = 3;
5
10
  //global state variables for SdfFontHandler
6
11
  const fontCache = new Map();
7
12
  const fontLoadPromises = new Map();
@@ -170,44 +175,55 @@ export const loadFont = (stage, options) => {
170
175
  if (atlasDataUrl === undefined) {
171
176
  return Promise.reject(new Error(`Atlas data URL must be provided for SDF font: ${fontFamily}`));
172
177
  }
173
- const nwff = (nodesWaitingForFont[fontFamily] = []);
174
- // Create loading promise
175
- const loadPromise = (async () => {
176
- const fontData = await new Promise((resolve, reject) => {
177
- const xhr = new XMLHttpRequest();
178
- xhr.open('GET', atlasDataUrl, true);
179
- xhr.responseType = 'json';
180
- xhr.onload = () => {
181
- if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) {
182
- let data = xhr.response;
183
- if (typeof data === 'string') {
184
- try {
185
- data = JSON.parse(data);
186
- }
187
- catch (e) {
188
- reject(new Error('Failed to parse font data JSON'));
189
- return;
190
- }
178
+ // Reuse an existing waiter list. A previous load attempt for this font may
179
+ // have failed and left nodes parked here; overwriting the list would strand
180
+ // them, so a successful retry could never wake them. The list is consumed
181
+ // (and deleted) on the next successful load.
182
+ let nwff = nodesWaitingForFont[fontFamily];
183
+ if (nwff === undefined) {
184
+ nwff = nodesWaitingForFont[fontFamily] = [];
185
+ }
186
+ // One attempt at fetching + decoding the JSON atlas description. A fresh
187
+ // XHR runs per attempt so a transient network/parse failure can recover.
188
+ const fetchFontData = () => new Promise((resolve, reject) => {
189
+ const xhr = new XMLHttpRequest();
190
+ xhr.open('GET', atlasDataUrl, true);
191
+ xhr.responseType = 'json';
192
+ xhr.onload = () => {
193
+ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) {
194
+ let data = xhr.response;
195
+ if (typeof data === 'string') {
196
+ try {
197
+ data = JSON.parse(data);
198
+ }
199
+ catch (e) {
200
+ reject(new Error('Failed to parse font data JSON'));
201
+ return;
191
202
  }
192
- resolve(data);
193
- }
194
- else {
195
- reject(new Error(`Failed to load font data: ${xhr.statusText}`));
196
203
  }
197
- };
198
- xhr.onerror = () => {
199
- reject(new Error('Network error occurred while trying to load the font data.'));
200
- };
201
- xhr.send(null);
202
- });
204
+ resolve(data);
205
+ }
206
+ else {
207
+ reject(new Error(`Failed to load font data: ${xhr.statusText}`));
208
+ }
209
+ };
210
+ xhr.onerror = () => {
211
+ reject(new Error('Network error occurred while trying to load the font data.'));
212
+ };
213
+ xhr.send(null);
214
+ });
215
+ // One attempt at loading the atlas texture for the given font data. On
216
+ // success it processes + caches the font and wakes parked nodes. On failure
217
+ // it drops the dead atlas texture (createTexture caches by `src`, so the
218
+ // next attempt must evict it to build a fresh one) and rejects.
219
+ const loadAtlas = (fontData) => {
203
220
  if (!fontData || !fontData.chars) {
204
- throw new Error('Invalid SDF font data format');
221
+ return Promise.reject(new Error('Invalid SDF font data format'));
205
222
  }
206
223
  // Atlas texture should be provided externally
207
224
  if (!atlasUrl) {
208
- throw new Error('Atlas texture must be provided for SDF fonts');
225
+ return Promise.reject(new Error('Atlas texture must be provided for SDF fonts'));
209
226
  }
210
- // Wait for atlas texture to load
211
227
  return new Promise((resolve, reject) => {
212
228
  // create new atlas texture using ImageTexture
213
229
  const atlasTexture = stage.txManager.createTexture('ImageTexture', {
@@ -216,41 +232,62 @@ export const loadFont = (stage, options) => {
216
232
  });
217
233
  atlasTexture.setRenderableOwner(fontFamily, true);
218
234
  atlasTexture.preventCleanup = true; // Prevent automatic cleanup
219
- if (atlasTexture.state === 'loaded') {
220
- // If already loaded, process immediately
221
- processFontData(fontFamily, fontData, atlasTexture, metrics);
222
- fontLoadPromises.delete(fontFamily);
223
- for (let key in nwff) {
224
- nwff[key].setUpdateType(UpdateType.Local);
225
- }
226
- delete nodesWaitingForFont[fontFamily];
227
- return resolve();
228
- }
229
- atlasTexture.on('loaded', () => {
235
+ const onLoaded = () => {
230
236
  // Process and cache font data
231
237
  processFontData(fontFamily, fontData, atlasTexture, metrics);
232
- // remove from promises
233
- fontLoadPromises.delete(fontFamily);
234
238
  for (let key in nwff) {
235
239
  nwff[key].setUpdateType(UpdateType.Local);
236
240
  }
237
241
  delete nodesWaitingForFont[fontFamily];
238
242
  resolve();
239
- });
243
+ };
244
+ if (atlasTexture.state === 'loaded') {
245
+ // If already loaded, process immediately
246
+ onLoaded();
247
+ return;
248
+ }
249
+ atlasTexture.on('loaded', onLoaded);
240
250
  // EventEmitter invokes listeners as (target, data), so the error payload
241
251
  // is the SECOND argument. The first arg is the Texture that emitted the
242
252
  // event. Reading it as the only param (the previous behavior) rejected
243
253
  // and logged the Texture instead of the actual TextureError.
244
254
  atlasTexture.on('failed', (_target, error) => {
245
- // Cleanup on error
246
- fontLoadPromises.delete(fontFamily);
247
- if (fontCache[fontFamily]) {
248
- delete fontCache[fontFamily];
249
- }
250
- console.error(`Failed to load SDF font: ${fontFamily}`, error);
255
+ // Drop the failed atlas so a retry builds a fresh texture rather than
256
+ // getting this dead instance back from the createTexture key-cache.
257
+ atlasTexture.setRenderableOwner(fontFamily, false);
258
+ stage.txManager.removeTextureFromCache(atlasTexture);
251
259
  reject(error);
252
260
  });
253
261
  });
262
+ };
263
+ // Initial attempt plus up to MAX_FONT_LOAD_RETRIES automatic reloads.
264
+ const loadPromise = (async () => {
265
+ let lastError;
266
+ for (let attempt = 0; attempt <= MAX_FONT_LOAD_RETRIES; attempt++) {
267
+ try {
268
+ await loadAtlas(await fetchFontData());
269
+ // Success: clear the in-flight marker (the font now lives in fontCache)
270
+ // — parked nodes were already woken inside loadAtlas.
271
+ fontLoadPromises.delete(fontFamily);
272
+ return;
273
+ }
274
+ catch (error) {
275
+ lastError = error;
276
+ if (attempt < MAX_FONT_LOAD_RETRIES) {
277
+ console.warn(`SDF font "${fontFamily}" failed to load (attempt ${attempt + 1} of ${MAX_FONT_LOAD_RETRIES + 1}), retrying.`, error);
278
+ }
279
+ }
280
+ }
281
+ // Every attempt failed. Clear the in-flight marker so the font can be
282
+ // requested again and drop any partial cache entry. nodesWaitingForFont
283
+ // is deliberately kept: nodes parked here must survive so a later
284
+ // loadFont() (which reuses the list) can still wake them if the font
285
+ // eventually loads. The list shrinks as nodes self-remove via
286
+ // stopWaitingForFont on destroy.
287
+ fontLoadPromises.delete(fontFamily);
288
+ fontCache.delete(fontFamily);
289
+ console.error(`Failed to load SDF font: ${fontFamily}`, lastError);
290
+ throw lastError;
254
291
  })();
255
292
  fontLoadPromises.set(fontFamily, loadPromise);
256
293
  return loadPromise;