commit
					c7bfdc6578
				
				 2 changed files with 1341 additions and 0 deletions
			
			
		@ -0,0 +1,657 @@
				@@ -0,0 +1,657 @@
					 | 
				
			||||
#include <stdint.h> | 
				
			||||
#include <stdio.h> | 
				
			||||
#include <stdlib.h> | 
				
			||||
#include <string.h> | 
				
			||||
#include <stdbool.h> | 
				
			||||
 | 
				
			||||
#include <assert.h> | 
				
			||||
 | 
				
			||||
#include <unistd.h> | 
				
			||||
#include <fcntl.h> | 
				
			||||
#include <math.h> | 
				
			||||
 | 
				
			||||
#include <X11/Xlib.h> | 
				
			||||
#include <X11/Xutil.h> | 
				
			||||
 | 
				
			||||
#include <sys/ioctl.h> | 
				
			||||
#include <sys/mman.h> | 
				
			||||
#include <sys/types.h> | 
				
			||||
#include <sys/stat.h> | 
				
			||||
 | 
				
			||||
#define ASSERT(expr) if (!expr) { fprintf(stderr, "[ASSERT] Assertion fail %s:%d\n", __FILE__, __LINE__); abort(); } | 
				
			||||
 | 
				
			||||
#define MAX_INTERSECTIONS 16 | 
				
			||||
#define F32EPS 1e-5f | 
				
			||||
#define MAX_CONTOURS 16 | 
				
			||||
 | 
				
			||||
#define PER_PIXEL 0 | 
				
			||||
#define PER_ROW 0 | 
				
			||||
#define DEBUG_SHOW_POINTS 1 | 
				
			||||
 | 
				
			||||
/*
 | 
				
			||||
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; | 
				
			||||
            } | 
				
			||||
*/ | 
				
			||||
 | 
				
			||||
typedef int64_t s64; | 
				
			||||
typedef int32_t s32; | 
				
			||||
typedef int16_t s16; | 
				
			||||
typedef int8_t  s8; | 
				
			||||
 | 
				
			||||
typedef uint64_t u64; | 
				
			||||
typedef uint32_t u32; | 
				
			||||
typedef uint16_t u16; | 
				
			||||
typedef uint8_t  u8; | 
				
			||||
 | 
				
			||||
typedef float  f32; | 
				
			||||
typedef double f64; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/************* TTF *************/ | 
				
			||||
 | 
				
			||||
struct v2 { | 
				
			||||
    int x; | 
				
			||||
    int 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 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; | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
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; | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
struct head_table { | 
				
			||||
    int itl_format; | 
				
			||||
    int units_per_em; | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
struct hhea_table { | 
				
			||||
    int ascent; | 
				
			||||
    int descent; | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
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; | 
				
			||||
     | 
				
			||||
    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; | 
				
			||||
     | 
				
			||||
    char *name; | 
				
			||||
     | 
				
			||||
    int cmap_format; | 
				
			||||
     | 
				
			||||
    int glyf_offset; | 
				
			||||
    int loca_offset; | 
				
			||||
    int cmap_offset; | 
				
			||||
    int hmtx_offset; | 
				
			||||
}; | 
				
			||||
 | 
				
			||||
/*******************************/ | 
				
			||||
 | 
				
			||||
 | 
				
			||||
#include "ttf2.c" | 
				
			||||
 | 
				
			||||
static Display *display; | 
				
			||||
static Window window; | 
				
			||||
static GC default_gc; | 
				
			||||
static XImage* xwindow_buffer; | 
				
			||||
 | 
				
			||||
static f32 | 
				
			||||
abs_f32(f32 v) | 
				
			||||
{ | 
				
			||||
    f32 result = (v > 0 ? v : -v); | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static f32 | 
				
			||||
round_f32(f32 v) | 
				
			||||
{ | 
				
			||||
    return(v); | 
				
			||||
     | 
				
			||||
#if 0 | 
				
			||||
    int towards_zero = (int) v; | 
				
			||||
    f32 diff = abs_f32(v - towards_zero); | 
				
			||||
     | 
				
			||||
    if (diff >= 0.5f) { | 
				
			||||
        return(v > 0 ? towards_zero + 1 : towards_zero - 1); | 
				
			||||
    } else { | 
				
			||||
        return(towards_zero); | 
				
			||||
    } | 
				
			||||
#endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static int | 
				
			||||
scanline_intersects_line(f32 x, f32 y, struct v2f p0, struct v2f p1, f32 lasty) | 
				
			||||
{ | 
				
			||||
    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 (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); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    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 x, f32 y) | 
				
			||||
{ | 
				
			||||
    int result = 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; | 
				
			||||
            int r = scanline_intersects_line(x, y, lines->data[i].a, lines->data[i].b, lasty); | 
				
			||||
            result += r; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
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 | 
				
			||||
render_glyph(struct glyph g, struct line_contour *lines, | 
				
			||||
             struct ttf_font font, int px_size, u32 *pixels, int width, int height) | 
				
			||||
{ | 
				
			||||
    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 + 2; | 
				
			||||
    int gheight = (g.ymax - g.ymin) * scale + 2; | 
				
			||||
     | 
				
			||||
    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; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
#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) { | 
				
			||||
             | 
				
			||||
            int total_hits = 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; | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            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); | 
				
			||||
#endif | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void | 
				
			||||
outline_to_lines(struct glyph g, f32 scale, struct line_contour *dest, int *cnt)  | 
				
			||||
{ | 
				
			||||
    int nlines = 0; | 
				
			||||
    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 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.xmin) * scale; | 
				
			||||
            f32 y1 = gp.y * scale; | 
				
			||||
             | 
				
			||||
            f32 x2 = (nextgp.x - g.xmin) * scale; | 
				
			||||
            f32 y2 = nextgp.y * 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.xmin) * scale; | 
				
			||||
                f32 y3 = nextnextgp.y * scale; | 
				
			||||
                 | 
				
			||||
                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; | 
				
			||||
                } | 
				
			||||
                 | 
				
			||||
                if (nextgp.y != nextnextgp.y) { | 
				
			||||
                    if (dest->data) { | 
				
			||||
                        dest->data[nlines].a = (struct v2f) { x2, y2 }; | 
				
			||||
                        dest->data[nlines].b = (struct v2f) { x3, y3 }; | 
				
			||||
                    } | 
				
			||||
                    ++nlines; | 
				
			||||
                } | 
				
			||||
                 | 
				
			||||
                ++p; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        dest->from[c + 1] = nlines; | 
				
			||||
         | 
				
			||||
        points_from = g.end_pts_of_contours[c] + 1; | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    dest->ncontours = g.ncontours; | 
				
			||||
     | 
				
			||||
    if (cnt) { | 
				
			||||
        *cnt = nlines; | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
int | 
				
			||||
main(int argc, char **argv) | 
				
			||||
{ | 
				
			||||
    if (argc != 2) { | 
				
			||||
        fprintf(stderr, "Usage: %s filename.ttf\n", argv[0]); | 
				
			||||
        return(1); | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    display = XOpenDisplay(0); | 
				
			||||
    Window root_window = DefaultRootWindow(display); | 
				
			||||
     | 
				
			||||
    int default_screen = DefaultScreen(display); | 
				
			||||
    int screen_bit_depth = 24; | 
				
			||||
    int width = 1280; | 
				
			||||
    int height = 720; | 
				
			||||
     | 
				
			||||
    XVisualInfo visinfo = { 0 }; | 
				
			||||
    XMatchVisualInfo(display, default_screen, screen_bit_depth, TrueColor, &visinfo); | 
				
			||||
     | 
				
			||||
    XSetWindowAttributes window_attr; | 
				
			||||
    window_attr.bit_gravity = StaticGravity; | 
				
			||||
    window_attr.background_pixel = 0; | 
				
			||||
    window_attr.colormap = XCreateColormap(display, root_window, visinfo.visual, AllocNone); | 
				
			||||
    window_attr.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask; | 
				
			||||
     | 
				
			||||
    unsigned long attribute_mask = CWBitGravity | CWBackPixel | CWColormap | CWEventMask; | 
				
			||||
     | 
				
			||||
    window = XCreateWindow(display, root_window, | 
				
			||||
                           0, 0, | 
				
			||||
                           width, height, 0, | 
				
			||||
                           visinfo.depth, InputOutput, | 
				
			||||
                           visinfo.visual, attribute_mask, &window_attr); | 
				
			||||
     | 
				
			||||
    XGrabPointer(display, window, False, ButtonPressMask | PointerMotionMask, | 
				
			||||
                 GrabModeAsync, GrabModeAsync, None, None, CurrentTime); | 
				
			||||
     | 
				
			||||
    XSelectInput(display, window, KeyPress | KeyRelease | ButtonRelease | PointerMotionMask); | 
				
			||||
     | 
				
			||||
    XStoreName(display, window, "ttf"); | 
				
			||||
    XMapWindow(display, window); | 
				
			||||
    XFlush(display); | 
				
			||||
     | 
				
			||||
    Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False); | 
				
			||||
    XSetWMProtocols(display, window, &WM_DELETE_WINDOW, 1); | 
				
			||||
     | 
				
			||||
    int pixel_bits = 32; | 
				
			||||
    int pixel_bytes = pixel_bits / 8; | 
				
			||||
    int window_buffer_size = width * height * pixel_bytes; | 
				
			||||
    void *hc_vram = malloc(window_buffer_size); | 
				
			||||
     | 
				
			||||
    xwindow_buffer = XCreateImage(display, visinfo.visual, visinfo.depth, ZPixmap, 0, hc_vram, width, height, | 
				
			||||
                                  pixel_bits, 0); | 
				
			||||
     | 
				
			||||
    default_gc = DefaultGC(display, default_screen); | 
				
			||||
     | 
				
			||||
    struct ttf_font font = parse_ttf_file(argv[1], "Inter"); | 
				
			||||
    printf("Loaded font\n"); | 
				
			||||
     | 
				
			||||
    //struct glyph g = get_outline(&font, 0x046C);
 | 
				
			||||
    int codepoint = 0x0431; | 
				
			||||
    struct glyph g = get_outline(&font, codepoint); | 
				
			||||
    int nlines = 0; | 
				
			||||
     | 
				
			||||
    u32 px_size = 120; | 
				
			||||
    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 0 | 
				
			||||
        //printf("%d\n", px_size);
 | 
				
			||||
        ++px_size; | 
				
			||||
         | 
				
			||||
        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 | 
				
			||||
         | 
				
			||||
        //sleep(1);
 | 
				
			||||
         | 
				
			||||
        ++t; | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(0); | 
				
			||||
} | 
				
			||||
@ -0,0 +1,684 @@
				@@ -0,0 +1,684 @@
					 | 
				
			||||
static u8 | 
				
			||||
read_8(struct font_buffer *buf) | 
				
			||||
{ | 
				
			||||
    u8 result = buf->data[buf->offset];  | 
				
			||||
    buf->offset += 1; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static s8 | 
				
			||||
read_8s(struct font_buffer *buf) | 
				
			||||
{ | 
				
			||||
    s8 result = buf->data[buf->offset];  | 
				
			||||
    buf->offset += 1; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u16 | 
				
			||||
be16(u8 *buf) | 
				
			||||
{ | 
				
			||||
    u16 result = buf[0] << 8 | buf[1]; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static s16 | 
				
			||||
be16s(u8 *buf) | 
				
			||||
{ | 
				
			||||
    s16 result = buf[0] << 8 | buf[1]; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u16 | 
				
			||||
read_be16(struct font_buffer *buf) | 
				
			||||
{ | 
				
			||||
    u16 result = be16(buf->data + buf->offset); | 
				
			||||
    buf->offset += 2; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static s16 | 
				
			||||
read_be16s(struct font_buffer *buf) | 
				
			||||
{ | 
				
			||||
    s16 result = be16s(buf->data + buf->offset); | 
				
			||||
    buf->offset += 2; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u32 | 
				
			||||
be32(u8 *buf) | 
				
			||||
{ | 
				
			||||
    u32 result = ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | ((u32) buf[2] << 8) | ((u32) buf[3]);  | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u32 | 
				
			||||
read_be32(struct font_buffer *buf) | 
				
			||||
{ | 
				
			||||
    u32 result = be32(buf->data + buf->offset); | 
				
			||||
    buf->offset += 4; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static f32 | 
				
			||||
read_be214(struct font_buffer *buf) | 
				
			||||
{ | 
				
			||||
    f32 result = read_be16s(buf) / 16384.0f; | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static struct font_buffer | 
				
			||||
read_file(char *filename) | 
				
			||||
{ | 
				
			||||
    FILE *file = fopen(filename, "rb"); | 
				
			||||
    struct font_buffer result = { 0 }; | 
				
			||||
     | 
				
			||||
    ASSERT(file); | 
				
			||||
     | 
				
			||||
    if (file) { | 
				
			||||
        fseek(file, 0, SEEK_END); | 
				
			||||
        result.size = ftell(file); | 
				
			||||
        result.data = malloc(result.size); | 
				
			||||
        fseek(file, 0, SEEK_SET); | 
				
			||||
        fread(result.data, result.size, 1, file); | 
				
			||||
        fclose(file); | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u32 | 
				
			||||
get_glyph_index_format12(struct ttf_font *font, u16 codepoint) | 
				
			||||
{ | 
				
			||||
    u32 ngroups = be32(font->file.data + font->cmap_offset + 12); | 
				
			||||
     | 
				
			||||
    font->file.offset = font->cmap_offset + 16; | 
				
			||||
     | 
				
			||||
    // TODO: binsearch
 | 
				
			||||
    for (u32 g = 0; g < ngroups; ++g) { | 
				
			||||
        u32 start_charcode = read_be32(&font->file); | 
				
			||||
        u32 end_charcode = read_be32(&font->file); | 
				
			||||
        u32 start_glyphcode = read_be32(&font->file); | 
				
			||||
         | 
				
			||||
        if (start_charcode <= codepoint && codepoint <= end_charcode) { | 
				
			||||
            u32 glyph_index = start_glyphcode + (codepoint - start_charcode); | 
				
			||||
            return(glyph_index); | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        if (start_charcode > codepoint) { | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(0); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static s16 | 
				
			||||
get_glyph_index_format4(struct ttf_font *font, u16 codepoint) | 
				
			||||
{ | 
				
			||||
    int segcount_x2 = be16(font->file.data + font->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); | 
				
			||||
     | 
				
			||||
    int index = -1; | 
				
			||||
     | 
				
			||||
    for (int i = 0; i < segcount_x2 / 2; ++i) { | 
				
			||||
        u16 end_code = be16((u8 *) (end_codes + i)); | 
				
			||||
		if (end_code >= codepoint) { | 
				
			||||
            index = i; | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	if (index == -1) { | 
				
			||||
        return(0); | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    u16 start_code = be16((u8 *) (start_codes + index)); | 
				
			||||
	if (start_code <= codepoint) { | 
				
			||||
        u16 ir_offset = be16((u8 *) (id_range_offset + index)); | 
				
			||||
		if (ir_offset != 0) { | 
				
			||||
			u16 glyph_index = be16((u8 *) (id_range_offset  | 
				
			||||
                                           + index + ir_offset / 2  | 
				
			||||
                                           + (codepoint - start_code))); | 
				
			||||
             | 
				
			||||
			if (!glyph_index) { | 
				
			||||
                return(0); | 
				
			||||
            } | 
				
			||||
			 | 
				
			||||
            u16 id_delta = be16((u8 *) (id_deltas + index)); | 
				
			||||
            u16 result = (u16) (glyph_index + id_delta); | 
				
			||||
             | 
				
			||||
            return(result); | 
				
			||||
		} | 
				
			||||
         | 
				
			||||
        u16 id_delta = be16((u8 *) (id_deltas + index)); | 
				
			||||
        u16 result = (u16) (codepoint + id_delta); | 
				
			||||
         | 
				
			||||
        return(result); | 
				
			||||
	} | 
				
			||||
     | 
				
			||||
	return(0); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
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); | 
				
			||||
     | 
				
			||||
    if (first_code <= codepoint && codepoint < first_code + entry_count) { | 
				
			||||
        u16 glyph_index = be16((u8 *) (glyph_index_array + (codepoint - first_code))); | 
				
			||||
        return(glyph_index); | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(0); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u16 | 
				
			||||
get_glyph_index_format0(struct ttf_font *font, u16 codepoint) | 
				
			||||
{ | 
				
			||||
    u8 *glyph_index_array = font->file.data + font->cmap_offset + 6; | 
				
			||||
     | 
				
			||||
    if (codepoint <= 0xFF) { | 
				
			||||
        u8 glyph_index = glyph_index_array[codepoint]; | 
				
			||||
        return(glyph_index); | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(0); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u32 | 
				
			||||
get_glyph_index(struct ttf_font *font, u16 codepoint)  | 
				
			||||
{ | 
				
			||||
    u32 result = 0; | 
				
			||||
     | 
				
			||||
    switch (font->cmap_format) { | 
				
			||||
        case 0: { | 
				
			||||
            result = get_glyph_index_format0(font, codepoint); | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        case 12: { | 
				
			||||
            result = get_glyph_index_format12(font, codepoint); | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        case 6: { | 
				
			||||
            result = get_glyph_index_format6(font, codepoint); | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        case 4: { | 
				
			||||
            result = get_glyph_index_format4(font, codepoint); | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        default: { | 
				
			||||
            ASSERT(false); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u32 | 
				
			||||
get_glyph_offset(struct ttf_font *font, u32 glyph_index) | 
				
			||||
{ | 
				
			||||
    u32 offset = 0; | 
				
			||||
    u32 offset_next; | 
				
			||||
     | 
				
			||||
    if (font->head.itl_format == 1) { | 
				
			||||
        font->file.offset = font->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; | 
				
			||||
        offset = read_be16(&font->file); | 
				
			||||
        offset *= 2; | 
				
			||||
        offset_next = read_be16(&font->file); | 
				
			||||
        offset_next *= 2; | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    if (offset == offset_next) { | 
				
			||||
        return(0); | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(offset); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static s16 | 
				
			||||
get_current_coordinate(struct font_buffer *font_file, int flag_combined) | 
				
			||||
{ | 
				
			||||
    s16 current_coordinate = 0; | 
				
			||||
     | 
				
			||||
    switch (flag_combined) { | 
				
			||||
        case 0: { | 
				
			||||
            current_coordinate = read_be16(font_file); | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        case 1: {  | 
				
			||||
            current_coordinate = 0;  | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        case 2: {  | 
				
			||||
            current_coordinate = read_8(font_file); | 
				
			||||
            current_coordinate *= -1; | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        case 3: {  | 
				
			||||
            current_coordinate = read_8(font_file); | 
				
			||||
            break; | 
				
			||||
        }  | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(current_coordinate); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void | 
				
			||||
get_simple_glyph_points(struct font_buffer *font_file, u16 number_of_countours, struct glyph *result) | 
				
			||||
{ | 
				
			||||
    u16 *end_pts_of_contours = calloc(1, number_of_countours * sizeof(u16)); | 
				
			||||
    for (int c = 0; c < number_of_countours; ++c) { | 
				
			||||
        end_pts_of_contours[c] = read_be16(font_file); | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    // NOTE: skip instructions
 | 
				
			||||
    u16 instruction_length = read_be16(font_file); | 
				
			||||
    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)); | 
				
			||||
     | 
				
			||||
    // NOTE: so that we can insert one point at the start if needed
 | 
				
			||||
    // points = points + 1;
 | 
				
			||||
     | 
				
			||||
    for (int i = 0; i < last_index + 1; ++i) { | 
				
			||||
        flags[i].flag = read_8(font_file); | 
				
			||||
        if (flags[i].repeat) { | 
				
			||||
            u8 repeat_count = read_8(font_file); | 
				
			||||
            while (repeat_count-- > 0) { | 
				
			||||
                i++; | 
				
			||||
                flags[i] = flags[i - 1]; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    s16 prev_coordinate = 0; | 
				
			||||
    int start = 0; | 
				
			||||
    int head = 0; | 
				
			||||
     | 
				
			||||
    for (int c = 0; c < number_of_countours; ++c) { | 
				
			||||
        int end = end_pts_of_contours[c]; | 
				
			||||
        struct glyph_point first_point = { 0 }; | 
				
			||||
         | 
				
			||||
        for (int i = start; i < end + 1; ++i) { | 
				
			||||
            // NOTE: read x coordinates
 | 
				
			||||
            int flag_combined = (flags[i].x_short << 1) | flags[i].x_short_pos; | 
				
			||||
            s16 current_coordinate = get_current_coordinate(font_file, flag_combined); | 
				
			||||
            s16 read_x = current_coordinate + prev_coordinate; | 
				
			||||
             | 
				
			||||
            int prev = i - 1; | 
				
			||||
            if (prev >= start) { | 
				
			||||
                //prev = end;
 | 
				
			||||
                 | 
				
			||||
                if (!flags[i].on_curve && !flags[prev].on_curve) { | 
				
			||||
                    // NOTE: implicit on-curve point
 | 
				
			||||
                    points[head].on_curve = true; | 
				
			||||
                    points[head].x = (read_x + points[head - 1].x) / 2; | 
				
			||||
                    ++head; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            points[head].on_curve = flags[i].on_curve; | 
				
			||||
            points[head].x = read_x; | 
				
			||||
            ++head; | 
				
			||||
             | 
				
			||||
            if (i == start) { | 
				
			||||
                // NOTE: first iteration could not have inserted an implicit point
 | 
				
			||||
                first_point = points[head - 1]; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            prev_coordinate = read_x; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        if (!flags[start].on_curve && !flags[end].on_curve) { | 
				
			||||
            points[head].on_curve = true; | 
				
			||||
            points[head].x = (first_point.x + points[head - 1].x) / 2; | 
				
			||||
            ++head; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        start = end + 1; | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
     | 
				
			||||
    prev_coordinate = 0; | 
				
			||||
    start = 0; | 
				
			||||
    head = 0; | 
				
			||||
     | 
				
			||||
    int added_points = 0; | 
				
			||||
     | 
				
			||||
    for (int c = 0; c < number_of_countours; ++c) { | 
				
			||||
        int end = end_pts_of_contours[c]; | 
				
			||||
        struct glyph_point first_point = { 0 }; | 
				
			||||
         | 
				
			||||
        for (int i = start; i < end + 1; ++i) { | 
				
			||||
            // NOTE: read y coordinates
 | 
				
			||||
            int flag_combined = (flags[i].y_short << 1) | flags[i].y_short_pos; | 
				
			||||
            s16 current_coordinate = get_current_coordinate(font_file, flag_combined); | 
				
			||||
            s16 read_y = current_coordinate + prev_coordinate; | 
				
			||||
             | 
				
			||||
            int prev = i - 1; | 
				
			||||
            if (prev >= start) { | 
				
			||||
                //prev = end;
 | 
				
			||||
                 | 
				
			||||
                if (!flags[i].on_curve && !flags[prev].on_curve) { | 
				
			||||
                    // NOTE: implicit on-curve point
 | 
				
			||||
                    points[head].on_curve = true; | 
				
			||||
                    points[head].y = (read_y + points[head - 1].y) / 2; | 
				
			||||
                    ++head; | 
				
			||||
                    ++added_points; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            points[head].on_curve = flags[i].on_curve; | 
				
			||||
            points[head].y = read_y; | 
				
			||||
            ++head; | 
				
			||||
             | 
				
			||||
            prev_coordinate = read_y; | 
				
			||||
             | 
				
			||||
            if (i == start) { | 
				
			||||
                // NOTE: first iteration could not have inserted an implicit point
 | 
				
			||||
                first_point = points[head - 1]; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        if (!flags[start].on_curve && !flags[end].on_curve) { | 
				
			||||
            points[head].on_curve = true; | 
				
			||||
            points[head].y = (first_point.y + points[head - 1].y) / 2; | 
				
			||||
            ++head; | 
				
			||||
            ++added_points; | 
				
			||||
        } | 
				
			||||
         | 
				
			||||
        start = end + 1; | 
				
			||||
        end_pts_of_contours[c] += added_points; | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
     | 
				
			||||
    result->points = points; | 
				
			||||
    result->ncontours = number_of_countours; | 
				
			||||
    result->end_pts_of_contours = end_pts_of_contours; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static struct glyph | 
				
			||||
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; | 
				
			||||
     | 
				
			||||
    struct glyph result = { 0 }; | 
				
			||||
    s16 number_of_countours = read_be16(font_file); | 
				
			||||
     | 
				
			||||
    result.xmin = read_be16(font_file); | 
				
			||||
    result.ymin = read_be16(font_file); | 
				
			||||
    result.xmax = read_be16(font_file); | 
				
			||||
    result.ymax = read_be16(font_file); | 
				
			||||
     | 
				
			||||
    if (number_of_countours > 0) { | 
				
			||||
        // NOTE: simple glyph
 | 
				
			||||
        get_simple_glyph_points(font_file, number_of_countours, &result); | 
				
			||||
    } else if (number_of_countours < 0) { | 
				
			||||
        // NOTE: compund glyph
 | 
				
			||||
        int head = 0; | 
				
			||||
         | 
				
			||||
        result.end_pts_of_contours = malloc(font->maxp.max_component_contours * sizeof(u16)); | 
				
			||||
        result.points = malloc(font->maxp.max_component_points * 2 * sizeof(struct glyph_point)); | 
				
			||||
         | 
				
			||||
        for (;;) { | 
				
			||||
            u16 flags = read_be16(font_file); | 
				
			||||
            u16 component_index = read_be16(font_file); | 
				
			||||
             | 
				
			||||
            f32 matrix[6] = {1, 0, 0, 1, 0, 0}; /* NOTE(aolo2): matrix stuff is highjacked from stb */ | 
				
			||||
             | 
				
			||||
            if (flags & ARG_1_AND_2_ARE_WORDS) { | 
				
			||||
                matrix[4] = read_be16(font_file); | 
				
			||||
                matrix[5] = read_be16(font_file); | 
				
			||||
            } else { | 
				
			||||
                matrix[4] = read_8s(font_file); | 
				
			||||
                matrix[5] = read_8s(font_file); | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            if (flags & WE_HAVE_A_SCALE) { | 
				
			||||
                f32 scale = read_be214(font_file); | 
				
			||||
                matrix[0] = scale; | 
				
			||||
                matrix[1] = 0; | 
				
			||||
                matrix[2] = 0; | 
				
			||||
                matrix[3] = scale; | 
				
			||||
            } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE ) { | 
				
			||||
                f32 xscale = read_be214(font_file); | 
				
			||||
                f32 yscale = read_be214(font_file); | 
				
			||||
                matrix[0] = xscale; | 
				
			||||
                matrix[1] = 0; | 
				
			||||
                matrix[2] = 0; | 
				
			||||
                matrix[3] = yscale; | 
				
			||||
            } else if (flags & WE_HAVE_A_TWO_BY_TWO) { | 
				
			||||
                f32 xscale = read_be214(font_file); | 
				
			||||
                f32 scale01 = read_be214(font_file); | 
				
			||||
                f32 scale10 = read_be214(font_file); | 
				
			||||
                f32 yscale = read_be214(font_file); | 
				
			||||
                matrix[0] = xscale; | 
				
			||||
                matrix[1] = scale01; | 
				
			||||
                matrix[2] = scale10; | 
				
			||||
                matrix[3] = yscale; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            f32 m = sqrtf(matrix[0] * matrix[0] + matrix[1] * matrix[1]); | 
				
			||||
            f32 n = sqrtf(matrix[2] * matrix[2] + matrix[3] * matrix[3]); | 
				
			||||
             | 
				
			||||
            u32 saved_offset = font_file->offset; | 
				
			||||
            struct glyph component = get_glyph_outline(font, component_index); | 
				
			||||
            font_file->offset = saved_offset; | 
				
			||||
             | 
				
			||||
            int startv = 0; | 
				
			||||
            for (int c = 0; c < component.ncontours; ++c) { | 
				
			||||
                int endv = component.end_pts_of_contours[c] + 1; | 
				
			||||
                 | 
				
			||||
                for (int v = startv; v < endv; ++v) { | 
				
			||||
                    s16 x = component.points[v].x; | 
				
			||||
                    s16 y = component.points[v].y; | 
				
			||||
                     | 
				
			||||
                    result.points[head].x = m * (matrix[0] * x + matrix[2] * y + matrix[4]); | 
				
			||||
                    result.points[head].y = n * (matrix[1] * x + matrix[3] * y + matrix[5]); | 
				
			||||
                    result.points[head].on_curve = component.points[v].on_curve; | 
				
			||||
                    ++head; | 
				
			||||
                } | 
				
			||||
                 | 
				
			||||
                startv = endv; | 
				
			||||
                result.end_pts_of_contours[result.ncontours] = head - 1; | 
				
			||||
                ++result.ncontours; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            if (!(flags & MORE_COMPONENTS)) { | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } else { | 
				
			||||
        // NOTE: space
 | 
				
			||||
        //advance = 0; // TODO
 | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void | 
				
			||||
get_hmtx(struct ttf_font *font, u32 glyph_index, struct glyph *dest) | 
				
			||||
{ | 
				
			||||
    struct font_buffer font_file = font->file; | 
				
			||||
     | 
				
			||||
    // TODO: monospaced font only has one record
 | 
				
			||||
     | 
				
			||||
    font_file.offset = font->hmtx_offset + glyph_index * 4; | 
				
			||||
     | 
				
			||||
    dest->advance = read_be16(&font_file); | 
				
			||||
    dest->lsb = read_be16s(&font_file); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static struct glyph | 
				
			||||
get_outline(struct ttf_font *font, u16 codepoint) | 
				
			||||
{ | 
				
			||||
    u32 glyph_index = get_glyph_index(font, codepoint); | 
				
			||||
     | 
				
			||||
    struct glyph result = get_glyph_outline(font, glyph_index); | 
				
			||||
    get_hmtx(font, glyph_index, &result); | 
				
			||||
     | 
				
			||||
#if 1 | 
				
			||||
    int ymax = result.ymax; | 
				
			||||
    //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;
 | 
				
			||||
    } | 
				
			||||
#endif  | 
				
			||||
     | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static u16 | 
				
			||||
read_offset_table(struct font_buffer *file) | 
				
			||||
{ | 
				
			||||
    u16 num_tables = be16(file->data + 4); | 
				
			||||
    return(num_tables); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static struct font_directory | 
				
			||||
read_font_directory(struct font_buffer *file) | 
				
			||||
{ | 
				
			||||
    struct font_directory result = { 0 }; | 
				
			||||
    u16 table_count = read_offset_table(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); | 
				
			||||
         | 
				
			||||
        switch (tag) { | 
				
			||||
            case 0x636d6170: { | 
				
			||||
                /* cmap */ | 
				
			||||
                result.cmap_offset = offset; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            case 0x6c6f6361: { | 
				
			||||
                /* loca */ | 
				
			||||
                result.loca_offset = offset; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            case 0x68656164: { | 
				
			||||
                /* head */ | 
				
			||||
                result.head_offset = offset; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            case 0x676c7966: { | 
				
			||||
                /* glyf */ | 
				
			||||
                result.glyf_offset = offset; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            case 0x686d7478: { | 
				
			||||
                /* hmtx */ | 
				
			||||
                result.hmtx_offset = offset; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            case 0x6d617870: { | 
				
			||||
                /* maxp */ | 
				
			||||
                result.maxp_offset = offset; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
             | 
				
			||||
            case 0x68686561: { | 
				
			||||
                /* hhea */ | 
				
			||||
                result.hhea_offset = offset; | 
				
			||||
                break; | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
     | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void | 
				
			||||
read_head(struct font_directory font_dir, struct ttf_font *font) | 
				
			||||
{ | 
				
			||||
    font->head.units_per_em = be16(font->file.data + font_dir.head_offset + 18); | 
				
			||||
    font->head.itl_format = be16(font->file.data + font_dir.head_offset + 50); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void | 
				
			||||
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); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void | 
				
			||||
read_cmap(struct font_directory font_dir, struct ttf_font *font) | 
				
			||||
{ | 
				
			||||
    font->file.offset = font_dir.cmap_offset + 2; | 
				
			||||
     | 
				
			||||
    u16 num_tables = read_be16(&font->file); | 
				
			||||
     | 
				
			||||
    for (int st = 0; st < num_tables; ++st) { | 
				
			||||
        u16 platform_id = read_be16(&font->file); | 
				
			||||
        u16 platform_specific_id = read_be16(&font->file); | 
				
			||||
        u32 subtable_offset = read_be32(&font->file); | 
				
			||||
         | 
				
			||||
        if ((platform_id == 0 && (platform_specific_id == 3 || platform_specific_id == 4)) || | 
				
			||||
            (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; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static void | 
				
			||||
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); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
static struct ttf_font | 
				
			||||
parse_ttf_file(char *filename, char *fontname) | 
				
			||||
{ | 
				
			||||
    struct ttf_font result = { 0 }; | 
				
			||||
    struct font_buffer font_file = read_file(filename); | 
				
			||||
    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; | 
				
			||||
     | 
				
			||||
    read_head(font_dir, &result); | 
				
			||||
    read_maxp(font_dir, &result); | 
				
			||||
    read_cmap(font_dir, &result); | 
				
			||||
    read_hhea(font_dir, &result); | 
				
			||||
     | 
				
			||||
    result.name = fontname; | 
				
			||||
     | 
				
			||||
    return(result); | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue