Skip to content

Commit 903fb07

Browse files
committed
refactor(runtime): tighten internal typing and split render tick contracts
1 parent 2822ba4 commit 903fb07

27 files changed

Lines changed: 1009 additions & 232 deletions

src/fonts/manager/entries.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { fontHeightUnits } from "../../grid/grid";
2-
import type { FontEntry, FontManagerState, ShapedCluster } from "../types";
2+
import type { Font, FontEntry, FontManagerState, ShapedCluster } from "../types";
33

44
/** Create a new FontEntry with empty caches and default metadata. */
5-
export function createFontEntry(font: any, label: string): FontEntry {
5+
export function createFontEntry(font: Font, label: string): FontEntry {
66
return {
77
font,
88
label,
@@ -43,7 +43,7 @@ export function createFontManagerState(): FontManagerState {
4343
}
4444

4545
/** Check whether a font has a non-zero glyph ID for the given character. */
46-
export function fontHasGlyph(font: any, ch: string): boolean {
46+
export function fontHasGlyph(font: Font, ch: string): boolean {
4747
const glyphId = font.glyphIdForChar(ch);
4848
return glyphId !== undefined && glyphId !== null && glyphId !== 0;
4949
}
@@ -65,7 +65,7 @@ export function fontAdvanceUnits(
6565
advance = shapeClusterWithFont(entry, "M").advance;
6666
}
6767
if (!advance) {
68-
advance = fontHeightUnits(entry.font) || entry.font.upem || 1000;
68+
advance = fontHeightUnits(entry.font) || entry.font.unitsPerEm || 1000;
6969
}
7070
entry.advanceUnits = advance;
7171
return advance;

src/fonts/types.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
1+
import type {
2+
Bitmap as TextShaperBitmap,
3+
Font as TextShaperFont,
4+
FontSizeMode as TextShaperFontSizeMode,
5+
GlyphAtlas as TextShaperGlyphAtlas,
6+
GlyphMetrics as TextShaperGlyphMetrics,
7+
} from "text-shaper";
8+
9+
/** text-shaper Font used by runtime/font manager. */
10+
export type Font = TextShaperFont;
11+
/** text-shaper Bitmap used by atlas builders. */
12+
export type FontAtlasBitmap = TextShaperBitmap;
13+
/** text-shaper glyph metrics used inside atlases. */
14+
export type FontAtlasGlyphMetrics = TextShaperGlyphMetrics;
15+
/** text-shaper font size mode ("em" | "height"). */
16+
export type FontSizeMode = TextShaperFontSizeMode;
17+
/** Restty atlas extends text-shaper atlas with extra caches used internally. */
18+
export type FontAtlas = TextShaperGlyphAtlas & {
19+
glyphsByWidth?: Map<number, Map<number, FontAtlasGlyphMetrics>>;
20+
inset?: number;
21+
colorGlyphs?: Set<number>;
22+
};
23+
124
/**
225
* A loaded font with its associated caches and rendering metadata.
326
*/
427
export type FontEntry = {
528
/** text-shaper Font instance. */
6-
font: any; // text-shaper Font instance
29+
font: Font; // text-shaper Font instance
730
/** Human-readable font name. */
831
label: string;
932
/** Cache of shaped glyph clusters keyed by input string. */
@@ -15,7 +38,7 @@ export type FontEntry = {
1538
/** Set of all glyph IDs available in this font. */
1639
glyphIds: Set<number>;
1740
/** GPU texture atlas for this font, or null if not yet built. */
18-
atlas: any | null;
41+
atlas: FontAtlas | null;
1942
/** Font size in CSS pixels. */
2043
fontSizePx: number;
2144
/** Scale factor applied when rasterizing to the atlas. */
@@ -57,18 +80,13 @@ export type ShapedGlyph = {
5780
*/
5881
export type FontManagerState = {
5982
/** Primary text-shaper Font instance, or null before initialization. */
60-
font: any | null;
83+
font: Font | null;
6184
/** Loaded font entries in priority order (primary + fallbacks). */
6285
fonts: FontEntry[];
6386
/** Current font size in CSS pixels. */
6487
fontSizePx: number;
65-
/**
66-
* How font size maps to design units.
67-
* - height: size equals cell height
68-
* - width: size equals cell width
69-
* - upem: size equals units-per-em
70-
*/
71-
sizeMode: "height" | "width" | "upem";
88+
/** How font size maps to design units ("em" | "height"). */
89+
sizeMode: FontSizeMode;
7290
/** Cache mapping text strings to the index of the font chosen to render them. */
7391
fontPickCache: Map<string, number>;
7492
};

src/grid/grid.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { CellMetrics, GridConfig, GridState } from "./types";
55
*/
66
export type FontMetricsProvider = {
77
/** Return the scale factor for a given pixel size and sizing mode. */
8-
scaleForSize(sizePx: number, sizeMode: string): number;
8+
scaleForSize(sizePx: number, sizeMode?: "em" | "height"): number;
99
/** Look up the glyph ID for a character, or null/undefined if missing. */
1010
glyphIdForChar(char: string): number | undefined | null;
1111
/** Return the advance width of a glyph in font units. */
@@ -17,15 +17,17 @@ export type FontMetricsProvider = {
1717
/** Explicit font height in font units, if available. */
1818
readonly height?: number;
1919
/** Units per em of the font. */
20-
readonly upem: number;
20+
readonly unitsPerEm: number;
21+
/** Legacy alias accepted for compatibility with older font objects. */
22+
readonly upem?: number;
2123
};
2224

2325
/** Result of shaping a text cluster, containing its advance width. */
2426
export type ShapeResult = {
2527
advance: number;
2628
};
2729

28-
/** Resolve the font height in font units, falling back to ascender-descender or upem. */
30+
/** Resolve the font height in font units, falling back to ascender-descender or units-per-em. */
2931
export function fontHeightUnits(font: FontMetricsProvider): number {
3032
if (!font) return 0;
3133
const height = font.height;
@@ -34,7 +36,7 @@ export function fontHeightUnits(font: FontMetricsProvider): number {
3436
const desc = font.descender ?? 0;
3537
const fallback = asc - desc;
3638
if (Number.isFinite(fallback) && fallback > 0) return fallback;
37-
return font.upem || 1000;
39+
return font.unitsPerEm || font.upem || 1000;
3840
}
3941

4042
/**

src/runtime/atlas-builder.ts

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import type { Font, FontAtlas, FontEntry, FontSizeMode } from "../fonts";
2+
import type {
3+
AtlasOptions,
4+
GlyphRasterizeOptions,
5+
Matrix2D,
6+
Matrix3x3,
7+
RasterizedGlyph,
8+
} from "text-shaper";
9+
110
/**
211
* Metadata for constrained glyph rendering.
312
* - cp: Unicode code point
@@ -39,24 +48,82 @@ export type AtlasConstraintContext = {
3948
iconHeight: number;
4049
iconHeightSingle: number;
4150
};
42-
fontEntry: any;
51+
fontEntry: FontEntry;
52+
};
53+
54+
type RasterizeGlyphTransformOptions = GlyphRasterizeOptions & {
55+
offsetX26?: number;
56+
offsetY26?: number;
57+
};
58+
59+
type RasterizeGlyphFn = (
60+
font: Font,
61+
glyphId: number,
62+
fontSize: number,
63+
options?: GlyphRasterizeOptions,
64+
) => RasterizedGlyph | null;
65+
66+
type RasterizeGlyphWithTransformFn = (
67+
font: Font,
68+
glyphId: number,
69+
fontSize: number,
70+
matrix: Matrix2D | Matrix3x3,
71+
options?: RasterizeGlyphTransformOptions,
72+
) => RasterizedGlyph | null;
73+
74+
type BuildGlyphAtlasWithConstraintsOptions = {
75+
font: Font;
76+
glyphIds: number[];
77+
fontSize: number;
78+
sizeMode: FontSizeMode;
79+
padding: number;
80+
maxWidth: number;
81+
maxHeight: number;
82+
pixelMode: number;
83+
hinting: boolean;
84+
rasterizeGlyph?: RasterizeGlyphFn;
85+
rasterizeGlyphWithTransform?: RasterizeGlyphWithTransformFn;
86+
glyphMeta?: Map<number, GlyphConstraintMeta>;
87+
constraintContext?: AtlasConstraintContext;
88+
};
89+
90+
type BuildGlyphAtlasWithConstraintsResult = {
91+
atlas: FontAtlas | null;
92+
constrainedGlyphWidths?: Map<number, number> | null;
93+
};
94+
95+
type BuildColorEmojiAtlasWithCanvasOptions = {
96+
font: Font;
97+
fontEntry: FontEntry;
98+
glyphIds: number[];
99+
fontSize: number;
100+
sizeMode: FontSizeMode;
101+
padding: number;
102+
maxWidth: number;
103+
maxHeight: number;
104+
pixelMode: number;
43105
};
44106

45107
type BuildAtlasDeps = {
46108
fontScaleOverrides: Array<{ match: RegExp; scale: number }>;
47-
sizeMode: string;
48-
isSymbolFont: (entry: any) => boolean;
49-
fontScaleOverride: (entry: any, overrides: Array<{ match: RegExp; scale: number }>) => number;
50-
resolveGlyphPixelMode: (entry: any) => number;
51-
atlasBitmapToRGBA: (atlas: any) => Uint8Array | null;
52-
padAtlasRGBA: (rgba: Uint8Array, atlas: any, padding: number) => Uint8Array;
53-
buildAtlas: (font: any, glyphIds: number[], options: any) => any;
109+
sizeMode: FontSizeMode;
110+
isSymbolFont: (entry: FontEntry | null | undefined) => boolean;
111+
fontScaleOverride: (
112+
entry: FontEntry | null | undefined,
113+
overrides: Array<{ match: RegExp; scale: number }>,
114+
) => number;
115+
resolveGlyphPixelMode: (entry: FontEntry) => number;
116+
atlasBitmapToRGBA: (atlas: FontAtlas) => Uint8Array | null;
117+
padAtlasRGBA: (rgba: Uint8Array, atlas: FontAtlas, padding: number) => Uint8Array;
118+
buildAtlas: (font: Font, glyphIds: number[], options: AtlasOptions) => FontAtlas;
54119
buildGlyphAtlasWithConstraints: (
55-
options: any,
56-
) => { atlas: any; constrainedGlyphWidths?: any } | null;
57-
buildColorEmojiAtlasWithCanvas: (options: any) => { atlas: any } | null;
58-
rasterizeGlyph?: any;
59-
rasterizeGlyphWithTransform?: any;
120+
options: BuildGlyphAtlasWithConstraintsOptions,
121+
) => BuildGlyphAtlasWithConstraintsResult | null;
122+
buildColorEmojiAtlasWithCanvas: (
123+
options: BuildColorEmojiAtlasWithCanvasOptions,
124+
) => { atlas: FontAtlas } | null;
125+
rasterizeGlyph?: RasterizeGlyphFn;
126+
rasterizeGlyphWithTransform?: RasterizeGlyphWithTransformFn;
60127
nerdConstraintSignature: (
61128
glyphMeta: Map<number, GlyphConstraintMeta> | undefined,
62129
constraintContext: AtlasConstraintContext | null | undefined,
@@ -87,7 +154,7 @@ type BuildAtlasDeps = {
87154
* - deps: external dependencies for atlas building
88155
*/
89156
export type BuildFontAtlasParams = {
90-
entry: any;
157+
entry: FontEntry;
91158
neededGlyphIds: Set<number>;
92159
glyphMeta?: Map<number, GlyphConstraintMeta>;
93160
fontSizePx: number;
@@ -107,7 +174,7 @@ export type BuildFontAtlasParams = {
107174
*/
108175
export type BuildFontAtlasResult = {
109176
rebuilt: boolean;
110-
atlas: any | null;
177+
atlas: FontAtlas | null;
111178
rgba: Uint8Array | null;
112179
colorGlyphs?: Set<number>;
113180
preferNearest: boolean;
@@ -220,7 +287,7 @@ export function buildFontAtlasIfNeeded(params: BuildFontAtlasParams): BuildFontA
220287
const colorGlyphAtlas = glyphPixelMode === constants.pixelModeRgbaValue || glyphPixelMode === 4;
221288
const useCanvasColorAtlas = colorGlyphAtlas;
222289

223-
let atlas = null;
290+
let atlas: FontAtlas | null = null;
224291
if (isSymbol && rasterizeGlyph && rasterizeGlyphWithTransform && constraintContext && glyphMeta) {
225292
const result = buildGlyphAtlasWithConstraints({
226293
font: entry.font,

src/runtime/create-runtime/atlas-debug-utils.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import type { FontEntry } from "../../fonts";
1+
import type { FontAtlas, FontAtlasGlyphMetrics, FontEntry } from "../../fonts";
22

33
export function atlasRegionToImageData(
4-
atlas: any,
4+
atlas: FontAtlas,
55
x: number,
66
y: number,
77
width: number,
@@ -39,7 +39,7 @@ export function atlasRegionToImageData(
3939
return new ImageData(rgba, width, height);
4040
}
4141

42-
export function padAtlasRGBA(rgba: Uint8Array, atlas: any, padding: number): Uint8Array {
42+
export function padAtlasRGBA(rgba: Uint8Array, atlas: FontAtlas, padding: number): Uint8Array {
4343
if (!padding || padding <= 0 || !atlas?.glyphs) return rgba;
4444
const width = atlas.bitmap?.width ?? 0;
4545
const height = atlas.bitmap?.rows ?? 0;
@@ -57,7 +57,7 @@ export function padAtlasRGBA(rgba: Uint8Array, atlas: any, padding: number): Uin
5757
out[dstIdx + 3] = out[srcIdx + 3];
5858
};
5959

60-
const padMetrics = (metrics: any) => {
60+
const padMetrics = (metrics: FontAtlasGlyphMetrics) => {
6161
const x0 = metrics.atlasX;
6262
const y0 = metrics.atlasY;
6363
const x1 = metrics.atlasX + metrics.width - 1;
@@ -110,9 +110,9 @@ export function resolveGlyphPixelMode(
110110
}
111111

112112
export function atlasBitmapToRGBA(
113-
atlas: any,
113+
atlas: FontAtlas,
114114
pixelModeRgba: number,
115-
atlasToRGBA: (atlas: any) => Uint8Array,
115+
atlasToRGBA: (atlas: FontAtlas) => Uint8Array,
116116
): Uint8Array | null {
117117
const bitmap = atlas?.bitmap;
118118
if (!bitmap?.width || !bitmap?.rows) return null;

0 commit comments

Comments
 (0)