summaryrefslogtreecommitdiff
path: root/graphics.c
diff options
context:
space:
mode:
Diffstat (limited to 'graphics.c')
-rw-r--r--graphics.c518
1 files changed, 431 insertions, 87 deletions
diff --git a/graphics.c b/graphics.c
index a853e39..6a61ac3 100644
--- a/graphics.c
+++ b/graphics.c
@@ -17,24 +17,58 @@
#include "reduced_system_layer.c"
-#define EPSILON 1e-9
+// ================================================================
-enum {
- OP_SET,
- OP_XOR,
-};
+#ifndef EPSILON
+#define EPSILON (1e-9)
+#endif
+
+typedef struct {
+ i64 width;
+ i64 height;
+ vec4_f32 *pixels;
+} Pixel_Buffer;
+
+typedef struct {
+ Pixel_Buffer buffer;
+ vec2 position;
+ vec2 scale;
+ b8 antialiasing : 1;
+ b8 alpha : 1;
+ b8 xor_color : 1;
+ vec4_f32 color;
+} Brush;
+
+vec3_f32 gamma_apply (vec3_f32 rgb);
+vec3_f32 gamma_revert(vec3_f32 rgb);
+vec3_f32 lab_from_rgb(vec3_f32 rgb);
+vec3_f32 rgb_from_lab(vec3_f32 lab);
+vec3_f32 lab_from_lch(vec3_f32 lch);
+vec3_f32 lch_from_lab(vec3_f32 lab);
+vec3_f32 rgb_from_lch(vec3_f32 lch);
+vec3_f32 lch_from_rgb(vec3_f32 rgb);
+
+vec4_f32 with_alpha(vec3_f32 color, f32 alpha);
+vec3_f32 without_alpha(vec4_f32 color);
+
+#define RGB(...) ((Brush) { .color = { __VA_ARGS__, 1.f } })
+#define RGBA(...) ((Brush) { .color = { __VA_ARGS__ } })
+#define LCH(...) ((Brush) { .color = with_alpha(gamma_apply(rgb_from_lch((vec3_f32) { __VA_ARGS__ })), 1.f) } })
+#define LCHA(...) ((Brush) { .color = with_alpha(gamma_apply(rgb_from_lch(without_alpha((vec4_f32) { __VA_ARGS__ }))), ((vec4_f32) { __VA_ARGS__ }).w) } })
b8 rectangle_contains(f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py);
b8 triangle_contains (f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 px, f64 py);
b8 ellipse_contains (f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py);
b8 line_contains (f64 x0, f64 y0, f64 x1, f64 y1, f64 width, f64 px, f64 py);
-void fill_rectangle (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height);
-void fill_triangle (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2);
-void fill_ellipse (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height);
-void fill_line (u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width);
-void draw_text_area (vec3_f32 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(vec3_f32 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 put_pixel (Brush brush, i64 x, i64 y);
+void draw_pixels (Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src);
+void fill_rectangle (Brush brush, f64 x, f64 y, f64 width, f64 height);
+void fill_triangle (Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2);
+void fill_ellipse (Brush brush, f64 x, f64 y, f64 width, f64 height);
+void fill_line (Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width);
+void draw_text_area (Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text);
+void draw_selection_cursor(Brush brush, f64 x, f64 y, 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_
@@ -44,7 +78,7 @@ void draw_selection_cursor(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height
#ifndef GRAPHICS_IMPL_GUARD_
#define GRAPHICS_IMPL_GUARD_
-f64 min3(f64 a, f64 b, f64 c) {
+static f64 min3(f64 a, f64 b, f64 c) {
if (a < b && a < c)
return a;
if (b < c)
@@ -52,7 +86,7 @@ f64 min3(f64 a, f64 b, f64 c) {
return c;
}
-f64 max3(f64 a, f64 b, f64 c) {
+static f64 max3(f64 a, f64 b, f64 c) {
if (a > b && a > c)
return a;
if (b > c)
@@ -60,7 +94,7 @@ f64 max3(f64 a, f64 b, f64 c) {
return c;
}
-b8 same_sign(f64 a, f64 b) {
+static b8 same_sign(f64 a, f64 b) {
if (a >= EPSILON && b <= -EPSILON) return 0;
if (a <= -EPSILON && b >= EPSILON) return 0;
return 1;
@@ -74,13 +108,13 @@ static u64 _bitfont[] = {
#define CHAR_NUM_BITS_Y 7
#define CHAR_NUM_BITS (CHAR_NUM_BITS_X * CHAR_NUM_BITS_Y)
-i64 char_column_offset(c32 c, i64 column_index) {
+static i64 char_column_offset(c32 c, i64 column_index) {
if (column_index < 0 || column_index >= CHAR_NUM_BITS_X)
return -1;
return (c - 32) * CHAR_NUM_BITS + column_index * CHAR_NUM_BITS_Y;
}
-b8 char_bit(i64 column_offset, i64 row_index) {
+static b8 char_bit(i64 column_offset, i64 row_index) {
if (column_offset < 0 || row_index < 0 || row_index >= CHAR_NUM_BITS_Y)
return 0;
@@ -92,7 +126,7 @@ b8 char_bit(i64 column_offset, i64 row_index) {
return !!(_bitfont[qword_index] & mask);
}
-u64 char_column_convolved(c32 c, i64 column_index) {
+static u64 char_column_convolved(c32 c, i64 column_index) {
if (column_index < 0 || column_index >= CHAR_NUM_BITS_X)
return 0;
@@ -106,7 +140,7 @@ u64 char_column_convolved(c32 c, i64 column_index) {
return column;
}
-b8 char_column_empty(c32 c, i64 column_index) {
+static b8 char_column_empty(c32 c, i64 column_index) {
if (column_index < 0 || column_index >= CHAR_NUM_BITS_X)
return 1;
@@ -119,7 +153,7 @@ b8 char_column_empty(c32 c, i64 column_index) {
return 1;
}
-i64 char_width(c32 c) {
+static i64 char_width(c32 c) {
if (c < 32)
return 0;
if (c == ' ' || c > 127)
@@ -134,7 +168,7 @@ i64 char_width(c32 c) {
return width;
}
-i64 char_spacing(i64 num_chars, c32 *text, i64 index) {
+static i64 char_spacing(i64 num_chars, c32 *text, i64 index) {
if (text == NULL)
return 0;
@@ -150,7 +184,7 @@ i64 char_spacing(i64 num_chars, c32 *text, i64 index) {
return 0;
}
-i64 text_cursor(i64 num_chars, c32 *text) {
+static i64 text_cursor(i64 num_chars, c32 *text) {
if (text == NULL)
return 0;
@@ -174,7 +208,7 @@ i64 text_cursor(i64 num_chars, c32 *text) {
return cursor;
}
-i64 enum_text_columns(i64 num_chars, c32 *text) {
+static i64 enum_text_columns(i64 num_chars, c32 *text) {
if (text == NULL)
return 0;
@@ -208,7 +242,7 @@ i64 enum_text_columns(i64 num_chars, c32 *text) {
return cols;
}
-i64 enum_text_rows(i64 num_chars, c32 *text) {
+static i64 enum_text_rows(i64 num_chars, c32 *text) {
if (text == NULL)
return 0;
@@ -224,12 +258,35 @@ i64 enum_text_rows(i64 num_chars, c32 *text) {
return rows;
}
-void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
+static void brush_defaults(Brush *b) {
+ if (g_platform.frame_width > 0) {
+ if (b->scale.x == 0.) b->scale.x = ((f64) g_platform.frame_width) / g_platform.real_width;
+ if (b->scale.y == 0.) b->scale.y = ((f64) g_platform.frame_height) / g_platform.real_height;
+ }
+
+ if (b->buffer.pixels == NULL) {
+ b->buffer.width = g_platform.frame_width;
+ b->buffer.height = g_platform.frame_height;
+ b->buffer.pixels = g_platform.pixels;
+ }
+}
+
+static void draw_text(Brush brush, f64 x_, f64 y_, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
if (text == NULL)
return;
- f64 x = x0;
- f64 y = y0;
+ brush_defaults(&brush);
+
+ x_ = brush.position.x + x_ * brush.scale.x;
+ y_ = brush.position.y + y_ * brush.scale.y;
+
+ if (brush.scale.x < 0) scale_x *= -brush.scale.x;
+ else scale_x *= brush.scale.x;
+ if (brush.scale.y < 0) scale_y *= -brush.scale.y;
+ else scale_y *= brush.scale.y;
+
+ f64 x = x_;
+ f64 y = y_;
f64 kx = scale_x;
f64 h = scale_y * CHAR_NUM_BITS_Y;
@@ -237,13 +294,13 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num
for (i64 n = 0; n < num_chars; ++n) {
if (text[n] <= ' ') {
if (text[n] == '\n') {
- x = x0;
+ x = x_;
y += scale_y * (CHAR_NUM_BITS_Y + 1);
}
else if (text[n] == '\b' && n > 0)
x -= kx * (char_width(text[n - 1]) + char_spacing(num_chars, text, n - 1));
else if (text[n] == '\r')
- x = x0;
+ x = x_;
else
x += kx * (char_width(' ') + char_spacing(num_chars, text, n));
continue;
@@ -252,10 +309,10 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num
i64 num_cols = char_width(text[n]);
f64 w = num_cols * kx;
- i64 i0 = (i64) floor(x + .5);
- i64 i1 = (i64) floor(x + w + .5);
- i64 j0 = (i64) floor(y + .5);
- i64 j1 = (i64) floor(y + h + .5);
+ i64 i0 = (i64) floor(x);
+ i64 i1 = (i64) ceil (x + w);
+ i64 j0 = (i64) floor(y);
+ i64 j1 = (i64) ceil (y + h);
for (i64 i = i0; i < i1; ++i) {
if (i < 0) continue;
@@ -271,7 +328,7 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num
i64 row = ((j - j0) * CHAR_NUM_BITS_Y) / (j1 - j0);
if (char_bit(offset, row))
- g_platform.pixels[j * g_platform.frame_width + i] = color;
+ put_pixel(brush, i, j);
}
}
@@ -279,13 +336,110 @@ void draw_text(vec3_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num
}
}
-void put_pixel(i64 i, i64 j, u32 op, vec3_f32 color) {
- if (i < 0 || i >= g_platform.frame_width || j < 0 || j >= g_platform.frame_height)
- return;
- if (op == OP_XOR)
- g_platform.pixels[j * g_platform.frame_width + i] = rgb_f32_from_u32(rgb_u32_from_f32(color) ^ rgb_u32_from_f32(g_platform.pixels[j * g_platform.frame_width + i]));
- else
- g_platform.pixels[j * g_platform.frame_width + i] = color;
+static f64 gamma_(f64 x) {
+ if (x >= 0.0031308)
+ return 1.055 * pow(x, 1.0 / 2.4) - 0.055;
+ return 12.92 * x;
+}
+
+static f64 gamma_re_(f64 x) {
+ if (x >= 0.04045)
+ return pow((x + 0.055) / 1.055, 2.4);
+ return x / 12.92;
+}
+
+// ================================================================
+
+vec3_f32 gamma_apply (vec3_f32 rgb) {
+ return (vec3_f32) {
+ .x = (f32) gamma_(rgb.x),
+ .y = (f32) gamma_(rgb.y),
+ .z = (f32) gamma_(rgb.z),
+ };
+}
+
+vec3_f32 gamma_revert(vec3_f32 rgb) {
+ return (vec3_f32) {
+ .x = (f32) gamma_re_(rgb.x),
+ .y = (f32) gamma_re_(rgb.y),
+ .z = (f32) gamma_re_(rgb.z),
+ };
+}
+
+vec3_f32 lab_from_rgb(vec3_f32 rgb) {
+ f64 l = 0.4122214708 * rgb.x + 0.5363325363 * rgb.y + 0.0514459929 * rgb.z;
+ f64 m = 0.2119034982 * rgb.x + 0.6806995451 * rgb.y + 0.1073969566 * rgb.z;
+ f64 s = 0.0883024619 * rgb.x + 0.2817188376 * rgb.y + 0.6299787005 * rgb.z;
+
+ f64 l_ = cbrt(l);
+ f64 m_ = cbrt(m);
+ f64 s_ = cbrt(s);
+
+ return (vec3_f32) {
+ .x = (f32) (0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_),
+ .y = (f32) (1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_),
+ .z = (f32) (0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_),
+ };
+}
+
+vec3_f32 rgb_from_lab(vec3_f32 lab) {
+ f64 l_ = lab.x + 0.3963377774 * lab.y + 0.2158037573 * lab.z;
+ f64 m_ = lab.x - 0.1055613458 * lab.y - 0.0638541728 * lab.z;
+ f64 s_ = lab.x - 0.0894841775 * lab.y - 1.2914855480 * lab.z;
+
+ f64 l = l_ * l_ * l_;
+ f64 m = m_ * m_ * m_;
+ f64 s = s_ * s_ * s_;
+
+ return (vec3_f32) {
+ .x = (f32) (+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
+ .y = (f32) (-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
+ .z = (f32) (-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s),
+ };
+}
+
+vec3_f32 lch_from_lab(vec3_f32 lab) {
+ f64 a = lab.y;
+ f64 b = lab.z;
+
+ return (vec3_f32) {
+ .x = lab.x,
+ .y = (f32) sqrt (a * a + b * b),
+ .z = (f32) atan2(b, a),
+ };
+}
+
+vec3_f32 lab_from_lch(vec3_f32 lch) {
+ return (vec3_f32) {
+ .x = lch.x,
+ .y = (f32) (lch.y * cos(lch.z)),
+ .z = (f32) (lch.y * sin(lch.z)),
+ };
+}
+
+vec3_f32 rgb_from_lch(vec3_f32 lch) {
+ return rgb_from_lab(lab_from_lch(lch));
+}
+
+vec3_f32 lch_from_rgb(vec3_f32 rgb) {
+ return lch_from_lab(lab_from_rgb(rgb));
+}
+
+vec4_f32 with_alpha(vec3_f32 color, f32 alpha) {
+ return (vec4_f32) {
+ .x = color.x,
+ .y = color.y,
+ .z = color.z,
+ .w = alpha,
+ };
+}
+
+vec3_f32 without_alpha(vec4_f32 color) {
+ return (vec3_f32) {
+ .x = color.x,
+ .y = color.y,
+ .z = color.z,
+ };
}
b8 rectangle_contains(f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py) {
@@ -349,26 +503,190 @@ b8 line_contains(f64 x0, f64 y0, f64 x1, f64 y1, f64 width, f64 px, f64 py) {
|| triangle_contains(x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty, px, py);
}
-void fill_rectangle(u32 op, vec3_f32 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);
- i64 j1 = (i64) floor(y0 + height + .5);
+void put_pixel(Brush brush, i64 x, i64 y) {
+ brush_defaults(&brush);
- if (i0 < 0) i0 = 0;
- if (j0 < 0) j0 = 0;
- if (i1 >= g_platform.frame_width) i1 = g_platform.frame_width - 1;
- if (j1 >= g_platform.frame_height) j1 = g_platform.frame_height - 1;
+ if (x < 0 || x >= brush.buffer.width || y < 0 || y >= brush.buffer.height)
+ return;
- for (i64 j = j0; j < j1; ++j)
+ i64 n = y * brush.buffer.width + x;
+
+ if (brush.xor_color) {
+ u32 dst = rgb_u32_from_f32((vec3_f32) {
+ .x = brush.buffer.pixels[n].x,
+ .y = brush.buffer.pixels[n].y,
+ .z = brush.buffer.pixels[n].z
+ });
+
+ u32 src = rgb_u32_from_f32((vec3_f32) {
+ .x = brush.color.x,
+ .y = brush.color.y,
+ .z = brush.color.z
+ });
+
+ vec3_f32 c = rgb_f32_from_u32(dst ^ src);
+
+ brush.buffer.pixels[n] = (vec4_f32) {
+ .x = c.x,
+ .y = c.y,
+ .z = c.z,
+ .w = 1.f,
+ };
+ } else if (brush.alpha) {
+ vec4_f32 dst = brush.buffer.pixels[n];
+ f64 a = brush.color.w;
+
+ brush.buffer.pixels[n] = (vec4_f32) {
+ .x = (f32) (dst.x * (1. - a) + brush.color.x * a),
+ .y = (f32) (dst.y * (1. - a) + brush.color.y * a),
+ .z = (f32) (dst.z * (1. - a) + brush.color.z * a),
+ .w = 1.f,
+ };
+ } else
+ brush.buffer.pixels[n] = brush.color;
+}
+
+void draw_pixels(Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src) {
+ // FIXME PERF
+
+ brush_defaults(&brush);
+
+ f64 x0, y0, x1, y1;
+
+ if (brush.scale.x < 0) {
+ x1 = brush.position.x + x * brush.scale.x;
+ x0 = x1 + width * brush.scale.x;
+ } else {
+ x0 = brush.position.x + x * brush.scale.x;
+ x1 = x0 + width * brush.scale.x;
+ }
+
+ if (brush.scale.y < 0) {
+ y1 = brush.position.y + y * brush.scale.y;
+ y0 = y1 + height * brush.scale.y;
+ } else {
+ y0 = brush.position.y + y * brush.scale.y;
+ y1 = y0 + height * brush.scale.y;
+ }
+
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
+
+ for (i64 j = j0; j < j1; ++j) {
+ i64 src_j = ((j - j0) * src.height) / (j1 - j0);
+ for (i64 i = i0; i < i1; ++i) {
+ i64 src_i = ((i - i0) * src.width) / (i1 - i0);
+ brush.color = src.pixels[src_j * src.width + src_i];
+ put_pixel(brush, i, j);
+ }
+ }
+}
+
+void fill_rectangle(Brush brush, f64 x, f64 y, f64 width, f64 height) {
+ brush_defaults(&brush);
+
+ f64 x0, y0, x1, y1;
+
+ if (brush.scale.x < 0) {
+ x1 = brush.position.x + x * brush.scale.x;
+ x0 = x1 + width * brush.scale.x;
+ } else {
+ x0 = brush.position.x + x * brush.scale.x;
+ x1 = x0 + width * brush.scale.x;
+ }
+
+ if (brush.scale.y < 0) {
+ y1 = brush.position.y + y * brush.scale.y;
+ y0 = y1 + height * brush.scale.y;
+ } else {
+ y0 = brush.position.y + y * brush.scale.y;
+ y1 = y0 + height * brush.scale.y;
+ }
+
+ if (brush.antialiasing) {
+ i64 i0 = (i64) ceil (x0);
+ i64 i1 = (i64) floor(x1);
+ i64 j0 = (i64) ceil (y0);
+ i64 j1 = (i64) floor(y1);
+
+ f64 kx0 = i0 - x0;
+ f64 kx1 = x1 - i1;
+ f64 ky0 = j0 - y0;
+ f64 ky1 = y1 - j1;
+
+ for (i64 j = j0; j < j1; ++j)
+ for (i64 i = i0; i < i1; ++i)
+ put_pixel(brush, i, j);
+
+ if (i0 > i1) {
+ kx0 *= kx1;
+ kx1 = 0.;
+ }
+
+ if (j0 > j1) {
+ ky0 *= ky1;
+ ky1 = 0.;
+ }
+
+ f64 alpha = brush.alpha ? brush.color.w : 1.;
+ brush.alpha = 1;
+
+ brush.color.w = alpha * gamma_(kx0);
+ for (i64 j = j0; j < j1; ++j)
+ put_pixel(brush, i0 - 1, j);
+
+ brush.color.w = alpha * gamma_(kx1);
+ for (i64 j = j0; j < j1; ++j)
+ put_pixel(brush, i1, j);
+
+ brush.color.w = alpha * gamma_(ky0);
+ for (i64 i = i0; i < i1; ++i)
+ put_pixel(brush, i, j0 - 1);
+
+ brush.color.w = alpha * gamma_(ky1);
for (i64 i = i0; i < i1; ++i)
- put_pixel(i, j, op, color);
+ put_pixel(brush, i, j1);
+
+ brush.color.w = alpha * gamma_(kx0 * ky0);
+ put_pixel(brush, i0 - 1, j0 - 1);
+
+ brush.color.w = alpha * gamma_(kx1 * ky0);
+ put_pixel(brush, i1, j0 - 1);
+
+ brush.color.w = alpha * gamma_(kx0 * ky1);
+ put_pixel(brush, i0 - 1, j1);
+
+ brush.color.w = alpha * gamma_(kx1 * ky1);
+ put_pixel(brush, i1, j1);
+ } else {
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
+
+ for (i64 j = j0; j < j1; ++j)
+ for (i64 i = i0; i < i1; ++i)
+ put_pixel(brush, i, j);
+ }
}
-void fill_triangle(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
+void fill_triangle(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
// FIXME PERF:
// Implement better algorithm.
+ brush_defaults(&brush);
+
+ x0 = brush.position.x + x0 * brush.scale.x;
+ y0 = brush.position.y + y0 * brush.scale.y;
+
+ x1 = brush.position.x + x1 * brush.scale.x;
+ y1 = brush.position.y + y1 * brush.scale.y;
+
+ x2 = brush.position.x + x2 * brush.scale.x;
+ y2 = brush.position.y + y2 * brush.scale.y;
+
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));
@@ -377,31 +695,46 @@ void fill_triangle(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 x
for (i64 j = min_y; j <= max_y; ++j)
for (i64 i = min_x; i <= max_x; ++i)
if (triangle_contains(x0, y0, x1, y1, x2, y2, (f64) i, (f64) j))
- put_pixel(i, j, op, color);
+ put_pixel(brush, i, j);
}
-void fill_ellipse(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height) {
+void fill_ellipse(Brush brush, f64 x, f64 y, 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);
+ brush_defaults(&brush);
- for (i64 j = j0; j < j1; ++j) {
- if (j < 0 || j >= g_platform.frame_height)
- continue;
+ f64 x0, y0, x1, y1;
+
+ if (brush.scale.x < 0) {
+ x1 = brush.position.x + x * brush.scale.x;
+ x0 = x1 + width * brush.scale.x;
+ } else {
+ x0 = brush.position.x + x * brush.scale.x;
+ x1 = x0 + width * brush.scale.x;
+ }
+
+ if (brush.scale.y < 0) {
+ y1 = brush.position.y + y * brush.scale.y;
+ y0 = y1 + height * brush.scale.y;
+ } else {
+ y0 = brush.position.y + y * brush.scale.y;
+ y1 = y0 + height * brush.scale.y;
+ }
+
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
+
+ for (i64 j = j0; j < j1; ++j)
for (i64 i = i0; i < i1; ++i) {
- if (i < 0 || i >= g_platform.frame_width)
- continue;
- if (ellipse_contains(x0, y0, width, height, (f64) i, (f64) j))
- put_pixel(i, j, op, color);
+ if (ellipse_contains(x0, y0, x1 - x0, y1 - y0, (f64) i, (f64) j))
+ put_pixel(brush, i, j);
}
- }
}
-void fill_line(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) {
+void fill_line(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) {
f64 dx = x1 - x0;
f64 dy = y1 - y0;
@@ -417,12 +750,12 @@ void fill_line(u32 op, vec3_f32 color, f64 x0, f64 y0, f64 x1, f64 y1, f64 width
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);
+ fill_triangle(brush, x0 - tx, y0 - ty, x0 + tx, y0 + ty, x1 + tx, y1 + ty);
+ fill_triangle(brush, x0 - tx, y0 - ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty);
}
-void draw_text_area(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text) {
- if (max_scale_x < 1e-6 || max_scale_y < 1e-6)
+void draw_text_area(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 num_chars, c32 *text) {
+ if (max_scale_x < EPSILON || max_scale_y < EPSILON)
return;
i64 num_columns = enum_text_columns(num_chars, text);
@@ -439,17 +772,18 @@ void draw_text_area(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height, f64 m
kx = k * max_scale_x;
ky = k * max_scale_y;
- draw_text(color, x0, y0, kx, ky, num_chars, text);
+ draw_text(brush, x, y, kx, ky, num_chars, text);
}
-void draw_selection_cursor(vec3_f32 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) {
- if (max_scale_x < 1e-6 || max_scale_y < 1e-6)
+void draw_selection_cursor(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_scale_x, f64 max_scale_y, i64 cursor, i64 selection, i64 num_chars, c32 *text) {
+ if (max_scale_x < EPSILON || max_scale_y < EPSILON)
return;
i64 num_columns = enum_text_columns(num_chars, text);
i64 num_rows = enum_text_rows(num_chars, text);
i64 cursor_x = text_cursor(cursor, text);
i64 cursor_y = enum_text_rows(cursor, text);
+
f64 scale_x = width / num_columns;
f64 scale_y = height / num_rows;
@@ -475,35 +809,45 @@ void draw_selection_cursor(vec3_f32 color, f64 x0, f64 y0, f64 width, f64 height
}
if (cursor_y == selection_y)
- fill_rectangle(OP_XOR, color,
- x0 + kx * cursor_x,
- y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1),
+ fill_rectangle(
+ brush,
+ x + kx * cursor_x,
+ y + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1),
kx * (selection_x - cursor_x),
ky * (CHAR_NUM_BITS_Y + 1)
);
else {
- fill_rectangle(OP_XOR, color,
- x0 + kx * cursor_x,
- y0 + ky * cursor_y - ky * (CHAR_NUM_BITS_Y + 1),
+ fill_rectangle(
+ brush,
+ x + kx * cursor_x,
+ y + 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)
- fill_rectangle(OP_XOR, color,
- x0,
- y0 + ky * j - ky * (CHAR_NUM_BITS_Y + 1),
+ fill_rectangle(
+ brush,
+ x,
+ y + ky * j - ky * (CHAR_NUM_BITS_Y + 1),
kx * num_columns,
ky * (CHAR_NUM_BITS_Y + 1)
);
- fill_rectangle(OP_XOR, color,
- x0,
- y0 + ky * selection_y - ky * (CHAR_NUM_BITS_Y + 1),
+ fill_rectangle(
+ brush,
+ x,
+ y + ky * selection_y - ky * (CHAR_NUM_BITS_Y + 1),
kx * selection_x,
ky * (CHAR_NUM_BITS_Y + 1)
);
}
} else
- 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));
+ fill_rectangle(
+ brush,
+ x + kx * cursor_x,
+ y + ky * cursor_y - ky * CHAR_NUM_BITS_Y,
+ kx * .5,
+ ky * (CHAR_NUM_BITS_Y - 1)
+ );
}
#endif // GRAPHICS_IMPL_GUARD_