diff options
Diffstat (limited to 'symbols/symbols.c')
| -rw-r--r-- | symbols/symbols.c | 80 |
1 files changed, 72 insertions, 8 deletions
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); +} |
