diff options
-rw-r--r-- | examples/julia_set.c | 23 | ||||
-rw-r--r-- | graphics.c | 536 | ||||
-rw-r--r-- | reduced_system_layer.c | 8 |
3 files changed, 416 insertions, 151 deletions
diff --git a/examples/julia_set.c b/examples/julia_set.c index a5b733e..e5a2734 100644 --- a/examples/julia_set.c +++ b/examples/julia_set.c @@ -1,6 +1,5 @@ #include "../graphics.c" -i64 p = 4; f64 view_x = 0.; f64 view_y = 0.; f64 view_s = 1.; @@ -26,9 +25,6 @@ void update_and_render_frame(void) { return; } - if (g_platform.key_pressed['\n']) - p = (p == 1 ? 4 : 1); - if (g_platform.key_pressed[KEY_ESCAPE]) { view_x = 0.; view_y = 0.; @@ -49,10 +45,13 @@ void update_and_render_frame(void) { view_s += .1 * g_platform.wheel_dy * view_s; - for (i32 j = 0; j + p <= g_platform.frame_height; j += p) - for (i32 i = 0; i + p <= g_platform.frame_width; i += p) { - f64 x = .003 * ((i - g_platform.frame_width * .5) * view_s - view_x); - f64 y = .003 * ((j - g_platform.frame_height * .5) * view_s - view_y); + for (i32 j = 0; j < g_platform.frame_height; ++j) + for (i32 i = 0; i < g_platform.frame_width; ++i) { + f64 kx = ((f64) g_platform.real_width) / g_platform.frame_width * view_s; + f64 ky = ((f64) g_platform.real_height) / g_platform.frame_height * view_s; + + f64 x = .003 * ((i - g_platform.frame_width * .5) * kx - view_x); + f64 y = .003 * ((j - g_platform.frame_height * .5) * ky - view_y); i64 n = 0; @@ -69,9 +68,7 @@ void update_and_render_frame(void) { else c = 0xffffff - n * 8 - n * 256 * 4; - for (i32 jj = 0; jj < p; ++jj) - for (i32 ii = 0; ii < p; ++ii) - g_platform.pixels[(j + jj) * g_platform.frame_width + (i + ii)] = vec4_from_vec3_f32(rgb_f32_from_u32(c), 1.f); + g_platform.pixels[j * g_platform.frame_width + i] = vec4_from_vec3_f32(rgb_f32_from_u32(c), 1.f); } p_render_frame(); @@ -82,9 +79,7 @@ i32 main(i32 argc, c8 **argv) { (void) argv; g_platform = (Platform) { - .title = "Julia Set", - .pixel_size = 1, - .exact_resolution = 1, + .title = "Julia Set", }; time_0 = p_time(); @@ -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) { diff --git a/reduced_system_layer.c b/reduced_system_layer.c index 8b35ea3..8370c46 100644 --- a/reduced_system_layer.c +++ b/reduced_system_layer.c @@ -29,8 +29,7 @@ // // - Work in progress // - Drop files - Web -// - Graphics - Anti-aliasing -// - Plain buffer allocator +// - Seldom allocator // - Logging // - Examples // - Conway's Game of Life @@ -42,6 +41,7 @@ // - Icons // - Vector math // - CMYK color +// - Textures // - Test suite // - System // - Window @@ -91,8 +91,9 @@ // - Adaptive resolution // - Oklab color // - Relative coordinates -// - Blending +// - Alpha blending // - Self-contained impl +// - Anti-aliasing // - System // - Window - X11, Web // - Sound - ALSA, Web @@ -539,6 +540,7 @@ typedef struct { Drop_File *drop_files; vec4_f32 pixels [MAX_NUM_PIXELS]; + vec4_f32 sketch [MAX_NUM_PIXELS]; Input_Key input [MAX_INPUT_SIZE]; c8 clipboard [MAX_CLIPBOARD_SIZE]; b8 key_down [MAX_NUM_KEYS]; |