From d4f820e37bbb6571af4587adb2a3b3519d76849a Mon Sep 17 00:00:00 2001 From: Mitya Selivanov Date: Sat, 5 Oct 2024 08:28:22 +0200 Subject: Draw triangles and lines --- graphics.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 146 insertions(+), 17 deletions(-) (limited to 'graphics.c') diff --git a/graphics.c b/graphics.c index b054db7..50f3b68 100644 --- a/graphics.c +++ b/graphics.c @@ -2,6 +2,12 @@ // // graphics.c // +// ---------------------------------------------------------------- +// +// TODO: +// - Blending. +// - Anti-aliasing. +// // ================================================================ #ifndef GRAPHICS_HEADER_GUARD_ @@ -13,24 +19,53 @@ #include "reduced_system_layer.c" +#define EPSILON 1e-9 + enum { OP_SET, OP_XOR, }; -u32 u32_from_rgb(f32 red, f32 green, f32 blue); -void draw_panel(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height); -void draw_text_area(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text); -void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text); +u32 u32_from_rgb (f32 red, f32 green, f32 blue); +void fill_rectangle (u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height); +void fill_triangle (u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2); +void fill_ellipse (u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height); +void fill_line (u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width); +void draw_text_area (u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text); +void draw_selection_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text); #endif // GRAPHICS_HEADER_GUARD_ +// ================================================================ + #ifndef GRAPHICS_HEADER #ifndef GRAPHICS_IMPL_GUARD_ #define GRAPHICS_IMPL_GUARD_ #include +f64 min3(f64 a, f64 b, f64 c) { + if (a < b && a < c) + return a; + if (b < c) + return b; + return c; +} + +f64 max3(f64 a, f64 b, f64 c) { + if (a > b && a > c) + return a; + if (b > c) + return b; + return c; +} + +b8 same_sign(f64 a, f64 b) { + if (a >= EPSILON && b <= -EPSILON) return 0; + if (a <= -EPSILON && b >= EPSILON) return 0; + return 1; +} + u32 u32_from_rgb(f32 red, f32 green, f32 blue) { i32 r = (i32) floor(red * 255.f); i32 g = (i32) floor(green * 255.f); @@ -202,7 +237,7 @@ i64 enum_text_rows(i64 num_chars, c32 *text) { return rows; } -void print_text(u32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) { +void draw_text(u32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) { assert(text != NULL); f64 x = x0; @@ -256,7 +291,16 @@ void print_text(u32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_cha } } -void draw_panel(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) { +void put_pixel(i64 i, i64 j, u32 op, u32 color) { + if (i < 0 || i >= platform.frame_width || j < 0 || j >= platform.frame_height) + return; + if (op == OP_XOR) + platform.pixels[j * platform.frame_width + i] ^= color; + else + platform.pixels[j * platform.frame_width + i] = color; +} + +void fill_rectangle(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) { i64 i0 = (i64) floor(x0 + .5); i64 j0 = (i64) floor(y0 + .5); i64 i1 = (i64) floor(x0 + width + .5); @@ -269,10 +313,95 @@ void draw_panel(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) { for (i64 j = j0; j < j1; ++j) for (i64 i = i0; i < i1; ++i) - if (op == OP_XOR) - platform.pixels[j * platform.frame_width + i] ^= color; - else - platform.pixels[j * platform.frame_width + i] = color; + put_pixel(i, j, op, color); +} + +void fill_triangle(u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) { + i64 min_x = (i64) floor(min3(x0, x1, x2)); + i64 min_y = (i64) floor(min3(y0, y1, y2)); + i64 max_x = (i64) ceil (max3(x0, x1, x2)); + i64 max_y = (i64) ceil (max3(y0, y1, y2)); + + // Z-components of cross-products + // + f64 z0 = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0); + f64 z1 = (x2 - x1) * (y0 - y1) - (x0 - x1) * (y2 - y1); + f64 z2 = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); + + for (i64 j = min_y; j <= max_y; ++j) + for (i64 i = min_x; i <= max_x; ++i) { + f64 x = (f64) i; + f64 y = (f64) j; + + // Z-components of cross-products + // + f64 pz0 = (x - x0) * (y2 - y0) - (x2 - x0) * (y - y0); + f64 pz1 = (x - x1) * (y0 - y1) - (x0 - x1) * (y - y1); + f64 pz2 = (x - x2) * (y1 - y2) - (x1 - x2) * (y - y2); + + // Check signs + // + if (!same_sign(z0, pz0)) continue; + if (!same_sign(z1, pz1)) continue; + if (!same_sign(z2, pz2)) continue; + + put_pixel(i, j, op, color); + } +} + +void fill_ellipse(u32 op, u32 color, f64 x0, f64 y0, f64 width, f64 height) { + // FIXME PERF: + // Implement better algorithm. + + i64 i0 = (i64) floor(x0 + .5); + i64 j0 = (i64) floor(y0 + .5); + i64 i1 = (i64) floor(x0 + width + .5); + i64 j1 = (i64) floor(y0 + height + .5); + + f64 dw = width / 2; + f64 dh = height / 2; + + if (dw < EPSILON || dh < EPSILON) + return; + + f64 cx = x0 + dw; + f64 cy = y0 + dh; + f64 kx = 1. / dw; + f64 ky = 1. / dh; + + for (i64 j = j0; j < j1; ++j) { + if (j < 0 || j >= platform.frame_height) + continue; + f64 dy = (((f64) j) - cy) * ky; + f64 dydy = dy * dy; + for (i64 i = i0; i < i1; ++i) { + if (i < 0 || i >= platform.frame_width) + continue; + f64 dx = (((f64) i) - cx) * kx; + if (dx * dx + dydy <= 1.0) + put_pixel(i, j, op, color); + } + } +} + +void fill_line(u32 op, u32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) { + f64 dx = x1 - x0; + f64 dy = y1 - y0; + + // Tangent + // + f64 tx = -dy; + f64 ty = dx; + f64 tl = sqrt(tx * tx + ty * ty); + if (tl >= EPSILON) { + tx /= tl; + ty /= tl; + } + tx *= width * .5; + ty *= width * .5; + + fill_triangle(op, color, x0 - tx, y0 - ty, x0 + tx, y0 + ty, x1 + tx, y1 + ty); + fill_triangle(op, color, x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty); } void draw_text_area(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text) { @@ -293,10 +422,10 @@ void draw_text_area(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_sc kx = k * max_scale_x; ky = k * max_scale_y; - print_text(color, x0, y0, kx, ky, num_chars, text); + draw_text(color, x0, y0, kx, ky, num_chars, text); } -void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text) { +void draw_selection_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text) { assert(max_scale_x > 1e-6); assert(max_scale_y > 1e-6); @@ -329,27 +458,27 @@ void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_ } if (cursor_y == selection_y) - draw_panel(OP_XOR, color, + fill_rectangle(OP_XOR, color, x0 + kx * cursor_x, y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1), kx * (selection_x - cursor_x), ky * (CHAR_NUM_BITS_Y + 1) ); else { - draw_panel(OP_XOR, color, + fill_rectangle(OP_XOR, color, x0 + kx * cursor_x, y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1), kx * (num_columns - cursor_x), ky * (CHAR_NUM_BITS_Y + 1) ); for (i64 j = cursor_y + CHAR_NUM_BITS_Y + 1; j < selection_y; j += CHAR_NUM_BITS_Y + 1) - draw_panel(OP_XOR, color, + fill_rectangle(OP_XOR, color, x0, y0 + ky * j - ky * (CHAR_NUM_BITS_Y + 1), kx * num_columns, ky * (CHAR_NUM_BITS_Y + 1) ); - draw_panel(OP_XOR, color, + fill_rectangle(OP_XOR, color, x0, y0 + ky * selection_y - ky * (CHAR_NUM_BITS_Y + 1), kx * selection_x, @@ -357,7 +486,7 @@ void draw_text_cursor(u32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_ ); } } else - draw_panel(OP_XOR, color, x0 + kx * cursor_x, y0 + ky * cursor_y - ky * CHAR_NUM_BITS_Y, kx * .5, ky * (CHAR_NUM_BITS_Y - 1)); + fill_rectangle(OP_XOR, color, x0 + kx * cursor_x, y0 + ky * cursor_y - ky * CHAR_NUM_BITS_Y, kx * .5, ky * (CHAR_NUM_BITS_Y - 1)); } #endif // GRAPHICS_IMPL_GUARD_ -- cgit v1.2.3