From d63a847eaba1d0141db204d095155e9043414850 Mon Sep 17 00:00:00 2001 From: "A.Olokhtonov" Date: Sun, 15 Aug 2021 18:18:22 +0300 Subject: [PATCH] Structure a bit more like a library --- main.c | 579 +----------------------------------------- ttf2.c => ttf-parse.c | 17 +- ttf-rasterize.c | 279 ++++++++++++++++++++ ttf.h | 154 +++++++++++ 4 files changed, 452 insertions(+), 577 deletions(-) rename ttf2.c => ttf-parse.c (99%) create mode 100644 ttf-rasterize.c create mode 100644 ttf.h diff --git a/main.c b/main.c index 05b692d..2dc2860 100644 --- a/main.c +++ b/main.c @@ -44,165 +44,6 @@ typedef uint8_t u8; typedef float f32; typedef double f64; - -/************* TTF *************/ - -struct v2 { - int x; - int y; -}; - -struct v2u32 { - u32 x; - u32 y; -}; - -struct v2f { - f32 x; - f32 y; -}; - -struct line { - struct v2f a; - struct v2f b; -}; - -struct line_contour { - int ncontours; - int from[MAX_CONTOURS]; - struct line *data; -}; - -struct intersection { - f32 x; - int dir; -}; - -enum rgb_channel { - RED = 0x000000FF, - GREEN = 0x0000FF00, - BLUE = 0x00FF0000, -}; - -struct font_buffer { - u8 *data; - u64 offset; - u64 size; -}; - -struct font_directory { - u32 cmap_offset; - u32 head_offset; - u32 hhea_offset; - u32 loca_offset; - u32 glyf_offset; - u32 hmtx_offset; - u32 maxp_offset; - u32 post_offset; - - u32 cvt_offset; - u32 cvt_size; - - u32 prep_offset; - u32 prep_size; - - u32 fpgm_offset; - u32 fpgm_size; -}; - -union glyph_flag { - struct { - u8 on_curve : 1; - u8 x_short : 1; - u8 y_short : 1; - u8 repeat : 1; - u8 x_short_pos : 1; - u8 y_short_pos : 1; - u8 reserved1 : 1; - u8 reserved2 : 1; - }; - u8 flag; -}; - -enum compund_glyph_flag { - ARG_1_AND_2_ARE_WORDS = 0x1, - ARGS_ARE_XY_VALUES = 0x2, - ROUND_XY_TO_GRID = 0x4, - WE_HAVE_A_SCALE = 0x8, - - MORE_COMPONENTS = 0x20, - WE_HAVE_AN_X_AND_Y_SCALE = 0x40, - WE_HAVE_A_TWO_BY_TWO = 0x80, - WE_HAVE_INSTRUCTIONS = 0x100, - USE_MY_METRICS = 0x200, - OVERLAP_COMPOUND = 0x400 -}; - -struct maxp_table { - u16 max_component_points; - u16 max_component_contours; - u16 max_storage; - u16 max_fdefs; -}; - -struct head_table { - int itl_format; - int units_per_em; -}; - -struct hhea_table { - int ascent; - int descent; - int line_gap; - int max_advance; -}; - -struct glyph_point { - s16 x; - s16 y; - bool on_curve; -}; - -struct glyph_segment { - bool is_curve; - struct v2f p0; - struct v2f p1; - struct v2f p2; -}; - -struct glyph { - s16 xmin, ymin; - s16 xmax, ymax; - - s32 baseline; - u16 advance; - s16 lsb; - - u16 ncontours; - u16 *end_pts_of_contours; - struct glyph_point *points; -}; - -struct ttf_font { - struct font_buffer file; - - struct maxp_table maxp; - struct head_table head; - struct hhea_table hhea; - - s16 *cvt; - int cmap_format; - int is_monospace; - - struct font_directory dir; - struct ttf_function *functions; - - u32 *storage; - - char *name; -}; - -/*******************************/ static int clamp_u32(u32 val, u32 cap) { @@ -221,7 +62,6 @@ abs_f32(f32 v) static int round_f32(f32 v) { -#if 1 int towards_zero = (int) v; f32 diff = abs_f32(v - towards_zero); @@ -230,9 +70,6 @@ round_f32(f32 v) } else { return(towards_zero); } -#else - return(v); -#endif } static int @@ -245,421 +82,17 @@ ceil_f32(f32 v) return(trunc + 1); } +#include "ttf.h" #include "ttf_engine.c" -#include "ttf2.c" +#include "ttf-parse.c" +#include "ttf-rasterize.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) -{ - bool goes_up = (p0.y > p1.y); - f32 t1 = (y - p0.y) / (p1.y - p0.y); /* NOTE(aolo2): no horizontal lines by design */ - - if (t1 == 0) { - f32 x1 = p0.x + (p1.x - p0.x) * t1; - if ((lasty < p0.y) && (p0.y > p1.y)) return(0); - if ((lasty > p0.y) && (p0.y < p1.y)) return(0); - int result = (goes_up ? 1 : -1); - *vx = x1; - return(result); - } - - if (0 < t1 && t1 < 1.0f) { - f32 x1 = p0.x + (p1.x - p0.x) * t1; - *vx = x1; - int result = (goes_up ? 1 : -1); - return(result); - } - - return(0); -} - -static void -render_line(struct v2 from, struct v2 to, u32 *pixels, int width, int height, u32 color) -{ - if (from.x >= width) { - from.x = width - 1; - } - - if (to.x >= width) { - to.x = width - 1; - } - - if (from.y >= height) { - from.y = height - 1; - } - - if (to.y >= height) { - to.y = height - 1; - } - - if (from.y == to.y) { - if (to.x < from.x) { - int tmp = to.x; - to.x = from.x; - from.x = tmp; - } - - u32 *out = pixels + width * from.y + from.x; - // printf("%ld %ld -> %ld %ld\n", from.x, from.y, to.x, to.y); - - for (int x = from.x; x <= to.x; ++x) { - // printf("%d\n", x); - *out = color; - ++out; - } - } else if (from.x == to.x) { - if (to.y < from.y) { - int tmp = to.y; - to.y = from.y; - from.y = tmp; - } - - u32 *out = pixels + width * from.y + from.x; - - //printf("%ld %ld -> %ld %ld\n", from.x, from.y, to.x, to.y); - - for (int y = from.y; y <= to.y; ++y) { - out = pixels + width * y + from.x; - *out = color; - } - } else { - int x0 = from.x; - int y0 = from.y; - int x1 = to.x; - int y1 = to.y; - - int dx = abs(x1 - x0); - int sx = (x0 < x1 ? 1 : -1); - int dy = -abs(y1 - y0); - int sy = (y0 < y1 ? 1 : -1); - int err = dx+dy; - - while (0 <= x0 && x0 < width && 0 <= y0 && y0 < height) { - u32 *out = pixels + width * y0 + x0; - *out = color; - - if (x0 == x1 && y0 == y1) { - break; - } - - int e2 = 2 * err; - - if (e2 >= dy) { - err += dy; - x0 += sx; - } - - if (e2 <= dx) { - err += dx; - y0 += sy; - } - } - } -} - -static void -render_grid(int x_step, int y_step, u32 *pixels, int width, int height) -{ - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - if (x % x_step == 0 || y % y_step == 0) { - pixels[y * width + x] = 0xFFEEEEE; - } - } - } -} - -static int -intersect_glyph(struct line_contour *lines, f32 y, struct intersection *intersections) -{ - int nints = 0; - - for (int c = 0; c < lines->ncontours; ++c) { - int from = lines->from[c]; - int to = lines->from[c + 1]; - - for (int i = from; i < to; ++i) { - int lasti = (i > from ? i - 1 : to - 1); - f32 lasty = lines->data[lasti].a.y; - f32 vx; - int r = scanline_intersects_line(y, lines->data[i].a, lines->data[i].b, lasty, &vx); - - if (r) { - intersections[nints].x = vx; - intersections[nints].dir = r; - ++nints; - } - } - } - - return(nints); -} - -static void -_D_render_glyph_points(struct glyph g, f32 scale, u32 *pixels, int width, int height) -{ - int points_from = 0; - for (int c = 0; c < g.ncontours; ++c) { - for (int p = points_from; p < g.end_pts_of_contours[c] + 1; ++p) { - struct glyph_point gp = g.points[p]; - - int x1 = round_f32((gp.x - g.xmin) * scale); - int y1 = round_f32(gp.y * scale); - - int next = p + 1; - if (p == g.end_pts_of_contours[c]) { - next = points_from; - } - - int x2 = round_f32((g.points[next].x - g.xmin) * scale); - int y2 = round_f32(g.points[next].y * scale); - - render_line((struct v2) {x1, y1}, (struct v2) {x2, y2}, pixels, width, height, 0xFFFF00FF); - //printf("%d %d\n", g.points[p].x, g.points[p].y); - - //XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); - - if (gp.on_curve) { - pixels[y1 * width + x1] = 0xFF0000FF; - } else { - pixels[y1 * width + x1] = 0xFFFFFF00; - } - } - - points_from = g.end_pts_of_contours[c] + 1; - } -} - -static void -sort_intersections(struct intersection *intersections, int size) -{ - bool swapped = true; - - while (swapped) { - swapped = false; - for (int i = 0; i < size - 1; ++i) { - f32 x1 = intersections[i].x; - f32 x2 = intersections[i + 1].x; - if (x1 > x2) { - struct intersection tmp = intersections[i]; - intersections[i] = intersections[i + 1]; - intersections[i + 1] = tmp; - swapped = true; - } - } - } -} - -static void -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 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)); - - 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); - if (ncross) { - sort_intersections(intersections, ncross); - - int state = 0; - - for (u32 i = 0; i < ncross - 1; ++i) { - struct intersection inter = intersections[i]; - struct intersection next_inter = intersections[i + 1]; - - state += inter.dir; - - if (state != 0) { - f32 x0 = inter.x; - f32 x1 = next_inter.x; - - int x_from = x0; - int x_to = (x1 > gwidth - 1 ? gwidth - 1 : x1); - - f32 start_brightness = (x_from + 1 - x0); - f32 end_brightness = (x1 - x_to); - - for (int x = x_from + 1; x < x_to; ++x) { - accumulator[y * gwidth + x] += oversample_norm; - } - - accumulator[y * gwidth + x_from] += start_brightness * oversample_norm; - accumulator[y * gwidth + x_to] += end_brightness * oversample_norm; - } - } - } - } - } - - 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; - - 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; - } - } - } - - free(accumulator); - - //exit(1); - - free(intersections); -} - -static void -outline_to_lines(struct glyph g, f32 scale, int max_descent, struct line_contour *dest, int *cnt) -{ - int nlines = 0; - int points_from = 0; - int curve_segments = 5; - - for (int c = 0; c < g.ncontours; ++c) { - for (int p = points_from; p < g.end_pts_of_contours[c] + 1; ++p) { - struct glyph_point gp = g.points[p]; - - int nexti = (p + 1 < g.end_pts_of_contours[c] + 1 ? p + 1 : points_from); - struct glyph_point nextgp = g.points[nexti]; - - if (p == points_from && !gp.on_curve) { - continue; - } - - f32 x1 = gp.x * scale; - f32 y1 = (gp.y - g.baseline - max_descent) * scale; - - f32 x2 = nextgp.x * scale; - f32 y2 = (nextgp.y - g.baseline - max_descent) * scale; - - if (nextgp.on_curve) { - if (gp.y != nextgp.y) { - if (dest->data) { - dest->data[nlines].a = (struct v2f) { x1, y1 }; - dest->data[nlines].b = (struct v2f) { x2, y2 }; - } - ++nlines; - } - } else { - int nextnexti = (nexti + 1 < g.end_pts_of_contours[c] + 1 ? nexti + 1 : points_from); - struct glyph_point nextnextgp = g.points[nextnexti]; - - 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; - f32 x_prev = x1; - f32 y_prev = y1; - - /* s = 1 for exact beginning */ - for (int s = 1; s <= curve_segments; ++s) { - f32 t_now = t_step * s; - f32 x_now; - f32 y_now; - - if (s < curve_segments) { - x_now = x3 * t_now * t_now - + x2 * 2.0f * t_now * (1.0f - t_now) - + x1 * (1.0f - t_now) * (1.0f - t_now); - - y_now = y3 * t_now * t_now - + y2 * 2.0f * t_now * (1.0f - t_now) - + y1 * (1.0f - t_now) * (1.0f - t_now); - } else { - /* For exact match between neighbours */ - x_now = x3; - y_now = y3; - } - - if (abs_f32(y_now - y_prev) > F32EPS) { - if (dest->data) { - dest->data[nlines].a = (struct v2f) { x_prev, y_prev }; - dest->data[nlines].b = (struct v2f) { x_now, y_now }; - } - ++nlines; - } - - x_prev = x_now; - y_prev = y_now; - } - - ++p; - } - } - - dest->from[c + 1] = nlines; - - points_from = g.end_pts_of_contours[c] + 1; - } - - dest->ncontours = g.ncontours; - - if (cnt) { - *cnt = nlines; - } -} - -static void -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; - f32 scale = (f32) px_size / ((f32) (font.hhea.ascent - font.hhea.descent)); - - u32 len = wcslen(string); - - for (u32 i = 0; i < len; ++i) { - u16 codepoint = string[i]; - if (codepoint != ' ') { - struct glyph g = get_outline(&font, codepoint); - - //exit(0); - - struct line_contour lines = { 0 }; - int nlines = 0; - outline_to_lines(g, scale, font.hhea.descent, &lines, &nlines); - lines.data = malloc(nlines * sizeof(struct line)); - outline_to_lines(g, scale, font.hhea.descent, &lines, 0); - - render_glyph(g, px_size, &lines, scale, pixels, width, offset_x + g.lsb * scale, offset_y); - - offset_x += ceil_f32(scale * g.advance); - - free(lines.data); - } else { - offset_x += px_size / 3; - } - - //XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); - //sleep(1); - } -} - int main(int argc, char **argv) { @@ -730,9 +163,9 @@ main(int argc, char **argv) //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, 26, pixels, width, L"memset(pixels, 0x00, width * height * 4);", 10, at); at += 24; + render_utf_string(font, 12, pixels, width, L"This text is seriously small", 100, at); at += 12; + render_utf_string(font, 18, pixels, width, L"aolo2@avx:~/Documents/code/ttf-test (master) $", 100, at); at += 20; + render_utf_string(font, 26, pixels, width, L"render_utf_string(font, 32, pixels, width, string3, 10 + w1 + w2, at);", 10, at); at += 24; render_utf_string(font, 24, pixels, width, L"The quick brown fox jumps over the lazy dog", 10, at); at += 24; wchar_t *string1 = L"iiiiiiiiiiiiiiiiiii"; diff --git a/ttf2.c b/ttf-parse.c similarity index 99% rename from ttf2.c rename to ttf-parse.c index 615ee26..96059a5 100644 --- a/ttf2.c +++ b/ttf-parse.c @@ -1463,18 +1463,26 @@ get_hmtx(struct ttf_font *font, u32 glyph_index, struct glyph *dest) dest->lsb = read_be16s(&font_file); } +static int +get_codepoint_width(struct ttf_font *font, f32 scale, u16 codepoint) +{ + struct glyph g = { 0 }; + u32 glyph_index = get_glyph_index(font, codepoint); + get_hmtx(font, glyph_index, &g); + int result = ceil_f32(scale * g.advance); + return(result); +} + 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 += ceil_f32(scale * g.advance); // NOTE(aolo2): ceil each time because that's what the rasterizing code does + // NOTE(aolo2): advance is rounded up for each glyph (and not once in the end), because that's what the rasterizer does + result += get_codepoint_width(font, scale, codepoint); } return(result); @@ -1499,6 +1507,7 @@ get_outline(struct ttf_font *font, u16 codepoint) } result.baseline = - (int) result.ymin; + result.lsb += xmin; result.xmin = 0; result.xmax -= xmin; result.ymin = 0; diff --git a/ttf-rasterize.c b/ttf-rasterize.c new file mode 100644 index 0000000..c72b279 --- /dev/null +++ b/ttf-rasterize.c @@ -0,0 +1,279 @@ + +static int +scanline_intersects_line(f32 y, struct v2f p0, struct v2f p1, f32 lasty, f32 *vx) +{ + bool goes_up = (p0.y > p1.y); + f32 t1 = (y - p0.y) / (p1.y - p0.y); /* NOTE(aolo2): no horizontal lines by design */ + + if (t1 == 0) { + f32 x1 = p0.x + (p1.x - p0.x) * t1; + if ((lasty < p0.y) && (p0.y > p1.y)) return(0); + if ((lasty > p0.y) && (p0.y < p1.y)) return(0); + int result = (goes_up ? 1 : -1); + *vx = x1; + return(result); + } + + if (0 < t1 && t1 < 1.0f) { + f32 x1 = p0.x + (p1.x - p0.x) * t1; + *vx = x1; + int result = (goes_up ? 1 : -1); + return(result); + } + + return(0); +} + + +static int +intersect_glyph(struct line_contour *lines, f32 y, struct intersection *intersections) +{ + int nints = 0; + + for (int c = 0; c < lines->ncontours; ++c) { + int from = lines->from[c]; + int to = lines->from[c + 1]; + + for (int i = from; i < to; ++i) { + int lasti = (i > from ? i - 1 : to - 1); + f32 lasty = lines->data[lasti].a.y; + f32 vx; + int r = scanline_intersects_line(y, lines->data[i].a, lines->data[i].b, lasty, &vx); + + if (r) { + intersections[nints].x = vx; + intersections[nints].dir = r; + ++nints; + } + } + } + + return(nints); +} + + +static void +sort_intersections(struct intersection *intersections, int size) +{ + bool swapped = true; + + while (swapped) { + swapped = false; + for (int i = 0; i < size - 1; ++i) { + f32 x1 = intersections[i].x; + f32 x2 = intersections[i + 1].x; + if (x1 > x2) { + struct intersection tmp = intersections[i]; + intersections[i] = intersections[i + 1]; + intersections[i + 1] = tmp; + swapped = true; + } + } + } +} + +static void +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 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 + g.lsb) * scale); + int gheight = px_size; + + struct intersection *intersections = malloc(lines->from[lines->ncontours] * sizeof(struct intersection)); + + f32 *accumulator = calloc(1, gwidth * gheight * sizeof(f32)); + + 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); + if (ncross) { + sort_intersections(intersections, ncross); + + int state = 0; + + for (u32 i = 0; i < ncross - 1; ++i) { + struct intersection inter = intersections[i]; + struct intersection next_inter = intersections[i + 1]; + + state += inter.dir; + + if (state != 0) { + f32 x0 = inter.x; + f32 x1 = next_inter.x; + + int x_from = x0; + int x_to = (x1 > gwidth - 1 ? gwidth - 1 : x1); + + f32 start_brightness = (x_from + 1 - x0); + f32 end_brightness = (x1 - x_to); + + for (int x = x_from + 1; x < x_to; ++x) { + accumulator[y * gwidth + x] += oversample_norm; + } + + accumulator[y * gwidth + x_from] += start_brightness * oversample_norm; + accumulator[y * gwidth + x_to] += end_brightness * oversample_norm; + } + } + } + } + } + + 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; + + 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; + } + } + } + + free(accumulator); + + //exit(1); + + free(intersections); +} + +static void +outline_to_lines(struct glyph g, f32 scale, int max_descent, struct line_contour *dest, int *cnt) +{ + int nlines = 0; + int points_from = 0; + int curve_segments = 5; + + for (int c = 0; c < g.ncontours; ++c) { + for (int p = points_from; p < g.end_pts_of_contours[c] + 1; ++p) { + struct glyph_point gp = g.points[p]; + + int nexti = (p + 1 < g.end_pts_of_contours[c] + 1 ? p + 1 : points_from); + struct glyph_point nextgp = g.points[nexti]; + + if (p == points_from && !gp.on_curve) { + continue; + } + + f32 x1 = (gp.x + g.lsb) * scale; + f32 y1 = (gp.y - g.baseline - max_descent) * scale; + + f32 x2 = (nextgp.x + g.lsb) * scale; + f32 y2 = (nextgp.y - g.baseline - max_descent) * scale; + + if (nextgp.on_curve) { + if (gp.y != nextgp.y) { + if (dest->data) { + dest->data[nlines].a = (struct v2f) { x1, y1 }; + dest->data[nlines].b = (struct v2f) { x2, y2 }; + } + ++nlines; + } + } else { + int nextnexti = (nexti + 1 < g.end_pts_of_contours[c] + 1 ? nexti + 1 : points_from); + struct glyph_point nextnextgp = g.points[nextnexti]; + + f32 x3 = (nextnextgp.x + g.lsb) * 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; + f32 x_prev = x1; + f32 y_prev = y1; + + /* s = 1 for exact beginning */ + for (int s = 1; s <= curve_segments; ++s) { + f32 t_now = t_step * s; + f32 x_now; + f32 y_now; + + if (s < curve_segments) { + x_now = x3 * t_now * t_now + + x2 * 2.0f * t_now * (1.0f - t_now) + + x1 * (1.0f - t_now) * (1.0f - t_now); + + y_now = y3 * t_now * t_now + + y2 * 2.0f * t_now * (1.0f - t_now) + + y1 * (1.0f - t_now) * (1.0f - t_now); + } else { + /* For exact match between neighbours */ + x_now = x3; + y_now = y3; + } + + if (abs_f32(y_now - y_prev) > F32EPS) { + if (dest->data) { + dest->data[nlines].a = (struct v2f) { x_prev, y_prev }; + dest->data[nlines].b = (struct v2f) { x_now, y_now }; + } + ++nlines; + } + + x_prev = x_now; + y_prev = y_now; + } + + ++p; + } + } + + dest->from[c + 1] = nlines; + + points_from = g.end_pts_of_contours[c] + 1; + } + + dest->ncontours = g.ncontours; + + if (cnt) { + *cnt = nlines; + } +} + + +static void +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; + f32 scale = (f32) px_size / ((f32) (font.hhea.ascent - font.hhea.descent)); + + u32 len = wcslen(string); + + for (u32 i = 0; i < len; ++i) { + u16 codepoint = string[i]; + if (codepoint != ' ') { + struct glyph g = get_outline(&font, codepoint); + + //exit(0); + + struct line_contour lines = { 0 }; + int nlines = 0; + outline_to_lines(g, scale, font.hhea.descent, &lines, &nlines); + lines.data = malloc(nlines * sizeof(struct line)); + outline_to_lines(g, scale, font.hhea.descent, &lines, 0); + + render_glyph(g, px_size, &lines, scale, pixels, width, offset_x, offset_y); + + offset_x += ceil_f32(scale * g.advance); + + free(lines.data); + } else { + int space_advance = get_codepoint_width(&font, scale, codepoint); + offset_x += space_advance; + } + + //XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); + //sleep(1); + } +} diff --git a/ttf.h b/ttf.h new file mode 100644 index 0000000..cdd20c7 --- /dev/null +++ b/ttf.h @@ -0,0 +1,154 @@ +struct v2 { + int x; + int y; +}; + +struct v2u32 { + u32 x; + u32 y; +}; + +struct v2f { + f32 x; + f32 y; +}; + +struct line { + struct v2f a; + struct v2f b; +}; + +struct line_contour { + int ncontours; + int from[MAX_CONTOURS]; + struct line *data; +}; + +struct intersection { + f32 x; + int dir; +}; + +enum rgb_channel { + RED = 0x000000FF, + GREEN = 0x0000FF00, + BLUE = 0x00FF0000, +}; + +struct font_buffer { + u8 *data; + u64 offset; + u64 size; +}; + +struct font_directory { + u32 cmap_offset; + u32 head_offset; + u32 hhea_offset; + u32 loca_offset; + u32 glyf_offset; + u32 hmtx_offset; + u32 maxp_offset; + u32 post_offset; + + u32 cvt_offset; + u32 cvt_size; + + u32 prep_offset; + u32 prep_size; + + u32 fpgm_offset; + u32 fpgm_size; +}; + +union glyph_flag { + struct { + u8 on_curve : 1; + u8 x_short : 1; + u8 y_short : 1; + u8 repeat : 1; + u8 x_short_pos : 1; + u8 y_short_pos : 1; + u8 reserved1 : 1; + u8 reserved2 : 1; + }; + u8 flag; +}; + +enum compund_glyph_flag { + ARG_1_AND_2_ARE_WORDS = 0x1, + ARGS_ARE_XY_VALUES = 0x2, + ROUND_XY_TO_GRID = 0x4, + WE_HAVE_A_SCALE = 0x8, + + MORE_COMPONENTS = 0x20, + WE_HAVE_AN_X_AND_Y_SCALE = 0x40, + WE_HAVE_A_TWO_BY_TWO = 0x80, + WE_HAVE_INSTRUCTIONS = 0x100, + USE_MY_METRICS = 0x200, + OVERLAP_COMPOUND = 0x400 +}; + +struct maxp_table { + u16 max_component_points; + u16 max_component_contours; + u16 max_storage; + u16 max_fdefs; +}; + +struct head_table { + int itl_format; + int units_per_em; +}; + +struct hhea_table { + int ascent; + int descent; + int line_gap; + int max_advance; +}; + +struct glyph_point { + s16 x; + s16 y; + bool on_curve; +}; + +struct glyph_segment { + bool is_curve; + struct v2f p0; + struct v2f p1; + struct v2f p2; +}; + +struct glyph { + s16 xmin, ymin; + s16 xmax, ymax; + + s32 baseline; + u16 advance; + s16 lsb; + + u16 ncontours; + u16 *end_pts_of_contours; + struct glyph_point *points; +}; + +struct ttf_font { + struct font_buffer file; + + struct maxp_table maxp; + struct head_table head; + struct hhea_table hhea; + + s16 *cvt; + int cmap_format; + int is_monospace; + + struct font_directory dir; + struct ttf_function *functions; + + u32 *storage; + + char *name; +};