Skip to content

[WIP] Text engine Refactor #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 41 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
513e1cf
refactor: Implement function Canvas Text Renderer & Font Handler
wouterlucas Jun 7, 2025
3b12b90
refactor: faster font resolver and remove some deadcode
wouterlucas Jun 10, 2025
9cdcced
refactor: remove fontWeight and fontStretch properties from CoreTextN…
wouterlucas Jun 10, 2025
5cb6d56
refactor: streamline text properties initialization in CoreTextNode a…
wouterlucas Jun 10, 2025
77c33ad
refactor: simplify font handling by removing TrFontFace and WebTrFont…
wouterlucas Jun 10, 2025
7711e05
refactor: remove unused font handling functions and streamline FontHa…
wouterlucas Jun 10, 2025
52711df
feat: Introduce new text rendering system for canvas support
wouterlucas Jun 11, 2025
6226be7
feat: first cut of the simplified MSDF Font handler
wouterlucas Jun 20, 2025
be396af
refactor: WIP MSDF text
wouterlucas Jun 27, 2025
5d2717f
chore: move SimpleFontShaper to sdf
wouterlucas Jun 27, 2025
5169231
refactor: streamline text rendering logic and improve SDF handling
wouterlucas Jun 28, 2025
1b1ade5
feat: implement new font loading API with auto-detection and renderer…
wouterlucas Jun 28, 2025
f460dd0
feat: unify font loading API with explicit renderer type specificatio…
wouterlucas Jul 5, 2025
abfe88c
refactor: update CanvasTextRenderer and SdfTextRenderer exports
wouterlucas Jul 5, 2025
87a19b1
fix: make examples work
wouterlucas Jul 5, 2025
090d34d
fix: single font engine hot path
wouterlucas Jul 5, 2025
ccb3a3c
refactor: update text layout types for improved type safety and perfo…
wouterlucas Jul 5, 2025
2a8f3ac
fix: SDF font handling & CoreTextNode updates
wouterlucas Jul 8, 2025
5aecbc3
added some micro optimisations
jfboeve Jul 10, 2025
fe5ac3b
added some micro optimisations (#611)
wouterlucas Jul 10, 2025
3047fc7
fix: correct font handler reference and improve font loading logic
wouterlucas Jul 11, 2025
9a2bc0a
refactor: update text renderer initialization
wouterlucas Jul 11, 2025
97c73c2
refactor: optimize SDF rendering and improve text layout calculations
wouterlucas Jul 12, 2025
ede122b
refactor: streamline text layout calculations and improve comments fo…
wouterlucas Jul 12, 2025
9c91c4c
performance: make render text sync instead of async
wouterlucas Jul 12, 2025
cdf927c
fix: update dimensions correctly and trigger ownership if in bounds
wouterlucas Jul 13, 2025
6382057
detached color parser from canvas renderer to lib
jfboeve Jul 15, 2025
9519833
reduced object creation canvas textrenderer, merged settings + render…
jfboeve Jul 15, 2025
36be055
changed canvas text renderer function
jfboeve Jul 15, 2025
07493ee
fix: correct render state check for texture loading
wouterlucas Jul 15, 2025
0b5cf09
fixed rendering issue canvas text
jfboeve Jul 16, 2025
3cb0ac3
remove willReadFrequently setting from canvas renderer
jfboeve Jul 16, 2025
79ed274
perf: performance changes to the canvas renderer
wouterlucas Jul 16, 2025
d522b70
perf: performance changes to the canvas text renderer (#613)
jfboeve Jul 17, 2025
475f6a0
fix: clear vertex and layout cache on text change, reset dimension to…
wouterlucas Jul 17, 2025
c4cd475
introduce maxWidth / maxHeight
jfboeve Jul 18, 2025
aa2aa9f
fix: text contain and align for SDF
wouterlucas Jul 18, 2025
ca39325
chore: move fns out of SdfTextRenderer to Utils. Add test cases and m…
wouterlucas Jul 18, 2025
b63a870
chore: cleanup unused old files
wouterlucas Jul 18, 2025
fb9cb8f
fix: pass textRendererOverride to resolveTextRenderer in Stage class
wouterlucas Jul 19, 2025
631feb9
fix canvas texture dimensions
jfboeve Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 13 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,7 @@ and SDF fonts (SdfTrFontFace). Install that fonts that your applications needs
at start up so they are ready when your application is rendered.

```ts
import {
RendererMain,
WebTrFontFace,
SdfTrFontFace,
} from '@lightningjs/renderer';
import { RendererMain } from '@lightningjs/renderer';

import {
WebGlCoreRenderer,
Expand All @@ -121,27 +117,17 @@ const renderer = new RendererMain(
'app', // id of div to insert Canvas.
);

// Load fonts into renderer
renderer.stage.fontManager.addFontFace(
new WebTrFontFace('Ubuntu', {}, '/fonts/Ubuntu-Regular.ttf'),
);

renderer.stage.fontManager.addFontFace(
new SdfTrFontFace(
'Ubuntu',
{},
'msdf',
stage,
'/fonts/Ubuntu-Regular.msdf.png',
'/fonts/Ubuntu-Regular.msdf.json',
),
);
// Load fonts by explicitly specifying the renderer type
await stage.loadFont('canvas', {
fontFamily: 'myWebFont',
fontUrl: '/fonts/my-font.ttf',
});

await stage.loadFont('sdf', {
fontFamily: 'mySdfFont',
atlasUrl: '/fonts/my-font-atlas.png',
atlasDataUrl: '/fonts/my-font-data.json',
});
```

Please note that the WebGL renderer supports both SDF Fonts and Web Fonts, however the
Canvas renderer only supports Web Fonts:

| Font Type Renderer | SDF Font | Web Font |
| ------------------ | -------- | -------- |
| WebGL | Y | Y |
| Canvas | N | Y |
For more information see [Font Loading](./docs/fontLoading.md)
66 changes: 66 additions & 0 deletions docs/fontLoading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Font Loading API Example

This document demonstrates how to use the new font loading API on the Stage.

## Basic Usage

```typescript
// Load Canvas fonts by explicitly specifying the renderer type
await stage.loadFont('canvas', {
fontFamily: 'myCoolFont',
fontUrl: '/fonts/my-font.ttf',
});

// Load SDF fonts by explicitly specifying the renderer type
await stage.loadFont('sdf', {
fontFamily: 'myCoolFont',
atlasUrl: '/fonts/my-font-atlas.png',
atlasDataUrl: '/fonts/my-font-data.json',
});

// Load fonts with custom metrics
await stage.loadFont('canvas', {
fontFamily: 'MyFontWithMetrics',
fontUrl: '/fonts/my-font.ttf',
metrics: {
ascender: 800,
descender: -200,
lineGap: 0,
unitsPerEm: 1000,
},
});
```

## API Requirements

The `loadFont` method requires explicit renderer type specification:

- **`rendererType`** (required): 'canvas' or 'sdf'
- **`options`** (required): Font loading options specific to the renderer type

### Canvas Font Options

- `fontFamily`: Name of the font family
- `fontUrl`: URL to the font file (.ttf, .woff, .woff2, etc.)
- `metrics`: Optional font metrics for layout calculations

### SDF Font Options

- `fontFamily`: Name of the font family
- `atlasUrl`: URL to the SDF atlas image (.png)
- `atlasDataUrl`: URL to the SDF glyph data (.json)
- `metrics`: Optional font metrics for layout calculations

## Performance Considerations

**SDF Fonts**: Better performance for scaled text and effects, but require pre-generated atlas files.

**Canvas Fonts**: Universal compatibility with any web font format, but may have lower performance for complex scaling.

Please note that the WebGL renderer supports both SDF Fonts and Web Fonts, however the
Canvas renderer only supports Web Fonts:

| Font Type Renderer | SDF Font | Web Font |
| ------------------ | -------- | -------- |
| WebGL | Y | Y |
| Canvas | N | Y |
169 changes: 69 additions & 100 deletions examples/common/installFonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,120 +17,89 @@
* limitations under the License.
*/

import {
WebTrFontFace,
type Stage,
SdfTrFontFace,
type FontMetrics,
} from '@lightningjs/renderer';
import { type Stage } from '@lightningjs/renderer';

export function installFonts(stage: Stage) {
stage.fontManager.addFontFace(
new WebTrFontFace({
fontFamily: 'NotoSans',
descriptors: {},
fontUrl: './fonts/NotoSans-Regular.ttf',
metrics: {
ascender: 1069,
descender: -293,
lineGap: 0,
unitsPerEm: 1000,
},
}),
);
export async function installFonts(stage: Stage) {
// Load Canvas fonts using the new unified API
stage.loadFont('canvas', {
fontFamily: 'NotoSans',
fontUrl: './fonts/NotoSans-Regular.ttf',
metrics: {
ascender: 1069,
descender: -293,
lineGap: 0,
unitsPerEm: 1000,
},
});

stage.fontManager.addFontFace(
new WebTrFontFace({
fontFamily: 'Ubuntu',
descriptors: {},
fontUrl: './fonts/Ubuntu-Regular.ttf',
metrics: {
ascender: 776,
descender: -185,
lineGap: 56,
unitsPerEm: 1000,
},
}),
);
await stage.loadFont('canvas', {
fontFamily: 'Ubuntu',
fontUrl: './fonts/Ubuntu-Regular.ttf',
metrics: {
ascender: 776,
descender: -185,
lineGap: 56,
unitsPerEm: 1000,
},
});

stage.fontManager.addFontFace(
new WebTrFontFace({
fontFamily: 'Ubuntu-No-Metrics',
descriptors: {},
fontUrl: './fonts/Ubuntu-Regular.ttf',
}),
);
await stage.loadFont('canvas', {
fontFamily: 'Ubuntu-No-Metrics',
fontUrl: './fonts/Ubuntu-Regular.ttf',
});

const ubuntuModifiedMetrics: FontMetrics = {
const ubuntuModifiedMetrics = {
ascender: 850,
descender: -250,
lineGap: 60,
unitsPerEm: 1000,
};

stage.fontManager.addFontFace(
new WebTrFontFace({
fontFamily: 'Ubuntu-Modified-Metrics',
descriptors: {},
fontUrl: './fonts/Ubuntu-Regular.ttf',
metrics: ubuntuModifiedMetrics,
}),
);
await stage.loadFont('canvas', {
fontFamily: 'Ubuntu-Modified-Metrics',
fontUrl: './fonts/Ubuntu-Regular.ttf',
metrics: ubuntuModifiedMetrics,
});

// Load SDF fonts for WebGL renderer using the new unified API
if (stage.renderer.mode === 'webgl') {
stage.fontManager.addFontFace(
new SdfTrFontFace('ssdf', {
fontFamily: 'NotoSans',
descriptors: {},
atlasUrl: './fonts/NotoSans-Regular.ssdf.png',
atlasDataUrl: './fonts/NotoSans-Regular.ssdf.json',
stage,
metrics: {
ascender: 1000,
descender: -200,
lineGap: 0,
unitsPerEm: 1000,
},
}),
);
await stage.loadFont('sdf', {
fontFamily: 'NotoSans',
atlasUrl: './fonts/NotoSans-Regular.ssdf.png',
atlasDataUrl: './fonts/NotoSans-Regular.ssdf.json',
metrics: {
ascender: 1000,
descender: -200,
lineGap: 0,
unitsPerEm: 1000,
},
});

stage.fontManager.addFontFace(
new SdfTrFontFace('msdf', {
fontFamily: 'Ubuntu',
descriptors: {},
atlasUrl: './fonts/Ubuntu-Regular.msdf.png',
atlasDataUrl: './fonts/Ubuntu-Regular.msdf.json',
stage,
// Instead of suppling `metrics` this font will rely on the ones
// encoded in the json file under `lightningMetrics`.
}),
);
await stage.loadFont('sdf', {
fontFamily: 'Ubuntu',
atlasUrl: './fonts/Ubuntu-Regular.msdf.png',
atlasDataUrl: './fonts/Ubuntu-Regular.msdf.json',
// Instead of supplying `metrics` this font will rely on the ones
// encoded in the json file under `lightningMetrics`.
});

stage.fontManager.addFontFace(
new SdfTrFontFace('msdf', {
fontFamily: 'Ubuntu-Modified-Metrics',
descriptors: {},
atlasUrl: './fonts/Ubuntu-Regular.msdf.png',
atlasDataUrl: './fonts/Ubuntu-Regular.msdf.json',
stage,
metrics: ubuntuModifiedMetrics,
}),
);
await stage.loadFont('sdf', {
fontFamily: 'Ubuntu-Modified-Metrics',
atlasUrl: './fonts/Ubuntu-Regular.msdf.png',
atlasDataUrl: './fonts/Ubuntu-Regular.msdf.json',
metrics: ubuntuModifiedMetrics,
});

stage.fontManager.addFontFace(
new SdfTrFontFace('ssdf', {
fontFamily: 'Ubuntu-ssdf',
descriptors: {},
atlasUrl: './fonts/Ubuntu-Regular.ssdf.png',
atlasDataUrl: './fonts/Ubuntu-Regular.ssdf.json',
stage,
metrics: {
ascender: 776,
descender: -185,
lineGap: 56,
unitsPerEm: 1000,
},
}),
);
await stage.loadFont('sdf', {
fontFamily: 'Ubuntu-ssdf',
atlasUrl: './fonts/Ubuntu-Regular.ssdf.png',
atlasDataUrl: './fonts/Ubuntu-Regular.ssdf.json',
metrics: {
ascender: 776,
descender: -185,
lineGap: 56,
unitsPerEm: 1000,
},
});
}
}
2 changes: 1 addition & 1 deletion examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ async function initRenderer(
'app',
);
await installShaders(renderer.stage, renderMode);
installFonts(renderer.stage);
await installFonts(renderer.stage);

/**
* Sample data captured
Expand Down
9 changes: 9 additions & 0 deletions examples/tests/text-mixed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ export default async function test(settings: ExampleSettings) {
if (canvas) canvas.x = offset;
if (factory) factory.x = offset;
}

if (e.key === ' ') {
if (ssdf) {
ssdf.text = 'SSDF Updated';
}
if (canvas) {
canvas.text = 'Canvas Updated';
}
}
});

drawText();
Expand Down
12 changes: 12 additions & 0 deletions examples/tests/text-offscreen-move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ export async function automation(settings: ExampleSettings) {
*/
export default async function test(settings: ExampleSettings) {
const { renderer } = settings;

// const controlbox = renderer.createNode({
// x: renderer.settings.appWidth / 2,
// y: renderer.settings.appHeight / 2,
// mount: 0.5,
// width: 400,
// height: 400,
// color: 0xffffffff,
// alpha: 0.2,
// parent: settings.testRoot
// });

const pageContainer = new PageContainer(settings, {
width: renderer.settings.appWidth,
height: renderer.settings.appHeight,
Expand Down
2 changes: 1 addition & 1 deletion exports/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* @packageDocumentation
*/

export { CanvasTextRenderer } from '../src/core/text-rendering/renderers/CanvasTextRenderer.js';
export { default as CanvasTextRenderer } from '../src/core/text-rendering/CanvasTextRenderer.js';
export { CanvasRenderer } from '../src/core/renderers/canvas/CanvasRenderer.js';
export { CanvasTexture } from '../src/core/renderers/canvas/CanvasTexture.js';
export * from '../src/core/renderers/canvas/CanvasShaderNode.js';
Expand Down
8 changes: 0 additions & 8 deletions exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ export {
type TextureMap,
} from '../src/core/CoreTextureManager.js';
export type { MemoryInfo } from '../src/core/TextureMemoryManager.js';
export type { TextRendererMap } from '../src/core/text-rendering/renderers/TextRenderer.js';
export type { TrFontFaceMap } from '../src/core/text-rendering/font-face-types/TrFontFace.js';
export type { AnimationSettings } from '../src/core/animations/CoreAnimation.js';
export type { Inspector } from '../src/main-api/Inspector.js';
export type { CoreNodeRenderState } from '../src/core/CoreNode.js';
Expand All @@ -71,12 +69,6 @@ export type { ShaderProgramSources } from '../src/core/renderers/webgl/internal/
export * from '../src/core/textures/Texture.js';
export { ImageTexture } from '../src/core/textures/ImageTexture.js';

// Text Rendering & Fonts
// export * from '../src/core/text-rendering/renderers/TextRenderer.js';
export * from '../src/core/text-rendering/font-face-types/TrFontFace.js';
export * from '../src/core/text-rendering/font-face-types/WebTrFontFace.js';
export * from '../src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js';

// Stage (type only for Core Extensions)
export type * from '../src/core/Stage.js';

Expand Down
4 changes: 3 additions & 1 deletion exports/webgl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* @packageDocumentation
*/

export { SdfTextRenderer } from '../src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js';
export * from '../src/core/text-rendering/SdfTextRenderer.js';
export { WebGlRenderer } from '../src/core/renderers/webgl/WebGlRenderer.js';
export { WebGlCtxTexture } from '../src/core/renderers/webgl/WebGlCtxTexture.js';

Expand All @@ -48,3 +48,5 @@ export { WebGlRenderer as WebGlCoreRenderer } from '../src/core/renderers/webgl/
export { WebGlRenderer as WebGlCoreCtxTexture } from '../src/core/renderers/webgl/WebGlRenderer.js';

export * as shaders from './webgl-shaders.js';

export { default as SdfTextRenderer } from '../src/core/text-rendering/SdfTextRenderer.js';
Loading