summaryrefslogtreecommitdiff
path: root/graphics.c
diff options
context:
space:
mode:
Diffstat (limited to 'graphics.c')
-rw-r--r--graphics.c163
1 files changed, 146 insertions, 17 deletions
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 <math.h>
+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_