Browse Source

No artifacts!

master
A.Olokhtonov 3 years ago
commit
c7bfdc6578
  1. 657
      main.c
  2. 684
      ttf2.c

657
main.c

@ -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);
}

684
ttf2.c

@ -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…
Cancel
Save