@flighthq/bitmaptext 0.5.1-next.597.ab004dd → 0.5.1-next.638.d4dbd4e
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/README.md +2 -2
- package/dist/bitmapText.d.ts +1 -0
- package/dist/bitmapText.d.ts.map +1 -1
- package/dist/bitmapText.js +14 -0
- package/dist/bitmapText.js.map +1 -1
- package/dist/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/enableBitmapTextGuards.d.ts +20 -0
- package/dist/enableBitmapTextGuards.d.ts.map +1 -0
- package/dist/enableBitmapTextGuards.js +35 -0
- package/dist/enableBitmapTextGuards.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/updateBitmapText.d.ts +5 -0
- package/dist/updateBitmapText.d.ts.map +1 -1
- package/dist/updateBitmapText.js +138 -75
- package/dist/updateBitmapText.js.map +1 -1
- package/package.json +8 -7
- package/src/bitmapText.test.ts +32 -0
- package/src/enableBitmapTextGuards.test.ts +77 -0
- package/src/updateBitmapText.test.ts +239 -4
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/bitmaptext`. The `@f
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/bitmaptext@0.5.1-next.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/bitmaptext@0.5.1-next.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/bitmaptext)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
|
package/dist/bitmapText.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare function createBitmapTextData(data?: Readonly<Partial<BitmapTextD
|
|
|
5
5
|
export declare function createBitmapTextRuntime(): BitmapTextRuntime;
|
|
6
6
|
export declare function getBitmapTextBounds(source: Readonly<BitmapText>): Rectangle;
|
|
7
7
|
export declare function getBitmapTextPages(source: Readonly<BitmapText>): readonly BitmapTextPage[];
|
|
8
|
+
export declare function isBitmapTextGlyphLayoutStale(source: Readonly<BitmapText>): boolean;
|
|
8
9
|
export declare function reserveBitmapText(target: BitmapText, glyphCapacity: number): void;
|
|
9
10
|
export declare function setBitmapTextAlign(target: BitmapText, align: BitmapTextAlign): void;
|
|
10
11
|
export declare function setBitmapTextGlyphSource(target: BitmapText, glyphSource: GlyphSource | null): void;
|
package/dist/bitmapText.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bitmapText.d.ts","sourceRoot":"","sources":["../src/bitmapText.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,WAAW,EAGX,SAAS,EACV,MAAM,0BAA0B,CAAC;AASlC,wBAAgB,qCAAqC,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAWxG;AASD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GAAG,UAAU,CAanH;AAED,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,cAAc,CAS7F;AAED,wBAAgB,uBAAuB,IAAI,iBAAiB,
|
|
1
|
+
{"version":3,"file":"bitmapText.d.ts","sourceRoot":"","sources":["../src/bitmapText.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,iBAAiB,EACjB,WAAW,EAGX,SAAS,EACV,MAAM,0BAA0B,CAAC;AASlC,wBAAgB,qCAAqC,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAWxG;AASD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,iBAAiB,CAAC,GAAG,UAAU,CAanH;AAED,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,cAAc,CAS7F;AAED,wBAAgB,uBAAuB,IAAI,iBAAiB,CAQ3D;AAID,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,CAI3E;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,SAAS,cAAc,EAAE,CAE1F;AAOD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,OAAO,CAIlF;AAKD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAMjF;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAEnF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAElG;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAE1F;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAEpF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAExE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAEzF"}
|
package/dist/bitmapText.js
CHANGED
|
@@ -48,6 +48,9 @@ export function createBitmapTextData(data) {
|
|
|
48
48
|
}
|
|
49
49
|
export function createBitmapTextRuntime() {
|
|
50
50
|
const runtime = createNode2DRuntime(defaultMethods);
|
|
51
|
+
// -1 is below every real version, so a node that has never been laid out reads stale and one
|
|
52
|
+
// `refreshBitmapTextGlyphLayout` brings it up — no separate "was it ever updated" flag.
|
|
53
|
+
runtime.glyphLayoutVersion = -1;
|
|
51
54
|
runtime.localBoundsRectangle = null;
|
|
52
55
|
runtime.pages = [];
|
|
53
56
|
return runtime;
|
|
@@ -64,6 +67,17 @@ export function getBitmapTextBounds(source) {
|
|
|
64
67
|
export function getBitmapTextPages(source) {
|
|
65
68
|
return getNode2DRuntime(source).pages;
|
|
66
69
|
}
|
|
70
|
+
// True when the page regions baked by the last `updateBitmapText` no longer describe the glyphs they
|
|
71
|
+
// were built for, because the bound glyph source has repacked since. Rects that have gone stale stay
|
|
72
|
+
// well-formed — they simply cover other glyphs now — so this version comparison is the only way to
|
|
73
|
+
// tell, and it is the check `refreshBitmapTextGlyphLayout` performs. A node that has never been laid
|
|
74
|
+
// out reads true; a node with no glyph source, or one bound to a static font, reads false forever.
|
|
75
|
+
export function isBitmapTextGlyphLayoutStale(source) {
|
|
76
|
+
const glyphSource = source.data.glyphSource;
|
|
77
|
+
if (glyphSource === null)
|
|
78
|
+
return false;
|
|
79
|
+
return getNode2DRuntime(source).glyphLayoutVersion !== glyphSource.getGlyphLayoutVersion();
|
|
80
|
+
}
|
|
67
81
|
// Grows each page's quad arrays to hold at least `glyphCapacity` glyph quads without reallocating during
|
|
68
82
|
// layout. Optional — `updateBitmapText` auto-grows — but avoids incremental reallocation for large
|
|
69
83
|
// strings. Reserving every page over-allocates for multi-page text but never under-sizes.
|
package/dist/bitmapText.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bitmapText.js","sourceRoot":"","sources":["../src/bitmapText.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bitmapText.js","sourceRoot":"","sources":["../src/bitmapText.ts"],"names":[],"mappings":"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"}
|
package/dist/contract.d.ts
CHANGED
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC"}
|
package/dist/contract.js
CHANGED
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Uninstalls the guard installed by `enableBitmapTextGuards`. */
|
|
2
|
+
export declare function disableBitmapTextGuards(): void;
|
|
3
|
+
/**
|
|
4
|
+
* Installs the caller-facing bitmap-text guard (opt-in, dev-only). It warns once — through
|
|
5
|
+
* `@flighthq/log` — when a layout never settles:
|
|
6
|
+
*
|
|
7
|
+
* **The layout did not converge.** `updateBitmapText` re-runs a layout whose glyph rects a repack
|
|
8
|
+
* invalidated mid-pass, and it ran out of passes. The string needs more glyphs resident at once than
|
|
9
|
+
* the atlas can hold, so rasterizing the end of it evicts the start, every pass invalidates the one
|
|
10
|
+
* before, and the quads that get drawn sample whatever displaced them. Unlike a full cache this does
|
|
11
|
+
* not resolve itself with use: the atlas has to be bigger or the font smaller.
|
|
12
|
+
*
|
|
13
|
+
* Pair with `isBitmapTextGlyphLayoutStale` for a pull-style answer about one node, and with
|
|
14
|
+
* `enableGlyphAtlasGuards` for the atlas-side view of the same pressure — that one reports the repacks
|
|
15
|
+
* and the glyphs they drop, this one reports the text that could not be laid out through them. Not
|
|
16
|
+
* importing this module costs production nothing: the wording and the `@flighthq/log` dependency live
|
|
17
|
+
* only here.
|
|
18
|
+
*/
|
|
19
|
+
export declare function enableBitmapTextGuards(): void;
|
|
20
|
+
//# sourceMappingURL=enableBitmapTextGuards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enableBitmapTextGuards.d.ts","sourceRoot":"","sources":["../src/enableBitmapTextGuards.ts"],"names":[],"mappings":"AAKA,kEAAkE;AAClE,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { logOnce } from '@flighthq/log/contract';
|
|
2
|
+
import { LogLevel } from '@flighthq/types/contract';
|
|
3
|
+
import { setBitmapTextLayoutGuard } from './updateBitmapText';
|
|
4
|
+
/** Uninstalls the guard installed by `enableBitmapTextGuards`. */
|
|
5
|
+
export function disableBitmapTextGuards() {
|
|
6
|
+
setBitmapTextLayoutGuard(null);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Installs the caller-facing bitmap-text guard (opt-in, dev-only). It warns once — through
|
|
10
|
+
* `@flighthq/log` — when a layout never settles:
|
|
11
|
+
*
|
|
12
|
+
* **The layout did not converge.** `updateBitmapText` re-runs a layout whose glyph rects a repack
|
|
13
|
+
* invalidated mid-pass, and it ran out of passes. The string needs more glyphs resident at once than
|
|
14
|
+
* the atlas can hold, so rasterizing the end of it evicts the start, every pass invalidates the one
|
|
15
|
+
* before, and the quads that get drawn sample whatever displaced them. Unlike a full cache this does
|
|
16
|
+
* not resolve itself with use: the atlas has to be bigger or the font smaller.
|
|
17
|
+
*
|
|
18
|
+
* Pair with `isBitmapTextGlyphLayoutStale` for a pull-style answer about one node, and with
|
|
19
|
+
* `enableGlyphAtlasGuards` for the atlas-side view of the same pressure — that one reports the repacks
|
|
20
|
+
* and the glyphs they drop, this one reports the text that could not be laid out through them. Not
|
|
21
|
+
* importing this module costs production nothing: the wording and the `@flighthq/log` dependency live
|
|
22
|
+
* only here.
|
|
23
|
+
*/
|
|
24
|
+
export function enableBitmapTextGuards() {
|
|
25
|
+
setBitmapTextLayoutGuard(warnOnBitmapTextLayoutBlocked);
|
|
26
|
+
}
|
|
27
|
+
function warnOnBitmapTextLayoutBlocked(_reason, attempts) {
|
|
28
|
+
logOnce('bitmaptext:layout-did-not-converge', LogLevel.Warn, {
|
|
29
|
+
message: `updateBitmapText: the glyph source repacked during all ${attempts} layout passes, so this text ` +
|
|
30
|
+
'never settled — its glyphs cannot all be resident in the atlas at once, and the quads drawn from ' +
|
|
31
|
+
'the final pass sample stale rects. Enlarge the glyph atlas or reduce the font size; ' +
|
|
32
|
+
'enableGlyphAtlasGuards reports the repacks from the atlas side.',
|
|
33
|
+
}, 'bitmaptext');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=enableBitmapTextGuards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enableBitmapTextGuards.js","sourceRoot":"","sources":["../src/enableBitmapTextGuards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,kEAAkE;AAClE,MAAM,UAAU,uBAAuB;IACrC,wBAAwB,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,sBAAsB;IACpC,wBAAwB,CAAC,6BAA6B,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,6BAA6B,CAAC,OAAe,EAAE,QAAgB;IACtE,OAAO,CACL,oCAAoC,EACpC,QAAQ,CAAC,IAAI,EACb;QACE,OAAO,EACL,0DAA0D,QAAQ,+BAA+B;YACjG,mGAAmG;YACnG,sFAAsF;YACtF,iEAAiE;KACpE,EACD,YAAY,CACb,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { computeBitmapTextLocalBoundsRectangle, createBitmapText, getBitmapTextBounds, getBitmapTextPages, reserveBitmapText, setBitmapTextAlign, setBitmapTextGlyphSource, setBitmapTextLetterSpacing, setBitmapTextLineHeight, setBitmapTextText, setBitmapTextWrapWidth, updateBitmapText, } from './contract';
|
|
1
|
+
export { computeBitmapTextLocalBoundsRectangle, createBitmapText, disableBitmapTextGuards, enableBitmapTextGuards, getBitmapTextBounds, getBitmapTextPages, isBitmapTextGlyphLayoutStale, refreshBitmapTextGlyphLayout, reserveBitmapText, setBitmapTextAlign, setBitmapTextGlyphSource, setBitmapTextLetterSpacing, setBitmapTextLineHeight, setBitmapTextText, setBitmapTextWrapWidth, updateBitmapText, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qCAAqC,EACrC,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qCAAqC,EACrC,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,EAC5B,4BAA4B,EAC5B,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { computeBitmapTextLocalBoundsRectangle, createBitmapText, getBitmapTextBounds, getBitmapTextPages, reserveBitmapText, setBitmapTextAlign, setBitmapTextGlyphSource, setBitmapTextLetterSpacing, setBitmapTextLineHeight, setBitmapTextText, setBitmapTextWrapWidth, updateBitmapText, } from './contract';
|
|
1
|
+
export { computeBitmapTextLocalBoundsRectangle, createBitmapText, disableBitmapTextGuards, enableBitmapTextGuards, getBitmapTextBounds, getBitmapTextPages, isBitmapTextGlyphLayoutStale, refreshBitmapTextGlyphLayout, reserveBitmapText, setBitmapTextAlign, setBitmapTextGlyphSource, setBitmapTextLetterSpacing, setBitmapTextLineHeight, setBitmapTextText, setBitmapTextWrapWidth, updateBitmapText, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qCAAqC,EACrC,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qCAAqC,EACrC,gBAAgB,EAChB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,4BAA4B,EAC5B,4BAA4B,EAC5B,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,0BAA0B,EAC1B,uBAAuB,EACvB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import type { BitmapText } from '@flighthq/types/contract';
|
|
2
|
+
export declare function refreshBitmapTextGlyphLayout(bitmapText: BitmapText): boolean;
|
|
3
|
+
/** Installs the bitmap-text guard, or clears it with `null`. The seam keeps the wording and the
|
|
4
|
+
* `@flighthq/log` dependency in the separately-importable guard module rather than in layout; not
|
|
5
|
+
* importing that module costs production nothing. Called by `enableBitmapTextGuards`. */
|
|
6
|
+
export declare function setBitmapTextLayoutGuard(guard: ((reason: string, attempts: number) => void) | null): void;
|
|
2
7
|
export declare function updateBitmapText(bitmapText: BitmapText): void;
|
|
3
8
|
//# sourceMappingURL=updateBitmapText.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateBitmapText.d.ts","sourceRoot":"","sources":["../src/updateBitmapText.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,UAAU,EAOX,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"updateBitmapText.d.ts","sourceRoot":"","sources":["../src/updateBitmapText.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,UAAU,EAOX,MAAM,0BAA0B,CAAC;AAWlC,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAI5E;AAED;;0FAE0F;AAC1F,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAEzG;AAsBD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAwB7D"}
|
package/dist/updateBitmapText.js
CHANGED
|
@@ -3,9 +3,26 @@ import { invalidateNodeLocalBounds } from '@flighthq/node/contract';
|
|
|
3
3
|
import { getNode2DRuntime } from '@flighthq/scene2d/contract';
|
|
4
4
|
import { createTexture, setTextureSource } from '@flighthq/texture/contract';
|
|
5
5
|
import { addTextureAtlasRegion, createTextureAtlas } from '@flighthq/textureatlas/contract';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import { isBitmapTextGlyphLayoutStale } from './bitmapText';
|
|
7
|
+
// Re-lays-out `bitmapText` when, and only when, its glyph source has repacked since the last layout —
|
|
8
|
+
// the pairing that keeps a dynamic atlas correct. Returns whether it re-laid-out, so a caller can count
|
|
9
|
+
// churn. Call it once per frame per BitmapText bound to a `@flighthq/glyphatlas` source: a repack
|
|
10
|
+
// triggered by ANY consumer of that atlas (another BitmapText adding a glyph, a direct
|
|
11
|
+
// `getGlyphAtlasEntry`) relocates this node's glyphs, and its baked page regions then sample whatever
|
|
12
|
+
// took their place. The comparison is one number against one number, so the frames where nothing
|
|
13
|
+
// repacked cost nothing. A node bound to a static bitmap font never re-lays-out here.
|
|
14
|
+
export function refreshBitmapTextGlyphLayout(bitmapText) {
|
|
15
|
+
if (!isBitmapTextGlyphLayoutStale(bitmapText))
|
|
16
|
+
return false;
|
|
17
|
+
updateBitmapText(bitmapText);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
/** Installs the bitmap-text guard, or clears it with `null`. The seam keeps the wording and the
|
|
21
|
+
* `@flighthq/log` dependency in the separately-importable guard module rather than in layout; not
|
|
22
|
+
* importing that module costs production nothing. Called by `enableBitmapTextGuards`. */
|
|
23
|
+
export function setBitmapTextLayoutGuard(guard) {
|
|
24
|
+
_layoutGuard = guard;
|
|
25
|
+
}
|
|
9
26
|
// Lays out `bitmapText`'s current string and rewrites its `BitmapTextPage` quads: one quad per visible
|
|
10
27
|
// glyph, partitioned by the glyph's atlas page into one page's quads, positioned by the glyph source's
|
|
11
28
|
// advances and kerning, broken on explicit newlines and (when `wrapWidth` is set) at word boundaries,
|
|
@@ -16,84 +33,40 @@ const SPACE = 0x20;
|
|
|
16
33
|
// glyphs are skipped. Missing glyphs (`getGlyphEntry` → null) are omitted entirely — no quad and no
|
|
17
34
|
// advance. Bounds span every drawn glyph across all pages. Tint is the node's own color-adjustment stack,
|
|
18
35
|
// not touched here.
|
|
36
|
+
//
|
|
37
|
+
// A dynamic glyph source can repack DURING this call: rasterizing a glyph late in the string can evict
|
|
38
|
+
// one placed early in it. A repack that merely relocates a survivor needs nothing from this pass — the
|
|
39
|
+
// atlas moves the same entry object the pass is holding, so the emit below reads the new rect. What
|
|
40
|
+
// this loop exists for is a repack that DROPS a glyph: the entry the pass captured is then orphaned at
|
|
41
|
+
// a rect another glyph has taken over, and only re-running the pass reaches the fresh one. So the
|
|
42
|
+
// layout runs against a placement version and repeats while that version moves under it, and the
|
|
43
|
+
// version the winning pass agreed with is what gets stamped for `isBitmapTextGlyphLayoutStale`. Passes
|
|
44
|
+
// are bounded — a string whose glyphs cannot all be resident at once would otherwise evict each other
|
|
45
|
+
// forever — and the guard layer reports a string that never settles.
|
|
19
46
|
export function updateBitmapText(bitmapText) {
|
|
20
|
-
const data = bitmapText.data;
|
|
21
47
|
const runtime = getNode2DRuntime(bitmapText);
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
page.instanceCount = 0;
|
|
27
|
-
page.atlas.regions.length = 0;
|
|
28
|
-
}
|
|
29
|
-
const glyphSource = data.glyphSource;
|
|
30
|
-
if (glyphSource === null || data.text.length === 0) {
|
|
31
|
-
setEmptyRectangle(bounds);
|
|
32
|
-
invalidateNodeLocalBounds(bitmapText);
|
|
48
|
+
const glyphSource = bitmapText.data.glyphSource;
|
|
49
|
+
if (glyphSource === null) {
|
|
50
|
+
layoutBitmapTextPages(bitmapText, runtime);
|
|
51
|
+
runtime.glyphLayoutVersion = -1;
|
|
33
52
|
return;
|
|
34
53
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let minY = Infinity;
|
|
42
|
-
let maxX = -Infinity;
|
|
43
|
-
let maxY = -Infinity;
|
|
44
|
-
for (let li = 0; li < lines.length; li++) {
|
|
45
|
-
const line = lines[li];
|
|
46
|
-
const baselineY = metrics.ascent + li * lineAdvance;
|
|
47
|
-
let startX = 0;
|
|
48
|
-
let gapExtra = 0;
|
|
49
|
-
if (data.align === 'center')
|
|
50
|
-
startX = (refWidth - line.width) / 2;
|
|
51
|
-
else if (data.align === 'right')
|
|
52
|
-
startX = refWidth - line.width;
|
|
53
|
-
else if (data.align === 'justify' && data.wrapWidth !== null && !line.paragraphEnd && line.gaps.length > 0) {
|
|
54
|
-
gapExtra = (data.wrapWidth - line.width) / line.gaps.length;
|
|
55
|
-
}
|
|
56
|
-
let penX = startX;
|
|
57
|
-
for (let wi = 0; wi < line.words.length; wi++) {
|
|
58
|
-
if (wi > 0)
|
|
59
|
-
penX += line.gaps[wi - 1] + gapExtra;
|
|
60
|
-
const word = line.words[wi];
|
|
61
|
-
for (const glyph of word.glyphs) {
|
|
62
|
-
const entry = glyph.entry;
|
|
63
|
-
const context = ensureBitmapTextPage(runtime, glyphSource, pages, entry.page);
|
|
64
|
-
if (context === null)
|
|
65
|
-
continue;
|
|
66
|
-
const quadX = penX + glyph.penWithinWord + entry.bearingX;
|
|
67
|
-
const quadY = baselineY - entry.bearingY;
|
|
68
|
-
let regionId = context.regionByCodepoint.get(glyph.codepoint);
|
|
69
|
-
if (regionId === undefined) {
|
|
70
|
-
addTextureAtlasRegion(context.page.atlas, entry.x, entry.y, entry.width, entry.height);
|
|
71
|
-
regionId = context.page.atlas.regions.length - 1;
|
|
72
|
-
context.regionByCodepoint.set(glyph.codepoint, regionId);
|
|
73
|
-
}
|
|
74
|
-
appendBitmapTextPageQuad(context.page, regionId, quadX, quadY);
|
|
75
|
-
if (quadX < minX)
|
|
76
|
-
minX = quadX;
|
|
77
|
-
if (quadY < minY)
|
|
78
|
-
minY = quadY;
|
|
79
|
-
if (quadX + entry.width > maxX)
|
|
80
|
-
maxX = quadX + entry.width;
|
|
81
|
-
if (quadY + entry.height > maxY)
|
|
82
|
-
maxY = quadY + entry.height;
|
|
83
|
-
}
|
|
84
|
-
penX += word.width;
|
|
54
|
+
for (let attempt = 1; attempt <= BITMAP_TEXT_LAYOUT_ATTEMPTS; attempt++) {
|
|
55
|
+
const version = glyphSource.getGlyphLayoutVersion();
|
|
56
|
+
layoutBitmapTextPages(bitmapText, runtime);
|
|
57
|
+
if (glyphSource.getGlyphLayoutVersion() === version) {
|
|
58
|
+
runtime.glyphLayoutVersion = version;
|
|
59
|
+
return;
|
|
85
60
|
}
|
|
86
61
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
invalidateNodeLocalBounds(bitmapText);
|
|
62
|
+
// Out of passes. The last layout is the best available and some of its rects are already wrong, and
|
|
63
|
+
// the current version is still what gets stamped: another pass would reach the same place, so leaving
|
|
64
|
+
// the node marked stale would only re-lay-out it every frame forever. The atlas is too small for this
|
|
65
|
+
// string, which is a sizing decision the caller has to make — hence the guard rather than a retry
|
|
66
|
+
// budget. A node here does come right on its own if the atlas stops repacking, because the version
|
|
67
|
+
// then holds still and the next refresh's single pass sees one placement.
|
|
68
|
+
runtime.glyphLayoutVersion = glyphSource.getGlyphLayoutVersion();
|
|
69
|
+
_layoutGuard?.('layout-did-not-converge', BITMAP_TEXT_LAYOUT_ATTEMPTS);
|
|
97
70
|
}
|
|
98
71
|
// Appends one glyph quad (region id + vector2 pen position) to `page`, auto-growing its arrays. The
|
|
99
72
|
// page-local twin of `appendQuadBatchInstance`, over plain owned arrays rather than a QuadBatch node.
|
|
@@ -220,6 +193,86 @@ function layoutBitmapTextLines(glyphSource, data) {
|
|
|
220
193
|
}
|
|
221
194
|
return lines;
|
|
222
195
|
}
|
|
196
|
+
// One layout pass: the glyph placement itself, with no version bookkeeping. Split out from
|
|
197
|
+
// `updateBitmapText` so the retry above re-runs exactly the work a repack invalidated.
|
|
198
|
+
function layoutBitmapTextPages(bitmapText, runtime) {
|
|
199
|
+
const data = bitmapText.data;
|
|
200
|
+
const bounds = ensureBoundsRectangle(runtime);
|
|
201
|
+
// Clear every existing page; pages with glyphs this layout are refilled below, pages that fall silent
|
|
202
|
+
// stay as empty pages drawing nothing.
|
|
203
|
+
for (const page of runtime.pages) {
|
|
204
|
+
page.instanceCount = 0;
|
|
205
|
+
page.atlas.regions.length = 0;
|
|
206
|
+
}
|
|
207
|
+
const glyphSource = data.glyphSource;
|
|
208
|
+
if (glyphSource === null || data.text.length === 0) {
|
|
209
|
+
setEmptyRectangle(bounds);
|
|
210
|
+
invalidateNodeLocalBounds(bitmapText);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const metrics = glyphSource.getGlyphMetrics();
|
|
214
|
+
const lineAdvance = (metrics.ascent + metrics.descent + metrics.lineGap) * data.lineHeight;
|
|
215
|
+
const lines = layoutBitmapTextLines(glyphSource, data);
|
|
216
|
+
const refWidth = data.wrapWidth ?? maxLineWidth(lines);
|
|
217
|
+
const pages = new Map();
|
|
218
|
+
let minX = Infinity;
|
|
219
|
+
let minY = Infinity;
|
|
220
|
+
let maxX = -Infinity;
|
|
221
|
+
let maxY = -Infinity;
|
|
222
|
+
for (let li = 0; li < lines.length; li++) {
|
|
223
|
+
const line = lines[li];
|
|
224
|
+
const baselineY = metrics.ascent + li * lineAdvance;
|
|
225
|
+
let startX = 0;
|
|
226
|
+
let gapExtra = 0;
|
|
227
|
+
if (data.align === 'center')
|
|
228
|
+
startX = (refWidth - line.width) / 2;
|
|
229
|
+
else if (data.align === 'right')
|
|
230
|
+
startX = refWidth - line.width;
|
|
231
|
+
else if (data.align === 'justify' && data.wrapWidth !== null && !line.paragraphEnd && line.gaps.length > 0) {
|
|
232
|
+
gapExtra = (data.wrapWidth - line.width) / line.gaps.length;
|
|
233
|
+
}
|
|
234
|
+
let penX = startX;
|
|
235
|
+
for (let wi = 0; wi < line.words.length; wi++) {
|
|
236
|
+
if (wi > 0)
|
|
237
|
+
penX += line.gaps[wi - 1] + gapExtra;
|
|
238
|
+
const word = line.words[wi];
|
|
239
|
+
for (const glyph of word.glyphs) {
|
|
240
|
+
const entry = glyph.entry;
|
|
241
|
+
const context = ensureBitmapTextPage(runtime, glyphSource, pages, entry.page);
|
|
242
|
+
if (context === null)
|
|
243
|
+
continue;
|
|
244
|
+
const quadX = penX + glyph.penWithinWord + entry.bearingX;
|
|
245
|
+
const quadY = baselineY - entry.bearingY;
|
|
246
|
+
let regionId = context.regionByCodepoint.get(glyph.codepoint);
|
|
247
|
+
if (regionId === undefined) {
|
|
248
|
+
addTextureAtlasRegion(context.page.atlas, entry.x, entry.y, entry.width, entry.height);
|
|
249
|
+
regionId = context.page.atlas.regions.length - 1;
|
|
250
|
+
context.regionByCodepoint.set(glyph.codepoint, regionId);
|
|
251
|
+
}
|
|
252
|
+
appendBitmapTextPageQuad(context.page, regionId, quadX, quadY);
|
|
253
|
+
if (quadX < minX)
|
|
254
|
+
minX = quadX;
|
|
255
|
+
if (quadY < minY)
|
|
256
|
+
minY = quadY;
|
|
257
|
+
if (quadX + entry.width > maxX)
|
|
258
|
+
maxX = quadX + entry.width;
|
|
259
|
+
if (quadY + entry.height > maxY)
|
|
260
|
+
maxY = quadY + entry.height;
|
|
261
|
+
}
|
|
262
|
+
penX += word.width;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (minX === Infinity) {
|
|
266
|
+
setEmptyRectangle(bounds);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
bounds.x = minX;
|
|
270
|
+
bounds.y = minY;
|
|
271
|
+
bounds.width = maxX - minX;
|
|
272
|
+
bounds.height = maxY - minY;
|
|
273
|
+
}
|
|
274
|
+
invalidateNodeLocalBounds(bitmapText);
|
|
275
|
+
}
|
|
223
276
|
function maxLineWidth(lines) {
|
|
224
277
|
let max = 0;
|
|
225
278
|
for (const line of lines)
|
|
@@ -233,4 +286,14 @@ function setEmptyRectangle(out) {
|
|
|
233
286
|
out.width = 0;
|
|
234
287
|
out.height = 0;
|
|
235
288
|
}
|
|
289
|
+
// How many times `updateBitmapText` re-runs a layout that a repack invalidated under it. Two would
|
|
290
|
+
// leave no room to distinguish "the first pass warmed the atlas" from "this string cannot settle";
|
|
291
|
+
// three is one full pass past a warm atlas, and every pass past that is the same evict-repack cycle.
|
|
292
|
+
const BITMAP_TEXT_LAYOUT_ATTEMPTS = 3;
|
|
293
|
+
// Two floats (x, y) per glyph quad — the vector2 (translation-only) transform stride the BitmapText
|
|
294
|
+
// renderer reads. Kept internal so callers never hand-write i*2.
|
|
295
|
+
const BITMAP_TEXT_TRANSFORM_STRIDE = 2;
|
|
296
|
+
const CARRIAGE_RETURN = 0x0d;
|
|
297
|
+
const SPACE = 0x20;
|
|
298
|
+
let _layoutGuard = null;
|
|
236
299
|
//# sourceMappingURL=updateBitmapText.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateBitmapText.js","sourceRoot":"","sources":["../src/updateBitmapText.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"updateBitmapText.js","sourceRoot":"","sources":["../src/updateBitmapText.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/bitmaptext",
|
|
3
|
-
"version": "0.5.1-next.
|
|
3
|
+
"version": "0.5.1-next.638.d4dbd4e",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,12 +38,13 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/scene2d": "0.5.1-next.
|
|
42
|
-
"@flighthq/geometry": "0.5.1-next.
|
|
43
|
-
"@flighthq/
|
|
44
|
-
"@flighthq/
|
|
45
|
-
"@flighthq/
|
|
46
|
-
"@flighthq/
|
|
41
|
+
"@flighthq/scene2d": "0.5.1-next.638.d4dbd4e",
|
|
42
|
+
"@flighthq/geometry": "0.5.1-next.638.d4dbd4e",
|
|
43
|
+
"@flighthq/log": "0.5.1-next.638.d4dbd4e",
|
|
44
|
+
"@flighthq/node": "0.5.1-next.638.d4dbd4e",
|
|
45
|
+
"@flighthq/texture": "0.5.1-next.638.d4dbd4e",
|
|
46
|
+
"@flighthq/textureatlas": "0.5.1-next.638.d4dbd4e",
|
|
47
|
+
"@flighthq/types": "0.5.1-next.638.d4dbd4e"
|
|
47
48
|
},
|
|
48
49
|
"devDependencies": {
|
|
49
50
|
"@flighthq/glyphatlas": "*",
|
package/src/bitmapText.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
createBitmapTextRuntime,
|
|
12
12
|
getBitmapTextBounds,
|
|
13
13
|
getBitmapTextPages,
|
|
14
|
+
isBitmapTextGlyphLayoutStale,
|
|
14
15
|
reserveBitmapText,
|
|
15
16
|
setBitmapTextAlign,
|
|
16
17
|
setBitmapTextGlyphSource,
|
|
@@ -35,6 +36,7 @@ function createTestGlyphSource(): GlyphSource {
|
|
|
35
36
|
getGlyphAtlasImage: (page = 0) => (page === 0 ? image : null),
|
|
36
37
|
getGlyphEntry: (cp) => entries.get(cp) ?? null,
|
|
37
38
|
getGlyphKerning: () => 0,
|
|
39
|
+
getGlyphLayoutVersion: () => 0,
|
|
38
40
|
getGlyphMetrics: () => ({ ascent: 8, descent: 2, lineGap: 0 }),
|
|
39
41
|
};
|
|
40
42
|
}
|
|
@@ -125,6 +127,8 @@ describe('createBitmapTextRuntime', () => {
|
|
|
125
127
|
const runtime = createBitmapTextRuntime();
|
|
126
128
|
expect(runtime.localBoundsRectangle).toBeNull();
|
|
127
129
|
expect(runtime.pages).toEqual([]);
|
|
130
|
+
// Below every real version, so a node that has never been laid out reads stale.
|
|
131
|
+
expect(runtime.glyphLayoutVersion).toBe(-1);
|
|
128
132
|
});
|
|
129
133
|
});
|
|
130
134
|
|
|
@@ -149,6 +153,34 @@ describe('getBitmapTextPages', () => {
|
|
|
149
153
|
});
|
|
150
154
|
});
|
|
151
155
|
|
|
156
|
+
describe('isBitmapTextGlyphLayoutStale', () => {
|
|
157
|
+
it('reads stale before the first layout and settled after it', () => {
|
|
158
|
+
const text = createBitmapText(createTestGlyphSource(), { text: 'AB' });
|
|
159
|
+
expect(isBitmapTextGlyphLayoutStale(text)).toBe(true);
|
|
160
|
+
updateBitmapText(text);
|
|
161
|
+
expect(isBitmapTextGlyphLayoutStale(text)).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// A node with nothing bound has no rects to go stale, so it must not report work to do — otherwise
|
|
165
|
+
// a per-frame refresh would re-lay-out every empty node in the scene forever.
|
|
166
|
+
it('reads settled for a node with no glyph source', () => {
|
|
167
|
+
expect(isBitmapTextGlyphLayoutStale(createBitmapText(null, { text: 'AB' }))).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Version numbering is per source, so a stamp from one source says nothing about another and this
|
|
171
|
+
// query does NOT detect a rebind: two sources at version 0 compare equal. That is why
|
|
172
|
+
// `setBitmapTextGlyphSource` documents an explicit `updateBitmapText` afterwards rather than leaving
|
|
173
|
+
// the rebind to the refresh path. Asserted so a later reader does not mistake the gap for coverage.
|
|
174
|
+
it('does not report a rebound glyph source as stale', () => {
|
|
175
|
+
const text = createBitmapText(createTestGlyphSource(), { text: 'AB' });
|
|
176
|
+
updateBitmapText(text);
|
|
177
|
+
|
|
178
|
+
setBitmapTextGlyphSource(text, createTestGlyphSource());
|
|
179
|
+
|
|
180
|
+
expect(isBitmapTextGlyphLayoutStale(text)).toBe(false);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
152
184
|
describe('reserveBitmapText', () => {
|
|
153
185
|
it('grows each page quad-array capacity', () => {
|
|
154
186
|
const text = createBitmapText(createTestGlyphSource());
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createGlyphAtlas, createGlyphSourceFromGlyphAtlas } from '@flighthq/glyphatlas/contract';
|
|
2
|
+
import { setLogSink } from '@flighthq/log/contract';
|
|
3
|
+
import type { GlyphAtlas, GlyphRasterizerBackend, LogEntry } from '@flighthq/types/contract';
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { createBitmapText } from './bitmapText';
|
|
7
|
+
import { disableBitmapTextGuards, enableBitmapTextGuards } from './enableBitmapTextGuards';
|
|
8
|
+
import { updateBitmapText } from './updateBitmapText';
|
|
9
|
+
|
|
10
|
+
let entries: LogEntry[];
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
entries = [];
|
|
14
|
+
setLogSink((entry) => entries.push(entry));
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
disableBitmapTextGuards();
|
|
19
|
+
setLogSink(null);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function messages(): string {
|
|
23
|
+
return entries.map((e) => String((e.data as { message?: unknown } | undefined)?.message ?? '')).join('\n');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Holds exactly nine 8x8 glyphs, so a string of more distinct characters than that can never have all
|
|
27
|
+
// of them resident at once: laying out its tail evicts its head, every pass invalidates the last, and
|
|
28
|
+
// the layout cannot settle however many passes it is given.
|
|
29
|
+
function createTinyGlyphAtlas(): GlyphAtlas {
|
|
30
|
+
const backend: GlyphRasterizerBackend = {
|
|
31
|
+
rasterize: () => ({
|
|
32
|
+
advance: 8,
|
|
33
|
+
bearingX: 0,
|
|
34
|
+
bearingY: 8,
|
|
35
|
+
height: 8,
|
|
36
|
+
pixels: new Uint8ClampedArray(8 * 8 * 4),
|
|
37
|
+
width: 8,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
return createGlyphAtlas({
|
|
41
|
+
fontFamily: 'block',
|
|
42
|
+
fontSize: 8,
|
|
43
|
+
height: 32,
|
|
44
|
+
padding: 1,
|
|
45
|
+
rasterizerBackend: backend,
|
|
46
|
+
width: 32,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe('disableBitmapTextGuards', () => {
|
|
51
|
+
it('stops the guard reporting later layouts', () => {
|
|
52
|
+
enableBitmapTextGuards();
|
|
53
|
+
disableBitmapTextGuards();
|
|
54
|
+
const source = createGlyphSourceFromGlyphAtlas(createTinyGlyphAtlas());
|
|
55
|
+
updateBitmapText(createBitmapText(source, { text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }));
|
|
56
|
+
expect(messages()).toBe('');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('enableBitmapTextGuards', () => {
|
|
61
|
+
it('says nothing for a layout that settles', () => {
|
|
62
|
+
enableBitmapTextGuards();
|
|
63
|
+
const source = createGlyphSourceFromGlyphAtlas(createTinyGlyphAtlas());
|
|
64
|
+
updateBitmapText(createBitmapText(source, { text: 'ABC' }));
|
|
65
|
+
expect(messages()).toBe('');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Without this the failure is silent and looks like a rendering bug: the text draws, every quad is
|
|
69
|
+
// where layout put it, and the pixels under them belong to other glyphs.
|
|
70
|
+
it('warns when a string needs more glyphs at once than the atlas can hold', () => {
|
|
71
|
+
enableBitmapTextGuards();
|
|
72
|
+
const source = createGlyphSourceFromGlyphAtlas(createTinyGlyphAtlas());
|
|
73
|
+
updateBitmapText(createBitmapText(source, { text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }));
|
|
74
|
+
expect(messages()).toContain('never settled');
|
|
75
|
+
expect(messages()).toContain('Enlarge the glyph atlas');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -2,14 +2,22 @@ import {
|
|
|
2
2
|
createGlyphAtlas,
|
|
3
3
|
createGlyphSourceFromGlyphAtlas,
|
|
4
4
|
createStubGlyphRasterizerBackend,
|
|
5
|
+
getGlyphAtlasBitmap,
|
|
5
6
|
setGlyphRasterizerBackend,
|
|
6
7
|
} from '@flighthq/glyphatlas/contract';
|
|
7
8
|
import { getTextureSource } from '@flighthq/texture/contract';
|
|
8
|
-
import type {
|
|
9
|
-
|
|
9
|
+
import type {
|
|
10
|
+
Bitmap,
|
|
11
|
+
GlyphAtlas,
|
|
12
|
+
GlyphEntry,
|
|
13
|
+
GlyphRasterizerBackend,
|
|
14
|
+
GlyphSource,
|
|
15
|
+
Image,
|
|
16
|
+
} from '@flighthq/types/contract';
|
|
17
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
10
18
|
|
|
11
|
-
import { createBitmapText, getBitmapTextBounds, getBitmapTextPages } from './bitmapText';
|
|
12
|
-
import { updateBitmapText } from './updateBitmapText';
|
|
19
|
+
import { createBitmapText, getBitmapTextBounds, getBitmapTextPages, isBitmapTextGlyphLayoutStale } from './bitmapText';
|
|
20
|
+
import { refreshBitmapTextGlyphLayout, setBitmapTextLayoutGuard, updateBitmapText } from './updateBitmapText';
|
|
13
21
|
|
|
14
22
|
// A deterministic single-page glyph source: every visible glyph is 6x8 with advance 10 and bearingY 8
|
|
15
23
|
// (so line tops sit at y=0), a space advances 5 with no pixels, and the pair (A, B) kerns by -2. All
|
|
@@ -28,6 +36,7 @@ function createTestGlyphSource(): GlyphSource {
|
|
|
28
36
|
getGlyphAtlasImage: (page = 0) => (page === 0 ? image : null),
|
|
29
37
|
getGlyphEntry: (cp) => entries.get(cp) ?? null,
|
|
30
38
|
getGlyphKerning: (l, r) => kerning.get((l << 16) | r) ?? 0,
|
|
39
|
+
getGlyphLayoutVersion: () => 0,
|
|
31
40
|
getGlyphMetrics: () => ({ ascent: 8, descent: 2, lineGap: 0 }),
|
|
32
41
|
};
|
|
33
42
|
}
|
|
@@ -44,12 +53,234 @@ function createTwoPageGlyphSource(): { source: GlyphSource; page0Image: Image; p
|
|
|
44
53
|
getGlyphAtlasImage: (page = 0) => (page === 0 ? page0Image : page === 1 ? page1Image : null),
|
|
45
54
|
getGlyphEntry: (cp) => entries.get(cp) ?? null,
|
|
46
55
|
getGlyphKerning: () => 0,
|
|
56
|
+
getGlyphLayoutVersion: () => 0,
|
|
47
57
|
getGlyphMetrics: () => ({ ascent: 8, descent: 2, lineGap: 0 }),
|
|
48
58
|
};
|
|
49
59
|
return { source, page0Image, page1Image };
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
// Rasterizes every glyph as a solid block whose RED channel IS the codepoint, so a rect in the atlas
|
|
63
|
+
// bitmap identifies which glyph actually occupies it. That is the oracle this file needs: after a
|
|
64
|
+
// repack every rect is still a well-formed rect over real pixels, so only reading the pixels can tell a
|
|
65
|
+
// correct region from one that now covers the wrong glyph. Codepoints stay under 0x100 for the channel
|
|
66
|
+
// to hold them exactly.
|
|
67
|
+
function createCodepointColorRasterizerBackend(size: number): GlyphRasterizerBackend {
|
|
68
|
+
return {
|
|
69
|
+
rasterize(codepoint) {
|
|
70
|
+
const pixels = new Uint8ClampedArray(size * size * 4);
|
|
71
|
+
for (let i = 0; i < size * size; i++) {
|
|
72
|
+
pixels[i * 4] = codepoint;
|
|
73
|
+
pixels[i * 4 + 3] = 0xff;
|
|
74
|
+
}
|
|
75
|
+
return { advance: size, bearingX: 0, bearingY: size, height: size, pixels, width: size };
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// The codepoint whose pixels sit at `region`'s top-left in the atlas bitmap, per the rasterizer above.
|
|
81
|
+
function readGlyphCodepointAtRegion(bitmap: Readonly<Bitmap>, x: number, y: number): number {
|
|
82
|
+
return bitmap.data[(y * bitmap.width + x) * 4];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// A source that REPLACES 'A' with a relocated entry and bumps its layout version once, on the `nth`
|
|
86
|
+
// glyph lookup — a repack landing part way through a layout, driven at the seam so the moment is exact.
|
|
87
|
+
//
|
|
88
|
+
// Replacing the object rather than moving the one already handed out is the whole point. A repack that
|
|
89
|
+
// only relocates survivors mutates their entries in place, so a pass that captured those references
|
|
90
|
+
// picks the move up for free and never needs re-running. The case that genuinely needs it is a repack
|
|
91
|
+
// that DROPS a glyph: the reference the pass captured is orphaned at its old rect, and the next lookup
|
|
92
|
+
// mints a fresh entry somewhere else. `nth: 0` never fires, for the held-still half of the pair.
|
|
93
|
+
function createMidLayoutRelocatingGlyphSource(nth: number): GlyphSource {
|
|
94
|
+
const image = {} as Image;
|
|
95
|
+
const entries = new Map<number, GlyphEntry>();
|
|
96
|
+
for (let codepoint = 0x41; codepoint <= 0x5a; codepoint++) {
|
|
97
|
+
entries.set(codepoint, { advance: 10, bearingX: 0, bearingY: 8, height: 8, page: 0, width: 6, x: 0, y: 0 });
|
|
98
|
+
}
|
|
99
|
+
let lookups = 0;
|
|
100
|
+
let version = 0;
|
|
101
|
+
return {
|
|
102
|
+
getGlyphAtlasImage: (page = 0) => (page === 0 ? image : null),
|
|
103
|
+
getGlyphEntry(codepoint) {
|
|
104
|
+
lookups++;
|
|
105
|
+
if (lookups === nth) {
|
|
106
|
+
entries.set(0x41, { ...entries.get(0x41)!, x: RELOCATED_GLYPH_X });
|
|
107
|
+
version++;
|
|
108
|
+
}
|
|
109
|
+
return entries.get(codepoint) ?? null;
|
|
110
|
+
},
|
|
111
|
+
getGlyphKerning: () => 0,
|
|
112
|
+
getGlyphLayoutVersion: () => version,
|
|
113
|
+
getGlyphMetrics: () => ({ ascent: 8, descent: 2, lineGap: 0 }),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// An atlas small enough that a modest run of glyphs exhausts it and forces the evict-then-repack path
|
|
118
|
+
// that relocates everything already cached — the situation `refreshBitmapTextGlyphLayout` exists for.
|
|
119
|
+
function createRepackingGlyphAtlas(): GlyphAtlas {
|
|
120
|
+
return createGlyphAtlas({
|
|
121
|
+
fontFamily: 'codepoint-color',
|
|
122
|
+
fontSize: 8,
|
|
123
|
+
height: 32,
|
|
124
|
+
padding: 1,
|
|
125
|
+
rasterizerBackend: createCodepointColorRasterizerBackend(8),
|
|
126
|
+
width: 32,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
describe('refreshBitmapTextGlyphLayout', () => {
|
|
131
|
+
// The defect this whole seam exists for: a repack relocates and re-uses atlas space, so a node laid
|
|
132
|
+
// out BEFORE it keeps regions that now cover other glyphs. Nothing about the node, the entry object,
|
|
133
|
+
// or the atlas image changes to reveal it — the pixels under the baked rect are the only witness.
|
|
134
|
+
it('re-bakes regions that a repack left covering the wrong glyphs', () => {
|
|
135
|
+
const atlas = createRepackingGlyphAtlas();
|
|
136
|
+
const source = createGlyphSourceFromGlyphAtlas(atlas);
|
|
137
|
+
const text = createBitmapText(source, { text: 'A' });
|
|
138
|
+
updateBitmapText(text);
|
|
139
|
+
|
|
140
|
+
const baked = getBitmapTextPages(text)[0].atlas.regions[0];
|
|
141
|
+
const bitmap = getGlyphAtlasBitmap(atlas);
|
|
142
|
+
expect(readGlyphCodepointAtRegion(bitmap, baked.x, baked.y)).toBe(0x41);
|
|
143
|
+
|
|
144
|
+
// Fill the atlas from elsewhere — a second text node, a direct lookup, any other consumer of the
|
|
145
|
+
// same atlas would do. 'A' is the least recently used, so it is evicted and its space handed on.
|
|
146
|
+
for (let codepoint = 0x42; codepoint <= 0x5a; codepoint++) source.getGlyphEntry(codepoint);
|
|
147
|
+
|
|
148
|
+
expect(isBitmapTextGlyphLayoutStale(text)).toBe(true);
|
|
149
|
+
expect(readGlyphCodepointAtRegion(bitmap, baked.x, baked.y)).not.toBe(0x41);
|
|
150
|
+
|
|
151
|
+
expect(refreshBitmapTextGlyphLayout(text)).toBe(true);
|
|
152
|
+
const rebaked = getBitmapTextPages(text)[0].atlas.regions[0];
|
|
153
|
+
expect(readGlyphCodepointAtRegion(bitmap, rebaked.x, rebaked.y)).toBe(0x41);
|
|
154
|
+
expect(isBitmapTextGlyphLayoutStale(text)).toBe(false);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('does nothing and reports nothing done when the glyph placement has not moved', () => {
|
|
158
|
+
const atlas = createRepackingGlyphAtlas();
|
|
159
|
+
const text = createBitmapText(createGlyphSourceFromGlyphAtlas(atlas), { text: 'A' });
|
|
160
|
+
updateBitmapText(text);
|
|
161
|
+
const before = getBitmapTextPages(text)[0].atlas.regions[0];
|
|
162
|
+
|
|
163
|
+
expect(refreshBitmapTextGlyphLayout(text)).toBe(false);
|
|
164
|
+
expect(getBitmapTextPages(text)[0].atlas.regions[0]).toBe(before);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('lays out a node that has never been laid out', () => {
|
|
168
|
+
const atlas = createRepackingGlyphAtlas();
|
|
169
|
+
const text = createBitmapText(createGlyphSourceFromGlyphAtlas(atlas), { text: 'A' });
|
|
170
|
+
|
|
171
|
+
expect(refreshBitmapTextGlyphLayout(text)).toBe(true);
|
|
172
|
+
expect(getBitmapTextPages(text)[0].instanceCount).toBe(1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Fan-out is the reason the seam versions rather than the consumer comparing rects: one atlas backs
|
|
176
|
+
// every node bound to it, and the node that triggered the repack must not be the only one repaired.
|
|
177
|
+
it('repairs every node sharing the atlas, not only the one that forced the repack', () => {
|
|
178
|
+
const atlas = createRepackingGlyphAtlas();
|
|
179
|
+
const source = createGlyphSourceFromGlyphAtlas(atlas);
|
|
180
|
+
const first = createBitmapText(source, { text: 'A' });
|
|
181
|
+
updateBitmapText(first);
|
|
182
|
+
|
|
183
|
+
const second = createBitmapText(source, { text: 'BCDEFGHIJKLMNOPQRSTUVWXYZ' });
|
|
184
|
+
updateBitmapText(second);
|
|
185
|
+
|
|
186
|
+
expect(isBitmapTextGlyphLayoutStale(first)).toBe(true);
|
|
187
|
+
refreshBitmapTextGlyphLayout(first);
|
|
188
|
+
const bitmap = getGlyphAtlasBitmap(atlas);
|
|
189
|
+
const region = getBitmapTextPages(first)[0].atlas.regions[0];
|
|
190
|
+
expect(readGlyphCodepointAtRegion(bitmap, region.x, region.y)).toBe(0x41);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe('setBitmapTextLayoutGuard', () => {
|
|
195
|
+
afterEach(() => setBitmapTextLayoutGuard(null));
|
|
196
|
+
|
|
197
|
+
it('reports the reason and the passes spent when a layout cannot settle', () => {
|
|
198
|
+
const seen: [string, number][] = [];
|
|
199
|
+
setBitmapTextLayoutGuard((reason, attempts) => seen.push([reason, attempts]));
|
|
200
|
+
const source = createGlyphSourceFromGlyphAtlas(createRepackingGlyphAtlas());
|
|
201
|
+
|
|
202
|
+
updateBitmapText(createBitmapText(source, { text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }));
|
|
203
|
+
|
|
204
|
+
expect(seen).toEqual([['layout-did-not-converge', 3]]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('stays silent for a layout that settles', () => {
|
|
208
|
+
const seen: string[] = [];
|
|
209
|
+
setBitmapTextLayoutGuard((reason) => seen.push(reason));
|
|
210
|
+
const source = createGlyphSourceFromGlyphAtlas(createRepackingGlyphAtlas());
|
|
211
|
+
|
|
212
|
+
updateBitmapText(createBitmapText(source, { text: 'AB' }));
|
|
213
|
+
|
|
214
|
+
expect(seen).toEqual([]);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('stops reporting once cleared with null', () => {
|
|
218
|
+
let calls = 0;
|
|
219
|
+
setBitmapTextLayoutGuard(() => (calls += 1));
|
|
220
|
+
const source = createGlyphSourceFromGlyphAtlas(createRepackingGlyphAtlas());
|
|
221
|
+
|
|
222
|
+
updateBitmapText(createBitmapText(source, { text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }));
|
|
223
|
+
setBitmapTextLayoutGuard(null);
|
|
224
|
+
updateBitmapText(createBitmapText(source, { text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' }));
|
|
225
|
+
|
|
226
|
+
expect(calls).toBe(1);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
52
230
|
describe('updateBitmapText', () => {
|
|
231
|
+
// A repack can land DURING a layout — a glyph late in the string evicting one placed early in it —
|
|
232
|
+
// and the pass that saw both placements is wrong about the first. The retry is what makes a single
|
|
233
|
+
// `updateBitmapText` call self-consistent rather than merely detectably stale afterwards.
|
|
234
|
+
// A pass that read a rect BEFORE the relocation and one that read it after cannot both be right, and
|
|
235
|
+
// the pass has no way to go back for the ones it already consumed. Re-running is what makes a single
|
|
236
|
+
// `updateBitmapText` call self-consistent rather than merely detectably stale afterwards.
|
|
237
|
+
it('re-runs a layout whose glyph placement moved part way through it', () => {
|
|
238
|
+
const source = createMidLayoutRelocatingGlyphSource(2);
|
|
239
|
+
const text = createBitmapText(source, { text: 'ABC' });
|
|
240
|
+
|
|
241
|
+
updateBitmapText(text);
|
|
242
|
+
|
|
243
|
+
// 'A' is the first region baked, and the relocation landed after the pass had already read it.
|
|
244
|
+
expect(getBitmapTextPages(text)[0].atlas.regions[0].x).toBe(RELOCATED_GLYPH_X);
|
|
245
|
+
expect(isBitmapTextGlyphLayoutStale(text)).toBe(false);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('does not re-run a layout whose glyph placement held still', () => {
|
|
249
|
+
const source = createMidLayoutRelocatingGlyphSource(0);
|
|
250
|
+
const text = createBitmapText(source, { text: 'ABC' });
|
|
251
|
+
const seen: string[] = [];
|
|
252
|
+
setBitmapTextLayoutGuard((reason) => seen.push(reason));
|
|
253
|
+
|
|
254
|
+
updateBitmapText(text);
|
|
255
|
+
|
|
256
|
+
expect(getBitmapTextPages(text)[0].atlas.regions[0].x).toBe(0);
|
|
257
|
+
expect(seen).toEqual([]);
|
|
258
|
+
setBitmapTextLayoutGuard(null);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// The end-to-end companion to the seam-driven pair above, over the real atlas. It passes without the
|
|
262
|
+
// retry as well — this atlas only relocates, and relocation moves the entry the pass is holding — so
|
|
263
|
+
// it is here to prove the whole path stays coherent, not to exercise the re-run.
|
|
264
|
+
it('ends coherent through an atlas that repacks during the layout', () => {
|
|
265
|
+
const atlas = createRepackingGlyphAtlas();
|
|
266
|
+
const source = createGlyphSourceFromGlyphAtlas(atlas);
|
|
267
|
+
// Fill most of the atlas with glyphs this text does not use, so laying it out has to evict and
|
|
268
|
+
// repack partway through — over the glyphs the same pass already placed.
|
|
269
|
+
for (let codepoint = 0x61; codepoint <= 0x66; codepoint++) source.getGlyphEntry(codepoint);
|
|
270
|
+
const text = createBitmapText(source, { text: 'ABCDEF' });
|
|
271
|
+
updateBitmapText(text);
|
|
272
|
+
|
|
273
|
+
const bitmap = getGlyphAtlasBitmap(atlas);
|
|
274
|
+
const page = getBitmapTextPages(text)[0];
|
|
275
|
+
expect(page.instanceCount).toBeGreaterThan(0);
|
|
276
|
+
for (let id = 0; id < page.atlas.regions.length; id++) {
|
|
277
|
+
const region = page.atlas.regions[id];
|
|
278
|
+
const codepoint = readGlyphCodepointAtRegion(bitmap, region.x, region.y);
|
|
279
|
+
expect(codepoint).toBe(0x41 + id);
|
|
280
|
+
}
|
|
281
|
+
expect(isBitmapTextGlyphLayoutStale(text)).toBe(false);
|
|
282
|
+
});
|
|
283
|
+
|
|
53
284
|
it('emits non-empty glyph quads end-to-end from a stub-fed glyph atlas (headless, issue #8)', () => {
|
|
54
285
|
// The real headless path: glyphatlas → GlyphSource → bitmaptext, with the deterministic stub
|
|
55
286
|
// rasterizer standing in for a web font that is unavailable in jsdom/CI. Proves BitmapText renders
|
|
@@ -246,3 +477,7 @@ describe('updateBitmapText', () => {
|
|
|
246
477
|
expect(bounds.height).toBe(8);
|
|
247
478
|
});
|
|
248
479
|
});
|
|
480
|
+
|
|
481
|
+
// Where the mid-layout relocation moves 'A' to — distinct from every rect the source starts with, so
|
|
482
|
+
// a region carrying it can only have come from a pass that ran AFTER the move.
|
|
483
|
+
const RELOCATED_GLYPH_X = 17;
|