diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2025-04-26 09:37:51 +0200 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2025-04-26 09:37:51 +0200 |
commit | 7cac379388df5a551321e407c29256f92c3085b5 (patch) | |
tree | e3c2e3d2a2a5117816ecd9f630197886821ba114 | |
parent | d62bebba889db9c3b69fa4f9da7a5de6c1b02a9b (diff) | |
download | reduced_system_layer-7cac379388df5a551321e407c29256f92c3085b5.zip |
Stencil buffer
-rw-r--r-- | graphics.c | 832 | ||||
-rw-r--r-- | runtime.c | 54 |
2 files changed, 649 insertions, 237 deletions
@@ -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"); @@ -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; |