@flighthq/bitmapfont 0.3.0-next.1315.1d4eb64 → 0.3.0-next.1392.f0bd0cc

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.
@@ -0,0 +1,19 @@
1
+ import type { BitmapFont, GlyphAtlas } from '@flighthq/types/contract';
2
+ /** Freezes a live `GlyphAtlas` into a static `BitmapFont` — the dynamic cache's contents as a fixed
3
+ * font, so a build step can bake what a run actually rasterized and ship it instead of rasterizing
4
+ * again at startup.
5
+ *
6
+ * This lives in `@flighthq/bitmapfont` rather than in `@flighthq/glyphatlas` because a conversion
7
+ * constructor belongs to the package that owns the PRODUCT, the same way `createImageResourceFromBitmap`
8
+ * lives in `@flighthq/image`. Every type is in `@flighthq/types`, so reading a `GlyphAtlas` here costs
9
+ * no dependency on the atlas package, and `glyphatlas` gains no edge to `textureatlas` or `texture`.
10
+ *
11
+ * The page is CPU-only: the atlas's own bitmap is already a valid `TextureSource` under the flat
12
+ * texture model, so the font's page is a `TextureAtlas` over a bitmap-sourced `Texture` with no GPU
13
+ * work and no readback. Whichever backend consumes the font uploads it later through the ordinary
14
+ * kind-keyed resolver path, exactly as it would for any other bitmap-sourced texture.
15
+ *
16
+ * The result is a snapshot: later rasterization into the source atlas does not reach the font, and the
17
+ * font holds its own reference to the atlas bitmap. */
18
+ export declare function createBitmapFontFromGlyphAtlas(atlas: Readonly<GlyphAtlas>): BitmapFont;
19
+ //# sourceMappingURL=bitmapFontFromGlyphAtlas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitmapFontFromGlyphAtlas.d.ts","sourceRoot":"","sources":["../src/bitmapFontFromGlyphAtlas.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAuB,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAI5F;;;;;;;;;;;;;;;wDAewD;AACxD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,UAAU,CAyBtF"}
@@ -0,0 +1,46 @@
1
+ import { createTexture } from '@flighthq/texture/contract';
2
+ import { createTextureAtlas } from '@flighthq/textureatlas/contract';
3
+ import { createBitmapFont } from './bitmapFont';
4
+ /** Freezes a live `GlyphAtlas` into a static `BitmapFont` — the dynamic cache's contents as a fixed
5
+ * font, so a build step can bake what a run actually rasterized and ship it instead of rasterizing
6
+ * again at startup.
7
+ *
8
+ * This lives in `@flighthq/bitmapfont` rather than in `@flighthq/glyphatlas` because a conversion
9
+ * constructor belongs to the package that owns the PRODUCT, the same way `createImageResourceFromBitmap`
10
+ * lives in `@flighthq/image`. Every type is in `@flighthq/types`, so reading a `GlyphAtlas` here costs
11
+ * no dependency on the atlas package, and `glyphatlas` gains no edge to `textureatlas` or `texture`.
12
+ *
13
+ * The page is CPU-only: the atlas's own bitmap is already a valid `TextureSource` under the flat
14
+ * texture model, so the font's page is a `TextureAtlas` over a bitmap-sourced `Texture` with no GPU
15
+ * work and no readback. Whichever backend consumes the font uploads it later through the ordinary
16
+ * kind-keyed resolver path, exactly as it would for any other bitmap-sourced texture.
17
+ *
18
+ * The result is a snapshot: later rasterization into the source atlas does not reach the font, and the
19
+ * font holds its own reference to the atlas bitmap. */
20
+ export function createBitmapFontFromGlyphAtlas(atlas) {
21
+ const runtime = atlas.runtime;
22
+ const glyphs = [];
23
+ for (const [codepoint, entry] of runtime.entries) {
24
+ glyphs.push({
25
+ advance: entry.advance,
26
+ bearingX: entry.bearingX,
27
+ bearingY: entry.bearingY,
28
+ codepoint,
29
+ height: entry.height,
30
+ // The dynamic atlas is a single growing bitmap, so every glyph is on page 0.
31
+ page: 0,
32
+ width: entry.width,
33
+ x: entry.x,
34
+ y: entry.y,
35
+ });
36
+ }
37
+ // Regions stay empty: a bitmap font addresses its glyphs through its own glyph table, so the page
38
+ // exists to carry the pixels, not to name sub-rectangles.
39
+ const page = createTextureAtlas({ texture: createTexture({ source: runtime.bitmap }) });
40
+ return createBitmapFont({
41
+ glyphs,
42
+ metrics: { ...runtime.metrics },
43
+ pages: [page],
44
+ });
45
+ }
46
+ //# sourceMappingURL=bitmapFontFromGlyphAtlas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitmapFontFromGlyphAtlas.js","sourceRoot":"","sources":["../src/bitmapFontFromGlyphAtlas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;;;;;;;;;wDAewD;AACxD,MAAM,UAAU,8BAA8B,CAAC,KAA2B;IACxE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,MAAM,MAAM,GAA0B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC;YACV,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,SAAS;YACT,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,6EAA6E;YAC7E,IAAI,EAAE,CAAC;YACP,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,CAAC,EAAE,KAAK,CAAC,CAAC;YACV,CAAC,EAAE,KAAK,CAAC,CAAC;SACX,CAAC,CAAC;IACL,CAAC;IACD,kGAAkG;IAClG,0DAA0D;IAC1D,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,OAAO,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IACxF,OAAO,gBAAgB,CAAC;QACtB,MAAM;QACN,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE;QAC/B,KAAK,EAAE,CAAC,IAAI,CAAC;KACd,CAAC,CAAC;AACL,CAAC"}
@@ -1,3 +1,4 @@
1
+ export * from './bitmapFontFromGlyphAtlas';
1
2
  export * from './bitmapFont';
2
3
  export * from './bitmapFontGlyphSource';
3
4
  //# sourceMappingURL=contract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC"}
package/dist/contract.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './bitmapFontFromGlyphAtlas';
1
2
  export * from './bitmapFont';
2
3
  export * from './bitmapFontGlyphSource';
3
4
  //# sourceMappingURL=contract.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC"}
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { createBitmapFont, createGlyphSourceFromBitmapFont, getBitmapFontGlyph, getBitmapFontKerning, getBitmapFontMetrics, getBitmapFontPage, getBitmapFontPages, } from './contract';
1
+ export { createBitmapFontFromGlyphAtlas, createBitmapFont, createGlyphSourceFromBitmapFont, getBitmapFontGlyph, getBitmapFontKerning, getBitmapFontMetrics, getBitmapFontPage, getBitmapFontPages, } from './contract';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,+BAA+B,EAC/B,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,gBAAgB,EAChB,+BAA+B,EAC/B,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { createBitmapFont, createGlyphSourceFromBitmapFont, getBitmapFontGlyph, getBitmapFontKerning, getBitmapFontMetrics, getBitmapFontPage, getBitmapFontPages, } from './contract';
1
+ export { createBitmapFontFromGlyphAtlas, createBitmapFont, createGlyphSourceFromBitmapFont, getBitmapFontGlyph, getBitmapFontKerning, getBitmapFontMetrics, getBitmapFontPage, getBitmapFontPages, } 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,gBAAgB,EAChB,+BAA+B,EAC/B,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,gBAAgB,EAChB,+BAA+B,EAC/B,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flighthq/bitmapfont",
3
- "version": "0.3.0-next.1315.1d4eb64",
3
+ "version": "0.3.0-next.1392.f0bd0cc",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/flighthq/flight.git",
@@ -36,10 +36,12 @@
36
36
  "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
37
37
  },
38
38
  "dependencies": {
39
- "@flighthq/textureatlas": "0.3.0-next.1315.1d4eb64",
40
- "@flighthq/types": "0.3.0-next.1315.1d4eb64"
39
+ "@flighthq/texture": "0.3.0-next.1392.f0bd0cc",
40
+ "@flighthq/textureatlas": "0.3.0-next.1392.f0bd0cc",
41
+ "@flighthq/types": "0.3.0-next.1392.f0bd0cc"
41
42
  },
42
43
  "devDependencies": {
44
+ "@flighthq/glyphatlas": "*",
43
45
  "typescript": "^5.3.0"
44
46
  },
45
47
  "description": "Static bitmap font — an immutable pre-baked atlas + glyph/kerning/metrics tables implementing the GlyphSource seam, the static sibling of the dynamic glyphatlas",
@@ -0,0 +1,81 @@
1
+ import { createGlyphAtlas, getGlyphAtlasEntry, setGlyphRasterizerBackend } from '@flighthq/glyphatlas/contract';
2
+ import type { GlyphRasterizerBackend } from '@flighthq/types/contract';
3
+ import { afterEach, describe, expect, it } from 'vitest';
4
+
5
+ import { getBitmapFontGlyph, getBitmapFontMetrics } from './bitmapFont';
6
+ import { createBitmapFontFromGlyphAtlas } from './bitmapFontFromGlyphAtlas';
7
+
8
+ function backendProducing(width: number, height: number): GlyphRasterizerBackend {
9
+ return {
10
+ measureMetrics: () => ({ ascent: 12, descent: 3, lineGap: 1 }),
11
+ rasterize: () => ({
12
+ advance: width,
13
+ bearingX: 1,
14
+ bearingY: 2,
15
+ height,
16
+ pixels: new Uint8ClampedArray(width * height * 4),
17
+ width,
18
+ }),
19
+ };
20
+ }
21
+
22
+ describe('createBitmapFontFromGlyphAtlas', () => {
23
+ afterEach(() => setGlyphRasterizerBackend(null));
24
+
25
+ function bakedFromTwoGlyphs() {
26
+ setGlyphRasterizerBackend(backendProducing(8, 8));
27
+ const atlas = createGlyphAtlas({ fontFamily: 'mock', fontSize: 16, height: 64, width: 64 });
28
+ getGlyphAtlasEntry(atlas, 65);
29
+ getGlyphAtlasEntry(atlas, 66);
30
+ return { atlas, font: createBitmapFontFromGlyphAtlas(atlas) };
31
+ }
32
+
33
+ it('carries every cached glyph across with its rect and pen data', () => {
34
+ const { atlas, font } = bakedFromTwoGlyphs();
35
+
36
+ const source = getGlyphAtlasEntry(atlas, 65)!;
37
+ const baked = getBitmapFontGlyph(font, 65)!;
38
+
39
+ expect(baked).not.toBeNull();
40
+ expect(baked.x).toBe(source.x);
41
+ expect(baked.y).toBe(source.y);
42
+ expect(baked.width).toBe(source.width);
43
+ expect(baked.advance).toBe(source.advance);
44
+ expect(baked.bearingY).toBe(source.bearingY);
45
+ expect(getBitmapFontGlyph(font, 66)).not.toBeNull();
46
+ });
47
+
48
+ it('carries the atlas metrics rather than re-deriving them', () => {
49
+ const { font } = bakedFromTwoGlyphs();
50
+ expect(getBitmapFontMetrics(font)).toEqual({ ascent: 12, descent: 3, lineGap: 1 });
51
+ });
52
+
53
+ // The page is CPU-only: the atlas bitmap is already a valid texture source under the flat model, so
54
+ // baking needs no GPU work and no readback.
55
+ it('builds a single page whose texture is sourced from the atlas bitmap', () => {
56
+ const { atlas, font } = bakedFromTwoGlyphs();
57
+
58
+ expect(font.pages).toHaveLength(1);
59
+ expect(font.pages[0]!.texture!.source).toBe(atlas.runtime.bitmap);
60
+ });
61
+
62
+ // A bake is a snapshot. Rasterizing more glyphs into the source atlas afterwards must not reach the
63
+ // font, or "bake" would just be another view of a live cache.
64
+ it('does not gain glyphs rasterized into the atlas after the bake', () => {
65
+ const { atlas, font } = bakedFromTwoGlyphs();
66
+
67
+ getGlyphAtlasEntry(atlas, 67);
68
+
69
+ expect(getBitmapFontGlyph(font, 67)).toBeNull();
70
+ });
71
+
72
+ it('bakes an empty atlas to a font with no glyphs rather than failing', () => {
73
+ setGlyphRasterizerBackend(backendProducing(8, 8));
74
+ const atlas = createGlyphAtlas({ fontFamily: 'mock', fontSize: 16, height: 64, width: 64 });
75
+
76
+ const font = createBitmapFontFromGlyphAtlas(atlas);
77
+
78
+ expect(getBitmapFontGlyph(font, 65)).toBeNull();
79
+ expect(font.pages).toHaveLength(1);
80
+ });
81
+ });