diff --git a/main.c b/main.c index 09d5e8c..a0403ab 100644 --- a/main.c +++ b/main.c @@ -4,6 +4,8 @@ #include #include +#include + #include #include @@ -24,8 +26,6 @@ #define F32EPS 1e-5f #define MAX_CONTOURS 16 -#define PER_PIXEL 0 -#define PER_ROW 0 #define DEBUG_SHOW_POINTS 0 /* @@ -82,6 +82,17 @@ struct line_contour { 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; @@ -139,6 +150,8 @@ struct head_table { struct hhea_table { int ascent; int descent; + int line_gap; + int max_advance; }; struct glyph_point { @@ -218,27 +231,25 @@ round_f32(f32 v) } static int -scanline_intersects_line(f32 x, f32 y, struct v2f p0, struct v2f p1, f32 lasty) +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 (x1 > x) { - 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); - return(result); - } + 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; - if (x1 > x) { - int result = (goes_up ? 1 : -1); - return(result); - } + *vx = x1; + int result = (goes_up ? 1 : -1); + return(result); } return(0); @@ -341,9 +352,9 @@ render_grid(int x_step, int y_step, u32 *pixels, int width, int height) } static int -intersect_glyph(struct line_contour *lines, f32 x, f32 y) +intersect_glyph(struct line_contour *lines, f32 y, struct intersection *intersections) { - int result = 0; + int nints = 0; for (int c = 0; c < lines->ncontours; ++c) { int from = lines->from[c]; @@ -352,12 +363,18 @@ intersect_glyph(struct line_contour *lines, f32 x, f32 y) for (int i = from; i < to; ++i) { int lasti = (i > from ? i - 1 : to - 1); f32 lasty = lines->data[lasti].a.y; - int r = scanline_intersects_line(x, y, lines->data[i].a, lines->data[i].b, lasty); - result += r; + 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(result); + return(nints); } static void @@ -396,83 +413,64 @@ _D_render_glyph_points(struct glyph g, f32 scale, u32 *pixels, int width, int he } static void -render_glyph(struct glyph g, struct line_contour *lines, - struct ttf_font font, int px_size, u32 *pixels, int width, int height) +sort_intersections(struct intersection *intersections, int size) { - f32 scale = (f32) px_size / ((f32) (font.hhea.ascent - font.hhea.descent)); - //int npoints = g.end_pts_of_contours[g.ncontours - 1] + 1; - - if (g.advance < 0) { - g.advance *= -1; - } - - int x0 = 0; // g.xmin * scale; - - int gwidth = (g.xmax - g.xmin) * scale; - int gheight = (g.ymax - g.ymin) * scale + 1; - - pixels = pixels + width * 100 + 100; - -#if 1 - for (int y = 0; y < gheight; ++y) { - - //u32 ncross = intersect_glyph(g, scale, x0 - 999.0f, y + 0.5f, width, intersections, &nints); - - //if (ncross) { - //continue; - //} - - for (int x = x0; x < x0 + gwidth; ++x) { - u32 ncross = intersect_glyph(lines, x + 0.5f, y + 0.5f); - if (ncross) { - pixels[y * width + x] = 0x00; - } else { - pixels[y * width + x] = 0xFFFF0000; + 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; } - -#if PER_PIXEL - XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); -#endif } - -#if PER_ROW - XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); -#endif } -#else - int oversample_x = 4; - int oversample_y = 4; - f32 norm_x = 1.0f / (oversample_x + 1); - f32 norm_y = 1.0f / (oversample_y + 1); - int nints; - - for (int y = 0; y < gheight; ++y) { - for (int x = x0; x < x0 + gwidth; ++x) { +} + +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) +{ + int gwidth = round_f32((g.xmax - g.xmin) * scale) + 1; + int gheight = round_f32((g.ymax - g.ymin) * scale) + 1; + + struct intersection *intersections = malloc(lines->from[lines->ncontours] * sizeof(struct intersection)); + + for (int y = 0; y < gheight && (at_y + y < height); ++y) { + u32 ncross = intersect_glyph(lines, y, intersections); + if (ncross) { + sort_intersections(intersections, ncross); - int total_hits = 0; + int state = 0; - for (int yy = 1; yy <= oversample_y; ++yy) { - for (int xx = 1; xx <= oversample_x; ++xx) { - u32 ncross = intersect_glyph(lines, nlines, x + norm_x * xx , y + norm_y * yy); - if (ncross) { - ++total_hits; + for (int 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; + + for (int x = x0; x < x1; ++x) { + pixels[(y + at_y) * width + (at_x + x)] = 0x00; } } } - - if (total_hits) { - u32 brightness = (256 - total_hits * 256 / (oversample_x * oversample_y)) * 0.99f; - pixels[y * width + x] = 0xFF000000 | brightness << 16 | brightness << 8 | brightness; - } else { - //pixels[y * width + x - 1] = 0xFFFF0000; - } } - } -#endif - + #if DEBUG_SHOW_POINTS - _D_render_glyph_points(g, scale, pixels, width, height); + _D_render_glyph_points(g, scale, pixels, width, height); #endif + } + + free(intersections); } static void @@ -493,11 +491,11 @@ outline_to_lines(struct glyph g, f32 scale, struct line_contour *dest, int *cnt) continue; } - f32 x1 = (gp.x - g.xmin) * scale; - f32 y1 = gp.y * scale; + f32 x1 = round_f32((gp.x - g.xmin) * scale); + f32 y1 = round_f32(gp.y * scale); - f32 x2 = (nextgp.x - g.xmin) * scale; - f32 y2 = nextgp.y * scale; + f32 x2 = round_f32((nextgp.x - g.xmin) * scale); + f32 y2 = round_f32(nextgp.y * scale); if (nextgp.on_curve) { if (gp.y != nextgp.y) { @@ -511,8 +509,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 = (nextnextgp.x - g.xmin) * scale; - f32 y3 = nextnextgp.y * scale; + f32 x3 = round_f32((nextnextgp.x - g.xmin) * scale); + f32 y3 = round_f32(nextnextgp.y * scale); /* P(t) = P0*t^2 + P1*2*t*(1-t) + P2*(1-t)^2 */ f32 t_step = 1.0f / curve_segments; @@ -567,6 +565,40 @@ 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) +{ + 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); + + struct line_contour lines = { 0 }; + int nlines = 0; + outline_to_lines(g, scale, &lines, &nlines); + lines.data = malloc(nlines * sizeof(struct line)); + outline_to_lines(g, scale, &lines, 0); + + int baseline_correction = round_f32(scale * g.ymax); + render_glyph(g, &lines, scale, pixels, width, height, offset_x + g.lsb * scale, offset_y - baseline_correction); + offset_x += 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) { @@ -625,50 +657,16 @@ main(int argc, char **argv) struct ttf_font font = parse_ttf_file(argv[1], "Inter"); printf("Loaded font\n"); - //struct glyph g = get_outline(&font, 0x046C); - int codepoint = 0x0020; - struct glyph g = get_outline(&font, codepoint); - int nlines = 0; - - u32 px_size = 20; u32 *pixels = hc_vram; int t = 0; - f32 scale = (f32) px_size / ((f32) (font.hhea.ascent - font.hhea.descent)); - - struct line_contour lines = { 0 }; - outline_to_lines(g, scale, &lines, &nlines); - lines.data = malloc(nlines * sizeof(struct line)); - outline_to_lines(g, scale, &lines, 0); - - memset(pixels, 0xFFFFFFFF, width * height * 4); - render_grid(10, 10, pixels, width, height); for (;;) { - memset(pixels, 0xFFFFFFFF, width * height * 4); - render_grid(10, 10, pixels, width, height); - render_glyph(g, &lines, font, px_size, pixels, width, height); - XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); - -#if 1 - //printf("%d\n", px_size); - ++px_size; + render_utf_string(font, 32, pixels, width, height, L"Привет, батя! Как поживаешь? Hello, Batya. Wazzaaap", 100, 100); + render_utf_string(font, 32, pixels, width, height, L"QQQQqqqqq", 100, 200); - scale = (f32) px_size / ((f32) (font.hhea.ascent - font.hhea.descent)); - free(lines.data); - lines.data = 0; - outline_to_lines(g, scale, &lines, &nlines); - lines.data = malloc(nlines * sizeof(struct line)); - outline_to_lines(g, scale, &lines, 0); - - if (px_size > 200) { - ++codepoint; - g = get_outline(&font, codepoint); - printf("Loaded codepoint %#x\n", codepoint); - px_size = 20; - } -#endif + XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height); //sleep(1); diff --git a/ttf2.c b/ttf2.c index bdfb6fa..9b4fb8b 100644 --- a/ttf2.c +++ b/ttf2.c @@ -631,6 +631,8 @@ read_hhea(struct font_directory font_dir, struct ttf_font *font) { font->hhea.ascent = be16s(font->file.data + font_dir.hhea_offset + 4); font->hhea.descent = be16s(font->file.data + font_dir.hhea_offset + 6); + font->hhea.line_gap = be16s(font->file.data + font_dir.hhea_offset + 10); + font->hhea.max_advance = be16(font->file.data + font_dir.hhea_offset + 14); } static void