@codexo/exojs 0.6.8 → 0.6.9

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/CHANGELOG.md CHANGED
@@ -4,6 +4,74 @@ All notable changes to ExoJS are documented in this file.
4
4
 
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.9] - 2026-05-02
8
+
9
+ > **Heads-up — breaking change despite the patch number.** `Text`'s
10
+ > internal architecture changed completely: glyph-quad meshing
11
+ > against a runtime atlas instead of canvas2d-rasterize-as-Sprite.
12
+ > The user-facing API for `text.text`, `text.style`, and standard
13
+ > Drawable transforms (`position`, `rotation`, `scale`, etc.) is
14
+ > unchanged, but `text.canvas`, `text.setCanvas`, `text.textureFrame`,
15
+ > `text.getWordWrappedText`, and the `Text instanceof Sprite` check
16
+ > are gone. Text is now `Text extends Container`, not Sprite.
17
+
18
+ GPU font glyphs (Pixi-style runtime cache). Replaces the prior
19
+ canvas-rasterize-the-whole-string-as-Sprite path with: rasterize
20
+ each glyph once into a shared atlas Texture, build a single Mesh
21
+ per Text whose quads sample the atlas. All Texts in the page share
22
+ one atlas — memory-efficient at scale, single drawcall per Text.
23
+
24
+ ### Added
25
+
26
+ - **`DynamicGlyphAtlas`** — public class. Constructor takes
27
+ `width = 1024, height = 1024`. Has `getGlyph(char, family, size,
28
+ weight, style) → GlyphInfo` (cached or rasterizes), `clear()` to
29
+ reset, and `texture` for binding to a Mesh. Internal shelf
30
+ bin-packing; throws on atlas-full (LRU eviction is V2).
31
+ - **`layoutText(text, style, atlas)`** — pure function. Returns
32
+ `readonly GlyphPlacement[]` with one quad per visible glyph.
33
+ Handles `\n` line breaks and `align: 'left' | 'center' | 'right'`
34
+ alignment per `style.align`. Empty text returns `[]`.
35
+ - **Types: `GlyphInfo`, `GlyphPlacement`, `GlyphKey`,
36
+ `TextAlignment`** — all exported for users who want to compose
37
+ their own atlas / layout pipelines.
38
+ - **TextStyle gets `fillColor: Color`** (defaults to white, used
39
+ via mesh.tint after glyph rasterization), **`fontStyle: 'normal'
40
+ | 'italic'`**, and **`lineHeight: number`** (multiplied by
41
+ fontSize for line spacing, defaults to 1.2). `align` field is
42
+ now strongly typed as `TextAlignment`.
43
+
44
+ ### Changed
45
+
46
+ - **`Text` extends `Container`** (was `Sprite`). It internally
47
+ manages a single `Mesh` child whose vertices/uvs/indices are
48
+ rebuilt on every `text` / `style` setter call. Empty string =
49
+ no internal mesh (no children).
50
+ - **Glyphs always rasterize white**; `style.fillColor` becomes
51
+ `mesh.tint`. Changing fillColor is cheap (mesh-tint update only,
52
+ no atlas re-rasterization).
53
+
54
+ ### Removed
55
+
56
+ - `Text.canvas` getter / setter, `Text.setCanvas(...)`,
57
+ `Text.textureFrame`, `Text.updateTexture(...)`,
58
+ `Text.getWordWrappedText(...)` — the old canvas2d path is gone.
59
+ Word-wrapping is V2; for now use `\n` for explicit line breaks.
60
+
61
+ ### Notes
62
+
63
+ - Atlas is a process-wide singleton via `getDefaultGlyphAtlas()`
64
+ (internal helper, not a public function). All `Text` instances
65
+ share one atlas. Tests can reset it via `atlas.clear()`.
66
+ - The atlas uses `OffscreenCanvas` when available, falls back to
67
+ `document.createElement('canvas')` (works in jsdom / older
68
+ browsers).
69
+ - First-render of a never-seen glyph costs one canvas2d round-trip
70
+ + texture re-upload. Cached glyphs are zero-cost on subsequent
71
+ renders.
72
+ - Per-character animation, MSDF rendering, word-wrap, BiDi, and
73
+ text outlines / drop-shadows are all V2.
74
+
7
75
  ## [0.6.8] - 2026-05-02
8
76
 
9
77
  > **Heads-up — breaking change despite the patch number.** Removes
package/dist/esm/index.js CHANGED
@@ -70,7 +70,9 @@ export { ShaderUniform } from './rendering/shader/ShaderUniform.js';
70
70
  export { Sprite, SpriteFlags } from './rendering/sprite/Sprite.js';
71
71
  export { Spritesheet } from './rendering/sprite/Spritesheet.js';
72
72
  export { AnimatedSprite } from './rendering/sprite/AnimatedSprite.js';
73
+ export { DynamicGlyphAtlas } from './rendering/text/DynamicGlyphAtlas.js';
73
74
  export { Text } from './rendering/text/Text.js';
75
+ export { layoutText } from './rendering/text/TextLayout.js';
74
76
  export { TextStyle } from './rendering/text/TextStyle.js';
75
77
  export { RenderTexture } from './rendering/texture/RenderTexture.js';
76
78
  export { Sampler } from './rendering/texture/Sampler.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -6,8 +6,11 @@ export * from './shader/ShaderUniform';
6
6
  export * from './sprite/Sprite';
7
7
  export * from './sprite/Spritesheet';
8
8
  export * from './sprite/AnimatedSprite';
9
+ export * from './text/DynamicGlyphAtlas';
9
10
  export * from './text/Text';
11
+ export * from './text/TextLayout';
10
12
  export * from './text/TextStyle';
13
+ export * from './text/types';
11
14
  export * from './texture/RenderTexture';
12
15
  export * from './texture/Sampler';
13
16
  export * from './texture/Texture';
@@ -0,0 +1,33 @@
1
+ import { Texture } from '@/rendering/texture/Texture';
2
+ import type { GlyphInfo } from './types';
3
+ /**
4
+ * A shared atlas that rasterizes glyphs on demand into an offscreen canvas
5
+ * and wraps it as a Texture for use by the Mesh-based Text renderer.
6
+ *
7
+ * Glyphs are always rasterized in white so that runtime tinting via
8
+ * `Mesh.tint` applies the fill color without requiring re-rasterization.
9
+ *
10
+ * Use `getDefaultGlyphAtlas()` from `atlas-singleton.ts` rather than
11
+ * constructing directly.
12
+ */
13
+ export declare class DynamicGlyphAtlas {
14
+ readonly texture: Texture;
15
+ private readonly _canvas;
16
+ private readonly _ctx;
17
+ private readonly _packer;
18
+ private readonly _cache;
19
+ private readonly _width;
20
+ private readonly _height;
21
+ constructor(width?: number, height?: number);
22
+ /**
23
+ * Returns the cached GlyphInfo for the given character + font parameters,
24
+ * rasterizing it into the atlas if not already present.
25
+ */
26
+ getGlyph(char: string, family: string, size: number, weight: string | number, style: 'normal' | 'italic'): GlyphInfo;
27
+ /**
28
+ * Clears all cached glyphs and resets the atlas packer.
29
+ * The underlying canvas pixels are also cleared.
30
+ */
31
+ clear(): void;
32
+ private _rasterize;
33
+ }
@@ -0,0 +1,140 @@
1
+ import { Texture } from '../texture/Texture.js';
2
+
3
+ const glyphPadding = 2;
4
+ class ShelfPacker {
5
+ _shelves = [];
6
+ _width;
7
+ _height;
8
+ constructor(width, height) {
9
+ this._width = width;
10
+ this._height = height;
11
+ }
12
+ insert(width, height) {
13
+ // Try existing shelves in order (ascending y)
14
+ for (const shelf of this._shelves) {
15
+ if (shelf.height >= height && shelf.cursorX + width <= this._width) {
16
+ const x = shelf.cursorX;
17
+ shelf.cursorX += width;
18
+ return { x, y: shelf.y };
19
+ }
20
+ }
21
+ // Create a new shelf at the bottom
22
+ const last = this._shelves[this._shelves.length - 1];
23
+ const bottomY = last === undefined ? 0 : last.y + last.height;
24
+ if (bottomY + height > this._height) {
25
+ throw new Error(`GlyphAtlas full — clear() and re-render, or instantiate with larger dims`);
26
+ }
27
+ this._shelves.push({ y: bottomY, height, cursorX: width });
28
+ return { x: 0, y: bottomY };
29
+ }
30
+ reset() {
31
+ this._shelves.length = 0;
32
+ }
33
+ }
34
+ /**
35
+ * A shared atlas that rasterizes glyphs on demand into an offscreen canvas
36
+ * and wraps it as a Texture for use by the Mesh-based Text renderer.
37
+ *
38
+ * Glyphs are always rasterized in white so that runtime tinting via
39
+ * `Mesh.tint` applies the fill color without requiring re-rasterization.
40
+ *
41
+ * Use `getDefaultGlyphAtlas()` from `atlas-singleton.ts` rather than
42
+ * constructing directly.
43
+ */
44
+ class DynamicGlyphAtlas {
45
+ texture;
46
+ _canvas;
47
+ _ctx;
48
+ _packer;
49
+ _cache = new Map();
50
+ _width;
51
+ _height;
52
+ constructor(width = 1024, height = 1024) {
53
+ this._width = width;
54
+ this._height = height;
55
+ // Use OffscreenCanvas when available, fall back to HTMLCanvasElement.
56
+ // In jsdom / Node the global may be absent; createCanvas falls through
57
+ // to document.createElement which jsdom provides.
58
+ const canvas = typeof OffscreenCanvas !== 'undefined'
59
+ ? new OffscreenCanvas(width, height)
60
+ : document.createElement('canvas');
61
+ if ('width' in canvas) {
62
+ canvas.width = width;
63
+ canvas.height = height;
64
+ }
65
+ this._canvas = canvas;
66
+ const ctx = canvas.getContext('2d');
67
+ if (ctx === null) {
68
+ throw new Error('DynamicGlyphAtlas: could not obtain a 2D context.');
69
+ }
70
+ this._ctx = ctx;
71
+ this._packer = new ShelfPacker(width, height);
72
+ this.texture = new Texture(canvas);
73
+ this.texture.setSize(width, height);
74
+ }
75
+ /**
76
+ * Returns the cached GlyphInfo for the given character + font parameters,
77
+ * rasterizing it into the atlas if not already present.
78
+ */
79
+ getGlyph(char, family, size, weight, style) {
80
+ const key = `${char}:${family}:${size}:${weight}:${style}`;
81
+ const cached = this._cache.get(key);
82
+ if (cached !== undefined) {
83
+ return cached;
84
+ }
85
+ const info = this._rasterize(char, family, size, weight, style, key);
86
+ this._cache.set(key, info);
87
+ // Bump texture version so GPU backends re-upload the canvas data.
88
+ this.texture.updateSource();
89
+ return info;
90
+ }
91
+ /**
92
+ * Clears all cached glyphs and resets the atlas packer.
93
+ * The underlying canvas pixels are also cleared.
94
+ */
95
+ clear() {
96
+ this._cache.clear();
97
+ this._packer.reset();
98
+ this._ctx.clearRect(0, 0, this._width, this._height);
99
+ this.texture.updateSource();
100
+ }
101
+ // -----------------------------------------------------------------------
102
+ _rasterize(char, family, size, weight, fontStyle, _key) {
103
+ const ctx = this._ctx;
104
+ const padding = glyphPadding;
105
+ ctx.font = `${fontStyle} ${weight} ${size}px ${family}`;
106
+ ctx.textBaseline = 'alphabetic';
107
+ ctx.fillStyle = '#ffffff';
108
+ const metrics = ctx.measureText(char);
109
+ const ascent = Math.ceil(metrics.fontBoundingBoxAscent
110
+ ?? metrics.actualBoundingBoxAscent
111
+ ?? size * 0.8);
112
+ const descent = Math.ceil(metrics.fontBoundingBoxDescent
113
+ ?? metrics.actualBoundingBoxDescent
114
+ ?? size * 0.2);
115
+ const advance = metrics.width;
116
+ const glyphWidth = Math.max(1, Math.ceil((metrics.actualBoundingBoxLeft ?? 0) + (metrics.actualBoundingBoxRight ?? 0)) || Math.ceil(advance));
117
+ const glyphHeight = Math.max(1, ascent + descent);
118
+ const slotW = glyphWidth + padding * 2;
119
+ const slotH = glyphHeight + padding * 2;
120
+ const slot = this._packer.insert(slotW, slotH);
121
+ // Draw the glyph white into the atlas slot
122
+ ctx.fillText(char, slot.x + padding + (metrics.actualBoundingBoxLeft ?? 0), slot.y + padding + ascent);
123
+ const info = {
124
+ x: slot.x,
125
+ y: slot.y,
126
+ width: glyphWidth,
127
+ height: glyphHeight,
128
+ advance,
129
+ ascent,
130
+ uvLeft: slot.x / this._width,
131
+ uvTop: slot.y / this._height,
132
+ uvRight: (slot.x + slotW) / this._width,
133
+ uvBottom: (slot.y + slotH) / this._height,
134
+ };
135
+ return info;
136
+ }
137
+ }
138
+
139
+ export { DynamicGlyphAtlas };
140
+ //# sourceMappingURL=DynamicGlyphAtlas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DynamicGlyphAtlas.js","sources":["../../../../../src/rendering/text/DynamicGlyphAtlas.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -1,26 +1,30 @@
1
- import { Sprite } from '@/rendering/sprite/Sprite';
1
+ import { Container } from '@/rendering/Container';
2
2
  import type { TextStyleOptions } from './TextStyle';
3
3
  import { TextStyle } from './TextStyle';
4
- import type { SamplerOptions } from '@/rendering/texture/Sampler';
5
- import type { RenderBackend } from '../RenderBackend';
6
- export declare class Text extends Sprite {
4
+ /**
5
+ * GPU-accelerated text node that rasterizes individual glyphs into a shared
6
+ * atlas ({@link DynamicGlyphAtlas}) and renders them as a single quad-per-
7
+ * glyph {@link Mesh} (one draw call per Text instance).
8
+ *
9
+ * Glyphs are always rasterized in white and tinted at runtime via
10
+ * `Mesh.tint`; changing `style.fillColor` only updates the mesh tint —
11
+ * no atlas re-rasterization is needed.
12
+ *
13
+ * The internal {@link Mesh} is the sole child of this {@link Container}.
14
+ * All transform properties (position, rotation, scale, origin) are
15
+ * inherited from {@link Container} → {@link RenderNode}.
16
+ */
17
+ export declare class Text extends Container {
7
18
  private _text;
8
19
  private _style;
9
- private _canvas;
10
- private _context;
11
- private _dirty;
12
- constructor(text: string, style?: TextStyle | TextStyleOptions, samplerOptions?: Partial<SamplerOptions>, canvas?: HTMLCanvasElement);
20
+ private _mesh;
21
+ constructor(text: string, style?: TextStyle | TextStyleOptions);
13
22
  get text(): string;
14
- set text(text: string);
23
+ set text(value: string);
15
24
  get style(): TextStyle;
16
- set style(style: TextStyle);
17
- get canvas(): HTMLCanvasElement;
18
- set canvas(canvas: HTMLCanvasElement);
25
+ set style(style: TextStyle | TextStyleOptions);
19
26
  setText(text: string): this;
20
27
  setStyle(style: TextStyle | TextStyleOptions): this;
21
- setCanvas(canvas: HTMLCanvasElement): this;
22
- updateTexture(): this;
23
- getWordWrappedText(): string;
24
- render(backend: RenderBackend): this;
25
- private _getContext;
28
+ destroy(): void;
29
+ private _rebuild;
26
30
  }
@@ -1,29 +1,84 @@
1
- import { Sprite } from '../sprite/Sprite.js';
2
- import { Texture } from '../texture/Texture.js';
1
+ import { Container } from '../Container.js';
2
+ import { Mesh } from '../mesh/Mesh.js';
3
+ import { getDefaultGlyphAtlas } from './atlas-singleton.js';
4
+ import { layoutText } from './TextLayout.js';
3
5
  import { TextStyle } from './TextStyle.js';
4
- import { Rectangle } from '../../math/Rectangle.js';
5
- import { determineFontHeight } from '../utils.js';
6
6
 
7
- const newLinePattern = /(?:\r\n|\r|\n)/;
8
- class Text extends Sprite {
7
+ function buildMesh(placements, style) {
8
+ const n = placements.length;
9
+ const vertices = new Float32Array(n * 4 * 2);
10
+ const uvs = new Float32Array(n * 4 * 2);
11
+ const indices = new Uint16Array(n * 6);
12
+ for (let i = 0; i < n; i++) {
13
+ const p = placements[i];
14
+ const v = i * 8;
15
+ const u = i * 8;
16
+ const idx = i * 6;
17
+ const baseV = i * 4;
18
+ // Vertices: TL, TR, BR, BL
19
+ vertices[v + 0] = p.x;
20
+ vertices[v + 1] = p.y;
21
+ vertices[v + 2] = p.x + p.width;
22
+ vertices[v + 3] = p.y;
23
+ vertices[v + 4] = p.x + p.width;
24
+ vertices[v + 5] = p.y + p.height;
25
+ vertices[v + 6] = p.x;
26
+ vertices[v + 7] = p.y + p.height;
27
+ // UVs: TL, TR, BR, BL
28
+ uvs[u + 0] = p.uvLeft;
29
+ uvs[u + 1] = p.uvTop;
30
+ uvs[u + 2] = p.uvRight;
31
+ uvs[u + 3] = p.uvTop;
32
+ uvs[u + 4] = p.uvRight;
33
+ uvs[u + 5] = p.uvBottom;
34
+ uvs[u + 6] = p.uvLeft;
35
+ uvs[u + 7] = p.uvBottom;
36
+ // Indices: [TL, TR, BR, TL, BR, BL]
37
+ indices[idx + 0] = baseV + 0;
38
+ indices[idx + 1] = baseV + 1;
39
+ indices[idx + 2] = baseV + 2;
40
+ indices[idx + 3] = baseV + 0;
41
+ indices[idx + 4] = baseV + 2;
42
+ indices[idx + 5] = baseV + 3;
43
+ }
44
+ const atlas = getDefaultGlyphAtlas();
45
+ const mesh = new Mesh({
46
+ vertices,
47
+ uvs,
48
+ indices,
49
+ texture: atlas.texture,
50
+ });
51
+ mesh.tint = style.fillColor;
52
+ return mesh;
53
+ }
54
+ /**
55
+ * GPU-accelerated text node that rasterizes individual glyphs into a shared
56
+ * atlas ({@link DynamicGlyphAtlas}) and renders them as a single quad-per-
57
+ * glyph {@link Mesh} (one draw call per Text instance).
58
+ *
59
+ * Glyphs are always rasterized in white and tinted at runtime via
60
+ * `Mesh.tint`; changing `style.fillColor` only updates the mesh tint —
61
+ * no atlas re-rasterization is needed.
62
+ *
63
+ * The internal {@link Mesh} is the sole child of this {@link Container}.
64
+ * All transform properties (position, rotation, scale, origin) are
65
+ * inherited from {@link Container} → {@link RenderNode}.
66
+ */
67
+ class Text extends Container {
9
68
  _text;
10
69
  _style;
11
- _canvas;
12
- _context;
13
- _dirty = true;
14
- constructor(text, style, samplerOptions, canvas = document.createElement('canvas')) {
15
- super(new Texture(canvas, samplerOptions));
70
+ _mesh = null;
71
+ constructor(text, style) {
72
+ super();
16
73
  this._text = text;
17
74
  this._style = (style && style instanceof TextStyle) ? style : new TextStyle(style);
18
- this._canvas = canvas;
19
- this._context = canvas.getContext('2d');
20
- this.updateTexture();
75
+ this._rebuild();
21
76
  }
22
77
  get text() {
23
78
  return this._text;
24
79
  }
25
- set text(text) {
26
- this.setText(text);
80
+ set text(value) {
81
+ this.setText(value);
27
82
  }
28
83
  get style() {
29
84
  return this._style;
@@ -31,98 +86,43 @@ class Text extends Sprite {
31
86
  set style(style) {
32
87
  this.setStyle(style);
33
88
  }
34
- get canvas() {
35
- return this._canvas;
36
- }
37
- set canvas(canvas) {
38
- this.setCanvas(canvas);
39
- }
40
89
  setText(text) {
41
90
  if (this._text !== text) {
42
91
  this._text = text;
43
- this._dirty = true;
92
+ this._rebuild();
44
93
  }
45
94
  return this;
46
95
  }
47
96
  setStyle(style) {
48
97
  this._style = (style instanceof TextStyle) ? style : new TextStyle(style);
49
- this._dirty = true;
98
+ this._rebuild();
50
99
  return this;
51
100
  }
52
- setCanvas(canvas) {
53
- if (this._canvas !== canvas) {
54
- this._canvas = canvas;
55
- this._context = this._getContext(canvas);
56
- this._dirty = true;
57
- this.texture.setSource.call(this.texture, canvas);
58
- this.setTextureFrame(Rectangle.temp.set(0, 0, canvas.width, canvas.height));
101
+ destroy() {
102
+ if (this._mesh !== null) {
103
+ this._mesh.destroy();
104
+ this._mesh = null;
59
105
  }
60
- return this;
106
+ super.destroy();
61
107
  }
62
- updateTexture() {
63
- if (this._style && (this._dirty || this._style.dirty)) {
64
- const canvas = this._canvas, context = this._context, style = this._style.apply(context), text = style.wordWrap ? this.getWordWrappedText() : this._text, lineHeight = determineFontHeight(context.font) + style.strokeThickness, lines = text.split(newLinePattern), lineMetrics = lines.map((line) => context.measureText(line)), maxLineWidth = lineMetrics.reduce((max, measure) => Math.max(max, measure.width), 0), canvasWidth = Math.ceil((maxLineWidth + style.strokeThickness) + (style.padding * 2)), canvasHeight = Math.ceil((lineHeight * lines.length) + (style.padding * 2));
65
- if (canvasWidth !== canvas.width || canvasHeight !== canvas.height) {
66
- canvas.width = canvasWidth;
67
- canvas.height = canvasHeight;
68
- this.setTextureFrame(Rectangle.temp.set(0, 0, canvasWidth, canvasHeight));
69
- }
70
- else {
71
- context.clearRect(0, 0, canvasWidth, canvasHeight);
72
- }
73
- style.apply(context);
74
- for (let i = 0; i < lines.length; i++) {
75
- const metrics = lineMetrics[i], lineWidth = (maxLineWidth - metrics.width), offset = (style.align === 'right') ? lineWidth : lineWidth / 2, padding = style.padding + (style.strokeThickness / 2), lineX = metrics.actualBoundingBoxLeft + (style.align === 'left' ? 0 : offset) + padding, lineY = metrics.actualBoundingBoxAscent + (lineHeight * i) + padding;
76
- if (style.stroke && style.strokeThickness) {
77
- context.strokeText(lines[i], lineX, lineY);
78
- }
79
- if (style.fill) {
80
- context.fillText(lines[i], lineX, lineY);
81
- }
82
- }
83
- this.texture.updateSource();
84
- this._dirty = false;
85
- this._style.dirty = false;
108
+ // -----------------------------------------------------------------------
109
+ _rebuild() {
110
+ // Remove and discard the old mesh (if any).
111
+ if (this._mesh !== null) {
112
+ this.removeChild(this._mesh);
113
+ this._mesh.destroy();
114
+ this._mesh = null;
86
115
  }
87
- return this;
88
- }
89
- getWordWrappedText() {
90
- const context = this._context, wrapWidth = this._style.wordWrapWidth, lines = this._text.split('\n'), spaceWidth = context.measureText(' ').width;
91
- let spaceLeft = wrapWidth, result = '';
92
- for (let y = 0; y < lines.length; y++) {
93
- const words = lines[y].split(' ');
94
- if (y > 0) {
95
- result += '\n';
96
- }
97
- for (let x = 0; x < words.length; x++) {
98
- const word = words[x], wordWidth = context.measureText(word).width, pairWidth = wordWidth + spaceWidth;
99
- if (pairWidth > spaceLeft) {
100
- if (x > 0) {
101
- result += '\n';
102
- }
103
- spaceLeft = wrapWidth;
104
- }
105
- else {
106
- spaceLeft -= pairWidth;
107
- }
108
- result += `${word} `;
109
- }
110
- }
111
- return result;
112
- }
113
- render(backend) {
114
- if (this.visible) {
115
- this.updateTexture();
116
- super.render(backend);
116
+ if (this._text.length === 0) {
117
+ return;
117
118
  }
118
- return this;
119
- }
120
- _getContext(canvas) {
121
- const context = canvas.getContext('2d');
122
- if (context === null) {
123
- throw new Error('Could not create a 2D canvas context.');
119
+ const atlas = getDefaultGlyphAtlas();
120
+ const placements = layoutText(this._text, this._style, atlas);
121
+ if (placements.length === 0) {
122
+ return;
124
123
  }
125
- return context;
124
+ this._mesh = buildMesh(placements, this._style);
125
+ this.addChild(this._mesh);
126
126
  }
127
127
  }
128
128
 
@@ -1 +1 @@
1
- {"version":3,"file":"Text.js","sources":["../../../../../src/rendering/text/Text.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Text.js","sources":["../../../../../src/rendering/text/Text.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { DynamicGlyphAtlas } from './DynamicGlyphAtlas';
2
+ import type { GlyphPlacement } from './types';
3
+ import type { TextStyle } from './TextStyle';
4
+ /**
5
+ * Computes per-glyph quad placements for the given text and style.
6
+ *
7
+ * Handles `\n` line breaks and left/center/right alignment. No word-wrap,
8
+ * no RTL, no ligature shaping — Unicode/diacritics are delegated to the
9
+ * browser's font engine via canvas `fillText`.
10
+ *
11
+ * Returns an empty array for empty text.
12
+ */
13
+ export declare function layoutText(text: string, style: TextStyle, atlas: DynamicGlyphAtlas): ReadonlyArray<GlyphPlacement>;