tecs.gfx.fonts

Font identity, glyph metrics, and the atlas that carries glyphs to the backend.

A game names a font once and every text that selects it shares the atlas that font fills. newTTF opens the file and reads its metrics; a glyph is rasterized and packed the first time something asks for it, and the atlas crosses to the backend as one ordinary tecs.gfx.images upload.

const font, reason = tecs.gfx.fonts.newTTF({source = "assets/fonts/JetBrainsMono-ExtraBold.ttf"})
assert(font, reason)

const glyph = tecs.gfx.fonts.glyph(font, 0x41)
const image = tecs.gfx.fonts.imageOf(font)

Every metric this module returns is in em units, so multiplying one by a text's em size gives world units. That keeps one atlas usable at every displayed size and keeps the layout in tecs.gfx.text free of the raster's own resolution.

The default "sdf" raster stores a signed distance field, which scales without regenerating glyphs. Choose a size near the largest ordinary on-screen size. An "alpha" raster stores direct coverage instead and draws correctly through a plain textured quad; a distance field needs a shader that recovers coverage by smoothstepping around the half-way value, so a backend without one draws an "sdf" font as a soft gradient rather than as text.

Residency is observed rather than waited on, exactly as an image's is. imageOf queues whatever the atlas gained since the last call and answers with the id; tecs.gfx.images.stateOf on that id then reports where the upload stands.

Module contents

Constructors

ConstructorDescription
newTTFReads a font's bytes and its metrics.

Types

TypeKindDescription
FontrecordA loaded font and the atlas its glyphs live in.
GlyphrecordOne glyph's place in the atlas and its box relative to the pen.
RastertypeSelects how a font's glyph images are stored.
TTFOptionstypeThe options newTTF accepts.

Functions

FunctionKindDescription
advanceOffunctionReturns one codepoint's horizontal advance in em units.
atlasNameOffunctionThe name the atlas is uploaded under.
atlasSizefunctionReturns the atlas dimensions in texels.
epochfunctionReturns how many times any atlas has been laid out again.
findfunctionReturns the font one name resolves to.
glyphfunctionReturns the glyph one codepoint selects, making it resident on first use.
glyphIndexOffunctionReturns the glyph index one codepoint selects.
hasGlyphfunctionReports whether the font carries a glyph for one codepoint.
imageOffunctionReturns the atlas image id, queuing whatever the atlas gained.
kerningOffunctionReturns the kerning between two codepoints in em units.
releasefunctionDrops the atlas from the backend.
residentGlyphsfunctionReturns how many glyphs the atlas holds.

Values

ValueKindDescription
DEFAULT_SIZEvariableThe point size a font rasterizes at when the caller names none.
SDF_SPREADvariableThe texels a distance field reaches on each side of an outline.

Constructors#

newTTFconstructor#

function newTTF(options: TTFOptions): Font?, string?

Reads a font's bytes and its metrics.

Loading a name a font already holds replaces what that name resolves to. A Text already carrying the earlier font keeps it, so a reload changes what new text picks up rather than what old text draws.

Arguments

NameTypeDescription
optionsTTFOptions

the source path or bytes, the identity, the raster point size, and the raster mode; name defaults to source, size to DEFAULT_SIZE, and raster to "sdf"

Returns

TypeDescription
Font?

the font, or nil when the file could not be read or parsed

string?

why the font could not be loaded, when unsuccessful

Raises

  • when neither source nor bytes is given, when the size is not greater than zero, or when the raster is not one of its declared values

Types#

Fontrecord#

record Font
    name: string
    source: string
    size: number
    raster: Raster
    ascent: number
    descent: number
    lineGap: number
    lineHeight: number
end

A loaded font and the atlas its glyphs live in.

Immutable in everything a game reads. The atlas behind it grows as glyphs are asked for, which is why imageOf rather than a stored id is what extraction reads.

Fields

name#
name: string

Read-only. Reports the identity a snapshot stores and find resolves.

source#
source: string

Read-only. Reports the path the bytes were read from, or the empty string for a font handed over as bytes.

size#
size: number

Read-only. Reports the point size glyphs are rasterized at. It is not the size text is drawn at.

raster#
raster: Raster

Read-only. Reports whether the atlas holds a distance field or direct coverage.

ascent#
ascent: number

Read-only. Reports the distance from the baseline to the top of the typographic ascent, in em units.

descent#
descent: number

Read-only. Reports the distance from the baseline to the bottom of the typographic descent, in em units. It is negative in every ordinary font.

lineGap#
lineGap: number

Read-only. Reports the extra leading between two lines, in em units.

lineHeight#
lineHeight: number

Read-only. Reports the baseline-to-baseline distance in em units, which is the ascent less the descent plus the leading.

Glyphrecord#

record Glyph
    index: integer
    advance: number
    left: number
    top: number
    right: number
    bottom: number
    u0: number
    v0: number
    u1: number
    v1: number
    width: integer
    height: integer
end

One glyph's place in the atlas and its box relative to the pen.

The four plane edges are in em units with y increasing downward from the baseline, so a glyph drawn at em size s with its pen at x, y covers x + left * s to x + right * s horizontally. A glyph with no ink, which is what a space is, reports four zeroes and no atlas region.

Fields

index#
index: integer

Read-only. Reports the face's glyph index, which is zero for the .notdef glyph a font draws for a character it does not carry.

advance#
advance: number

Read-only. Reports the horizontal advance in em units.

left#
left: number

Read-only. Reports the left edge of the drawn box in em units.

top#
top: number

Read-only. Reports the top edge of the drawn box in em units, above the baseline and so usually negative.

right#
right: number

Read-only. Reports the right edge of the drawn box in em units.

bottom#
bottom: number

Read-only. Reports the bottom edge of the drawn box in em units.

u0#
u0: number

Read-only. Reports the left edge of the atlas region, from zero to one.

v0#
v0: number

Read-only. Reports the top edge of the atlas region, from zero to one.

u1#
u1: number

Read-only. Reports the right edge of the atlas region, from zero to one.

v1#
v1: number

Read-only. Reports the bottom edge of the atlas region, from zero to one.

width#
width: integer

Read-only. Reports the region width in atlas texels, which is zero for a glyph with no ink.

height#
height: integer

Read-only. Reports the region height in atlas texels.

Rastertype#

type Raster = "sdf" | "alpha"

Selects how a font's glyph images are stored.

These identifiers reach a game's font configuration and are a compatibility surface.

TTFOptionstype#

type TTFOptions = {
    source: string?,
    bytes: string?,
    name: string?,
    size: number?,
    raster: Raster?
}

The options newTTF accepts.

Functions#

advanceOffunction#

function advanceOf(font: Font, codepoint: integer): number

Returns one codepoint's horizontal advance in em units.

Reads the horizontal metrics directly, so it costs nothing and makes no glyph resident. Measuring a string that will never be drawn goes through here rather than through glyph.

Arguments

NameTypeDescription
fontFont

the loaded font

codepointinteger

the Unicode scalar to measure

Returns

TypeDescription
number

the advance in em units

atlasNameOffunction#

function atlasNameOf(name: string): string

The name the atlas is uploaded under.

A glyph entity stores this through its Sprite, and a snapshot stores a sprite's image by name, so the prefix is a compatibility surface.

Arguments

NameTypeDescription
namestring

the font identity

Returns

TypeDescription
string

the image name, which is stable for the life of the font

atlasSizefunction#

function atlasSize(font: Font): integer, integer

Returns the atlas dimensions in texels.

Arguments

NameTypeDescription
fontFont

the loaded font

Returns

TypeDescription
integer

the width, then the height, both powers of two

integer

epochfunction#

function epoch(): integer

Returns how many times any atlas has been laid out again.

Growing an atlas moves every region in it, so anything holding a texture coordinate read from a Glyph compares this against what it last saw and reads the regions again when the two differ.

Returns

TypeDescription
integer

a count that only increases, shared by every loaded font

findfunction#

function find(name: string): Font?

Returns the font one name resolves to.

Arguments

NameTypeDescription
namestring

the font identity

Returns

TypeDescription
Font?

the font, or nil when nothing has been loaded under that name

glyphfunction#

function glyph(font: Font, codepoint: integer): Glyph

Returns the glyph one codepoint selects, making it resident on first use.

A codepoint the font does not carry answers with the face's .notdef glyph, which is the box a font draws for a missing character, rather than with nil. A codepoint with no ink, which is what a space is, answers with a glyph whose advance is set and whose region is empty.

The returned record is the font's own and is updated in place when the atlas grows. Read what you need from it rather than keeping it across frames.

Arguments

NameTypeDescription
fontFont

the loaded font

codepointinteger

the Unicode scalar to draw

Returns

TypeDescription
Glyph

the glyph, whose plane box and advance are in em units

glyphIndexOffunction#

function glyphIndexOf(font: Font, codepoint: integer): integer

Returns the glyph index one codepoint selects.

Arguments

NameTypeDescription
fontFont

the loaded font

codepointinteger

the Unicode scalar to look up

Returns

TypeDescription
integer

the face's glyph index, which is zero for a codepoint the font does not carry

hasGlyphfunction#

function hasGlyph(font: Font, codepoint: integer): boolean

Reports whether the font carries a glyph for one codepoint.

Arguments

NameTypeDescription
fontFont

the loaded font

codepointinteger

the Unicode scalar to look up

Returns

TypeDescription
boolean

whether the font maps it to anything but .notdef

imageOffunction#

function imageOf(font: Font): integer

Returns the atlas image id, queuing whatever the atlas gained.

The id is permanent, so a caller may store it. Residency is not: call this once per frame that might have added a glyph and let tecs.gfx.images.stateOf report where the upload stands.

Arguments

NameTypeDescription
fontFont

the loaded font

Returns

TypeDescription
integer

the process-wide image id the atlas uploads under

kerningOffunction#

function kerningOf(font: Font, left: integer, right: integer): number

Returns the kerning between two codepoints in em units.

Only a format-zero kern table is read. A font that carries its kerning in GPOS alone reports zero for every pair, which lays the text out on its advance widths.

Arguments

NameTypeDescription
fontFont

the loaded font

leftinteger

the Unicode scalar on the left

rightinteger

the Unicode scalar on the right

Returns

TypeDescription
number

the adjustment to add after the left glyph's advance, in em units

releasefunction#

function release(font: Font): boolean, string?

Drops the atlas from the backend.

The glyphs stay packed and the id survives, so the next imageOf makes the same atlas resident again under the same id. Text drawn in between samples the backend's white fallback.

Arguments

NameTypeDescription
fontFont

the loaded font

Returns

TypeDescription
boolean

whether a release was queued, which is false before the first upload

string?

why nothing was queued, when unsuccessful

residentGlyphsfunction#

function residentGlyphs(font: Font): integer

Returns how many glyphs the atlas holds.

Counts every glyph asked for, including the inkless ones that occupy no region, because each of those cost a lookup and a metric.

Arguments

NameTypeDescription
fontFont

the loaded font

Returns

TypeDescription
integer

the resident glyph count

Values#

DEFAULT_SIZEvariable#

const DEFAULT_SIZE: number

The point size a font rasterizes at when the caller names none.

SDF_SPREADvariable#

const SDF_SPREAD: integer

The texels a distance field reaches on each side of an outline.

The same number is the padding around every glyph, because the field has to have somewhere to run before the region ends. A wider spread costs atlas room and buys a thicker outline or glow; four texels carry an ordinary antialiased edge at any size the atlas is used at.