You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
10 KiB
316 lines
10 KiB
3 years ago
|
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,
|
||
2 years ago
|
f32 scale, u32 *pixels, int width, int at_x, int at_y, u32 color)
|
||
3 years ago
|
{
|
||
|
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);
|
||
|
|
||
|
if (brightness > 0) {
|
||
2 years ago
|
f32 alpha = brightness / 256.0f;
|
||
|
u32 bg = pixels[(at_y + (gheight - 1 - y)) * width + (at_x + x)];
|
||
|
u8 r = ((bg & 0xff0000) >> 16) * (1.0f - alpha) + alpha * ((color & 0xff0000) >> 16);
|
||
|
u8 g = ((bg & 0x00ff00) >> 8) * (1.0f - alpha) + alpha * ((color & 0x00ff00) >> 8);
|
||
|
u8 b = ((bg & 0x0000ff) >> 0) * (1.0f - alpha) + alpha * ((color & 0x0000ff) >> 0);
|
||
|
u32 value = 0xFF000000 | r << 16 | g << 8 | b;
|
||
|
pixels[(at_y + (gheight - 1 - y)) * width + (at_x + x)] = value;
|
||
3 years ago
|
} 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
3 years ago
|
static struct v2
|
||
2 years ago
|
render_utf_string(struct ttf_font font, int px_size, u32 *pixels, u32 width, wchar_t *string, int fit_width, int at_x, int at_y)
|
||
3 years ago
|
{
|
||
2 years ago
|
u32 color = 0xffffffff;
|
||
|
s32 offset_x = at_x;
|
||
|
s32 offset_y = at_y;
|
||
3 years ago
|
f32 scale = (f32) px_size / ((f32) (font.hhea.ascent - font.hhea.descent));
|
||
|
|
||
|
u32 len = wcslen(string);
|
||
|
|
||
3 years ago
|
struct v2 box = { 0 };
|
||
|
|
||
|
box.y = px_size;
|
||
|
|
||
3 years ago
|
for (u32 i = 0; i < len; ++i) {
|
||
|
u16 codepoint = string[i];
|
||
3 years ago
|
int advance;
|
||
2 years ago
|
if (codepoint == ' ') {
|
||
|
if (offset_x > 0) {
|
||
|
advance = get_codepoint_width(&font, scale, codepoint);
|
||
|
} else {
|
||
|
advance = 0;
|
||
|
}
|
||
|
} else if (codepoint == '\t') {
|
||
|
advance = get_codepoint_width(&font, scale, codepoint);
|
||
|
advance *= 4;
|
||
|
} else if (codepoint == '\n') {
|
||
|
if (offset_x > box.x) {
|
||
|
box.x = offset_x;
|
||
|
}
|
||
|
|
||
|
advance = 0;
|
||
|
offset_x = at_x;
|
||
|
offset_y += px_size;
|
||
|
box.y += px_size;
|
||
|
} else {
|
||
3 years ago
|
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);
|
||
|
|
||
2 years ago
|
render_glyph(g, px_size, &lines, scale, pixels, width, offset_x, offset_y, color);
|
||
3 years ago
|
|
||
3 years ago
|
advance = ceil_f32(scale * g.advance);
|
||
3 years ago
|
free(lines.data);
|
||
|
}
|
||
|
|
||
3 years ago
|
offset_x += advance;
|
||
|
|
||
|
if (offset_x - at_x >= fit_width) {
|
||
|
offset_x = at_x;
|
||
|
offset_y += px_size;
|
||
|
box.y += px_size;
|
||
2 years ago
|
box.x = fit_width;
|
||
3 years ago
|
}
|
||
3 years ago
|
}
|
||
3 years ago
|
|
||
2 years ago
|
if (offset_x > box.x) {
|
||
|
box.x = offset_x - at_x;
|
||
|
}
|
||
|
|
||
3 years ago
|
return(box);
|
||
3 years ago
|
}
|