diff options
Diffstat (limited to 'graphics.c')
-rw-r--r-- | graphics.c | 518 |
1 files changed, 431 insertions, 87 deletions
@@ -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_ |