summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2025-04-26 09:37:51 +0200
committerMitya Selivanov <automainint@guattari.tech>2025-04-26 09:37:51 +0200
commit7cac379388df5a551321e407c29256f92c3085b5 (patch)
treee3c2e3d2a2a5117816ecd9f630197886821ba114
parentd62bebba889db9c3b69fa4f9da7a5de6c1b02a9b (diff)
downloadreduced_system_layer-7cac379388df5a551321e407c29256f92c3085b5.zip
Stencil buffer
-rw-r--r--graphics.c832
-rw-r--r--runtime.c54
2 files changed, 649 insertions, 237 deletions
diff --git a/graphics.c b/graphics.c
index 02a0e63..22cc666 100644
--- a/graphics.c
+++ b/graphics.c
@@ -132,16 +132,16 @@ typedef struct graphics_request_ {
f64 width;
} line;
struct {
- i32 font;
vec4_f32 color;
+ i32 font;
Box area;
vec2 max_size;
i64 num_chars;
c32 * text;
} text_area;
struct {
- i32 font;
vec4_f32 color;
+ i32 font;
Box area;
vec2 max_size;
i64 num_chars;
@@ -204,15 +204,28 @@ b8 hit_quad (vec2 vertices[4], vec2 point);
b8 hit_ellipse (Box area, vec2 point);
b8 hit_line (vec2 vertices[2], f64 width, vec2 point);
+Stencil_Buffer default_stencil(i64 width, i64 height);
+
+void fill_stencil_to_stencil (Stencil_Buffer dst, u8 value, Box area, Stencil_Buffer src);
+void fill_rectangle_to_stencil (Stencil_Buffer dst, u8 value, Box area);
+void fill_triangle_to_stencil (Stencil_Buffer dst, u8 value, vec2 vertices[3]);
+void fill_triangles_to_stencil (Stencil_Buffer dst, u8 value, vec2 position, vec2 scale, i64 num_triangles, vec2 *vertices);
+void fill_quad_to_stencil (Stencil_Buffer dst, u8 value, vec2 vertices[4]);
+void fill_ellipse_to_stencil (Stencil_Buffer dst, u8 value, Box area);
+void fill_line_to_stencil (Stencil_Buffer dst, u8 value, vec2 vertices[2], f64 width);
+void draw_text_area_to_stencil (Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text);
+void draw_text_cursor_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection);
+
void draw_pixels_to_buffer (Pixel_Buffer dst, Box area, Pixel_Buffer src);
+void fill_stencil_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area, Stencil_Buffer src);
void fill_rectangle_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area);
-void fill_triangle_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[3]);
-void fill_triangles_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 position, vec2 scale, i64 num_triangles, vec2 *vertices);
-void fill_quad_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4]);
-void fill_ellipse_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area);
-void fill_line_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width);
-void draw_text_area_to_buffer (Pixel_Buffer dst, i32 font, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text);
-void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection);
+void fill_triangle_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[3], Stencil_Buffer stencil);
+void fill_triangles_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 position, vec2 scale, i64 num_triangles, vec2 *vertices, Stencil_Buffer stencil);
+void fill_quad_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4], Stencil_Buffer stencil);
+void fill_ellipse_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area, Stencil_Buffer stencil);
+void fill_line_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width, Stencil_Buffer stencil);
+void draw_text_area_to_buffer (Pixel_Buffer dst, vec4_f32 color, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, Stencil_Buffer stencil);
+void draw_text_cursor_to_buffer(Pixel_Buffer dst, vec4_f32 color, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection, Stencil_Buffer stencil);
void draw_pixels_cached (Box area, Pixel_Buffer src);
void fill_rectangle_cached (vec4_f32 color, Box area);
@@ -600,30 +613,48 @@ b8 hit_line(vec2 vertices[2], f64 width, vec2 point) {
}, point);
}
-static void put_pixel_(vec4_f32 *dst, vec4_f32 color) {
- if (color.w == 1.f)
- *dst = color;
- else {
- f32 dst_amount = 1. - color.w;
+static u8 mix_u8_(u8 x, u8 y) {
+ u32 z = (((u32) x) * ((u32) y) + 0x7f) / 0xff;
+ return z >= 0xff ? (u8) 0xff : (u8) z;
+}
- *dst = (vec4_f32) {
- .x = dst->x * dst_amount + color.x * color.w,
- .y = dst->y * dst_amount + color.y * color.w,
- .z = dst->z * dst_amount + color.z * color.w,
- .w = max2_f32_(dst->w, color.w),
+static i64 _default_stencil_len = 0;
+static u8 *_default_stencil = NULL;
+
+Stencil_Buffer default_stencil(i64 width, i64 height) {
+ i64 len = width * height;
+ if (len < 0)
+ len = 1;
+
+ if (len > _default_stencil_len)
+ resize_dynamic_array_exact(&_default_stencil_len, (void **) &_default_stencil, sizeof *_default_stencil, len);
+
+ if (width <= 0 || height <= 0)
+ return (Stencil_Buffer) {
+ .width = 0,
+ .height = 0,
+ .stride = 1,
+ .pixels = _default_stencil,
};
- }
-}
-void draw_pixels_to_buffer(Pixel_Buffer dst, Box area, Pixel_Buffer src) {
- if (area.width < EPSILON || area.height < EPSILON)
- return;
+ i64 h = height;
+ if (len > _default_stencil_len)
+ h = _default_stencil_len / width;
- PROFILER_begin(PROFILE_DRAW_PIXELS);
+ mem_set_(_default_stencil, 0, width * h);
+ return (Stencil_Buffer) {
+ .width = width,
+ .height = h,
+ .stride = width,
+ .pixels = _default_stencil,
+ };
+}
+
+void fill_stencil_to_stencil(Stencil_Buffer dst, u8 value, Box area, Stencil_Buffer src) {
i64 x0 = (i64) floor(area.x + 0.5);
i64 x1 = (i64) floor(area.x + area.width + 0.5);
- i64 y0 = (i64) floor(area.y + 0.5);
+ i64 y0 = (i64) floor(area.y);
i64 y1 = (i64) floor(area.y + area.height + 0.5);
i64 i0 = max2_i64_(x0, 0);
@@ -631,6 +662,8 @@ void draw_pixels_to_buffer(Pixel_Buffer dst, Box area, Pixel_Buffer src) {
i64 j0 = max2_i64_(y0, 0);
i64 j1 = min2_i64_(y1, dst.height);
+ if (i1 <= i0 || j1 <= j0) return;
+
u32 x_ratio = (src.width << 16) / (x1 - x0);
u32 y_ratio = (src.height << 16) / (y1 - y0);
@@ -638,218 +671,150 @@ void draw_pixels_to_buffer(Pixel_Buffer dst, Box area, Pixel_Buffer src) {
u32 y_half = y_ratio / 2;
for (i64 j = j0; j < j1; ++j) {
- u32 src_j = ((j - y0) * y_ratio + y_half) >> 16;
- vec4_f32 *d = dst.pixels + j * dst.stride + i0;
- vec4_f32 *d_end = d + (i1 - i0);
- vec4_f32 *s = src.pixels + src_j * src.stride;
- for (u32 i = i0 - x0; d < d_end; ++d, ++i) {
- u32 src_i = (i * x_ratio + x_half) >> 16;
- put_pixel_(d, s[src_i]);
- }
+ u32 jj = ((j - y0) * y_ratio + y_half) >> 16;
+ u8 *s = src.pixels + jj * src.stride;
+ u8 *d = dst.pixels + j * dst.stride;
+ u8 *d_end = d + (i1 - i0);
+ for (u32 x = i0 - x0; d < d_end; ++d, ++x)
+ *d = max2_u8_(*d, mix_u8_(value, s[(x * x_ratio + x_half) >> 16]));
}
-
- PROFILER_end(PROFILE_DRAW_PIXELS);
}
-void fill_rectangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) {
- PROFILER_begin(PROFILE_FILL_RECTANGLE);
-
- i64 i0 = (i64) floor(area.x + 0.5);
- i64 i1 = (i64) floor(area.x + area.width + 0.5);
- i64 j0 = (i64) floor(area.y + 0.5);
- i64 j1 = (i64) floor(area.y + area.height + 0.5);
+void fill_rectangle_to_stencil(Stencil_Buffer dst, u8 value, Box area) {
+ i64 x0 = (i64) floor(area.x + 0.5);
+ i64 x1 = (i64) floor(area.x + area.width + 0.5);
+ i64 y0 = (i64) floor(area.y);
+ i64 y1 = (i64) floor(area.y + area.height + 0.5);
- if (i0 < 0) i0 = 0;
- if (i1 > dst.width) i1 = dst.width;
- if (j0 < 0) j0 = 0;
- if (j1 > dst.height) j1 = dst.height;
+ i64 i0 = max2_i64_(x0, 0);
+ i64 i1 = min2_i64_(x1, dst.width);
+ i64 j0 = max2_i64_(y0, 0);
+ i64 j1 = min2_i64_(y1, dst.height);
for (i64 j = j0; j < j1; ++j) {
- vec4_f32 *p = dst.pixels + j * dst.stride + i0;
- vec4_f32 *p_end = dst.pixels + j * dst.stride + i1;
- for (; p < p_end; ++p)
- put_pixel_(p, color);
+ u8 *d = dst.pixels + j * dst.stride + i0;
+ u8 *d_end = d + (i1 - i0);
+ for (; d < d_end; ++d)
+ *d = max2_u8_(*d, value);
}
-
- PROFILER_end(PROFILE_FILL_RECTANGLE);
}
-void fill_triangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[3]) {
- PROFILER_begin(PROFILE_FILL_TRIANGLE);
+void fill_triangle_to_stencil(Stencil_Buffer dst, u8 value, vec2 vertices[3]) {
+ i64 x0 = (i64) floor(vertices[0].x + 0.5);
+ i64 y0 = (i64) floor(vertices[0].y + 0.5);
+ i64 x1 = (i64) floor(vertices[1].x + 0.5);
+ i64 y1 = (i64) floor(vertices[1].y + 0.5);
+ i64 x2 = (i64) floor(vertices[2].x + 0.5);
+ i64 y2 = (i64) floor(vertices[2].y + 0.5);
- f64 x0 = vertices[0].x;
- f64 y0 = vertices[0].y;
- f64 x1 = vertices[1].x;
- f64 y1 = vertices[1].y;
- f64 x2 = vertices[2].x;
- f64 y2 = vertices[2].y;
+ i64 i0 = max2_i64_(min3_i64_(x0, x1, x2), 0);
+ i64 j0 = max2_i64_(min3_i64_(y0, y1, y2), 0);
+ i64 i1 = min2_i64_(max3_i64_(x0, x1, x2) + 1, dst.width);
+ i64 j1 = min2_i64_(max3_i64_(y0, y1, y2) + 1, dst.height);
- i64 i0 = (i64) floor(min3_(x0, x1, x2));
- i64 j0 = (i64) floor(min3_(y0, y1, y2));
- i64 i1 = (i64) ceil (max3_(x0, x1, x2));
- i64 j1 = (i64) ceil (max3_(y0, y1, y2));
-
- if (i0 < 0) i0 = 0;
- if (i1 >= dst.width) i1 = dst.width - 1;
- if (j0 < 0) j0 = 0;
- if (j1 >= dst.height) j1 = dst.height - 1;
-
- for (i64 j = j0; j <= j1; ++j) {
+ for (i64 j = j0; j < j1; ++j) {
// FIXME, PERF: Calculate the exact intersection.
- i64 left = i1;
+ i64 left = i1 - 1;
i64 right = i0;
- for (i64 i = i0; i <= i1; ++i)
+ for (i64 i = i0; i < i1; ++i)
if (hit_triangle(vertices, (vec2) { (f64) i, (f64) j, })) {
left = i;
break;
}
- for (i64 i = i1; i >= i0; --i)
+ for (i64 i = i1 - 1; i >= i0; --i)
if (hit_triangle(vertices, (vec2) { (f64) i, (f64) j, })) {
right = i;
break;
}
- vec4_f32 *p = dst.pixels + j * dst.stride + left;
- vec4_f32 *p_end = dst.pixels + j * dst.stride + right + 1;
- for (; p < p_end; ++p)
- put_pixel_(p, color);
+ u8 *d = dst.pixels + j * dst.stride + left;
+ u8 *d_end = dst.pixels + j * dst.stride + right + 1;
+ for (; d < d_end; ++d)
+ *d = max2_u8_(*d, value);
}
-
- PROFILER_end(PROFILE_FILL_TRIANGLE);
}
-static void triangles_area_(f64 *x0, f64 *y0, f64 *x1, f64 *y1, vec2 p, vec2 s, i64 num, vec2 *v) {
- if (num <= 0)
+void fill_triangles_to_stencil(Stencil_Buffer dst, u8 value, vec2 position, vec2 scale, i64 num_triangles, vec2 *vertices) {
+ if (vertices == NULL) {
+ LOG_error("Sanity");
return;
-
- *x0 = min3_(p.x + v[0].x * s.x, p.x + v[1].x * s.x, p.x + v[2].x * s.x);
- *y0 = min3_(p.y + v[0].y * s.y, p.y + v[1].y * s.y, p.y + v[2].y * s.y);
- *x1 = max3_(p.x + v[0].x * s.x, p.x + v[1].x * s.x, p.x + v[2].x * s.x);
- *y1 = max3_(p.y + v[0].y * s.y, p.y + v[1].y * s.y, p.y + v[2].y * s.y);
-
- for (i64 i = 1; i < num; ++i) {
- *x0 = min4_(*x0, p.x + v[i * 3].x * s.x, p.x + v[i * 3 + 1].x * s.x, p.x + v[i * 3 + 2].x * s.x);
- *y0 = min4_(*y0, p.y + v[i * 3].y * s.y, p.y + v[i * 3 + 1].y * s.y, p.y + v[i * 3 + 2].y * s.y);
- *x1 = max4_(*x1, p.x + v[i * 3].x * s.x, p.x + v[i * 3 + 1].x * s.x, p.x + v[i * 3 + 2].x * s.x);
- *y1 = max4_(*y1, p.y + v[i * 3].y * s.y, p.y + v[i * 3 + 1].y * s.y, p.y + v[i * 3 + 2].y * s.y);
}
-}
-void fill_triangles_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 position, vec2 scale, i64 num_triangles, vec2 *vertices) {
- PROFILER_begin(PROFILE_FILL_TRIANGLES);
+ // NOTE: Stencil filling is additive.
- f64 x0, y0, x1, y1;
- triangles_area_(&x0, &y0, &x1, &y1, position, scale, num_triangles, vertices);
-
- i64 i0 = (i64) floor(x0);
- i64 j0 = (i64) floor(y0);
- i64 i1 = (i64) ceil (x1);
- i64 j1 = (i64) ceil (y1);
-
- if (i0 < 0) i0 = 0;
- if (i1 >= dst.width) i1 = dst.width - 1;
- if (j0 < 0) j0 = 0;
- if (j1 >= dst.height) j1 = dst.height - 1;
-
- // FIXME, PERF: Use stencil buffer.
-
- for (i64 j = j0; j <= j1; ++j)
- for (i64 i = i0; i <= i1; ++i)
- for (i64 k = 0; k < num_triangles; ++k) {
- vec2 triangle[3] = {
- vertices[3 * k],
- vertices[3 * k + 1],
- vertices[3 * k + 2],
- };
-
- for (i64 r = 0; r < 3; ++r) {
- triangle[r].x = position.x + triangle[r].x * scale. x;
- triangle[r].y = position.y + triangle[r].y * scale.y;
- }
-
- if (hit_triangle(triangle, (vec2) { (f64) i, (f64) j, })) {
- put_pixel_(dst.pixels + j * dst.stride + i, color);
- break;
- }
- }
-
- PROFILER_end(PROFILE_FILL_TRIANGLES);
+ for (i64 i = 0; i < num_triangles; ++i)
+ fill_triangle_to_stencil(dst, value, (vec2[3]) {
+ { position.x + vertices[i * 3].x * scale.x,
+ position.y + vertices[i * 3].y * scale.y },
+ { position.x + vertices[i * 3 + 1].x * scale.x,
+ position.y + vertices[i * 3 + 1].y * scale.y },
+ { position.x + vertices[i * 3 + 2].x * scale.x,
+ position.y + vertices[i * 3 + 2].y * scale.y },
+ });
}
-void fill_quad_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4]) {
- PROFILER_begin(PROFILE_FILL_QUAD);
-
- f64 x0 = vertices[0].x;
- f64 y0 = vertices[0].y;
- f64 x1 = vertices[1].x;
- f64 y1 = vertices[1].y;
- f64 x2 = vertices[2].x;
- f64 y2 = vertices[2].y;
- f64 x3 = vertices[3].x;
- f64 y3 = vertices[3].y;
-
- i64 i0 = (i64) floor(min4_(x0, x1, x2, x3));
- i64 j0 = (i64) floor(min4_(y0, y1, y2, y3));
- i64 i1 = (i64) ceil (max4_(x0, x1, x2, x3));
- i64 j1 = (i64) ceil (max4_(y0, y1, y2, y3));
+void fill_quad_to_stencil(Stencil_Buffer dst, u8 value, vec2 vertices[4]) {
+ i64 x0 = (i64) floor(vertices[0].x + 0.5);
+ i64 y0 = (i64) floor(vertices[0].y + 0.5);
+ i64 x1 = (i64) floor(vertices[1].x + 0.5);
+ i64 y1 = (i64) floor(vertices[1].y + 0.5);
+ i64 x2 = (i64) floor(vertices[2].x + 0.5);
+ i64 y2 = (i64) floor(vertices[2].y + 0.5);
+ i64 x3 = (i64) floor(vertices[3].x + 0.5);
+ i64 y3 = (i64) floor(vertices[3].y + 0.5);
- if (i0 < 0) i0 = 0;
- if (i1 >= dst.width) i1 = dst.width - 1;
- if (j0 < 0) j0 = 0;
- if (j1 >= dst.height) j1 = dst.height - 1;
+ i64 i0 = max2_i64_(min4_i64_(x0, x1, x2, x3), 0);
+ i64 j0 = max2_i64_(min4_i64_(y0, y1, y2, y3), 0);
+ i64 i1 = min2_i64_(max4_i64_(x0, x1, x2, x3) + 1, dst.width);
+ i64 j1 = min2_i64_(max4_i64_(y0, y1, y2, y3) + 1, dst.height);
- for (i64 j = j0; j <= j1; ++j) {
+ for (i64 j = j0; j < j1; ++j) {
// FIXME, PERF: Calculate the exact intersection.
- i64 left = i1;
+ i64 left = i1 - 1;
i64 right = i0;
- for (i64 i = i0; i <= i1; ++i)
+ for (i64 i = i0; i < i1; ++i)
if (hit_quad(vertices, (vec2) { (f64) i, (f64) j, })) {
left = i;
break;
}
- for (i64 i = i1; i >= i0; --i)
+ for (i64 i = i1 - 1; i >= i0; --i)
if (hit_quad(vertices, (vec2) { (f64) i, (f64) j, })) {
right = i;
break;
}
- vec4_f32 *p = dst.pixels + j * dst.stride + left;
- vec4_f32 *p_end = dst.pixels + j * dst.stride + right + 1;
- for (; p < p_end; ++p)
- put_pixel_(p, color);
+ u8 *d = dst.pixels + j * dst.stride + left;
+ u8 *d_end = dst.pixels + j * dst.stride + right + 1;
+ for (; d < d_end; ++d)
+ *d = max2_u8_(*d, value);
}
-
- PROFILER_end(PROFILE_FILL_QUAD);
}
-void fill_ellipse_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) {
- PROFILER_begin(PROFILE_FILL_ELLIPSE);
-
+void fill_ellipse_to_stencil(Stencil_Buffer dst, u8 value, Box area) {
f64 y1 = area.y + area.height;
- i64 j0 = (i64) floor(area.y);
- i64 j1 = (i64) ceil (y1);
+ i64 j0 = (i64) floor(area.y + 0.5);
+ i64 j1 = (i64) floor(y1 + 0.5);
- if (j0 < 0) j0 = 0;
- if (j1 >= dst.height) j1 = dst.height - 1;
+ if (j0 < 0) j0 = 0;
+ if (j1 > dst.height) j1 = dst.height;
f64 half_w = area.width / 2.0;
f64 half_h = area.height / 2.0;
- if (half_h < EPSILON)
- goto _finish;
+ if (half_h < EPSILON) return;
f64 inv_half_h = 1.0 / half_h;
f64 cx = area.x + half_w;
f64 cy = area.y + half_h;
- for (i64 j = j0; j <= j1; ++j) {
+ for (i64 j = j0; j < j1; ++j) {
f64 ry = (j - cy + 0.5) * inv_half_h;
if (ry > 1.0 - EPSILON || ry < -1.0 + EPSILON)
continue;
@@ -862,14 +827,11 @@ void fill_ellipse_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) {
if (left < 0) left = 0;
if (right >= dst.width) right = dst.width - 1;
- vec4_f32 *p = dst.pixels + j * dst.stride + left;
- vec4_f32 *p_end = dst.pixels + j * dst.stride + right + 1;
- for (; p < p_end; ++p)
- put_pixel_(p, color);
+ u8 *d = dst.pixels + j * dst.stride + left;
+ u8 *d_end = dst.pixels + j * dst.stride + right + 1;
+ for (; d < d_end; ++d)
+ *d = max2_u8_(*d, value);
}
-
-_finish:
- PROFILER_end(PROFILE_FILL_ELLIPSE);
}
static b8 quad_from_line_(vec2 *dst, vec2 vertices[2], f64 width) {
@@ -901,17 +863,287 @@ static b8 quad_from_line_(vec2 *dst, vec2 vertices[2], f64 width) {
return 1;
}
-void fill_line_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width) {
+void fill_line_to_stencil(Stencil_Buffer dst, u8 value, vec2 vertices[2], f64 width) {
// FIXME, PERF: Use Bresenham's algorithm when width is 1.
if (width < EPSILON) return;
- PROFILER_begin(PROFILE_FILL_LINE);
vec2 quad[4];
if (!quad_from_line_(quad, vertices, width)) return;
- fill_quad_to_buffer(dst, color, quad);
+ fill_quad_to_stencil(dst, value, quad);
+
+ if (width > 1.0) {
+ f64 half_w = width * 0.5;
+ fill_ellipse_to_stencil(dst, value, (Box) { .x = vertices[0].x - half_w, .y = vertices[0].y - half_w, .width = width, .height = width, });
+ fill_ellipse_to_stencil(dst, value, (Box) { .x = vertices[1].x - half_w, .y = vertices[1].y - half_w, .width = width, .height = width, });
+ }
+}
+
+static void put_pixel_(vec4_f32 *dst, vec4_f32 color) {
+ if (color.w == 1.f)
+ *dst = color;
+ else {
+ f32 dst_amount = 1. - color.w;
+
+ *dst = (vec4_f32) {
+ .x = dst->x * dst_amount + color.x * color.w,
+ .y = dst->y * dst_amount + color.y * color.w,
+ .z = dst->z * dst_amount + color.z * color.w,
+ .w = max2_f32_(dst->w, color.w),
+ };
+ }
+}
+
+void draw_pixels_to_buffer(Pixel_Buffer dst, Box area, Pixel_Buffer src) {
+ if (area.width < EPSILON || area.height < EPSILON)
+ return;
+
+ PROFILER_begin(PROFILE_DRAW_PIXELS);
+
+ i64 x0 = (i64) floor(area.x + 0.5);
+ i64 x1 = (i64) floor(area.x + area.width + 0.5);
+ i64 y0 = (i64) floor(area.y + 0.5);
+ i64 y1 = (i64) floor(area.y + area.height + 0.5);
+
+ i64 i0 = max2_i64_(x0, 0);
+ i64 i1 = min2_i64_(x1, dst.width);
+ i64 j0 = max2_i64_(y0, 0);
+ i64 j1 = min2_i64_(y1, dst.height);
+
+ u32 x_ratio = (src.width << 16) / (x1 - x0);
+ u32 y_ratio = (src.height << 16) / (y1 - y0);
+
+ u32 x_half = x_ratio / 2;
+ u32 y_half = y_ratio / 2;
+
+ for (i64 j = j0; j < j1; ++j) {
+ u32 src_j = ((j - y0) * y_ratio + y_half) >> 16;
+ vec4_f32 *d = dst.pixels + j * dst.stride + i0;
+ vec4_f32 *d_end = d + (i1 - i0);
+ vec4_f32 *s = src.pixels + src_j * src.stride;
+ for (u32 i = i0 - x0; d < d_end; ++d, ++i) {
+ u32 src_i = (i * x_ratio + x_half) >> 16;
+ put_pixel_(d, s[src_i]);
+ }
+ }
+
+ PROFILER_end(PROFILE_DRAW_PIXELS);
+}
+
+static vec4_f32 mix_u8_color_(u8 value, vec4_f32 color) {
+ return (vec4_f32) {
+ color.x,
+ color.y,
+ color.z,
+ (color.w * value) / 255.0,
+ };
+}
+
+void fill_stencil_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area, Stencil_Buffer src) {
+ if (area.width < EPSILON || area.height < EPSILON)
+ return;
+
+ i64 x0 = (i64) floor(area.x + 0.5);
+ i64 x1 = (i64) floor(area.x + area.width + 0.5);
+ i64 y0 = (i64) floor(area.y + 0.5);
+ i64 y1 = (i64) floor(area.y + area.height + 0.5);
+
+ i64 i0 = max2_i64_(x0, 0);
+ i64 i1 = min2_i64_(x1, dst.width);
+ i64 j0 = max2_i64_(y0, 0);
+ i64 j1 = min2_i64_(y1, dst.height);
+
+ u32 x_ratio = (src.width << 16) / (x1 - x0);
+ u32 y_ratio = (src.height << 16) / (y1 - y0);
+
+ u32 x_half = x_ratio / 2;
+ u32 y_half = y_ratio / 2;
+
+ for (i64 j = j0; j < j1; ++j) {
+ u32 src_j = ((j - y0) * y_ratio + y_half) >> 16;
+ vec4_f32 *d = dst.pixels + j * dst.stride + i0;
+ vec4_f32 *d_end = d + (i1 - i0);
+ u8 *s = src.pixels + src_j * src.stride;
+ for (u32 i = i0 - x0; d < d_end; ++d, ++i) {
+ u32 src_i = (i * x_ratio + x_half) >> 16;
+ put_pixel_(d, mix_u8_color_(s[src_i], color));
+ }
+ }
+}
+
+void fill_rectangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) {
+ PROFILER_begin(PROFILE_FILL_RECTANGLE);
+
+ i64 i0 = (i64) floor(area.x + 0.5);
+ i64 i1 = (i64) floor(area.x + area.width + 0.5);
+ i64 j0 = (i64) floor(area.y + 0.5);
+ i64 j1 = (i64) floor(area.y + area.height + 0.5);
+
+ if (i0 < 0) i0 = 0;
+ if (i1 > dst.width) i1 = dst.width;
+ if (j0 < 0) j0 = 0;
+ if (j1 > dst.height) j1 = dst.height;
+
+ for (i64 j = j0; j < j1; ++j) {
+ vec4_f32 *p = dst.pixels + j * dst.stride + i0;
+ vec4_f32 *p_end = p + (i1 - i0);
+ for (; p < p_end; ++p)
+ put_pixel_(p, color);
+ }
+
+ PROFILER_end(PROFILE_FILL_RECTANGLE);
+}
+
+void fill_triangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[3], Stencil_Buffer stencil) {
+ PROFILER_begin(PROFILE_FILL_TRIANGLE);
+
+ f64 x0 = floor(min3_(vertices[0].x, vertices[1].x, vertices[2].x));
+ f64 y0 = floor(min3_(vertices[0].y, vertices[1].y, vertices[2].y));
+
+ fill_triangle_to_stencil(
+ stencil,
+ 255,
+ (vec2[3]) {
+ { vertices[0].x - x0, vertices[0].y - y0 },
+ { vertices[1].x - x0, vertices[1].y - y0 },
+ { vertices[2].x - x0, vertices[2].y - y0 },
+ }
+ );
+
+ Box area = {
+ .x = x0,
+ .y = y0,
+ .width = stencil.width,
+ .height = stencil.height,
+ };
+
+ fill_stencil_to_buffer(dst, color, area, stencil);
+
+ PROFILER_end(PROFILE_FILL_TRIANGLE);
+}
+
+static void triangles_origin_(f64 *x0, f64 *y0, vec2 p, vec2 s, i64 num, vec2 *v) {
+ if (num <= 0)
+ return;
+
+ *x0 = min3_(p.x + v[0].x * s.x, p.x + v[1].x * s.x, p.x + v[2].x * s.x);
+ *y0 = min3_(p.y + v[0].y * s.y, p.y + v[1].y * s.y, p.y + v[2].y * s.y);
+
+ for (i64 i = 1; i < num; ++i) {
+ *x0 = min4_(*x0, p.x + v[i * 3].x * s.x, p.x + v[i * 3 + 1].x * s.x, p.x + v[i * 3 + 2].x * s.x);
+ *y0 = min4_(*y0, p.y + v[i * 3].y * s.y, p.y + v[i * 3 + 1].y * s.y, p.y + v[i * 3 + 2].y * s.y);
+ }
+}
+
+void fill_triangles_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 position, vec2 scale, i64 num_triangles, vec2 *vertices, Stencil_Buffer stencil) {
+ PROFILER_begin(PROFILE_FILL_TRIANGLES);
+
+ f64 x0 = 0.0, y0 = 0.0;
+ triangles_origin_(&x0, &y0, position, scale, num_triangles, vertices);
+
+ x0 = floor(x0);
+ y0 = floor(y0);
+
+ fill_triangles_to_stencil(
+ stencil,
+ 255,
+ (vec2) { position.x - x0, position.y - y0 },
+ scale,
+ num_triangles,
+ vertices
+ );
+
+ Box area = {
+ .x = x0,
+ .y = y0,
+ .width = stencil.width,
+ .height = stencil.height,
+ };
+
+ fill_stencil_to_buffer(dst, color, area, stencil);
+
+ PROFILER_end(PROFILE_FILL_TRIANGLES);
+}
+
+void fill_quad_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4], Stencil_Buffer stencil) {
+ PROFILER_begin(PROFILE_FILL_QUAD);
+
+ f64 x0 = floor(min4_(vertices[0].x, vertices[1].x, vertices[2].x, vertices[3].x));
+ f64 y0 = floor(min4_(vertices[0].y, vertices[1].y, vertices[2].y, vertices[3].y));
+
+ fill_quad_to_stencil(
+ stencil,
+ 255,
+ (vec2[4]) {
+ { vertices[0].x - x0, vertices[0].y - y0 },
+ { vertices[1].x - x0, vertices[1].y - y0 },
+ { vertices[2].x - x0, vertices[2].y - y0 },
+ { vertices[3].x - x0, vertices[3].y - y0 },
+ }
+ );
+
+ Box area = {
+ .x = x0,
+ .y = y0,
+ .width = stencil.width,
+ .height = stencil.height,
+ };
+
+ fill_stencil_to_buffer(dst, color, area, stencil);
+
+ PROFILER_end(PROFILE_FILL_QUAD);
+}
+
+void fill_ellipse_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area, Stencil_Buffer stencil) {
+ PROFILER_begin(PROFILE_FILL_ELLIPSE);
+
+ f64 x0 = floor(area.x);
+ f64 y0 = floor(area.y);
+
+ fill_ellipse_to_stencil(
+ stencil,
+ 255,
+ (Box) { .x = area.x - x0, .y = area.y - y0, .width = area.width, .height = area.height, }
+ );
+
+ Box dst_area = {
+ .x = x0,
+ .y = y0,
+ .width = stencil.width,
+ .height = stencil.height,
+ };
+
+ fill_stencil_to_buffer(dst, color, dst_area, stencil);
+
+ PROFILER_end(PROFILE_FILL_ELLIPSE);
+}
+
+void fill_line_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width, Stencil_Buffer stencil) {
+ PROFILER_begin(PROFILE_FILL_LINE);
+
+ f64 x0 = floor(min2_(vertices[0].x, vertices[1].x) - width);
+ f64 y0 = floor(min2_(vertices[0].y, vertices[1].y) - width);
+
+ fill_line_to_stencil(
+ stencil,
+ 255,
+ (vec2[2]) {
+ { vertices[0].x - x0, vertices[0].y - y0 },
+ { vertices[1].x - x0, vertices[1].y - y0 },
+ },
+ width
+ );
+
+ Box area = {
+ .x = x0,
+ .y = y0,
+ .width = stencil.width,
+ .height = stencil.height,
+ };
+
+ fill_stencil_to_buffer(dst, color, area, stencil);
+
- // TODO: Also render line ends as circles, use stencil buffer.
PROFILER_end(PROFILE_FILL_LINE);
}
@@ -1162,7 +1394,7 @@ static i64 lcd_enum_text_rows_(i64 num_chars, c32 *text) {
return rows;
}
-static void lcd_draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
+static void lcd_draw_text_(Stencil_Buffer dst, u8 value, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
f64 kx = scale_x;
f64 h = scale_y * LCD_Height;
@@ -1217,7 +1449,7 @@ static void lcd_draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64
u32 column = (((i - i00) * num_cols) * w_inv + w_half) >> 16;
if ((_lcd_bits[index] & (1ull << (row * 8 + column))) != 0)
- put_pixel_(dst.pixels + j * dst.stride + i, color);
+ dst.pixels[j * dst.stride + i] = max2_u8_(dst.pixels[j * dst.stride + i], value);
}
}
}
@@ -1250,14 +1482,12 @@ void dispatch_font_render_to_stencil(Stencil_Buffer dst, i32 font, vec2 position
}
#endif // !defined(ENABLE_FONT_CUSTOM_DISPATCH)
-void draw_text_area_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text) {
+void draw_text_area_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text) {
(void) font;
if (text == NULL || num_chars <= 0 || max_size.x < EPSILON || max_size.y < EPSILON)
return;
- PROFILER_begin(PROFILE_DRAW_TEXT_AREA);
-
i64 num_columns = lcd_enum_text_columns_(num_chars, text);
i64 num_rows = lcd_enum_text_rows_(num_chars, text);
@@ -1272,19 +1502,15 @@ void draw_text_area_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box ar
kx = k * max_size.x;
ky = k * max_size.y;
- lcd_draw_text_(dst, color, area.x, area.y, kx, ky, num_chars, text);
-
- PROFILER_end(PROFILE_DRAW_TEXT_AREA);
+ lcd_draw_text_(dst, value, area.x, area.y, kx, ky, num_chars, text);
}
-void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection) {
+void draw_text_cursor_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection) {
(void) font;
if (max_size.x < EPSILON || max_size.y < EPSILON)
return;
- PROFILER_begin(PROFILE_DRAW_TEXT_CURSOR);
-
i64 num_columns = lcd_enum_text_columns_(num_chars, text);
i64 num_rows = lcd_enum_text_rows_(num_chars, text);
i64 cursor_x = lcd_cursor_(cursor, text);
@@ -1315,53 +1541,53 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box
}
if (cursor_y == selection_y)
- fill_rectangle_to_buffer(
+ fill_rectangle_to_stencil(
dst,
- color,
+ value,
(Box) {
.x = area.x + kx * cursor_x,
- .y = area.y + ky * cursor_y - ky * (LCD_Height + 1),
+ .y = area.y + ky * cursor_y - ky * LCD_Height,
.width = kx * (selection_x - cursor_x),
.height = ky * (LCD_Height + 1),
}
);
else {
- fill_rectangle_to_buffer(
+ fill_rectangle_to_stencil(
dst,
- color,
+ value,
(Box) {
.x = area.x + kx * cursor_x,
- .y = area.y + ky * cursor_y - ky * (LCD_Height + 1),
+ .y = area.y + ky * cursor_y - ky * LCD_Height,
.width = kx * (num_columns - cursor_x),
.height = ky * (LCD_Height + 1),
}
);
for (i64 j = cursor_y + LCD_Height + 1; j < selection_y; j += LCD_Height + 1)
- fill_rectangle_to_buffer(
+ fill_rectangle_to_stencil(
dst,
- color,
+ value,
(Box) {
.x = area.x,
- .y = area.y + ky * j - ky * (LCD_Height + 1),
+ .y = area.y + ky * j - ky * LCD_Height,
.width = kx * num_columns,
.height = ky * (LCD_Height + 1),
}
);
- fill_rectangle_to_buffer(
+ fill_rectangle_to_stencil(
dst,
- color,
+ value,
(Box) {
.x = area.x,
- .y = area.y + ky * selection_y - ky * (LCD_Height + 1),
+ .y = area.y + ky * selection_y - ky * LCD_Height,
.width = kx * selection_x,
.height = ky * (LCD_Height + 1),
}
);
}
} else
- fill_rectangle_to_buffer(
+ fill_rectangle_to_stencil(
dst,
- color,
+ value,
(Box) {
.x = area.x + kx * cursor_x,
.y = area.y + ky * cursor_y - ky * LCD_Height,
@@ -1369,6 +1595,62 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box
.height = ky * (LCD_Height - 1),
}
);
+}
+
+void draw_text_area_to_buffer(Pixel_Buffer dst, vec4_f32 color, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, Stencil_Buffer stencil) {
+ PROFILER_begin(PROFILE_DRAW_TEXT_AREA);
+
+ f64 x0 = floor(area.x);
+ f64 y0 = floor(area.y);
+
+ draw_text_area_to_stencil(
+ stencil,
+ 255,
+ font,
+ (Box) { .x = area.x - x0, .y = area.y - y0, .width = area.width, .height = area.height, },
+ max_size,
+ num_chars,
+ text
+ );
+
+ Box dst_area = {
+ .x = x0,
+ .y = y0,
+ .width = stencil.width,
+ .height = stencil.height,
+ };
+
+ fill_stencil_to_buffer(dst, color, dst_area, stencil);
+
+ PROFILER_end(PROFILE_DRAW_TEXT_AREA);
+}
+
+void draw_text_cursor_to_buffer(Pixel_Buffer dst, vec4_f32 color, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection, Stencil_Buffer stencil) {
+ PROFILER_begin(PROFILE_DRAW_TEXT_CURSOR);
+
+ f64 x0 = floor(area.x);
+ f64 y0 = floor(area.y);
+
+ draw_text_cursor_to_stencil(
+ stencil,
+ 255,
+ font,
+ (Box) { .x = area.x - x0, .y = area.y - y0, .width = area.width, .height = area.height, },
+ max_size,
+ num_chars,
+ text,
+ cursor,
+ selection
+ );
+
+ Box dst_area = {
+ .x = x0,
+ .y = y0,
+ .width = stencil.width,
+ .height = stencil.height,
+ };
+
+ fill_stencil_to_buffer(dst, color, dst_area, stencil);
PROFILER_end(PROFILE_DRAW_TEXT_CURSOR);
}
@@ -1377,6 +1659,8 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box
//
// Requests cache
//
+// TODO: Cache stencils.
+//
// ================================================================
typedef struct {
@@ -1637,6 +1921,23 @@ static Graphics_Request graphics_request_moved_(Graphics_Request req, vec2 offse
return moved;
}
+static void triangles_area_(f64 *x0, f64 *y0, f64 *x1, f64 *y1, vec2 p, vec2 s, i64 num, vec2 *v) {
+ if (num <= 0)
+ return;
+
+ *x0 = min3_(p.x + v[0].x * s.x, p.x + v[1].x * s.x, p.x + v[2].x * s.x);
+ *y0 = min3_(p.y + v[0].y * s.y, p.y + v[1].y * s.y, p.y + v[2].y * s.y);
+ *x1 = max3_(p.x + v[0].x * s.x, p.x + v[1].x * s.x, p.x + v[2].x * s.x);
+ *y1 = max3_(p.y + v[0].y * s.y, p.y + v[1].y * s.y, p.y + v[2].y * s.y);
+
+ for (i64 i = 1; i < num; ++i) {
+ *x0 = min4_(*x0, p.x + v[i * 3].x * s.x, p.x + v[i * 3 + 1].x * s.x, p.x + v[i * 3 + 2].x * s.x);
+ *y0 = min4_(*y0, p.y + v[i * 3].y * s.y, p.y + v[i * 3 + 1].y * s.y, p.y + v[i * 3 + 2].y * s.y);
+ *x1 = max4_(*x1, p.x + v[i * 3].x * s.x, p.x + v[i * 3 + 1].x * s.x, p.x + v[i * 3 + 2].x * s.x);
+ *y1 = max4_(*y1, p.y + v[i * 3].y * s.y, p.y + v[i * 3 + 1].y * s.y, p.y + v[i * 3 + 2].y * s.y);
+ }
+}
+
static Box graphics_request_area_(Graphics_Request req) {
Box area = {0};
@@ -1657,7 +1958,7 @@ static Box graphics_request_area_(Graphics_Request req) {
case GRAPHICS_FILL_TRIANGLES:
if (req.triangles.num_triangles > 0) {
- f64 x0, y0, x1, y1;
+ f64 x0 = 0.0, y0 = 0.0, x1 = 0.0, y1 = 0.0;
triangles_area_(&x0, &y0, &x1, &y1, req.triangles.position, req.triangles.scale, req.triangles.num_triangles, req.triangles.vertices);
area.x = x0;
@@ -1709,6 +2010,11 @@ static Graphics_Request_Norm_ normalize_graphics_request_(Graphics_Request req)
}
static void perform_graphics_request_to_buffer_(Pixel_Buffer dst, Graphics_Request req) {
+ Box area = graphics_request_area_(req);
+
+ i64 width = (i64) floor(area.width + 0.5);
+ i64 height = (i64) floor(area.height + 0.5);
+
switch (req.op) {
case GRAPHICS_DRAW_PIXELS:
draw_pixels_to_buffer(
@@ -1730,7 +2036,8 @@ static void perform_graphics_request_to_buffer_(Pixel_Buffer dst, Graphics_Reque
fill_triangle_to_buffer(
dst,
req.triangle.color,
- req.triangle.vertices
+ req.triangle.vertices,
+ default_stencil(width, height)
);
break;
@@ -1741,7 +2048,8 @@ static void perform_graphics_request_to_buffer_(Pixel_Buffer dst, Graphics_Reque
req.triangles.position,
req.triangles.scale,
req.triangles.num_triangles,
- req.triangles.vertices
+ req.triangles.vertices,
+ default_stencil(width, height)
);
break;
@@ -1749,7 +2057,8 @@ static void perform_graphics_request_to_buffer_(Pixel_Buffer dst, Graphics_Reque
fill_quad_to_buffer(
dst,
req.quad.color,
- req.quad.vertices
+ req.quad.vertices,
+ default_stencil(width, height)
);
break;
@@ -1757,7 +2066,8 @@ static void perform_graphics_request_to_buffer_(Pixel_Buffer dst, Graphics_Reque
fill_ellipse_to_buffer(
dst,
req.ellipse.color,
- req.ellipse.area
+ req.ellipse.area,
+ default_stencil(width, height)
);
break;
@@ -1766,33 +2076,36 @@ static void perform_graphics_request_to_buffer_(Pixel_Buffer dst, Graphics_Reque
dst,
req.line.color,
req.line.vertices,
- req.line.width
+ req.line.width,
+ default_stencil(width, height)
);
break;
case GRAPHICS_DRAW_TEXT_AREA:
draw_text_area_to_buffer(
dst,
- req.text_area.font,
req.text_area.color,
+ req.text_area.font,
req.text_area.area,
req.text_area.max_size,
req.text_area.num_chars,
- req.text_area.text
+ req.text_area.text,
+ default_stencil(width, height)
);
break;
case GRAPHICS_DRAW_TEXT_CURSOR:
draw_text_cursor_to_buffer(
dst,
- req.text_cursor.font,
req.text_cursor.color,
+ req.text_cursor.font,
req.text_cursor.area,
req.text_cursor.max_size,
req.text_cursor.num_chars,
req.text_cursor.text,
req.text_cursor.cursor,
- req.text_cursor.selection
+ req.text_cursor.selection,
+ default_stencil(width, height)
);
break;
@@ -2510,9 +2823,19 @@ TEST("triangle") {
{ 1.0, 1.0 },
{ 5.9, 1.0 },
{ 1.0, 5.9 },
- }
+ },
+ default_stencil(w, h)
);
+ // printf("\n");
+ // for (i64 j = 0; j < h; ++j) {
+ // for (i64 i = 0; i < w; ++i) {
+ // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.');
+ // }
+ // printf("\n");
+ // }
+ // printf("\n");
+
b8 expect_bits[100] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0, 0, 0,
@@ -2556,9 +2879,19 @@ TEST("triangles") {
(vec2[6]) {
{ 0.0, 0.0 }, { 3.1, 0.0 }, { 0.0, 3.1 },
{ 4.1, 0.9 }, { 0.9, 4.1 }, { 4.1, 4.1 },
- }
+ },
+ default_stencil(w, h)
);
+ // printf("\n");
+ // for (i64 j = 0; j < h; ++j) {
+ // for (i64 i = 0; i < w; ++i) {
+ // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.');
+ // }
+ // printf("\n");
+ // }
+ // printf("\n");
+
b8 expect_bits[100] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 0, 0, 0, 0, 0,
@@ -2601,9 +2934,19 @@ TEST("quad") {
{ 7.1, 4.0 },
{ 4.0, 7.1 },
{ 0.9, 4.0 },
- }
+ },
+ default_stencil(w, h)
);
+ // printf("\n");
+ // for (i64 j = 0; j < h; ++j) {
+ // for (i64 i = 0; i < w; ++i) {
+ // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.');
+ // }
+ // printf("\n");
+ // }
+ // printf("\n");
+
b8 expect_bits[100] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
@@ -2641,9 +2984,19 @@ TEST("ellipse") {
fill_ellipse_to_buffer(
(Pixel_Buffer) { .width = w, .height = h, .stride = w, .pixels = pixels, },
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f },
- (Box) { 0.9, 0.9, 9.2, 9.2 }
+ (Box) { 0.9, 0.9, 9.2, 9.2 },
+ default_stencil(w, h)
);
+ // printf("\n");
+ // for (i64 j = 0; j < h; ++j) {
+ // for (i64 i = 0; i < w; ++i) {
+ // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.');
+ // }
+ // printf("\n");
+ // }
+ // printf("\n");
+
b8 expect_bits[100] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 0, 0,
@@ -2682,7 +3035,8 @@ TEST("line") {
(Pixel_Buffer) { .width = w, .height = h, .stride = w, .pixels = pixels, },
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f },
(vec2[2]) { { 1.0, 1.0 }, { 8.0, 3.0 }, },
- 1.0
+ 1.0,
+ default_stencil(w, h)
);
// printf("\n");
@@ -2735,12 +3089,13 @@ TEST("text letter A") {
.stride = w,
.pixels = pixels,
},
- 0,
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f, },
+ 0,
(Box) { .x = 0, .y = 0, .width = 10, .height = 10, },
(vec2) { .x = 10.0, .y = 10.0, },
1,
- (c32[1]) { 'A', }
+ (c32[1]) { 'A', },
+ default_stencil(w, h)
);
// printf("\n");
@@ -2793,12 +3148,13 @@ TEST("text letter z") {
.stride = w,
.pixels = pixels,
},
- 0,
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f, },
+ 0,
(Box) { .x = 0, .y = 0, .width = 10, .height = 10, },
(vec2) { .x = 10.0, .y = 10.0, },
1,
- (c32[1]) { 'z', }
+ (c32[1]) { 'z', },
+ default_stencil(w, h)
);
// printf("\n");
@@ -2851,12 +3207,13 @@ TEST("text digits 42") {
.stride = w,
.pixels = pixels,
},
- 0,
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f, },
+ 0,
(Box) { .x = 0, .y = 0, .width = 10, .height = 10, },
(vec2) { .x = 10.0, .y = 10.0, },
2,
- (c32[2]) { '4', '2', }
+ (c32[2]) { '4', '2', },
+ default_stencil(w, h)
);
// printf("\n");
@@ -2909,14 +3266,15 @@ TEST("text cursor") {
.stride = w,
.pixels = pixels,
},
- 0,
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f, },
+ 0,
(Box) { .x = 1.0, .y = 1.0, .width = 8.1, .height = 6.1, },
(vec2) { .x = 10.0, .y = 10.0, },
2,
(c32[2]) { 'a', 'a', },
1,
- 0
+ 0,
+ default_stencil(w, h)
);
// printf("\n");
@@ -2969,14 +3327,15 @@ TEST("text selection forward") {
.stride = w,
.pixels = pixels,
},
- 0,
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f, },
+ 0,
(Box) { .x = 1.0, .y = 1.0, .width = 8.1, .height = 6.1, },
(vec2) { .x = 3.0, .y = 2.0, },
14,
(c32[14]) { 'a', 'a', 'a', 'a', '\n', 'b', 'b', 'b', 'b', '\n', 'c', 'c', 'c', 'c', },
2,
- 10
+ 10,
+ default_stencil(w, h)
);
// printf("\n");
@@ -3029,14 +3388,15 @@ TEST("text selection backward") {
.stride = w,
.pixels = pixels,
},
- 0,
(vec4_f32) { 1.0f, 1.0f, 1.0f, 1.0f, },
+ 0,
(Box) { .x = 1.0, .y = 1.0, .width = 8.1, .height = 6.1, },
(vec2) { .x = 3.0, .y = 2.0, },
14,
(c32[14]) { 'a', 'a', 'a', 'a', '\n', 'b', 'b', 'b', 'b', '\n', 'c', 'c', 'c', 'c', },
12,
- -10
+ -10,
+ default_stencil(w, h)
);
// printf("\n");
diff --git a/runtime.c b/runtime.c
index bc715c5..e0e1b5b 100644
--- a/runtime.c
+++ b/runtime.c
@@ -45,7 +45,6 @@
//
// - Work in progress
// - Graphics
-// - Stencil buffer
// - Custom fonts - integrate stb_truetype.h
// - UI
// - Icons
@@ -141,6 +140,7 @@
// - Self-contained impl
// - Anti-aliasing
// - Gamma correction
+// - Stencil buffer
// - System
// - Dynamic libraries
// - Window - X11, Web
@@ -797,6 +797,22 @@ static void mem_cpy_(void *dst, void *src, i64 len) {
*d = *s;
}
+static f64 min2_(f64 x, f64 y) {
+ return x < y ? x : y;
+}
+
+static f64 max2_(f64 x, f64 y) {
+ return x > y ? x : y;
+}
+
+static u8 min2_u8_(u8 x, u8 y) {
+ return x < y ? x : y;
+}
+
+static u8 max2_u8_(u8 x, u8 y) {
+ return x > y ? x : y;
+}
+
static i32 min2_i32_(i32 x, i32 y) {
return x < y ? x : y;
}
@@ -813,6 +829,22 @@ static f32 max2_f32_(f32 a, f32 b) {
return a < b ? b : a;
}
+static f64 min3_i64_(i64 a, i64 b, i64 c) {
+ if (a < b && a < c)
+ return a;
+ if (b < c)
+ return b;
+ return c;
+}
+
+static f64 max3_i64_(i64 a, i64 b, i64 c) {
+ if (a > b && a > c)
+ return a;
+ if (b > c)
+ return b;
+ return c;
+}
+
static f64 min3_(f64 a, f64 b, f64 c) {
if (a < b && a < c)
return a;
@@ -829,6 +861,26 @@ static f64 max3_(f64 a, f64 b, f64 c) {
return c;
}
+static f64 min4_i64_(i64 a, i64 b, i64 c, i64 d) {
+ if (a < b && a < c && a < d)
+ return a;
+ if (b < c && b < d)
+ return b;
+ if (c < d)
+ return c;
+ return d;
+}
+
+static f64 max4_i64_(i64 a, i64 b, i64 c, i64 d) {
+ if (a > b && a > c && a > d)
+ return a;
+ if (b > c && b > d)
+ return b;
+ if (c > d)
+ return c;
+ return d;
+}
+
static f64 min4_(f64 a, f64 b, f64 c, f64 d) {
if (a < b && a < c && a < d)
return a;