diff --git a/main.c b/main.c index e9d80f4..71db615 100644 --- a/main.c +++ b/main.c @@ -22,28 +22,14 @@ #define ASSERT(expr) if (!expr) { fprintf(stderr, "[ASSERT] Assertion fail %s:%d\n", __FILE__, __LINE__); abort(); } +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + #define MAX_INTERSECTIONS 16 #define F32EPS 1e-5f -#define MAX_CONTOURS 16 +#define MAX_CONTOURS 256 -#define DEBUG_SHOW_POINTS 0 - -/* -int total_hits = 0; - for (int yy = 1; yy <= oversample_y; ++yy) { - for (int xx = 1; xx <= oversample_x; ++xx) { - u32 ncross = intersect_glyph(g, scale, x + norm_x * xx , y + norm_y * yy, width); - if (ncross) { - ++total_hits; - } - } - } - - if (total_hits) { - u32 brightness = (256 - total_hits * 256 / (oversample_x * oversample_y)) * 0.99f; - pixels[(y - baseline_correction) * width + x] = 0xFF000000 | brightness << 16 | brightness << 8 | brightness; - } -*/ +#define TTF_ENGINE_STACK 4096 typedef int64_t s64; typedef int32_t s32; @@ -66,6 +52,11 @@ struct v2 { int y; }; +struct v2u32 { + u32 x; + u32 y; +}; + struct v2f { f32 x; f32 y; @@ -107,6 +98,15 @@ struct font_directory { u32 glyf_offset; u32 hmtx_offset; u32 maxp_offset; + + u32 cvt_offset; + u32 cvt_size; + + u32 prep_offset; + u32 prep_size; + + u32 fpgm_offset; + u32 fpgm_size; }; union glyph_flag { @@ -140,6 +140,8 @@ enum compund_glyph_flag { struct maxp_table { u16 max_component_points; u16 max_component_contours; + u16 max_storage; + u16 max_fdefs; }; struct head_table { @@ -171,6 +173,7 @@ struct glyph { s16 xmin, ymin; s16 xmax, ymax; + s32 baseline; u16 advance; s16 lsb; @@ -186,26 +189,18 @@ struct ttf_font { struct head_table head; struct hhea_table hhea; - char *name; - + s16 *cvt; int cmap_format; - int glyf_offset; - int loca_offset; - int cmap_offset; - int hmtx_offset; + struct font_directory dir; + struct ttf_function *functions; + + u32 *storage; + + char *name; }; /*******************************/ - - -#include "ttf2.c" - -static Display *display; -static Window window; -static GC default_gc; -static XImage* xwindow_buffer; - static int clamp_u32(u32 val, u32 cap) { @@ -221,12 +216,10 @@ abs_f32(f32 v) return(result); } -static f32 +static int round_f32(f32 v) { - return(v); - -#if 0 +#if 1 int towards_zero = (int) v; f32 diff = abs_f32(v - towards_zero); @@ -235,9 +228,30 @@ round_f32(f32 v) } else { return(towards_zero); } +#else + return(v); #endif } +static int +ceil_f32(f32 v) +{ + int trunc = v; + if (v - trunc < F32EPS) { + return(trunc); + } + return(trunc + 1); +} + + +#include "ttf_engine.c" +#include "ttf2.c" + +static Display *display; +static Window window; +static GC default_gc; +static XImage* xwindow_buffer; + static int scanline_intersects_line(f32 y, struct v2f p0, struct v2f p1, f32 lasty, f32 *vx) { @@ -441,20 +455,24 @@ sort_intersections(struct intersection *intersections, int size) } static void -render_glyph(struct glyph g, struct line_contour *lines, - f32 scale, u32 *pixels, int width, int height, int at_x, int at_y) +render_glyph(struct glyph g, int px_size, struct line_contour *lines, + f32 scale, u32 *pixels, int width, int at_x, int at_y) { - int gwidth = round_f32((g.xmax - g.xmin) * scale) + 1; - int gheight = round_f32((g.ymax - g.ymin) * scale) + 1; + int oversample_y = 4; + if (px_size <= 12) { + oversample_y = 8; + } + + f32 oversample_step = 1.0f / (oversample_y + 1); + f32 oversample_norm = 1.0f / oversample_y; + + int gwidth = ceil_f32((g.xmax - g.xmin) * scale); + int gheight = px_size; struct intersection *intersections = malloc(lines->from[lines->ncontours] * sizeof(struct intersection)); f32 *accumulator = calloc(1, gwidth * gheight * sizeof(f32)); - int oversample_y = 5; - f32 oversample_step = 1.0f / (oversample_y + 1); - f32 oversample_norm = 1.0f / oversample_y; - for (int y = 0; y < gheight; ++y) { for (int yy = 1; yy <= oversample_y; ++yy) { u32 ncross = intersect_glyph(lines, y + oversample_step * yy, intersections); @@ -463,7 +481,7 @@ render_glyph(struct glyph g, struct line_contour *lines, int state = 0; - for (int i = 0; i < ncross - 1; ++i) { + for (u32 i = 0; i < ncross - 1; ++i) { struct intersection inter = intersections[i]; struct intersection next_inter = intersections[i + 1]; @@ -474,7 +492,7 @@ render_glyph(struct glyph g, struct line_contour *lines, f32 x1 = next_inter.x; int x_from = x0; - int x_to = x1; + int x_to = (x1 > gwidth - 1 ? gwidth - 1 : x1); f32 start_brightness = (x_from + 1 - x0); f32 end_brightness = (x1 - x_to); @@ -491,19 +509,21 @@ render_glyph(struct glyph g, struct line_contour *lines, } } -#if 1 for (int y = 0; y < gheight; ++y) { for (int x = 0; x < gwidth; ++x) { //printf(" %.2f", accumulator[y * gwidth + x]); u32 brightness = clamp_u32(accumulator[y * gwidth + x] * 255.99f, 255); u32 white = 0xFF000000 | brightness << 16 | brightness << 8 | brightness; - pixels[(at_y + y) * width + (at_x + x)] = white; + + if (brightness > 0) { + pixels[(at_y + (gheight - 1 - y)) * width + (at_x + x)] = white; + } else { + //pixels[(at_y + (gheight - 1 - y)) * width + (at_x + x)] = 0xFFFF0000; + } } - //printf("\n"); } free(accumulator); -#endif //exit(1); @@ -511,7 +531,7 @@ render_glyph(struct glyph g, struct line_contour *lines, } static void -outline_to_lines(struct glyph g, f32 scale, struct line_contour *dest, int *cnt) +outline_to_lines(struct glyph g, f32 scale, int max_descent, struct line_contour *dest, int *cnt) { int nlines = 0; int points_from = 0; @@ -528,11 +548,11 @@ outline_to_lines(struct glyph g, f32 scale, struct line_contour *dest, int *cnt) continue; } - f32 x1 = round_f32((gp.x - g.xmin) * scale); - f32 y1 = round_f32(gp.y * scale); + f32 x1 = gp.x * scale; + f32 y1 = (gp.y - g.baseline - max_descent) * scale; - f32 x2 = round_f32((nextgp.x - g.xmin) * scale); - f32 y2 = round_f32(nextgp.y * scale); + f32 x2 = nextgp.x * scale; + f32 y2 = (nextgp.y - g.baseline - max_descent) * scale; if (nextgp.on_curve) { if (gp.y != nextgp.y) { @@ -546,8 +566,8 @@ outline_to_lines(struct glyph g, f32 scale, struct line_contour *dest, int *cnt) int nextnexti = (nexti + 1 < g.end_pts_of_contours[c] + 1 ? nexti + 1 : points_from); struct glyph_point nextnextgp = g.points[nextnexti]; - f32 x3 = round_f32((nextnextgp.x - g.xmin) * scale); - f32 y3 = round_f32(nextnextgp.y * scale); + f32 x3 = nextnextgp.x * scale; + f32 y3 = (nextnextgp.y - g.baseline - max_descent) * scale; /* P(t) = P0*t^2 + P1*2*t*(1-t) + P2*(1-t)^2 */ f32 t_step = 1.0f / curve_segments; @@ -603,7 +623,7 @@ outline_to_lines(struct glyph g, f32 scale, struct line_contour *dest, int *cnt) } static void -render_utf_string(struct ttf_font font, int px_size, u32 *pixels, u32 width, u32 height, wchar_t *string, int at_x, int at_y) +render_utf_string(struct ttf_font font, int px_size, u32 *pixels, u32 width, wchar_t *string, int at_x, int at_y) { u32 offset_x = at_x; u32 offset_y = at_y; @@ -616,15 +636,21 @@ render_utf_string(struct ttf_font font, int px_size, u32 *pixels, u32 width, u32 if (codepoint != ' ') { struct glyph g = get_outline(&font, codepoint); + //exit(0); + struct line_contour lines = { 0 }; int nlines = 0; - outline_to_lines(g, scale, &lines, &nlines); + outline_to_lines(g, scale, font.hhea.descent, &lines, &nlines); lines.data = malloc(nlines * sizeof(struct line)); - outline_to_lines(g, scale, &lines, 0); + outline_to_lines(g, scale, font.hhea.descent, &lines, 0); - int baseline_correction = round_f32(scale * g.ymax); - render_glyph(g, &lines, scale, pixels, width, height, offset_x + round_f32(g.lsb * scale), offset_y - baseline_correction); - offset_x += scale * g.advance; + render_glyph(g, px_size, &lines, scale, pixels, width, offset_x + g.lsb * scale, offset_y); + +#if 1 + offset_x += round_f32(scale * g.advance); +#else + offset_x += px_size * 2; +#endif free(lines.data); } else { @@ -698,17 +724,35 @@ main(int argc, char **argv) int t = 0; for (;;) { + //memset(pixels, 0xFF, width * height * 4); //memset(pixels, 0xFF, width * height * 4); memset(pixels, 0x00, width * height * 4); -#if 1 - render_utf_string(font, 24, pixels, width, height, L"Пельмени - это очень просто! Много фарша, мало мяса...", 100, 100); - render_utf_string(font, 24, pixels, width, height, L"Good pelmeni are very simple! Much meat, little dough...", 100, 150); -#endif + int at = 75; + //render_utf_string(font, 18, pixels, width, L"The quick brown fox jumps over the lazy dog", 100, 150); + //render_utf_string(font, 12, pixels, width, L"This text is seriously small", 100, at); at += 12; + //render_utf_string(font, 13, pixels, width, L"This text is seriously small", 100, at); at += 13; + //render_utf_string(font, 14, pixels, width, L"This text is seriously small", 100, at); at += 14; + //render_utf_string(font, 15, pixels, width, L"This text is seriously small", 100, at); at += 14; + render_utf_string(font, 20, pixels, width, L"result = XCreateImage(display, visinfo.visual, visinfo.depth, ZPixmap, 0, hc_vram, width, height, pixel_bits, 0);", 100, at); at += 20; + render_utf_string(font, 24, pixels, width, L"The quick brown fox jumps over the lazy dog", 100, at); at += 24; + + wchar_t *string1 = L"iiiiiiiiil"; + wchar_t *string2 = L"something-some"; + wchar_t *string3 = L"MORE MORE"; + + int w1 = get_string_width(&font, 32, string1, wcslen(string1)); + int w2 = get_string_width(&font, 32, string2, wcslen(string2)); + + render_utf_string(font, 32, pixels, width, string1, 100, at); + render_utf_string(font, 32, pixels, width, string2, 100 + w1, at); + render_utf_string(font, 32, pixels, width, string3, 100 + w1 + w2, at); + at += 80; + render_utf_string(font, 80, pixels, width, L"~!@#$%^&*()_+;:,./\\|", 100, at); XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); - usleep(100000); + sleep(1); ++t; } diff --git a/ttf2.c b/ttf2.c index 98f35a4..3d674d6 100644 --- a/ttf2.c +++ b/ttf2.c @@ -89,9 +89,9 @@ read_file(char *filename) static u32 get_glyph_index_format12(struct ttf_font *font, u16 codepoint) { - u32 ngroups = be32(font->file.data + font->cmap_offset + 12); + u32 ngroups = be32(font->file.data + font->dir.cmap_offset + 12); - font->file.offset = font->cmap_offset + 16; + font->file.offset = font->dir.cmap_offset + 16; // TODO: binsearch for (u32 g = 0; g < ngroups; ++g) { @@ -115,12 +115,12 @@ get_glyph_index_format12(struct ttf_font *font, u16 codepoint) static s16 get_glyph_index_format4(struct ttf_font *font, u16 codepoint) { - int segcount_x2 = be16(font->file.data + font->cmap_offset + 6); + int segcount_x2 = be16(font->file.data + font->dir.cmap_offset + 6); - u16 *end_codes = (u16 *) (font->file.data + font->cmap_offset + 16); - u16 *start_codes = (u16 *) (font->file.data + font->cmap_offset + 16 + segcount_x2 + 2); - u16 *id_deltas = (u16 *) (font->file.data + font->cmap_offset + 16 + segcount_x2 * 2 + 2); - u16 *id_range_offset = (u16 *) (font->file.data + font->cmap_offset + 16 + segcount_x2 * 3 + 2); + u16 *end_codes = (u16 *) (font->file.data + font->dir.cmap_offset + 16); + u16 *start_codes = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 + 2); + u16 *id_deltas = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 * 2 + 2); + u16 *id_range_offset = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 * 3 + 2); int index = -1; @@ -166,9 +166,9 @@ get_glyph_index_format4(struct ttf_font *font, u16 codepoint) static u16 get_glyph_index_format6(struct ttf_font *font, u16 codepoint) { - u16 first_code = be16(font->file.data + font->cmap_offset + 6); - u16 entry_count = be16(font->file.data + font->cmap_offset + 8); - u16 *glyph_index_array = (u16 *) (font->file.data + font->cmap_offset + 10); + u16 first_code = be16(font->file.data + font->dir.cmap_offset + 6); + u16 entry_count = be16(font->file.data + font->dir.cmap_offset + 8); + u16 *glyph_index_array = (u16 *) (font->file.data + font->dir.cmap_offset + 10); if (first_code <= codepoint && codepoint < first_code + entry_count) { u16 glyph_index = be16((u8 *) (glyph_index_array + (codepoint - first_code))); @@ -181,7 +181,7 @@ get_glyph_index_format6(struct ttf_font *font, u16 codepoint) static u16 get_glyph_index_format0(struct ttf_font *font, u16 codepoint) { - u8 *glyph_index_array = font->file.data + font->cmap_offset + 6; + u8 *glyph_index_array = font->file.data + font->dir.cmap_offset + 6; if (codepoint <= 0xFF) { u8 glyph_index = glyph_index_array[codepoint]; @@ -232,11 +232,11 @@ get_glyph_offset(struct ttf_font *font, u32 glyph_index) u32 offset_next; if (font->head.itl_format == 1) { - font->file.offset = font->loca_offset + glyph_index * 4; + font->file.offset = font->dir.loca_offset + glyph_index * 4; offset = read_be32(&font->file); offset_next = read_be32(&font->file); } else { - font->file.offset = font->loca_offset + glyph_index * 2; + font->file.offset = font->dir.loca_offset + glyph_index * 2; offset = read_be16(&font->file); offset *= 2; offset_next = read_be16(&font->file); @@ -281,10 +281,931 @@ get_current_coordinate(struct font_buffer *font_file, int flag_combined) return(current_coordinate); } +static inline u32 +pop(struct ttf_graphics_state *state) +{ + u32 result = state->stack[state->stack_head - 1]; + --state->stack_head; + return(result); +} + +static inline f32 +popf(struct ttf_graphics_state *state) +{ + u32 integer = state->stack[state->stack_head - 1]; + --state->stack_head; + f32 result; + memcpy(&result, &integer, sizeof(f32)); + return(result); +} + +static inline void +push(struct ttf_graphics_state *state, u32 value) +{ + state->stack[state->stack_head] = value; + ++state->stack_head; +} + +static u32 +skip_until(struct font_buffer *font_file, u32 start, u32 size, enum ttf_instruction until) +{ + u32 result = -1; // -1 means not found + + u32 base = start; + u32 offset = start; + + while (offset - base < size) { + enum ttf_instruction inst = font_file->data[offset]; + + if (inst == until) { + result = offset; + break; + } + + ++offset; + + // thankfully, ONLY push instructions take data from the instructin stream + if (inst == NPUSHB) { + u8 n = font_file->data[offset++]; + offset += n; + } else if (inst == NPUSHW) { + u8 n = font_file->data[offset++]; + offset += n * 2; + } else if (PUSHB <= inst && inst <= PUSHB_TOP) { + u8 n = inst - PUSHB + 1; + offset += n; + } else if (PUSHW <= inst && inst <= PUSHW_TOP) { + u8 n = inst - PUSHW + 1; + offset += n * 2; + } + } + + return(result); +} + +static struct v2u32 +find_if_pair(struct font_buffer *font_file, u32 start, u32 size) +{ + // Find matching ELSE and EIF for an IF, -1 means not found + struct v2u32 result = { -1, -1 }; + + u32 base = start; + u32 offset = start; + u32 depth = 1; + + while (offset - base < size) { + enum ttf_instruction inst = font_file->data[offset++]; + + if (inst == NPUSHB) { + u8 n = font_file->data[offset++]; + offset += n; + } else if (inst == NPUSHW) { + u8 n = font_file->data[offset++]; + offset += n * 2; + } else if (PUSHB <= inst && inst <= PUSHB_TOP) { + u8 n = inst - PUSHB + 1; + offset += n; + } else if (PUSHW <= inst && inst <= PUSHW_TOP) { + u8 n = inst - PUSHW + 1; + offset += n * 2; + } else if (inst == IF) { + ++depth; + } else if (inst == ELSE) { + if (depth == 1) { + result.x = offset; + } + } else if (inst == EIF) { + if (depth == 1) { + result.y = offset; + break; + } else { + --depth; + } + } + } + + return(result); +} + +static s16 +funits_to_pixels(u32 funits) +{ + // TODO + return(funits); +} + static void -iterate_instructions(struct font_buffer *font_file) +iterate_instructions(struct ttf_font *font, struct font_buffer *font_file, u32 offset, u32 size, struct ttf_graphics_state *state) { + const struct v2f X_AXIS = { 1.0f, 0.0f }; + const struct v2f Y_AXIS = { 0.0f, 1.0f }; + u32 base_offset = offset; + u32 n_fdefs = 0; + + int function_define = -1; + int if_true = 0; + + while (offset - base_offset < size) { + enum ttf_instruction inst = font_file->data[offset++]; + + if (function_define >= 0 && inst != ENDF) { + continue; + } + + if (offset == 13622) { + int __sdfsdf = 0; + } + +#if 0 + printf("%6d: ", offset); + printf("%s\n", ttfe_instruction_name(inst)); +#endif + + switch (inst) { + case NPUSHB: { + u8 n = font_file->data[offset++]; + for (int b = 0; b < n; ++b) { + u8 b = font_file->data[offset++]; + push(state, b); + } + break; + } + + case NPUSHW: { + u8 n = font_file->data[offset++]; + for (int b = 0; b < n; ++b) { + u16 word = be16(font_file->data + offset); + offset += 2; + push(state, word); + } + break; + } + + case PUSHB ... PUSHB_TOP: { + u8 overflow = inst - PUSHB; + int abc = overflow + 1; + + for (int i = 0; i < abc; ++i) { + u8 b = font_file->data[offset++]; + push(state, b); + } + + break; + } + + case PUSHW ... PUSHW_TOP: { + u8 overflow = inst - PUSHW; + int abc = overflow + 1; + + for (int i = 0; i < abc; ++i) { + u16 word = be16(font_file->data + offset); + offset += 2; + push(state, word); + } + + break; + } + + case RS: { + u32 location = pop(state); + u32 value = font->storage[location]; + push(state, value); + break; + } + + case WS: { + u32 value = pop(state); + u32 location = pop(state); + font->storage[location] = value; + break; + } + + case WCVTP: { + u32 value = pop(state); + u32 location = pop(state); + s16 cvt_value = value; + assert(location <= font->dir.cvt_size / sizeof(s16)); + font->cvt[location] = cvt_value; + break; + } + + case WCVTF: { + u32 value = pop(state); + u32 location = pop(state); + s16 cvt_value = funits_to_pixels(value); + assert(location <= font->dir.cvt_size / sizeof(s16)); + font->cvt[location] = cvt_value; + break; + } + + case RCVT: { + u32 location = pop(state); + assert(location <= font->dir.cvt_size / sizeof(s16)); + s16 integer = font->cvt[location]; + push(state, integer); + break; + } + + case SVTCA ... SVTCA_TOP: { + u8 overflow = inst - SVTCA; + int a = overflow; + + if (a == 0) { + state->projection_vector = Y_AXIS; + state->freedom_vector = Y_AXIS; + } else if (a == 1) { + state->projection_vector = X_AXIS; + state->freedom_vector = X_AXIS; + } + + break; + } + + case SPVTCA ... SPVTCA_TOP: { + u8 overflow = inst - SPVTCA; + int a = overflow; + + if (a == 0) { + state->projection_vector = Y_AXIS; + } else if (a == 1) { + state->projection_vector = X_AXIS; + } + + break; + } + + case SFVTCA ... SFVTCA_TOP: { + u8 overflow = inst - SFVTCA; + int a = overflow; + + if (a == 0) { + state->freedom_vector = Y_AXIS; + } else if (a == 1) { + state->freedom_vector = X_AXIS; + } + + break; + } + + case SPVTL ... SPVTL_TOP: { + u8 overflow = inst - SFVTCA; + int a = overflow; + + u32 p1 = pop(state); + u32 p2 = pop(state); + + + + break; + } + + case SFVTL ... SFVTL_TOP: { + break; + } + + case SFVTPV: { + break; + } + + case SDPVTL ... SDPVTL_TOP: { + break; + } + + case SPVFS: { + u32 y = pop(state); + u32 x = pop(state); + // TODO: convert to 2.14 fixed point + break; + } + + case SFVFS: { + break; + } + + case GPV: { + break; + } + + case GFV: { + break; + } + + case SRP0: { + break; + } + + case SRP1: { + break; + } + + case SRP2: { + break; + } + + case SZP0: { + break; + } + + case SZP1: { + break; + } + + case SZP2: { + break; + } + + case SZPS: { + break; + } + + case RTHG: { + break; + } + + case RTG: { + state->round_state = 1; + break; + } + + case RTDG: { + break; + } + + case RDTG: { + break; + } + + case RUTG: { + break; + } + + case ROFF: { + break; + } + + case SROUND: { + break; + } + + case S45ROUND: { + break; + } + + case SLOOP: { + break; + } + + case SMD: { + break; + } + + case INSTCTRL: { + break; + } + + case SCANCTRL: { + // TODO: turn dropout control (state->scan_control) on or off + u32 n = pop(state); + break; + } + + case SCANTYPE: { + // TODO: dropout control mode + u32 val = pop(state); + u16 n = (u16) val; + break; + } + + case SCVTCI: { + u32 cut_in_64th = pop(state); + f32 cut_in = cut_in_64th / 64.0f; + state->control_value_cut_in = cut_in; + break; + } + + case SSWCI: { + break; + } + + case SSW: { + break; + } + + case FLIPON: { + break; + } + + case FLIPOFF: { + break; + } + + case SANGW: { + break; + } + + case SDB: { + break; + } + + case SDS: { + break; + } + + case GC_ ... GC_TOP: { + break; + } + + case SCFS: { + break; + } + + case MD ... MD_TOP: { + break; + } + + case MPPEM: { + /* (funit -> px) scale = pointSize * resolution / ( 72 points per inch * units_per_em ) */ + /* 1em = units_per_em funits */ + /* 1em = scale * units_per_em pixels */ + + u32 dpi = 96; // TODO: query system + u32 point_size = 12; // TODO: pass as parameter + u16 ppem = point_size * dpi / 72; + + push(state, ppem); + + break; + } + + case MPS: { + break; + } + + case FLIPPT: { + break; + } + + case FLIPRGON: { + break; + } + + case FLIPRGOFF: { + break; + } + + case SHP ... SHP_TOP: { + break; + } + + case SHC ... SHC_TOP: { + u8 overflow = inst - SHC; + u32 c = pop(state); + // TODO + break; + } + + case SHZ ... SHZ_TOP: { + u8 overflow = inst - SHZ; + u32 e = pop(state); + // TODO + break; + } + + case SHPIX: { + break; + } + + case MSIRP ... MSIRP_TOP: { + u8 overflow = inst - MSIRP; + f32 d = popf(state); + u32 p = pop(state); + // TODO + break; + } + + case MDAP ... MDAP_TOP: { + break; + } + + case MIAP ... MIAP_TOP: { + // 0: don’t round the distance and don’t look at the control_value_cut_in + // 1: round the distance and look at the control_value_cut_in + int a = inst - MIAP; + + assert(state->stack_head >= 2); + + u32 n = pop(state); // CVT entry number + u32 p = pop(state); // point number + + // TODO + + break; + } + + case MDRP ... MDRP_TOP: { + break; + } + + case MIRP ... MIRP_TOP: { + break; + } + + case ALIGNRP: { + break; + } + + case ISECT: { + break; + } + + case ALIGNPTS: { + break; + } + + case IP: { + u32 p = pop(state); + // TODO + break; + } + + case UTP: { + u32 p = pop(state); + // TODO + break; + } + + case IUP ... IUP_TOP: { + u8 overflow = inst - IUP; + break; + } + + case DELTAP1: { + u32 n = pop(state); + for (u32 i = 0; i < n; ++i) { + u32 pi = pop(state); + u32 argi = pop(state); + // TODO + } + break; + } + + case DELTAP2: { + u32 n = pop(state); + for (u32 i = 0; i < n; ++i) { + u32 pi = pop(state); + u32 argi = pop(state); + // TODO + } + break; + } + + case DELTAP3: { + u32 n = pop(state); + for (u32 i = 0; i < n; ++i) { + u32 pi = pop(state); + u32 argi = pop(state); + // TODO + } + break; + } + + case DELTAC1: { + u32 n = pop(state); + for (u32 i = 0; i < n; ++i) { + u32 ci = pop(state); + u32 argi = pop(state); + // TODO + } + break; + } + + case DELTAC2: { + u32 n = pop(state); + for (u32 i = 0; i < n; ++i) { + u32 ci = pop(state); + u32 argi = pop(state); + // TODO + } + break; + } + + case DELTAC3: { + u32 n = pop(state); + for (u32 i = 0; i < n; ++i) { + u32 ci = pop(state); + u32 argi = pop(state); + // TODO + } + break; + } + + case DUP: { + u32 e = pop(state); + push(state, e); + push(state, e); + break; + } + + case POP: { + --state->stack_head; + break; + } + + case CLEAR: { + state->stack_head = 0; + break; + } + + case SWAP: { + assert(state->stack_head >= 2); + + u32 last = state->stack[state->stack_head - 1]; + u32 prelast = state->stack[state->stack_head - 2]; + + state->stack[state->stack_head - 2] = last; + state->stack[state->stack_head - 1] = prelast; + + break; + } + + case DEPTH: { + break; + } + + case CINDEX: { + break; + } + + case MINDEX: { + break; + } + + case ROLL: { + break; + } + + case IF: { + struct v2u32 pair = find_if_pair(font_file, offset, size - (offset - base_offset)); + + if_true = pop(state); + + if (!if_true) { + assert(pair.x != (u32) -1 || pair.y != (u32) -1); + offset = MIN(pair.x, pair.y); + } + + break; + } + + case ELSE: { + if (if_true) { + offset = skip_until(font_file, offset, size - (offset - base_offset), EIF); + assert(offset != (u32) -1); + } + + break; + } + + case EIF: { + break; + } + + case JROT: { + break; + } + + case JMPR: { + break; + } + + case JROF: { + break; + } + + case LT: { + u32 e2 = pop(state); + u32 e1 = pop(state); + u32 less = (e1 < e2 ? 1 : 0); + push(state, less); + break; + } + + case LTEQ: { + u32 e2 = pop(state); + u32 e1 = pop(state); + u32 leq = (e1 <= e2 ? 1 : 0); + push(state, leq); + break; + } + + case GT: { + u32 e2 = pop(state); + u32 e1 = pop(state); + u32 gt = (e1 > e2 ? 1 : 0); + push(state, gt); + break; + } + + case GTEQ: { + u32 e2 = pop(state); + u32 e1 = pop(state); + u32 geq = (e1 >= e2 ? 1 : 0); + push(state, geq); + break; + } + + case EQ: { + u32 e2 = pop(state); + u32 e1 = pop(state); + u32 equal = (e1 == e2 ? 1 : 0); + push(state, equal); + break; + } + + case NEQ: { + u32 e2 = pop(state); + u32 e1 = pop(state); + u32 neq = (e1 != e2 ? 1 : 0); + push(state, neq); + break; + } + + case ODD: { + u32 e1 = pop(state); + u32 odd = e1 & 1; + push(state, odd); + break; + } + + case EVEN: { + u32 e1 = pop(state); + u32 even = 1 - (e1 & 1); + push(state, even); + break; + } + + case AND: { + u32 e1 = pop(state); + u32 e2 = pop(state); + u32 and = (e1 & e2 ? 1 : 0); + push(state, and); + break; + } + + case OR: { + u32 e1 = pop(state); + u32 e2 = pop(state); + u32 or = (e1 | e2 ? 1 : 0); + push(state, or); + break; + } + + case NOT: { + u32 e1 = pop(state); + u32 not = (e1 ? 0 : 1); + push(state, not); + break; + } + + case ADD: { + f32 n1 = popf(state); + f32 n2 = popf(state); + f32 add = n2 + n1; + push(state, add); + break; + } + + case SUB: { + f32 n1 = popf(state); + f32 n2 = popf(state); + f32 sub = n2 - n1; + push(state, sub); + break; + } + + case DIV: { + f32 n1 = popf(state); + f32 n2 = popf(state); + f32 div = n2 / n1; + push(state, div); + break; + } + + case MUL: { + f32 n1 = popf(state); + f32 n2 = popf(state); + f32 mul = n2 * n1; + push(state, mul); + break; + } + + case ABS: { + f32 n1 = popf(state); + f32 abs = fabsf(n1); + push(state, abs); + break; + } + + case NEG: { + f32 n1 = popf(state); + f32 neg = -n1; + push(state, neg); + break; + } + + case FLOOR: { + f32 n1 = popf(state); + f32 floor = floorf(n1); + push(state, floor); + break; + } + + case CEILING: { + f32 n1 = popf(state); + f32 ceil = ceilf(n1); + push(state, ceil); + break; + } + + case MAX: { + u32 e1 = pop(state); + u32 e2 = pop(state); + u32 max = (e1 > e2 ? e1 : e2); + push(state, max); + break; + } + + case MIN: { + u32 e1 = pop(state); + u32 e2 = pop(state); + u32 min = (e1 < e2 ? e1 : e2); + push(state, min); + break; + } + + case ROUND ... ROUND_TOP: { + u8 overflow = inst - ROUND; + u32 n1 = pop(state); + // TODO: do rounding + push(state, n1); + break; + } + + case NROUND ... NROUND_TOP: { + break; + } + + case FDEF: { + int f = state->stack[--state->stack_head]; + font->functions[f].from = offset; + function_define = f; + break; + } + + case ENDF: { + font->functions[function_define].to = offset - 1; // NOTE: do not include ENDF in subproc + function_define = -1; + break; + } + + case CALL: { + int f = state->stack[--state->stack_head]; + assert(f <= font->maxp.max_fdefs); + struct ttf_function proc = font->functions[f]; + iterate_instructions(font, font_file, proc.from, proc.to - proc.from, state); + break; + } + + case LOOPCALL: { + break; + } + + case IDEF: { + break; + } + + case DEBUG: { + break; + } + + case GETINFO: { + // TODO? + int selector = pop(state); + push(state, 0); + break; + } + + case GETVARIATION: { + break; + } + + default: { + printf("Unknown ttf instruction %x\n", inst); + __builtin_trap(); + } + } + } } static void @@ -297,10 +1218,10 @@ get_simple_glyph_points(struct font_buffer *font_file, u16 number_of_countours, // NOTE: skip instructions u16 instruction_length = read_be16(font_file); - iterate_instructions(font_file); + struct ttf_graphics_state glyph_state = default_graphics_state(); + //iterate_instructions(font, font_file, font_file->offset, instruction_length, &glyph_state); font_file->offset += instruction_length; - int last_index = end_pts_of_contours[number_of_countours - 1]; union glyph_flag *flags = calloc(1, last_index + 1); struct glyph_point *points = malloc((last_index + 2) * 2 * sizeof(struct v2)); @@ -431,7 +1352,7 @@ get_glyph_outline(struct ttf_font *font, u32 glyph_index) u32 glyph_offset = get_glyph_offset(font, glyph_index); struct font_buffer *font_file = &font->file; - font_file->offset = font->glyf_offset + glyph_offset; + font_file->offset = font->dir.glyf_offset + glyph_offset; struct glyph result = { 0 }; s16 number_of_countours = read_be16(font_file); @@ -534,12 +1455,31 @@ get_hmtx(struct ttf_font *font, u32 glyph_index, struct glyph *dest) // TODO: monospaced font only has one record - font_file.offset = font->hmtx_offset + glyph_index * 4; + font_file.offset = font->dir.hmtx_offset + glyph_index * 4; dest->advance = read_be16(&font_file); dest->lsb = read_be16s(&font_file); } +static int +get_string_width(struct ttf_font *font, int px_size, wchar_t *string, int length) +{ + int result = 0; + struct glyph g = { 0 }; + f32 scale = (f32) px_size / ((f32) (font->hhea.ascent - font->hhea.descent)); + + for (int i = 0; i < length; ++i) { + u16 codepoint = string[i]; + u32 glyph_index = get_glyph_index(font, codepoint); + get_hmtx(font, glyph_index, &g); + result += g.advance; + } + + result = ceil_f32(result * scale); + + return(result); +} + static struct glyph get_outline(struct ttf_font *font, u16 codepoint) { @@ -548,16 +1488,21 @@ get_outline(struct ttf_font *font, u16 codepoint) struct glyph result = get_glyph_outline(font, glyph_index); get_hmtx(font, glyph_index, &result); -#if 1 - int ymax = result.ymax; + int xmin = result.xmin; + int ymin = result.ymin; //int ymin = result.ymin; for (int p = 0; p < result.end_pts_of_contours[result.ncontours - 1] + 1; ++p) { struct glyph_point *gp = result.points + p; - gp->y = ymax - gp->y; - //gp->y += ymin; + gp->x -= xmin; + gp->y -= ymin; } -#endif + + result.baseline = - (int) result.ymin; + result.xmin = 0; + result.xmax -= xmin; + result.ymin = 0; + result.ymax -= result.ymin; return(result); } @@ -578,6 +1523,7 @@ read_font_directory(struct font_buffer *file) for (int i = 0; i < table_count; ++i) { u32 tag = be32(file->data + 12 + 16 * i + 0); u32 offset = be32(file->data + 12 + 16 * i + 8); + u32 length = be32(file->data + 12 + 16 * i + 12); switch (tag) { case 0x636d6170: { @@ -586,6 +1532,27 @@ read_font_directory(struct font_buffer *file) break; } + case 0x63767420: { + /* cvt */ + result.cvt_offset = offset; + result.cvt_size = length; + break; + } + + case 0x70726570: { + /* prep */ + result.prep_offset = offset; + result.prep_size = length; + break; + } + + case 0x6670676d: { + /* fpgm */ + result.fpgm_offset = offset; + result.fpgm_size = length; + break; + } + case 0x6c6f6361: { /* loca */ result.loca_offset = offset; @@ -659,7 +1626,7 @@ read_cmap(struct font_directory font_dir, struct ttf_font *font) (platform_id == 3 && ((platform_specific_id == 1 || platform_specific_id == 10)))) { font->cmap_format = be16(font->file.data + font_dir.cmap_offset + subtable_offset); - font->cmap_offset = font_dir.cmap_offset + subtable_offset; + font->dir.cmap_offset = font_dir.cmap_offset + subtable_offset; } } } @@ -669,6 +1636,20 @@ read_maxp(struct font_directory font_dir, struct ttf_font *font) { font->maxp.max_component_points = be16(font->file.data + font_dir.maxp_offset + 10); font->maxp.max_component_contours = be16(font->file.data + font_dir.maxp_offset + 12); + font->maxp.max_storage = be16(font->file.data + font_dir.maxp_offset + 18); + font->maxp.max_fdefs = be16(font->file.data + font_dir.maxp_offset + 20); +} + +static void +read_fpgm(struct ttf_font *font, struct font_buffer *font_file, struct font_directory font_dir, struct ttf_graphics_state *state) +{ + iterate_instructions(font, font_file, font_dir.fpgm_offset, font_dir.fpgm_size, state); +} + +static void +read_cvt(struct ttf_font *font, struct font_buffer *font_file, struct font_directory font_dir, struct ttf_graphics_state *state) +{ + iterate_instructions(font, font_file, font_dir.prep_offset, font_dir.prep_size, state); } static struct ttf_font @@ -679,15 +1660,33 @@ parse_ttf_file(char *filename, char *fontname) struct font_directory font_dir = read_font_directory(&font_file); result.file = font_file; - result.glyf_offset = font_dir.glyf_offset; - result.loca_offset = font_dir.loca_offset; - result.hmtx_offset = font_dir.hmtx_offset; + result.dir = font_dir; + result.dir.glyf_offset = font_dir.glyf_offset; + result.dir.loca_offset = font_dir.loca_offset; + result.dir.hmtx_offset = font_dir.hmtx_offset; + result.cvt = (s16 *) (font_file.data + font_dir.cvt_offset); read_head(font_dir, &result); read_maxp(font_dir, &result); read_cmap(font_dir, &result); read_hhea(font_dir, &result); + if (result.maxp.max_fdefs) { + result.functions = malloc(result.maxp.max_fdefs * sizeof(struct ttf_function)); + } + + if (result.maxp.max_storage) { + result.storage = malloc(result.maxp.max_storage * sizeof(struct ttf_function)); + } + + //struct ttf_graphics_state fpgm_state = default_graphics_state(); + //struct ttf_graphics_state prep_state = default_graphics_state(); + + //read_fpgm(&result, &font_file, font_dir, &fpgm_state); // the font program + //printf("------------- FPGM END -------------\n"); + //read_cvt(&result, &font_file, font_dir, &prep_state); // the cvt program + //printf("------------- CVT END -------------\n"); + result.name = fontname; return(result); diff --git a/ttf_engine.c b/ttf_engine.c new file mode 100644 index 0000000..f0de930 --- /dev/null +++ b/ttf_engine.c @@ -0,0 +1,355 @@ +struct ttf_graphics_state { + u32 stack[TTF_ENGINE_STACK]; + u32 stack_head; + + bool auto_flip; + f32 control_value_cut_in; // f26dot6 + u32 delta_base; + u32 delta_shift; + + struct v2f projection_vector; + struct v2f freedom_vector; + struct v2f dual_projection_vector; + + int gep0; + int gep1; + int gep2; + + u32 instruction_control; + int loop; + f32 minimum_distance; // 1/64-th of a pixel, f26dot6 + int round_state; + + int rp0; + int rp1; + int rp2; + + bool scan_control; + f32 single_width_cut_in; // 1/64-th of a pixel, f26dot6 + int single_width_value; // Funits +}; + +struct ttf_function { + u32 from; + u32 to; +}; + +enum ttf_instruction { + /* Pushing data onto the interpreter stack */ + NPUSHB = 0x40, + NPUSHW = 0x41, + PUSHB = 0xB0, PUSHB_TOP = 0xB7, + PUSHW = 0xB8, PUSHW_TOP = 0xBF, + + /* Managing the Storage Area */ + RS = 0x43, + WS = 0x42, + WCVTP = 0x44, + WCVTF = 0x70, + RCVT = 0x45, + + /* Managing the Graphics State */ + SVTCA = 0x00, SVTCA_TOP = 0x01, + SPVTCA = 0x02, SPVTCA_TOP = 0x03, + SFVTCA = 0x04, SFVTCA_TOP = 0x05, + SPVTL = 0x06, SPVTL_TOP = 0x07, + SFVTL = 0x08, SFVTL_TOP = 0x09, + SFVTPV = 0x0E, + SDPVTL = 0x86, SDPVTL_TOP = 0x87, + SPVFS = 0x0A, + SFVFS = 0x0B, + GPV = 0x0C, + GFV = 0x0D, + SRP0 = 0x10, + SRP1 = 0x11, + SRP2 = 0x12, + SZP0 = 0x13, + SZP1 = 0x14, + SZP2 = 0x15, + SZPS = 0x16, + RTHG = 0x19, + RTG = 0x18, + RTDG = 0x3D, + RDTG = 0x7D, + RUTG = 0x7C, + ROFF = 0x7A, + SROUND = 0x76, + S45ROUND = 0x77, + SLOOP = 0x17, + SMD = 0x1A, + INSTCTRL = 0x8E, + SCANCTRL = 0x85, + SCANTYPE = 0x8D, + SCVTCI = 0x1D, + SSWCI = 0x1E, + SSW = 0x1F, + FLIPON = 0x4D, + FLIPOFF = 0x4E, + SANGW = 0x7E, + SDB = 0x5E, + SDS = 0x5F, + + /* Reading and writing data */ + GC_ = 0x46, GC_TOP = 0x47, // name collisiton with GC from Xlib.h + SCFS = 0x48, + MD = 0x49, MD_TOP = 0x4A, + MPPEM = 0x4B, + MPS = 0x4C, + + /* Managing outlines */ + FLIPPT = 0x80, + FLIPRGON = 0x81, + FLIPRGOFF = 0x82, + SHP = 0x32, SHP_TOP = 0x33, + SHC = 0x34, SHC_TOP = 0x35, + SHZ = 0x36, SHZ_TOP = 0x37, + SHPIX = 0x38, + MSIRP = 0x3A, MSIRP_TOP = 0x3B, + MDAP = 0x2E, MDAP_TOP = 0x2F, + MIAP = 0x3E, MIAP_TOP = 0x3F, + MDRP = 0xC0, MDRP_TOP = 0xDF, + MIRP = 0xE0, MIRP_TOP = 0xFF, + ALIGNRP = 0x3C, + ISECT = 0x0F, + ALIGNPTS = 0x27, + IP = 0x39, + UTP = 0x29, + IUP = 0x30, IUP_TOP = 0x31, + + /* Managing exceptions */ + DELTAP1 = 0x5D, + DELTAP2 = 0x71, + DELTAP3 = 0x72, + DELTAC1 = 0x73, + DELTAC2 = 0x74, + DELTAC3 = 0x75, + + /* Managing the stack */ + DUP = 0x20, + POP = 0x21, + CLEAR = 0x22, + SWAP = 0x23, + DEPTH = 0x24, + CINDEX = 0x25, + MINDEX = 0x26, + ROLL = 0x8A, + + /* Managing the flow of control */ + IF = 0x58, + ELSE = 0x1B, + EIF = 0x59, + JROT = 0x78, + JMPR = 0x1C, + JROF = 0x79, + + /* Logical functions */ + LT = 0x50, + LTEQ = 0x51, + GT = 0x52, + GTEQ = 0x53, + EQ = 0x54, + NEQ = 0x55, + ODD = 0x56, + EVEN = 0x57, + AND = 0x5A, + OR = 0x5B, + NOT = 0x5C, + + /* Arithmetic and math instructions */ + ADD = 0x60, + SUB = 0x61, + DIV = 0x62, + MUL = 0x63, + ABS = 0x64, + NEG = 0x65, + FLOOR = 0x66, + CEILING = 0x67, + MAX = 0x8B, + MIN = 0x8C, + + /* Compensating for the engine characteristics */ + ROUND = 0x68, ROUND_TOP = 0x6B, + NROUND = 0x6C, NROUND_TOP = 0x6F, + + /* Defining and using functions and instructions */ + FDEF = 0x2C, + ENDF = 0x2D, + CALL = 0x2B, + LOOPCALL = 0x2A, + IDEF = 0x89, + + /* Debugging */ + DEBUG = 0x4F, + + /* Miscellaneous instructions */ + GETINFO = 0x88, + GETVARIATION = 0x91, + +}; + + +static char * +ttfe_instruction_name(enum ttf_instruction instruction) +{ + switch (instruction) { + case NPUSHB: return("NPUSHB"); + case NPUSHW: return("NPUSHW"); + case PUSHB ... PUSHB_TOP: return("PUSHB"); + case PUSHW ... PUSHW_TOP: return("PUSHW"); + case RS: return("RS"); + case WS: return("WS"); + case WCVTP: return("WCVTP"); + case WCVTF: return("WCVTF"); + case RCVT: return("RCVT"); + case SVTCA ... SVTCA_TOP: return("SVTCA"); + case SPVTCA ... SPVTCA_TOP: return("SPVTCA"); + case SFVTCA ... SFVTCA_TOP: return("SFVTCA"); + case SPVTL ... SPVTL_TOP: return("SPVTL"); + case SFVTL ... SFVTL_TOP: return("SFVTL"); + case SFVTPV: return("SFVTPV"); + case SDPVTL ... SDPVTL_TOP: return("SDPVTL"); + case SPVFS: return("SPVFS"); + case SFVFS: return("SFVFS"); + case GPV: return("GPV"); + case GFV: return("GFV"); + case SRP0: return("SRP0"); + case SRP1: return("SRP1"); + case SRP2: return("SRP2"); + case SZP0: return("SZP0"); + case SZP1: return("SZP1"); + case SZP2: return("SZP2"); + case SZPS: return("SZPS"); + case RTHG: return("RTHG"); + case RTG: return("RTG"); + case RTDG: return("RTDG"); + case RDTG: return("RDTG"); + case RUTG: return("RUTG"); + case ROFF: return("ROFF"); + case SROUND: return("SROUND"); + case S45ROUND: return("S45ROUND"); + case SLOOP: return("SLOOP"); + case SMD: return("SMD"); + case INSTCTRL: return("INSTCTRL"); + case SCANCTRL: return("SCANCTRL"); + case SCANTYPE: return("SCANTYPE"); + case SCVTCI: return("SCVTCI"); + case SSWCI: return("SSWCI"); + case SSW: return("SSW"); + case FLIPON: return("FLIPON"); + case FLIPOFF: return("FLIPOFF"); + case SANGW: return("SANGW"); + case SDB: return("SDB"); + case SDS: return("SDS"); + case GC_ ... GC_TOP: return("GC_"); + case SCFS: return("SCFS"); + case MD ... MD_TOP: return("MD"); + case MPPEM: return("MPPEM"); + case MPS: return("MPS"); + case FLIPPT: return("FLIPPT"); + case FLIPRGON: return("FLIPRGON"); + case FLIPRGOFF: return("FLIPRGOFF"); + case SHP ... SHP_TOP: return("SHP"); + case SHC ... SHC_TOP: return("SHC"); + case SHZ ... SHZ_TOP: return("SHZ"); + case SHPIX: return("SHPIX"); + case MSIRP ... MSIRP_TOP: return("MSIRP"); + case MDAP ... MDAP_TOP: return("MDAP"); + case MIAP ... MIAP_TOP: return("MIAP"); + case MDRP ... MDRP_TOP: return("MDRP"); + case MIRP ... MIRP_TOP: return("MIRP"); + case ALIGNRP: return("ALIGNRP"); + case ISECT: return("ISECT"); + case ALIGNPTS: return("ALIGNPTS"); + case IP: return("IP"); + case UTP: return("UTP"); + case IUP ... IUP_TOP: return("IUP"); + case DELTAP1: return("DELTAP1"); + case DELTAP2: return("DELTAP2"); + case DELTAP3: return("DELTAP3"); + case DELTAC1: return("DELTAC1"); + case DELTAC2: return("DELTAC2"); + case DELTAC3: return("DELTAC3"); + case DUP: return("DUP"); + case POP: return("POP"); + case CLEAR: return("CLEAR"); + case SWAP: return("SWAP"); + case DEPTH: return("DEPTH"); + case CINDEX: return("CINDEX"); + case MINDEX: return("MINDEX"); + case ROLL: return("ROLL"); + case IF: return("IF"); + case ELSE: return("ELSE"); + case EIF: return("EIF"); + case JROT: return("JROT"); + case JMPR: return("JMPR"); + case JROF: return("JROF"); + case LT: return("LT"); + case LTEQ: return("LTEQ"); + case GT: return("GT"); + case GTEQ: return("GTEQ"); + case EQ: return("EQ"); + case NEQ: return("NEQ"); + case ODD: return("ODD"); + case EVEN: return("EVEN"); + case AND: return("AND"); + case OR: return("OR"); + case NOT: return("NOT"); + case ADD: return("ADD"); + case SUB: return("SUB"); + case DIV: return("DIV"); + case MUL: return("MUL"); + case ABS: return("ABS"); + case NEG: return("NEG"); + case FLOOR: return("FLOOR"); + case CEILING: return("CEILING"); + case MAX: return("MAX"); + case MIN: return("MIN"); + case ROUND ... ROUND_TOP: return("ROUND"); + case NROUND ... NROUND_TOP: return("NROUND"); + case FDEF: return("FDEF"); + case ENDF: return("ENDF"); + case CALL: return("CALL"); + case LOOPCALL: return("LOOPCALL"); + case IDEF: return("IDEF"); + case DEBUG: return("DEBUG"); + case GETINFO: return("GETINFO"); + case GETVARIATION: return("GETVARIATION"); + default: return(""); + } +} + +static struct ttf_graphics_state +default_graphics_state() +{ + struct ttf_graphics_state result; + + result.stack_head = 0; + + result.auto_flip = false; + result.control_value_cut_in = 17.0f / 16.0f; + result.delta_base = 0; + result.delta_shift = 3; + + //result.dual_projection_vector + result.freedom_vector = (struct v2f) { 1.0f, 0.0f }; + result.projection_vector = (struct v2f) { 1.0f, 0.0f }; + + result.gep0 = 1; + result.gep1 = 1; + result.gep2 = 1; + + result.instruction_control = 0; + result.loop = 1; + result.minimum_distance = 64; // 1/64-th of a pixel, f26dot6 + result.round_state = 1; + + result.rp0 = 0; + result.rp1 = 0; + result.rp2 = 0; + + result.scan_control = 0; + result.single_width_cut_in = 0; // 1/64-th of a pixel, f26dot6 + result.single_width_value = 0; // Funits + + return(result); +} \ No newline at end of file