aboutsummaryrefslogtreecommitdiffstats
path: root/symbols
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-27 14:54:53 +0100
committereunhhu <[email protected]>2026-09-27 14:54:53 +0100
commit993871c6dfa19cf4e0bd4a16c9aad26cb0025d70 (patch)
treea83e5bc75d859ca5bee96b26700a9419e687cd1a /symbols
parent2c1e29780da41bba13ac84ad01472d550e6d02e8 (diff)
downloadvega-993871c6dfa19cf4e0bd4a16c9aad26cb0025d70.tar.gz
vega-993871c6dfa19cf4e0bd4a16c9aad26cb0025d70.zip
Draw the TUI's cells as pixels with the symbols' glyphs
vega pixels runs the same scripts as tui --dump and writes each frame as an image: the view's cells (only those on screen are evaluated) become glyphs, each drawn once per size with the exact area of its curves, in its tone's colour on its row's background. No page, no scene in between. A 1920x1350 grid takes 12 ms a frame here, writing the image included. Symbol files carry the font's line now (format 3: ascent and descent where format 2 kept zeros), the cell height and baseline; and the build tool takes fonts in order, later ones filling the codes earlier ones lack: a CJK font alone drew ambiguous-width arrows two cells wide in a one-cell grid, which the new check caught, so a monospace Latin font comes first and Hangul after.
Diffstat (limited to 'symbols')
-rw-r--r--symbols/symbols-gl.c12
-rw-r--r--symbols/symbols-gl.h7
-rw-r--r--symbols/symbols-tool.c104
-rw-r--r--symbols/symbols.c80
-rw-r--r--symbols/symbols.h20
5 files changed, 163 insertions, 60 deletions
diff --git a/symbols/symbols-gl.c b/symbols/symbols-gl.c
index c1ce64e..d91a5dc 100644
--- a/symbols/symbols-gl.c
+++ b/symbols/symbols-gl.c
@@ -87,14 +87,6 @@ bool vega_gl_open(VegaGl *gl, char *error, size_t error_size) {
return true;
}
-/* The pixels a symbol reaches at scale, one more on each side. */
-static VegaPlace place_of(const VegaSymbol *symbol, float scale) {
- if (!symbol->count) return (VegaPlace){0};
- int left = (int)floorf(symbol->left * scale) - 1, right = (int)ceilf(symbol->right * scale) + 1;
- int top = (int)ceilf(symbol->top * scale) + 1, bottom = (int)floorf(symbol->bottom * scale) - 1;
- return (VegaPlace){0, 0, right - left, top - bottom, left, top};
-}
-
bool vega_gl_atlas(VegaGl *gl, const VegaSymbols *s, float scale, const uint32_t *wanted, uint32_t count, VegaAtlas *out,
char *error, size_t error_size) {
(void)gl;
@@ -112,7 +104,7 @@ bool vega_gl_atlas(VegaGl *gl, const VegaSymbols *s, float scale, const uint32_t
uint32_t index = wanted ? wanted[i] : i;
if (index >= s->symbols) continue;
VegaSymbol symbol = vega_symbol_at(s, index);
- VegaPlace p = place_of(&symbol, scale);
+ VegaPlace p = vega_symbol_place(&symbol, scale);
area += (uint64_t)p.width * (uint64_t)p.height;
}
int width = 64;
@@ -122,7 +114,7 @@ bool vega_gl_atlas(VegaGl *gl, const VegaSymbols *s, float scale, const uint32_t
uint32_t index = wanted ? wanted[i] : i;
if (index >= s->symbols) continue;
VegaSymbol symbol = vega_symbol_at(s, index);
- VegaPlace p = place_of(&symbol, scale);
+ VegaPlace p = vega_symbol_place(&symbol, scale);
if (!p.width) continue;
if (p.width > width) width = p.width;
if (x + p.width > width) { x = 0; y += shelf; shelf = 0; }
diff --git a/symbols/symbols-gl.h b/symbols/symbols-gl.h
index 919a114..cc316a4 100644
--- a/symbols/symbols-gl.h
+++ b/symbols/symbols-gl.h
@@ -14,17 +14,12 @@ typedef struct {
GLint target;
} VegaGl;
-/* Where each symbol is in an atlas: its rectangle there (x, y, width, height)
- * and the pixel offset of that rectangle's top left from the symbol's origin
- * (right, up). A symbol with nothing to draw has an empty rectangle. */
-typedef struct { int32_t x, y, width, height, left, top; } VegaPlace;
-
typedef struct {
GLuint texture; /* 8-bit coverage */
int width, height;
float scale; /* pixels per unit */
uint32_t symbols;
- VegaPlace *places; /* per symbol index */
+ VegaPlace *places; /* per symbol index: its box and where the atlas keeps it */
} VegaAtlas;
/* A symbol at a pen position (whole pixels from the target's top left, on the
diff --git a/symbols/symbols-tool.c b/symbols/symbols-tool.c
index b003978..b705d84 100644
--- a/symbols/symbols-tool.c
+++ b/symbols/symbols-tool.c
@@ -1,7 +1,7 @@
#define _POSIX_C_SOURCE 200809L
/* vega-symbols: build symbol files from fonts and SVG icons, and measure them
* against FreeType (docs/design/002-symbol-format.md).
- * vega-symbols build [--font FONT.ttf --codes 0x20-0x7e,...] [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym
+ * vega-symbols build [--font FONT.ttf [--face N] --codes 0x20-0x7e,...]... [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym
* vega-symbols compare FONT.ttf OUT.vsym --sizes 12,16,32,64
* vega-symbols info OUT.vsym
* vega-symbols render OUT.vsym CODE PIXELS OUT.pgm
@@ -407,7 +407,9 @@ static int by_code(const void *a, const void *b) {
return (x > y) - (x < y);
}
-static void write_symbols(const char *out, uint16_t units, Built *built, size_t count, const Path *path) {
+/* line: a font's ascent and descent in the build's units (0 and 0 without a font). */
+static void write_symbols(const char *out, uint16_t units, const long line[2], Built *built, size_t count, const Path *path) {
+ long ascent = line[0], descent = line[1];
qsort(built, count, sizeof(*built), by_code);
for (size_t i = 1; i < count; ++i) if (built[i].code == built[i - 1].code) fail("a code is given twice");
Points points = {0};
@@ -422,16 +424,21 @@ static void write_symbols(const char *out, uint16_t units, Built *built, size_t
bool even = units % 2 == 0 && units > 16;
for (size_t i = 0; even && i < points.count; ++i) even = points.items[i].x % 2 == 0 && points.items[i].y % 2 == 0;
for (size_t i = 0; even && i < count; ++i) even = built[i].advance % 2 == 0;
+ even = even && ascent % 2 == 0 && descent % 2 == 0;
if (!even) break;
for (size_t i = 0; i < points.count; ++i) { points.items[i].x /= 2; points.items[i].y /= 2; }
for (size_t i = 0; i < count; ++i) built[i].advance = (int16_t)(built[i].advance / 2);
+ ascent /= 2, descent /= 2;
units /= 2;
}
+ if (ascent > INT16_MAX || descent < INT16_MIN) fail("the font's line does not fit 16 bits");
size_t size = VEGA_SYMBOLS_HEADER + count * VEGA_SYMBOL_BYTES + points.count * VEGA_POINT_BYTES;
unsigned char *bytes = calloc(1, size);
if (!bytes) fail("out of memory");
- memcpy(bytes, "VEGASYM2", 8);
+ memcpy(bytes, "VEGASYM3", 8);
put16(bytes + 8, units);
+ put16(bytes + 24, (int)ascent);
+ put16(bytes + 26, (int)descent);
put32(bytes + 12, (uint32_t)count);
put32(bytes + 16, (uint32_t)points.count);
put32(bytes + 20, (uint32_t)size);
@@ -506,48 +513,68 @@ static double seconds(void) {
return (double)t.tv_sec + (double)t.tv_nsec / 1e9;
}
-#define BUILD_USAGE "usage: vega-symbols build [--font FONT.ttf --codes 0x20-0x7e,...] [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym"
+#define BUILD_USAGE \
+ "usage: vega-symbols build [--font FONT.ttf [--face N] --codes 0x20-0x7e,...]... [--svg CODE=ICON.svg ...] [--units N] -o OUT.vsym"
+
+/* Fonts in order, each with the codes to take from it: a code an earlier
+ * font gave is not taken again, so later fonts fill what earlier ones lack
+ * (a monospace Latin font, then a CJK one for Hangul). */
+typedef struct { const char *path, *codes; long face; } Font;
static int build(int argc, char **argv) {
- const char *font = NULL, *out = NULL, *codes = NULL, **icons = calloc((size_t)argc, sizeof(*icons));
- size_t icon_count = 0;
+ const char *out = NULL, **icons = calloc((size_t)argc, sizeof(*icons));
+ Font *fonts = calloc((size_t)argc, sizeof(*fonts));
+ size_t icon_count = 0, font_count = 0;
long units = 0;
for (int i = 2; i < argc; i += 2) {
if (i + 1 == argc) fail(BUILD_USAGE);
if (!strcmp(argv[i], "-o")) out = argv[i + 1];
- else if (!strcmp(argv[i], "--font")) font = argv[i + 1];
- else if (!strcmp(argv[i], "--codes")) codes = argv[i + 1];
+ else if (!strcmp(argv[i], "--font")) fonts[font_count++] = (Font){argv[i + 1], NULL, 0};
+ else if (!strcmp(argv[i], "--face") && font_count) fonts[font_count - 1].face = strtol(argv[i + 1], NULL, 10); /* in a .ttc */
+ else if (!strcmp(argv[i], "--codes") && font_count) fonts[font_count - 1].codes = argv[i + 1];
else if (!strcmp(argv[i], "--units")) units = strtol(argv[i + 1], NULL, 10);
else if (!strcmp(argv[i], "--svg")) icons[icon_count++] = argv[i + 1];
else fail(BUILD_USAGE);
}
- if (!out || !font != !codes) fail(BUILD_USAGE);
+ if (!out) fail(BUILD_USAGE);
+ for (size_t f = 0; f < font_count; ++f) if (!fonts[f].codes) fail(BUILD_USAGE);
FT_Library library = NULL;
- FT_Face face = NULL;
- if (font && (FT_Init_FreeType(&library) || FT_New_Face(library, font, 0, &face))) fail("cannot open the font");
- /* Twice a font's units by default: TrueType's implied points, halfway
- * between two control points, then land on whole units. (FreeType finds
- * them by integer halving, so outlines are doubled before it walks them.) */
- if (!units) units = face ? 2 * face->units_per_EM : 4096;
+ FT_Face *faces = calloc(font_count + 1, sizeof(*faces));
+ if (font_count && FT_Init_FreeType(&library)) fail("cannot start FreeType");
+ for (size_t f = 0; f < font_count; ++f)
+ if (FT_New_Face(library, fonts[f].path, fonts[f].face, &faces[f])) fail("cannot open a font");
+ /* Twice the first font's units by default: TrueType's implied points,
+ * halfway between two control points, then land on whole units. (FreeType
+ * finds them by integer halving, so outlines are doubled before it walks
+ * them.) Other fonts are scaled into the same units. */
+ if (!units) units = font_count ? 2 * faces[0]->units_per_EM : 4096;
if (units < 16 || units > 16384) fail("--units: 16 to 16384");
- uint32_t *list = NULL;
- size_t count = codes ? codes_of(codes, &list) : 0;
- Built *built = calloc(count + icon_count + 1, sizeof(*built));
- Path path = {.font_scale = face ? (double)units / (2.0 * face->units_per_EM) : 1};
+ size_t capacity = icon_count + 1;
+ uint32_t **lists = calloc(font_count + 1, sizeof(*lists));
+ size_t *counts = calloc(font_count + 1, sizeof(*counts));
+ for (size_t f = 0; f < font_count; ++f) capacity += counts[f] = codes_of(fonts[f].codes, &lists[f]);
+ Built *built = calloc(capacity, sizeof(*built));
+ Path path = {.font_scale = 1};
size_t made = 0;
FT_Outline_Funcs walk = {ft_move, ft_line, ft_conic, ft_cubic, 0, 0};
- for (size_t i = 0; i < count; ++i) {
- FT_UInt glyph = FT_Get_Char_Index(face, list[i]);
- if (!glyph || FT_Load_Glyph(face, glyph, FT_LOAD_NO_SCALE)) continue; /* the font lacks it */
- size_t first = path.count;
- path.open = false;
- if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
- FT_Outline_Transform(&face->glyph->outline, &(FT_Matrix){0x20000, 0, 0, 0x20000});
- if (FT_Outline_Decompose(&face->glyph->outline, &walk, &path)) fail("cannot walk an outline");
+ for (size_t f = 0; f < font_count; ++f) {
+ FT_Face face = faces[f];
+ path.font_scale = (double)units / (2.0 * face->units_per_EM);
+ for (size_t i = 0; i < counts[f]; ++i) {
+ bool given = false;
+ for (size_t k = 0; k < made && !given; ++k) given = built[k].code == lists[f][i];
+ FT_UInt glyph = given ? 0 : FT_Get_Char_Index(face, lists[f][i]);
+ if (!glyph || FT_Load_Glyph(face, glyph, FT_LOAD_NO_SCALE)) continue; /* given, or the font lacks it */
+ size_t first = path.count;
+ path.open = false;
+ if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+ FT_Outline_Transform(&face->glyph->outline, &(FT_Matrix){0x20000, 0, 0, 0x20000});
+ if (FT_Outline_Decompose(&face->glyph->outline, &walk, &path)) fail("cannot walk an outline");
+ }
+ close_path(&path);
+ long advance = lround((double)face->glyph->metrics.horiAdvance * units / face->units_per_EM);
+ built[made++] = (Built){lists[f][i], first, path.count - first, (int16_t)(advance > INT16_MAX ? INT16_MAX : advance)};
}
- close_path(&path);
- long advance = lround((double)face->glyph->metrics.horiAdvance * units / face->units_per_EM);
- built[made++] = (Built){list[i], first, path.count - first, (int16_t)(advance > INT16_MAX ? INT16_MAX : advance)};
}
for (size_t i = 0; i < icon_count; ++i) {
char *equals = strchr(icons[i], '=');
@@ -558,11 +585,20 @@ static int build(int argc, char **argv) {
svg_icon(&path, equals + 1, (uint16_t)units, &advance);
built[made++] = (Built){(uint32_t)strtoul(icons[i], NULL, 0), first, path.count - first, advance};
}
- write_symbols(out, (uint16_t)units, built, made, &path);
- if (face) FT_Done_Face(face);
+ /* The first font's line as FreeType reports it (hhea or typo metrics). */
+ long line[2] = {font_count ? lround((double)faces[0]->ascender * units / faces[0]->units_per_EM) : 0,
+ font_count ? lround((double)faces[0]->descender * units / faces[0]->units_per_EM) : 0};
+ write_symbols(out, (uint16_t)units, line, built, made, &path);
+ for (size_t f = 0; f < font_count; ++f) {
+ FT_Done_Face(faces[f]);
+ free(lists[f]);
+ }
if (library) FT_Done_FreeType(library);
+ free(faces);
+ free(lists);
+ free(counts);
+ free(fonts);
free(icons);
- free(list);
free(built);
free(path.items);
return 0;
@@ -698,7 +734,7 @@ static int info(int argc, char **argv) {
if (argc < 3) fail("usage: vega-symbols info SYMBOLS.vsym");
char *bytes;
VegaSymbols s = open_symbols(argv[2], &bytes);
- printf("{\"units\":%u,\"symbols\":[", s.units);
+ printf("{\"units\":%u,\"ascent\":%d,\"descent\":%d,\"symbols\":[", s.units, s.ascent, s.descent);
for (uint32_t i = 0; i < s.symbols; ++i) {
VegaSymbol symbol = vega_symbol_at(&s, i);
double area = 0;
diff --git a/symbols/symbols.c b/symbols/symbols.c
index 6f355f1..f00639a 100644
--- a/symbols/symbols.c
+++ b/symbols/symbols.c
@@ -4,7 +4,7 @@
#include <stdlib.h>
#include <string.h>
-static const unsigned char magic[8] = {'V', 'E', 'G', 'A', 'S', 'Y', 'M', '2'};
+static const unsigned char magic[8] = {'V', 'E', 'G', 'A', 'S', 'Y', 'M', '3'};
static uint16_t u16(const unsigned char *p) { return (uint16_t)(p[0] | p[1] << 8); }
static int16_t i16(const unsigned char *p) { return (int16_t)u16(p); }
@@ -17,7 +17,7 @@ VegaSymbol vega_symbol_at(const VegaSymbols *s, uint32_t index) {
/* A point is two i16s, each its coordinate times two plus a flag: off the
* curve in x, starting a contour in y. */
-VegaPoint vega_point(const VegaSymbols *s, uint32_t index) {
+VegaPoint vega_symbols_point(const VegaSymbols *s, uint32_t index) {
const unsigned char *p = s->bytes + VEGA_SYMBOLS_HEADER + (size_t)s->symbols * VEGA_SYMBOL_BYTES +
(size_t)index * VEGA_POINT_BYTES;
int x = i16(p), y = i16(p + 2);
@@ -29,9 +29,9 @@ VegaPoint vega_point(const VegaSymbols *s, uint32_t index) {
bool vega_symbols_open(VegaSymbols *s, const unsigned char *bytes, size_t size, char *error, size_t error_size) {
if (size < VEGA_SYMBOLS_HEADER || memcmp(bytes, magic, sizeof(magic))) REFUSE("not a symbol file");
- *s = (VegaSymbols){bytes, size, u16(bytes + 8), u32(bytes + 12), u32(bytes + 16)};
+ *s = (VegaSymbols){bytes, size, u16(bytes + 8), i16(bytes + 24), i16(bytes + 26), u32(bytes + 12), u32(bytes + 16)};
uint64_t expected = VEGA_SYMBOLS_HEADER + (uint64_t)s->symbols * VEGA_SYMBOL_BYTES + (uint64_t)s->points * VEGA_POINT_BYTES;
- if (u16(bytes + 10) || u32(bytes + 24) || !s->units) REFUSE("header");
+ if (u16(bytes + 10) || !s->units || s->ascent < s->descent) REFUSE("header");
if (u32(bytes + 20) != size || expected != size) REFUSE("size: %zu bytes for %u symbols and %u points", size, s->symbols, s->points);
for (uint32_t i = 0; i < s->symbols; ++i) {
VegaSymbol symbol = vega_symbol_at(s, i);
@@ -42,7 +42,7 @@ bool vega_symbols_open(VegaSymbols *s, const unsigned char *bytes, size_t size,
if (symbol.left > symbol.right || symbol.bottom > symbol.top) REFUSE("symbol %u: bounds", i);
/* An off point then always has both neighbours within the symbol. */
if (symbol.count) {
- VegaPoint first = vega_point(s, symbol.first), last = vega_point(s, symbol.first + symbol.count - 1);
+ VegaPoint first = vega_symbols_point(s, symbol.first), last = vega_symbols_point(s, symbol.first + symbol.count - 1);
if (first.off || !first.move) REFUSE("symbol %u: its first point does not start a contour on the curve", i);
if (last.off) REFUSE("symbol %u: its last point is off the curve", i);
}
@@ -62,15 +62,15 @@ bool vega_symbol(const VegaSymbols *s, uint32_t code, VegaSymbol *out) {
}
bool vega_symbol_curve(const VegaSymbols *s, const VegaSymbol *symbol, uint32_t index, VegaCurve *out) {
- VegaPoint p = vega_point(s, index);
+ VegaPoint p = vega_symbols_point(s, index);
if (p.off) {
- VegaPoint a = vega_point(s, index - 1), b = vega_point(s, index + 1);
+ VegaPoint a = vega_symbols_point(s, index - 1), b = vega_symbols_point(s, index + 1);
*out = (VegaCurve){a.off ? (a.x + p.x) * 0.5f : a.x, a.off ? (a.y + p.y) * 0.5f : a.y, p.x, p.y,
b.off ? (p.x + b.x) * 0.5f : b.x, b.off ? (p.y + b.y) * 0.5f : b.y};
return true;
}
if (index + 1 >= symbol->first + symbol->count) return false;
- VegaPoint b = vega_point(s, index + 1);
+ VegaPoint b = vega_symbols_point(s, index + 1);
if (b.off || b.move) return false;
*out = (VegaCurve){p.x, p.y, (p.x + b.x) * 0.5f, (p.y + b.y) * 0.5f, b.x, b.y};
return true;
@@ -183,3 +183,67 @@ void vega_symbol_raster(const VegaSymbols *s, const VegaSymbol *symbol, double s
}
free(curves);
}
+
+VegaPlace vega_symbol_place(const VegaSymbol *symbol, float scale) {
+ if (!symbol->count) return (VegaPlace){0};
+ int left = (int)floorf(symbol->left * scale) - 1, right = (int)ceilf(symbol->right * scale) + 1;
+ int top = (int)ceilf(symbol->top * scale) + 1, bottom = (int)floorf(symbol->bottom * scale) - 1;
+ return (VegaPlace){0, 0, right - left, top - bottom, left, top};
+}
+
+/* An open table from code to bitmap, twice as large as what it holds. */
+typedef struct { uint32_t code; bool used, missing; VegaBitmap bitmap; } Slot;
+struct VegaGlyphs {
+ const VegaSymbols *symbols;
+ float scale;
+ Slot *slots;
+ size_t capacity, count;
+};
+
+VegaGlyphs *vega_glyphs_open(const VegaSymbols *s, float scale) {
+ VegaGlyphs *glyphs = calloc(1, sizeof(*glyphs));
+ if (!glyphs) return NULL;
+ *glyphs = (VegaGlyphs){s, scale, calloc(256, sizeof(Slot)), 256, 0};
+ if (!glyphs->slots) { free(glyphs); return NULL; }
+ return glyphs;
+}
+
+static Slot *slot_of(Slot *slots, size_t capacity, uint32_t code) {
+ size_t at = (code * 2654435761u) & (capacity - 1);
+ while (slots[at].used && slots[at].code != code) at = (at + 1) & (capacity - 1);
+ return &slots[at];
+}
+
+const VegaBitmap *vega_glyph(VegaGlyphs *glyphs, uint32_t code) {
+ Slot *slot = slot_of(glyphs->slots, glyphs->capacity, code);
+ if (slot->used) return slot->missing ? NULL : &slot->bitmap;
+ if (2 * (glyphs->count + 1) > glyphs->capacity) {
+ Slot *larger = calloc(2 * glyphs->capacity, sizeof(Slot));
+ if (!larger) return NULL;
+ for (size_t i = 0; i < glyphs->capacity; ++i)
+ if (glyphs->slots[i].used) *slot_of(larger, 2 * glyphs->capacity, glyphs->slots[i].code) = glyphs->slots[i];
+ free(glyphs->slots);
+ glyphs->slots = larger;
+ glyphs->capacity *= 2;
+ slot = slot_of(glyphs->slots, glyphs->capacity, code);
+ }
+ VegaSymbol symbol;
+ *slot = (Slot){.code = code, .used = true};
+ ++glyphs->count;
+ if (!vega_symbol(glyphs->symbols, code, &symbol)) { slot->missing = true; return NULL; }
+ VegaPlace place = vega_symbol_place(&symbol, glyphs->scale);
+ unsigned char *pixels = place.width ? malloc((size_t)place.width * (size_t)place.height) : NULL;
+ if (place.width && !pixels) { slot->missing = true; return NULL; }
+ if (pixels)
+ vega_symbol_raster(glyphs->symbols, &symbol, glyphs->scale, place.left / glyphs->scale, place.top / glyphs->scale,
+ place.width, place.height, pixels, (size_t)place.width);
+ slot->bitmap = (VegaBitmap){place, pixels};
+ return &slot->bitmap;
+}
+
+void vega_glyphs_close(VegaGlyphs *glyphs) {
+ if (!glyphs) return;
+ for (size_t i = 0; i < glyphs->capacity; ++i) free((void *)glyphs->slots[i].bitmap.pixels);
+ free(glyphs->slots);
+ free(glyphs);
+}
diff --git a/symbols/symbols.h b/symbols/symbols.h
index 20fbd22..0af3412 100644
--- a/symbols/symbols.h
+++ b/symbols/symbols.h
@@ -23,7 +23,8 @@ typedef struct {
typedef struct {
const unsigned char *bytes;
size_t size;
- uint16_t units; /* per em */
+ uint16_t units; /* per em */
+ int16_t ascent, descent; /* a font's line, in units from the baseline, y up (0 and 0 for icons) */
uint32_t symbols, points;
} VegaSymbols;
@@ -35,7 +36,7 @@ bool vega_symbols_open(VegaSymbols *, const unsigned char *bytes, size_t size, c
/* The symbol of code (a binary search), or false. */
bool vega_symbol(const VegaSymbols *, uint32_t code, VegaSymbol *out);
VegaSymbol vega_symbol_at(const VegaSymbols *, uint32_t index);
-VegaPoint vega_point(const VegaSymbols *, uint32_t index);
+VegaPoint vega_symbols_point(const VegaSymbols *, uint32_t index);
/* The curve that point index of symbol begins, if any: an off point is the
* control of one, and an on point followed by another on point in the same
* contour starts a line. Every curve of a symbol is had once this way, from
@@ -47,9 +48,24 @@ uint32_t vega_symbol_curves(const VegaSymbols *, const VegaSymbol *, VegaCurve *
/* The coverage (0 to 1) of the pixel whose center is (x, y) in units, at
* scale pixels per unit: the exact area the curves' outline covers. */
double vega_coverage(const VegaCurve *, uint32_t count, double scale, double x, double y);
+/* The pixels a symbol reaches at scale (pixels per unit), one more on each
+ * side: width and height, and the offset of the box's top left from the
+ * symbol's origin (right, up). x and y are where a set of bitmaps (an atlas)
+ * keeps it. A symbol with nothing to draw has an empty box. */
+typedef struct { int32_t x, y, width, height, left, top; } VegaPlace;
+VegaPlace vega_symbol_place(const VegaSymbol *, float scale);
/* width x height 8-bit coverage, rows top first, stride bytes apart; pixel
* (i, j) has its center at (left + (i + 0.5) / scale, top - (j + 0.5) / scale)
* in units. */
void vega_symbol_raster(const VegaSymbols *, const VegaSymbol *, double scale, double left, double top, int width,
int height, unsigned char *out, size_t stride);
+
+/* Symbols drawn once at one scale and kept, for surfaces that compose them
+ * on the CPU: each the first time it is asked for. */
+typedef struct { VegaPlace place; const unsigned char *pixels; } VegaBitmap; /* pixels: place.width per row */
+typedef struct VegaGlyphs VegaGlyphs;
+VegaGlyphs *vega_glyphs_open(const VegaSymbols *, float scale);
+/* The bitmap of code, or NULL when the file has no such symbol. */
+const VegaBitmap *vega_glyph(VegaGlyphs *, uint32_t code);
+void vega_glyphs_close(VegaGlyphs *);
#endif