summaryrefslogtreecommitdiff
path: root/graphics.c
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2025-01-16 22:46:37 +0100
committerMitya Selivanov <automainint@guattari.tech>2025-01-16 22:46:37 +0100
commit308a1cf24208816b92ab830ec082757ca30738c9 (patch)
tree1bc2bdc8c6d179d144e4753bee08a59e3199cb1c /graphics.c
parent39aad7f9ca931342f58bf638b5aadd1923db0f50 (diff)
downloadreduced_system_layer-308a1cf24208816b92ab830ec082757ca30738c9.zip
Simple anti-aliasing
Diffstat (limited to 'graphics.c')
-rw-r--r--graphics.c536
1 files changed, 402 insertions, 134 deletions
diff --git a/graphics.c b/graphics.c
index 62a6472..3b3f539 100644
--- a/graphics.c
+++ b/graphics.c
@@ -19,6 +19,20 @@
// ================================================================
//
+// Options
+//
+// ================================================================
+
+#ifndef EPSILON
+#define EPSILON (1e-9)
+#endif
+
+#ifndef ANTIALIASING_SCALE
+#define ANTIALIASING_SCALE 3
+#endif
+
+// ================================================================
+//
// Vector math
//
// ================================================================
@@ -30,10 +44,6 @@ vec4_f32 vec4_f32_lerp (vec4_f32 a, vec4_f32 b, f32 t);
// ================================================================
-#ifndef EPSILON
-#define EPSILON (1e-9)
-#endif
-
typedef struct {
i64 width;
i64 height;
@@ -45,9 +55,9 @@ typedef struct {
Pixel_Buffer buffer;
vec2 position;
vec2 scale;
- b8 quick : 1;
- b8 alpha : 1;
- b8 xor_color : 1;
+ b8 quick : 1; // If set, anti-aliasing is skipped.
+ b8 alpha : 1; // If set, the resulting color is: dst * (1 - alpha) + src * alpha.
+ b8 xor_color : 1; // If set, the source color is XORed with the destination color.
vec4_f32 color;
} Brush;
@@ -58,6 +68,11 @@ vec3_f32 lch_from_lab(vec3_f32 lab);
vec3_f32 rgb_from_lch(vec3_f32 lch);
vec3_f32 lch_from_rgb(vec3_f32 rgb);
+// NOTE:
+// General rule to work with gamma is removing it,
+// then doing computations like blending, interpolation, etc,
+// then adding it back.
+
vec3_f32 rgb_gamma_add (vec3_f32 rgb);
vec3_f32 rgb_gamma_remove (vec3_f32 rgb);
vec4_f32 rgba_gamma_add (vec4_f32 rgb);
@@ -80,18 +95,21 @@ vec4_f32 rgba_f32_from_u32(u32 color);
#define LCH(...) ((Brush) { .color = vec4_from_vec3_f32(rgb_from_lch((vec3_f32) { __VA_ARGS__ }), 1.f) } })
#define LCHA(...) ((Brush) { .color = vec4_from_vec3_f32(rgb_from_lch(vec3_from_vec4_f32((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);
+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 quad_contains (f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 x3, f64 y3, 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);
-Pixel_Buffer window_pixels(void);
-Pixel_Buffer subimage(Pixel_Buffer image, i64 x, i64 y, i64 width, i64 height);
+Pixel_Buffer frame_pixels (void);
+Pixel_Buffer sketch_pixels(i64 width, i64 height);
+Pixel_Buffer subimage (Pixel_Buffer image, i64 x, i64 y, i64 width, i64 height);
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_quad (Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 x3, f64 y3);
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);
@@ -131,6 +149,26 @@ static f64 max3_(f64 a, f64 b, f64 c) {
return c;
}
+static f64 min4_(f64 a, f64 b, f64 c, f64 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_(f64 a, f64 b, f64 c, f64 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 b8 same_sign_(f64 a, f64 b) {
if (a >= EPSILON && b <= -EPSILON) return 0;
if (a <= -EPSILON && b >= EPSILON) return 0;
@@ -163,7 +201,7 @@ static void put_pixel_alpha_(Brush brush, i64 x, i64 y) {
.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,
+ .w = 1.,
};
}
@@ -192,27 +230,174 @@ static void put_pixel_(Brush brush, i64 x, i64 y) {
put_pixel_color_(brush, x, y);
}
-void fill_rectangle_(Brush brush, f64 x, f64 y, f64 width, f64 height) {
- if (width < EPSILON || height < EPSILON)
- return;
+f64 transform_x_(Brush brush, f64 x) {
+ return (brush.position.x + x) * brush.scale.x;
+}
- f64 x0, y0, x1, y1;
+f64 transform_y_(Brush brush, f64 y) {
+ return (brush.position.y + y) * brush.scale.y;
+}
+void transform_box_(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 *x0, f64 *y0, f64 *x1, f64 *y1) {
if (brush.scale.x < 0) {
- x1 = brush.position.x + x * brush.scale.x;
- x0 = x1 + width * brush.scale.x;
+ *x1 = transform_x_(brush, x);
+ *x0 = *x1 + width * brush.scale.x;
} else {
- x0 = brush.position.x + x * brush.scale.x;
- x1 = x0 + width * brush.scale.x;
+ *x0 = transform_x_(brush, 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;
+ *y1 = transform_y_(brush, y);
+ *y0 = *y1 + height * brush.scale.y;
} else {
- y0 = brush.position.y + y * brush.scale.y;
- y1 = y0 + height * brush.scale.y;
+ *y0 = transform_y_(brush, y);
+ *y1 = *y0 + height * brush.scale.y;
}
+}
+
+Brush antialiasing_brush_(Brush brush) {
+ return (Brush) {
+ .buffer = sketch_pixels(brush.buffer.width * ANTIALIASING_SCALE, brush.buffer.height * ANTIALIASING_SCALE),
+ .position = brush.position,
+ .scale = {
+ .x = brush.scale.x * ANTIALIASING_SCALE,
+ .y = brush.scale.y * ANTIALIASING_SCALE,
+ },
+ .quick = 1,
+ .alpha = 0,
+ .xor_color = 0,
+ .color = brush.alpha
+ ? brush.color
+ : vec4_from_vec3_f32(vec3_from_vec4_f32(brush.color), 1.f),
+ };
+}
+
+Brush copy_brush_(Brush brush) {
+ return (Brush) {
+ .buffer = brush.buffer,
+ .position = brush.position,
+ .scale = brush.scale,
+ .alpha = 1,
+ .xor_color = brush.xor_color,
+ };
+}
+
+void draw_pixels_(Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src) {
+ // FIXME PERF
+
+ f64 x0, y0, x1, y1;
+ transform_box_(brush, x, y, width, height, &x0, &y0, &x1, &y1);
+
+ f64 w = x1 - x0;
+ f64 h = y1 - y0;
+
+ if (w < EPSILON || h < EPSILON)
+ return;
+
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) floor(x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) floor(y1);
+
+ if (i0 < 0) i0 = 0;
+ if (i1 >= brush.buffer.width) i1 = brush.buffer.width - 1;
+ if (j0 < 0) j0 = 0;
+ if (j1 >= brush.buffer.height) j1 = brush.buffer.height - 1;
+
+ if (!brush.quick)
+ for (i64 j = j0; j <= j1; ++j) {
+ i64 src_j0 = (i64) floor(((j - y0) * src.height) / h + .5);
+ i64 src_j1 = (i64) floor(((j + 1 - y0) * src.height) / h + .5);
+ if (src_j1 <= 0 || src_j0 >= src.height) continue;
+ if (src_j0 < 0) src_j0 = 0;
+ if (src_j1 > src.height) src_j1 = src.height;
+ for (i64 i = i0; i <= i1; ++i) {
+ i64 src_i0 = (i64) floor(((i - x0) * src.width) / w + .5);
+ i64 src_i1 = (i64) floor(((i + 1 - x0) * src.width) / w + .5);
+ if (src_i1 <= 0 || src_i0 >= src.width) continue;
+ if (src_i0 < 0) src_i0 = 0;
+ if (src_i1 > src.width) src_i1 = src.width;
+ brush.color = (vec4_f32) {0};
+ i64 n = 0;
+ for (i64 jj = src_j0; jj < src_j1; ++jj)
+ for (i64 ii = src_i0; ii < src_i1; ++ii) {
+ brush.color.x += gamma_re_(src.pixels[jj * src.line_size + ii].x);
+ brush.color.y += gamma_re_(src.pixels[jj * src.line_size + ii].y);
+ brush.color.z += gamma_re_(src.pixels[jj * src.line_size + ii].z);
+ brush.color.w += gamma_re_(src.pixels[jj * src.line_size + ii].w);
+ ++n;
+ }
+ if (n == 0)
+ continue;
+ brush.color.x = gamma_(brush.color.x / n);
+ brush.color.y = gamma_(brush.color.y / n);
+ brush.color.z = gamma_(brush.color.z / n);
+ brush.color.w = gamma_(brush.color.w / n);
+ put_pixel_(brush, i, j);
+ }
+ }
+ else
+ for (i64 j = j0; j <= j1; ++j) {
+ i64 src_j = (i64) floor(((j - y0) * src.height) / h + .5);
+ if (src_j < 0 || src_j >= src.height) continue;
+ for (i64 i = i0; i <= i1; ++i) {
+ i64 src_i = (i64) floor(((i - x0) * src.width) / w + .5);
+ if (src_i < 0 || src_i >= src.width) continue;
+ brush.color = src.pixels[src_j * src.line_size + src_i];
+ put_pixel_(brush, i, j);
+ }
+ }
+}
+
+void draw_pixels_from_(Brush brush, f64 x, f64 y, f64 width, f64 height, Pixel_Buffer src, i64 src_x, i64 src_y, i64 src_width, i64 src_height) {
+ // NOTE: In the future we may need to implement
+ // pixel buffers with bounding boxes.
+
+ f64 bx = 0;
+ f64 by = 0;
+ f64 bw = 0;
+ f64 bh = 0;
+
+ if (src_width > src.width - src_x) {
+ bw += src_width - src.width + src_x;
+ src_width = src.width - src_x;
+ }
+
+ if (src_height > src.height - src_y) {
+ bh += src_height - src.height + src_y;
+ src_height = src.height - src_y;
+ }
+
+ if (src_x < 0) {
+ bx -= src_x;
+ bw -= src_x;
+ src_x = 0;
+ }
+
+ if (src_y < 0) {
+ by -= src_y;
+ bh -= src_y;
+ src_y = 0;
+ }
+
+ if (src_x + src_width > src.width || src_y + src_height > src.height || src_width <= 0 || src_height <= 0)
+ return;
+
+ bx *= width / src_width;
+ by *= height / src_height;
+ bw *= width / src_width;
+ bh *= height / src_height;
+
+ draw_pixels_(brush, x + bx, y + by, width - bw, height - bh, subimage(src, src_x, src_y, src_width, src_height));
+}
+
+void fill_rectangle_(Brush brush, f64 x, f64 y, f64 width, f64 height) {
+ if (width < EPSILON || height < EPSILON)
+ return;
+
+ f64 x0, y0, x1, y1;
+ transform_box_(brush, x, y, width, height, &x0, &y0, &x1, &y1);
if (!brush.quick) {
i64 i0 = (i64) ceil (x0);
@@ -329,14 +514,14 @@ void fill_triangle_(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2)
// FIXME PERF:
// Implement better algorithm.
- x0 = brush.position.x + x0 * brush.scale.x;
- y0 = brush.position.y + y0 * brush.scale.y;
+ x0 = transform_x_(brush, x0);
+ y0 = transform_y_(brush, y0);
- x1 = brush.position.x + x1 * brush.scale.x;
- y1 = brush.position.y + y1 * brush.scale.y;
+ x1 = transform_x_(brush, x1);
+ y1 = transform_y_(brush, y1);
- x2 = brush.position.x + x2 * brush.scale.x;
- y2 = brush.position.y + y2 * brush.scale.y;
+ x2 = transform_x_(brush, x2);
+ y2 = transform_y_(brush, y2);
i64 i0 = (i64) floor(min3_(x0, x1, x2));
i64 j0 = (i64) floor(min3_(y0, y1, y2));
@@ -370,6 +555,48 @@ void fill_triangle_(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2)
}
}
+void fill_quad_(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 x3, f64 y3) {
+ fill_triangle_(brush, x0, y0, x1, y1, x2, y2);
+ fill_triangle_(brush, x0, y0, x2, y2, x3, y3);
+}
+
+void fill_ellipse_(Brush brush, f64 x, f64 y, f64 width, f64 height) {
+ f64 x0, y0, x1, y1;
+ transform_box_(brush, x, y, width, height, &x0, &y0, &x1, &y1);
+
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
+
+ if (j0 < 0) j0 = 0;
+ if (j1 >= brush.buffer.height) j1 = brush.buffer.height - 1;
+
+ for (i64 j = j0; j <= j1; ++j) {
+ i64 left = i1;
+ i64 right = i0;
+
+ for (i64 i = i0; i <= i1; ++i)
+ if (ellipse_contains(x0, y0, x1 - x0, y1 - y0, (f64) i, (f64) j)) {
+ left = i;
+ right = (i64) ceil(x0 + x1 - left); // symmetry
+ break;
+ }
+
+ for (i64 i = right; i >= i0; --i)
+ if (ellipse_contains(x0, y0, x1 - x0, y1 - y0, (f64) i, (f64) j)) {
+ right = i;
+ break;
+ }
+
+ if (left < 0) left = 0;
+ if (right >= brush.buffer.width) right = brush.buffer.width - 1;
+
+ for (i64 i = left; i <= right; ++i)
+ put_pixel_(brush, i, j);
+ }
+}
+
static i64 char_column_offset_(c32 c, i64 column_index) {
if (column_index < 0 || column_index >= CHAR_NUM_BITS_X_)
return -1;
@@ -521,8 +748,8 @@ static i64 enum_text_rows_(i64 num_chars, c32 *text) {
}
static void draw_text_(Brush brush, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) {
- x0 = brush.position.x + x0 * brush.scale.x;
- y0 = brush.position.y + y0 * brush.scale.y;
+ x0 = transform_x_(brush, x0);
+ y0 = transform_y_(brush, y0);
if (brush.scale.x < 0) scale_x *= -brush.scale.x;
else scale_x *= brush.scale.x;
@@ -591,7 +818,7 @@ static Brush brush_defaults_(Brush b) {
}
if (b.buffer.pixels == NULL)
- b.buffer = window_pixels();
+ b.buffer = frame_pixels();
if (b.buffer.line_size == 0)
b.buffer.line_size = b.buffer.width;
@@ -856,10 +1083,14 @@ b8 triangle_contains(f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 px, f64
f64 pz1 = x10 * (py - y1) - (px - x1) * y10;
f64 pz2 = x21 * (py - y2) - (px - x2) * y21;
- return
- same_sign_(z0, pz0)
- && same_sign_(z1, pz1)
- && same_sign_(z2, pz2);
+ return same_sign_(z0, pz0)
+ && same_sign_(z1, pz1)
+ && same_sign_(z2, pz2);
+}
+
+b8 quad_contains(f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 x3, f64 y3, f64 px, f64 py) {
+ return triangle_contains(x0, y0, x1, y1, x2, y2, px, py)
+ || triangle_contains(x0, y0, x2, y2, x3, y3, px, py);
}
b8 ellipse_contains(f64 x0, f64 y0, f64 width, f64 height, f64 px, f64 py) {
@@ -901,7 +1132,7 @@ 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);
}
-Pixel_Buffer window_pixels(void) {
+Pixel_Buffer frame_pixels(void) {
return (Pixel_Buffer) {
.width = g_platform.frame_width,
.height = g_platform.frame_height,
@@ -910,15 +1141,34 @@ Pixel_Buffer window_pixels(void) {
};
}
+Pixel_Buffer sketch_pixels(i64 width, i64 height) {
+ if (width <= 0 || height <= 0) {
+ width = g_platform.frame_width;
+ height = g_platform.frame_height;
+ }
+
+ if (width * height > MAX_NUM_PIXELS) {
+ width = 0;
+ height = 0;
+ }
+
+ return (Pixel_Buffer) {
+ .width = width,
+ .height = height,
+ .line_size = width,
+ .pixels = g_platform.sketch,
+ };
+}
+
Pixel_Buffer subimage(Pixel_Buffer image, i64 x, i64 y, i64 width, i64 height) {
- if (x < 0 || y < 0 || width < 0 || height < 0 || x + width > image.width || y + height > image.height || image.pixels == NULL)
+ if (x < 0 || y < 0 || width <= 0 || height <= 0 || x + width > image.width || y + height > image.height || image.pixels == NULL)
return (Pixel_Buffer) {
.width = 0,
.height = 0,
.line_size = 0,
// Set pixels pointer to prevent default initialization.
- .pixels = g_platform.pixels,
+ .pixels = g_platform.sketch,
};
return (Pixel_Buffer) {
@@ -939,62 +1189,82 @@ 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) {
- // FIXME PERF
+ if (width < EPSILON || height < EPSILON || src.width <= 0 || src.height <= 0 || src.line_size <= 0 || src.pixels == NULL)
+ return;
- brush = brush_defaults_(brush);
+ draw_pixels_(brush_defaults_(brush), x, y, width, height, src);
+}
- f64 x0, y0, x1, y1;
+void fill_rectangle(Brush brush, f64 x, f64 y, f64 width, f64 height) {
+ fill_rectangle_(brush_defaults_(brush), x, y, width, height);
+}
- 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;
- }
+void fill_triangle(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
+ brush = brush_defaults_(brush);
- 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.quick) {
+ Brush aa = antialiasing_brush_(brush);
- i64 i0 = (i64) floor(x0);
- i64 i1 = (i64) ceil (x1);
- i64 j0 = (i64) floor(y0);
- i64 j1 = (i64) ceil (y1);
+ f64 x = min3_(x0, x1, x2);
+ f64 y = min3_(y0, y1, y2);
+ f64 width = max3_(x0, x1, x2) - x;
+ f64 height = max3_(y0, y1, y2) - y;
- f64 w = x1 - x0;
- f64 h = y1 - y0;
+ Brush clear = aa;
+ clear.color = (vec4_f32) {0};
+ fill_rectangle_(clear, 0, 0, width, height);
- if (w < EPSILON || h < EPSILON)
- return;
+ fill_triangle_(aa, x0 - x, y0 - y, x1 - x, y1 - y, x2 - x, y2 - y);
- if (i0 < 0) i0 = 0;
- if (i1 >= brush.buffer.width) i1 = brush.buffer.width - 1;
- if (j0 < 0) j0 = 0;
- if (j1 >= brush.buffer.height) j1 = brush.buffer.height - 1;
+ f64 x0, y0, x1, y1;
+ transform_box_(aa, 0, 0, width, height, &x0, &y0, &x1, &y1);
- for (i64 j = j0; j <= j1; ++j) {
- i64 src_j = (i64) floor(((j - y0) * src.height) / h + .5);
- if (src_j < 0 || src_j >= src.height) continue;
- for (i64 i = i0; i <= i1; ++i) {
- i64 src_i = (i64) floor(((i - x0) * src.width) / w + .5);
- if (src_i < 0 || src_i >= src.width) continue;
- brush.color = src.pixels[src_j * src.line_size + src_i];
- put_pixel_(brush, i, j);
- }
- }
-}
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
-void fill_rectangle(Brush brush, f64 x, f64 y, f64 width, f64 height) {
- fill_rectangle_(brush_defaults_(brush), x, y, width, height);
+ draw_pixels_from_(
+ copy_brush_(brush),
+ x, y, width, height,
+ aa.buffer, i0, j0, i1 - i0, j1 - j0
+ );
+ } else
+ fill_triangle_(brush, x0, y0, x1, y1, x2, y2);
}
-void fill_triangle(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2) {
- fill_triangle_(brush_defaults_(brush), x0, y0, x1, y1, x2, y2);
+void fill_quad(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 x2, f64 y2, f64 x3, f64 y3) {
+ brush = brush_defaults_(brush);
+
+ if (!brush.quick) {
+ Brush aa = antialiasing_brush_(brush);
+
+ f64 x = min4_(x0, x1, x2, x3);
+ f64 y = min4_(y0, y1, y2, y3);
+ f64 width = max4_(x0, x1, x2, x3) - x;
+ f64 height = max4_(y0, y1, y2, y3) - y;
+
+ Brush clear = aa;
+ clear.color = (vec4_f32) {0};
+ fill_rectangle_(clear, 0, 0, width, height);
+
+ fill_quad_(aa, x0 - x, y0 - y, x1 - x, y1 - y, x2 - x, y2 - y, x3 - x, y3 - y);
+
+ f64 x0, y0, x1, y1;
+ transform_box_(aa, 0, 0, width, height, &x0, &y0, &x1, &y1);
+
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
+
+ draw_pixels_from_(
+ copy_brush_(brush),
+ x, y, width, height,
+ aa.buffer, i0, j0, i1 - i0, j1 - j0
+ );
+ } else
+ fill_quad_(brush, x0, y0, x1, y1, x2, y2, x3, y3);
}
void fill_ellipse(Brush brush, f64 x, f64 y, f64 width, f64 height) {
@@ -1006,55 +1276,30 @@ void fill_ellipse(Brush brush, f64 x, f64 y, f64 width, f64 height) {
brush = 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);
+ if (!brush.quick) {
+ Brush aa = antialiasing_brush_(brush);
- if (j0 < 0) j0 = 0;
- if (j1 >= brush.buffer.height) j1 = brush.buffer.height - 1;
+ Brush clear = aa;
+ clear.color = (vec4_f32) {0};
+ fill_rectangle_(clear, 0, 0, width, height);
- for (i64 j = j0; j <= j1; ++j) {
- i64 left = i1;
- i64 right = i0;
+ fill_ellipse_(aa, 0, 0, width, height);
- for (i64 i = i0; i <= i1; ++i)
- if (ellipse_contains(x0, y0, x1 - x0, y1 - y0, (f64) i, (f64) j)) {
- left = i;
- right = (i64) ceil(x0 + x1 - left); // symmetry
- break;
- }
+ f64 x0, y0, x1, y1;
+ transform_box_(aa, 0, 0, width, height, &x0, &y0, &x1, &y1);
- for (i64 i = right; i >= i0; --i)
- if (ellipse_contains(x0, y0, x1 - x0, y1 - y0, (f64) i, (f64) j)) {
- right = i;
- break;
- }
-
- if (left < 0) left = 0;
- if (right >= brush.buffer.width) right = brush.buffer.width - 1;
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
- for (i64 i = left; i <= right; ++i)
- put_pixel_(brush, i, j);
- }
+ draw_pixels_from_(
+ copy_brush_(brush),
+ x, y, width, height,
+ aa.buffer, i0, j0, i1 - i0, j1 - j0
+ );
+ } else
+ fill_ellipse_(brush, x, y, width, height);
}
void fill_line(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) {
@@ -1076,9 +1321,7 @@ void fill_line(Brush brush, f64 x0, f64 y0, f64 x1, f64 y1, f64 width) {
tx *= width / (tl * 2.0);
ty *= width / (tl * 2.0);
- brush = brush_defaults_(brush);
- 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);
+ fill_quad(brush, x0 - tx, y0 - ty, x0 + tx, y0 + ty, x1 + tx, y1 + ty, x1 - tx, y1 - ty);
}
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) {
@@ -1099,7 +1342,32 @@ void draw_text_area(Brush brush, f64 x, f64 y, f64 width, f64 height, f64 max_sc
kx = k * max_scale_x;
ky = k * max_scale_y;
- draw_text_(brush_defaults_(brush), x, y, kx, ky, num_chars, text);
+ brush = brush_defaults_(brush);
+
+ if (!brush.quick) {
+ Brush aa = antialiasing_brush_(brush);
+
+ Brush clear = aa;
+ clear.color = (vec4_f32) {0};
+ fill_rectangle_(clear, 0, 0, width, height);
+
+ draw_text_(aa, 0, 0, kx, ky, num_chars, text);
+
+ f64 x0, y0, x1, y1;
+ transform_box_(aa, 0, 0, width, height, &x0, &y0, &x1, &y1);
+
+ i64 i0 = (i64) floor(x0);
+ i64 i1 = (i64) ceil (x1);
+ i64 j0 = (i64) floor(y0);
+ i64 j1 = (i64) ceil (y1);
+
+ draw_pixels_from_(
+ copy_brush_(brush),
+ x, y, width, height,
+ aa.buffer, i0, j0, i1 - i0, j1 - j0
+ );
+ } else
+ draw_text_(brush, x, y, kx, ky, num_chars, text);
}
void draw_text_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) {