A.Olokhtonov
3 years ago
4 changed files with 452 additions and 577 deletions
@ -0,0 +1,279 @@
@@ -0,0 +1,279 @@
|
||||
|
||||
static int |
||||
scanline_intersects_line(f32 y, struct v2f p0, struct v2f p1, f32 lasty, f32 *vx) |
||||
{ |
||||
bool goes_up = (p0.y > p1.y); |
||||
f32 t1 = (y - p0.y) / (p1.y - p0.y); /* NOTE(aolo2): no horizontal lines by design */ |
||||
|
||||
if (t1 == 0) { |
||||
f32 x1 = p0.x + (p1.x - p0.x) * t1; |
||||
if ((lasty < p0.y) && (p0.y > p1.y)) return(0); |
||||
if ((lasty > p0.y) && (p0.y < p1.y)) return(0); |
||||
int result = (goes_up ? 1 : -1); |
||||
*vx = x1; |
||||
return(result); |
||||
} |
||||
|
||||
if (0 < t1 && t1 < 1.0f) { |
||||
f32 x1 = p0.x + (p1.x - p0.x) * t1; |
||||
*vx = x1; |
||||
int result = (goes_up ? 1 : -1); |
||||
return(result); |
||||
} |
||||
|
||||
return(0); |
||||
} |
||||
|
||||
|
||||
static int |
||||
intersect_glyph(struct line_contour *lines, f32 y, struct intersection *intersections) |
||||
{ |
||||
int nints = 0; |
||||
|
||||
for (int c = 0; c < lines->ncontours; ++c) { |
||||
int from = lines->from[c]; |
||||
int to = lines->from[c + 1]; |
||||
|
||||
for (int i = from; i < to; ++i) { |
||||
int lasti = (i > from ? i - 1 : to - 1); |
||||
f32 lasty = lines->data[lasti].a.y; |
||||
f32 vx; |
||||
int r = scanline_intersects_line(y, lines->data[i].a, lines->data[i].b, lasty, &vx); |
||||
|
||||
if (r) { |
||||
intersections[nints].x = vx; |
||||
intersections[nints].dir = r; |
||||
++nints; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return(nints); |
||||
} |
||||
|
||||
|
||||
static void |
||||
sort_intersections(struct intersection *intersections, int size) |
||||
{ |
||||
bool swapped = true; |
||||
|
||||
while (swapped) { |
||||
swapped = false; |
||||
for (int i = 0; i < size - 1; ++i) { |
||||
f32 x1 = intersections[i].x; |
||||
f32 x2 = intersections[i + 1].x; |
||||
if (x1 > x2) { |
||||
struct intersection tmp = intersections[i]; |
||||
intersections[i] = intersections[i + 1]; |
||||
intersections[i + 1] = tmp; |
||||
swapped = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void |
||||
render_glyph(struct glyph g, int px_size, struct line_contour *lines, |
||||
f32 scale, u32 *pixels, int width, int at_x, int at_y) |
||||
{ |
||||
int oversample_y = 4; |
||||
if (px_size <= 12) { |
||||
oversample_y = 8; |
||||
} |
||||
|
||||
f32 oversample_step = 1.0f / (oversample_y + 1); |
||||
f32 oversample_norm = 1.0f / oversample_y; |
||||
|
||||
int gwidth = ceil_f32((g.xmax - g.xmin + g.lsb) * scale); |
||||
int gheight = px_size; |
||||
|
||||
struct intersection *intersections = malloc(lines->from[lines->ncontours] * sizeof(struct intersection)); |
||||
|
||||
f32 *accumulator = calloc(1, gwidth * gheight * sizeof(f32)); |
||||
|
||||
for (int y = 0; y < gheight; ++y) { |
||||
for (int yy = 1; yy <= oversample_y; ++yy) { |
||||
u32 ncross = intersect_glyph(lines, y + oversample_step * yy, intersections); |
||||
if (ncross) { |
||||
sort_intersections(intersections, ncross); |
||||
|
||||
int state = 0; |
||||
|
||||
for (u32 i = 0; i < ncross - 1; ++i) { |
||||
struct intersection inter = intersections[i]; |
||||
struct intersection next_inter = intersections[i + 1]; |
||||
|
||||
state += inter.dir; |
||||
|
||||
if (state != 0) { |
||||
f32 x0 = inter.x; |
||||
f32 x1 = next_inter.x; |
||||
|
||||
int x_from = x0; |
||||
int x_to = (x1 > gwidth - 1 ? gwidth - 1 : x1); |
||||
|
||||
f32 start_brightness = (x_from + 1 - x0); |
||||
f32 end_brightness = (x1 - x_to); |
||||
|
||||
for (int x = x_from + 1; x < x_to; ++x) { |
||||
accumulator[y * gwidth + x] += oversample_norm; |
||||
} |
||||
|
||||
accumulator[y * gwidth + x_from] += start_brightness * oversample_norm; |
||||
accumulator[y * gwidth + x_to] += end_brightness * oversample_norm; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (int y = 0; y < gheight; ++y) { |
||||
for (int x = 0; x < gwidth; ++x) { |
||||
//printf(" %.2f", accumulator[y * gwidth + x]);
|
||||
u32 brightness = clamp_u32(accumulator[y * gwidth + x] * 255.99f, 255); |
||||
u32 white = 0xFF000000 | brightness << 16 | brightness << 8 | brightness; |
||||
|
||||
if (brightness > 0) { |
||||
pixels[(at_y + (gheight - 1 - y)) * width + (at_x + x)] = white; |
||||
} else { |
||||
//pixels[(at_y + (gheight - 1 - y)) * width + (at_x + x)] = 0xFFFF0000;
|
||||
} |
||||
} |
||||
} |
||||
|
||||
free(accumulator); |
||||
|
||||
//exit(1);
|
||||
|
||||
free(intersections); |
||||
} |
||||
|
||||
static void |
||||
outline_to_lines(struct glyph g, f32 scale, int max_descent, struct line_contour *dest, int *cnt) |
||||
{ |
||||
int nlines = 0; |
||||
int points_from = 0; |
||||
int curve_segments = 5; |
||||
|
||||
for (int c = 0; c < g.ncontours; ++c) { |
||||
for (int p = points_from; p < g.end_pts_of_contours[c] + 1; ++p) { |
||||
struct glyph_point gp = g.points[p]; |
||||
|
||||
int nexti = (p + 1 < g.end_pts_of_contours[c] + 1 ? p + 1 : points_from); |
||||
struct glyph_point nextgp = g.points[nexti]; |
||||
|
||||
if (p == points_from && !gp.on_curve) { |
||||
continue; |
||||
} |
||||
|
||||
f32 x1 = (gp.x + g.lsb) * scale; |
||||
f32 y1 = (gp.y - g.baseline - max_descent) * scale; |
||||
|
||||
f32 x2 = (nextgp.x + g.lsb) * scale; |
||||
f32 y2 = (nextgp.y - g.baseline - max_descent) * scale; |
||||
|
||||
if (nextgp.on_curve) { |
||||
if (gp.y != nextgp.y) { |
||||
if (dest->data) { |
||||
dest->data[nlines].a = (struct v2f) { x1, y1 }; |
||||
dest->data[nlines].b = (struct v2f) { x2, y2 }; |
||||
} |
||||
++nlines; |
||||
} |
||||
} else { |
||||
int nextnexti = (nexti + 1 < g.end_pts_of_contours[c] + 1 ? nexti + 1 : points_from); |
||||
struct glyph_point nextnextgp = g.points[nextnexti]; |
||||
|
||||
f32 x3 = (nextnextgp.x + g.lsb) * scale; |
||||
f32 y3 = (nextnextgp.y - g.baseline - max_descent) * scale; |
||||
|
||||
/* P(t) = P0*t^2 + P1*2*t*(1-t) + P2*(1-t)^2 */ |
||||
f32 t_step = 1.0f / curve_segments; |
||||
f32 x_prev = x1; |
||||
f32 y_prev = y1; |
||||
|
||||
/* s = 1 for exact beginning */ |
||||
for (int s = 1; s <= curve_segments; ++s) { |
||||
f32 t_now = t_step * s; |
||||
f32 x_now; |
||||
f32 y_now; |
||||
|
||||
if (s < curve_segments) { |
||||
x_now = x3 * t_now * t_now |
||||
+ x2 * 2.0f * t_now * (1.0f - t_now) |
||||
+ x1 * (1.0f - t_now) * (1.0f - t_now); |
||||
|
||||
y_now = y3 * t_now * t_now |
||||
+ y2 * 2.0f * t_now * (1.0f - t_now) |
||||
+ y1 * (1.0f - t_now) * (1.0f - t_now); |
||||
} else { |
||||
/* For exact match between neighbours */ |
||||
x_now = x3; |
||||
y_now = y3; |
||||
} |
||||
|
||||
if (abs_f32(y_now - y_prev) > F32EPS) { |
||||
if (dest->data) { |
||||
dest->data[nlines].a = (struct v2f) { x_prev, y_prev }; |
||||
dest->data[nlines].b = (struct v2f) { x_now, y_now }; |
||||
} |
||||
++nlines; |
||||
} |
||||
|
||||
x_prev = x_now; |
||||
y_prev = y_now; |
||||
} |
||||
|
||||
++p; |
||||
} |
||||
} |
||||
|
||||
dest->from[c + 1] = nlines; |
||||
|
||||
points_from = g.end_pts_of_contours[c] + 1; |
||||
} |
||||
|
||||
dest->ncontours = g.ncontours; |
||||
|
||||
if (cnt) { |
||||
*cnt = nlines; |
||||
} |
||||
} |
||||
|
||||
|
||||
static void |
||||
render_utf_string(struct ttf_font font, int px_size, u32 *pixels, u32 width, wchar_t *string, int at_x, int at_y) |
||||
{ |
||||
u32 offset_x = at_x; |
||||
u32 offset_y = at_y; |
||||
f32 scale = (f32) px_size / ((f32) (font.hhea.ascent - font.hhea.descent)); |
||||
|
||||
u32 len = wcslen(string); |
||||
|
||||
for (u32 i = 0; i < len; ++i) { |
||||
u16 codepoint = string[i]; |
||||
if (codepoint != ' ') { |
||||
struct glyph g = get_outline(&font, codepoint); |
||||
|
||||
//exit(0);
|
||||
|
||||
struct line_contour lines = { 0 }; |
||||
int nlines = 0; |
||||
outline_to_lines(g, scale, font.hhea.descent, &lines, &nlines); |
||||
lines.data = malloc(nlines * sizeof(struct line)); |
||||
outline_to_lines(g, scale, font.hhea.descent, &lines, 0); |
||||
|
||||
render_glyph(g, px_size, &lines, scale, pixels, width, offset_x, offset_y); |
||||
|
||||
offset_x += ceil_f32(scale * g.advance); |
||||
|
||||
free(lines.data); |
||||
} else { |
||||
int space_advance = get_codepoint_width(&font, scale, codepoint); |
||||
offset_x += space_advance; |
||||
} |
||||
|
||||
//XPutImage(display, window, default_gc, xwindow_buffer, 0, 0, 0, 0, width, height);
|
||||
//sleep(1);
|
||||
} |
||||
} |
@ -0,0 +1,154 @@
@@ -0,0 +1,154 @@
|
||||
struct v2 { |
||||
int x; |
||||
int y; |
||||
}; |
||||
|
||||
struct v2u32 { |
||||
u32 x; |
||||
u32 y; |
||||
}; |
||||
|
||||
struct v2f { |
||||
f32 x; |
||||
f32 y; |
||||
}; |
||||
|
||||
struct line { |
||||
struct v2f a; |
||||
struct v2f b; |
||||
}; |
||||
|
||||
struct line_contour { |
||||
int ncontours; |
||||
int from[MAX_CONTOURS]; |
||||
struct line *data; |
||||
}; |
||||
|
||||
struct intersection { |
||||
f32 x; |
||||
int dir; |
||||
}; |
||||
|
||||
enum rgb_channel { |
||||
RED = 0x000000FF, |
||||
GREEN = 0x0000FF00, |
||||
BLUE = 0x00FF0000, |
||||
}; |
||||
|
||||
struct font_buffer { |
||||
u8 *data; |
||||
u64 offset; |
||||
u64 size; |
||||
}; |
||||
|
||||
struct font_directory { |
||||
u32 cmap_offset; |
||||
u32 head_offset; |
||||
u32 hhea_offset; |
||||
u32 loca_offset; |
||||
u32 glyf_offset; |
||||
u32 hmtx_offset; |
||||
u32 maxp_offset; |
||||
u32 post_offset; |
||||
|
||||
u32 cvt_offset; |
||||
u32 cvt_size; |
||||
|
||||
u32 prep_offset; |
||||
u32 prep_size; |
||||
|
||||
u32 fpgm_offset; |
||||
u32 fpgm_size; |
||||
}; |
||||
|
||||
union glyph_flag { |
||||
struct { |
||||
u8 on_curve : 1; |
||||
u8 x_short : 1; |
||||
u8 y_short : 1; |
||||
u8 repeat : 1; |
||||
u8 x_short_pos : 1; |
||||
u8 y_short_pos : 1; |
||||
u8 reserved1 : 1; |
||||
u8 reserved2 : 1; |
||||
}; |
||||
u8 flag; |
||||
}; |
||||
|
||||
enum compund_glyph_flag { |
||||
ARG_1_AND_2_ARE_WORDS = 0x1, |
||||
ARGS_ARE_XY_VALUES = 0x2, |
||||
ROUND_XY_TO_GRID = 0x4, |
||||
WE_HAVE_A_SCALE = 0x8, |
||||
|
||||
MORE_COMPONENTS = 0x20, |
||||
WE_HAVE_AN_X_AND_Y_SCALE = 0x40, |
||||
WE_HAVE_A_TWO_BY_TWO = 0x80, |
||||
WE_HAVE_INSTRUCTIONS = 0x100, |
||||
USE_MY_METRICS = 0x200, |
||||
OVERLAP_COMPOUND = 0x400 |
||||
}; |
||||
|
||||
struct maxp_table { |
||||
u16 max_component_points; |
||||
u16 max_component_contours; |
||||
u16 max_storage; |
||||
u16 max_fdefs; |
||||
}; |
||||
|
||||
struct head_table { |
||||
int itl_format; |
||||
int units_per_em; |
||||
}; |
||||
|
||||
struct hhea_table { |
||||
int ascent; |
||||
int descent; |
||||
int line_gap; |
||||
int max_advance; |
||||
}; |
||||
|
||||
struct glyph_point { |
||||
s16 x; |
||||
s16 y; |
||||
bool on_curve; |
||||
}; |
||||
|
||||
struct glyph_segment { |
||||
bool is_curve; |
||||
struct v2f p0; |
||||
struct v2f p1; |
||||
struct v2f p2; |
||||
}; |
||||
|
||||
struct glyph { |
||||
s16 xmin, ymin; |
||||
s16 xmax, ymax; |
||||
|
||||
s32 baseline; |
||||
u16 advance; |
||||
s16 lsb; |
||||
|
||||
u16 ncontours; |
||||
u16 *end_pts_of_contours; |
||||
struct glyph_point *points; |
||||
}; |
||||
|
||||
struct ttf_font { |
||||
struct font_buffer file; |
||||
|
||||
struct maxp_table maxp; |
||||
struct head_table head; |
||||
struct hhea_table hhea; |
||||
|
||||
s16 *cvt; |
||||
int cmap_format; |
||||
int is_monospace; |
||||
|
||||
struct font_directory dir; |
||||
struct ttf_function *functions; |
||||
|
||||
u32 *storage; |
||||
|
||||
char *name; |
||||
}; |
Loading…
Reference in new issue