summaryrefslogtreecommitdiff
path: root/graphics.c
diff options
context:
space:
mode:
Diffstat (limited to 'graphics.c')
-rwxr-xr-xgraphics.c319
1 files changed, 282 insertions, 37 deletions
diff --git a/graphics.c b/graphics.c
index c8df0c8..34d3021 100755
--- a/graphics.c
+++ b/graphics.c
@@ -97,6 +97,7 @@ enum {
GRAPHICS_DRAW_PIXELS,
GRAPHICS_FILL_RECTANGLE,
GRAPHICS_FILL_TRIANGLE,
+ GRAPHICS_FILL_TRIANGLES,
GRAPHICS_FILL_QUAD,
GRAPHICS_FILL_ELLIPSE,
GRAPHICS_FILL_LINE,
@@ -121,6 +122,12 @@ typedef struct graphics_request_ {
} triangle;
struct {
vec4_f32 color;
+ Box area;
+ i64 num_triangles;
+ vec2 * vertices;
+ } triangles;
+ struct {
+ vec4_f32 color;
vec2 vertices[4];
} quad;
struct {
@@ -152,11 +159,23 @@ typedef struct graphics_request_ {
} Graphics_Request;
typedef struct {
- b8 disable_antialiasing;
+ b8 disable_cache;
vec2 scale;
Pixel_Buffer dst;
} Graphics_Context;
+// ================================================================
+//
+// User program interface
+//
+// ================================================================
+
+Box external_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text);
+
+void external_font_render_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, vec2 position, vec2 scale, i64 num_chars, c32 *text);
+
+// ================================================================
+
vec3_f32 lab_from_rgb(vec3_f32 rgb);
vec3_f32 rgb_from_lab(vec3_f32 lab);
vec3_f32 lab_from_lch(vec3_f32 lch);
@@ -194,24 +213,33 @@ b8 hit_line (vec2 vertices[2], f64 width, vec2 point);
void draw_pixels_to_buffer (Pixel_Buffer dst, Box area, Pixel_Buffer src);
void fill_rectangle_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area);
void fill_triangle_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[3]);
+void fill_triangles_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area, i64 num_triangles, vec2 *vertices);
void fill_quad_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4]);
void fill_ellipse_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area);
void fill_line_to_buffer (Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width);
void draw_text_area_to_buffer (Pixel_Buffer dst, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text);
void draw_text_cursor_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection);
+void draw_pixels_cached (Box area, Pixel_Buffer src);
+void fill_rectangle_cached (vec4_f32 color, Box area);
+void fill_triangle_cached (vec4_f32 color, vec2 vertices[3]);
+void fill_triangles_cached (vec4_f32 color, Box area, i64 num_triangles, vec2 *vertices);
+void fill_quad_cached (vec4_f32 color, vec2 vertices[4]);
+void fill_ellipse_cached (vec4_f32 color, Box area);
+void fill_line_cached (vec4_f32 color, vec2 vertices[2], f64 width);
+void draw_text_area_cached (vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text);
+void draw_text_cursor_cached(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection);
+
void draw_pixels (Box area, Pixel_Buffer src);
void fill_rectangle (vec4_f32 color, Box area);
void fill_triangle (vec4_f32 color, vec2 vertices[3]);
+void fill_triangles (vec4_f32 color, Box area, i64 num_triangles, vec2 *vertices);
void fill_quad (vec4_f32 color, vec2 vertices[4]);
void fill_ellipse (vec4_f32 color, Box area);
void fill_line (vec4_f32 color, vec2 vertices[2], f64 width);
void draw_text_area (vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text);
void draw_text_cursor(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection);
-void draw_pixels_quick (Box area, Pixel_Buffer src);
-void fill_rectangle_quick(vec4_f32 color, Box area);
-
void perform_graphics_request(Graphics_Context context, Graphics_Request req);
#endif // GRAPHICS_HEADER_GUARD_
@@ -332,11 +360,6 @@ void fill_rectangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) {
i64 j0 = (i64) floor(area.y);
i64 j1 = (i64) floor(area.y + area.height);
- i64 i00 = i0;
- i64 j00 = j0;
- i64 i10 = i1;
- i64 j10 = j1;
-
if (i0 < 0) i0 = 0;
if (i1 > dst.width) i1 = dst.width;
if (j0 < 0) j0 = 0;
@@ -393,6 +416,43 @@ void fill_triangle_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[3])
}
}
+static void triangles_area_(f64 *x0, f64 *y0, f64 *x1, f64 *y1, i64 num, vec2 *v) {
+ if (num <= 0)
+ return;
+
+ *x0 = min3_(v[0].x, v[1].x, v[2].x);
+ *y0 = min3_(v[0].y, v[1].y, v[2].y);
+ *x1 = max3_(v[0].x, v[1].x, v[2].x);
+ *y1 = max3_(v[0].y, v[1].y, v[2].y);
+
+ for (i64 i = 1; i < num; ++i) {
+ *x0 = min4_(*x0, v[i * 3].x, v[i * 3 + 1].x, v[i * 3 + 2].x);
+ *y0 = min4_(*y0, v[i * 3].y, v[i * 3 + 1].y, v[i * 3 + 2].y);
+ *x1 = max4_(*x1, v[i * 3].x, v[i * 3 + 1].x, v[i * 3 + 2].x);
+ *y1 = max4_(*y1, v[i * 3].y, v[i * 3 + 1].y, v[i * 3 + 2].y);
+ }
+}
+
+void fill_triangles_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area, i64 num_triangles, vec2 *vertices) {
+ f64 x0, y0, x1, y1;
+ triangles_area_(&x0, &y0, &x1, &y1, num_triangles, vertices);
+
+ i64 i0 = (i64) x0;
+ i64 j0 = (i64) y0;
+ i64 i1 = (i64) x1;
+ i64 j1 = (i64) y1;
+
+ // FIXME, PERF
+
+ for (i64 j = j0; j <= j1; ++j)
+ for (i64 i = i0; i <= i1; ++i)
+ for (i64 k = 0; k < num_triangles; ++k)
+ if (hit_triangle(vertices + 3 * k, (vec2) { (f64) i, (f64) j, })) {
+ put_pixel_(dst.pixels + j * dst.stride + i, color);
+ break;
+ }
+}
+
void fill_quad_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4]) {
f64 x0 = vertices[0].x;
f64 y0 = vertices[0].y;
@@ -403,10 +463,10 @@ void fill_quad_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[4]) {
f64 x3 = vertices[3].x;
f64 y3 = vertices[3].y;
- i64 i0 = (i64) floor(min3_(x0, x1, x2));
- i64 j0 = (i64) floor(min3_(y0, y1, y2));
- i64 i1 = (i64) ceil (max3_(x0, x1, x2));
- i64 j1 = (i64) ceil (max3_(y0, y1, y2));
+ i64 i0 = (i64) floor(min4_(x0, x1, x2, x3));
+ i64 j0 = (i64) floor(min4_(y0, y1, y2, y3));
+ i64 i1 = (i64) ceil (max4_(x0, x1, x2, x3));
+ i64 j1 = (i64) ceil (max4_(y0, y1, y2, y3));
if (i0 < 0) i0 = 0;
if (i1 >= dst.width) i1 = dst.width - 1;
@@ -477,7 +537,7 @@ void fill_ellipse_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area) {
}
}
-b8 quad_from_line_(vec2 *dst, vec2 vertices[2], f64 width) {
+static b8 quad_from_line_(vec2 *dst, vec2 vertices[2], f64 width) {
f64 x0 = vertices[0].x;
f64 y0 = vertices[0].y;
f64 x1 = vertices[1].x;
@@ -511,6 +571,8 @@ void fill_line_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64
vec2 quad[4];
if (!quad_from_line_(quad, vertices, width)) return;
fill_quad_to_buffer(dst, color, quad);
+
+ // TODO: Also render line ends as circles.
}
static i64 char_column_offset_(c32 c, i64 column_index) {
@@ -721,6 +783,17 @@ static void draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64 sca
}
}
+#if !defined(GRAPHICS_ENABLE_EXTERNAL_FONT)
+Box external_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text) {
+ // TODO
+ return (Box) {0};
+}
+
+void external_font_render_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, vec2 position, vec2 scale, i64 num_chars, c32 *text) {
+ // TODO
+}
+#endif // !defined(GRAPHICS_ENABLE_EXTERNAL_FONT)
+
void draw_text_area_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text) {
if (text == NULL || num_chars <= 0 || max_size.x < EPSILON || max_size.y < EPSILON)
return;
@@ -743,7 +816,7 @@ void draw_text_area_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area, vec2 m
}
void draw_text_cursor_to_buffer(Pixel_Buffer dst, vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection) {
- if (max_size.x< EPSILON || max_size.y < EPSILON)
+ if (max_size.x < EPSILON || max_size.y < EPSILON)
return;
i64 num_columns = enum_text_columns_(num_chars, text);
@@ -1070,9 +1143,6 @@ b8 hit_rectangle(Box area, vec2 point) {
}
b8 hit_triangle(vec2 vertices[3], vec2 point) {
- // Z-components of cross-products
- //
-
f64 x0 = vertices[0].x;
f64 y0 = vertices[0].y;
f64 x1 = vertices[1].x;
@@ -1082,6 +1152,12 @@ b8 hit_triangle(vec2 vertices[3], vec2 point) {
f64 px = point.x;
f64 py = point.y;
+ if ( (px < x0 && px < x1 && px < x2)
+ || (px > x0 && px > x1 && px > x2)
+ || (py < y0 && py < y1 && py < y2)
+ || (py > y0 && py > y1 && py > y2) )
+ return 0;
+
f64 x10 = x1 - x0;
f64 x21 = x2 - x1;
f64 x02 = x0 - x2;
@@ -1090,6 +1166,8 @@ b8 hit_triangle(vec2 vertices[3], vec2 point) {
f64 y21 = y2 - y1;
f64 y02 = y0 - y2;
+ // Z-components of cross-products
+
f64 z0 = x02 * y10 - x10 * y02;
f64 z1 = x10 * y21 - x21 * y10;
f64 z2 = x21 * y02 - x02 * y21;
@@ -1164,7 +1242,7 @@ b8 hit_line(vec2 vertices[2], f64 width, vec2 point) {
}, point);
}
-void draw_pixels(Box area, Pixel_Buffer src) {
+void draw_pixels_cached(Box area, Pixel_Buffer src) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1177,7 +1255,7 @@ void draw_pixels(Box area, Pixel_Buffer src) {
);
}
-void fill_rectangle(vec4_f32 color, Box area) {
+void fill_rectangle_cached(vec4_f32 color, Box area) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1190,7 +1268,7 @@ void fill_rectangle(vec4_f32 color, Box area) {
);
}
-void fill_triangle(vec4_f32 color, vec2 vertices[3]) {
+void fill_triangle_cached(vec4_f32 color, vec2 vertices[3]) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1207,7 +1285,22 @@ void fill_triangle(vec4_f32 color, vec2 vertices[3]) {
);
}
-void fill_quad(vec4_f32 color, vec2 vertices[4]) {
+void fill_triangles_cached(vec4_f32 color, Box area, i64 num_triangles, vec2 *vertices) {
+ perform_graphics_request(
+ (Graphics_Context) {0},
+ (Graphics_Request) {
+ .op = GRAPHICS_FILL_TRIANGLES,
+ .triangles = {
+ .color = color,
+ .area = area,
+ .num_triangles = num_triangles,
+ .vertices = vertices,
+ },
+ }
+ );
+}
+
+void fill_quad_cached(vec4_f32 color, vec2 vertices[4]) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1225,7 +1318,7 @@ void fill_quad(vec4_f32 color, vec2 vertices[4]) {
);
}
-void fill_ellipse(vec4_f32 color, Box area) {
+void fill_ellipse_cached(vec4_f32 color, Box area) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1238,7 +1331,7 @@ void fill_ellipse(vec4_f32 color, Box area) {
);
}
-void fill_line(vec4_f32 color, vec2 vertices[2], f64 width) {
+void fill_line_cached(vec4_f32 color, vec2 vertices[2], f64 width) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1252,7 +1345,7 @@ void fill_line(vec4_f32 color, vec2 vertices[2], f64 width) {
);
}
-void draw_text_area(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text) {
+void draw_text_area_cached(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1268,7 +1361,7 @@ void draw_text_area(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32
);
}
-void draw_text_cursor(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection) {
+void draw_text_cursor_cached(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection) {
perform_graphics_request(
(Graphics_Context) {0},
(Graphics_Request) {
@@ -1286,9 +1379,9 @@ void draw_text_cursor(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c3
);
}
-void draw_pixels_quick(Box area, Pixel_Buffer src) {
+void draw_pixels(Box area, Pixel_Buffer src) {
perform_graphics_request(
- (Graphics_Context) { .disable_antialiasing = 1, },
+ (Graphics_Context) { .disable_cache = 1, },
(Graphics_Request) {
.op = GRAPHICS_DRAW_PIXELS,
.pixels = {
@@ -1299,9 +1392,9 @@ void draw_pixels_quick(Box area, Pixel_Buffer src) {
);
}
-void fill_rectangle_quick(vec4_f32 color, Box area) {
+void fill_rectangle(vec4_f32 color, Box area) {
perform_graphics_request(
- (Graphics_Context) { .disable_antialiasing = 1, },
+ (Graphics_Context) { .disable_cache = 1, },
(Graphics_Request) {
.op = GRAPHICS_FILL_RECTANGLE,
.rectangle = {
@@ -1312,6 +1405,117 @@ void fill_rectangle_quick(vec4_f32 color, Box area) {
);
}
+void fill_triangle(vec4_f32 color, vec2 vertices[3]) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_cache = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_FILL_TRIANGLE,
+ .triangle = {
+ .color = color,
+ .vertices = {
+ vertices[0],
+ vertices[1],
+ vertices[2],
+ },
+ },
+ }
+ );
+}
+
+void fill_triangles(vec4_f32 color, Box area, i64 num_triangles, vec2 *vertices) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_cache = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_FILL_TRIANGLES,
+ .triangles = {
+ .color = color,
+ .area = area,
+ .num_triangles = num_triangles,
+ .vertices = vertices,
+ },
+ }
+ );
+}
+
+void fill_quad(vec4_f32 color, vec2 vertices[4]) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_cache = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_FILL_QUAD,
+ .quad = {
+ .color = color,
+ .vertices = {
+ vertices[0],
+ vertices[1],
+ vertices[2],
+ vertices[3],
+ },
+ },
+ }
+ );
+}
+
+void fill_ellipse(vec4_f32 color, Box area) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_cache = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_FILL_ELLIPSE,
+ .ellipse = {
+ .color = color,
+ .area = area,
+ },
+ }
+ );
+}
+
+void fill_line(vec4_f32 color, vec2 vertices[2], f64 width) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_cache = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_FILL_LINE,
+ .line = {
+ .color = color,
+ .vertices = { vertices[0], vertices[1], },
+ .width = width,
+ },
+ }
+ );
+}
+
+void draw_text_area(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_cache = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_DRAW_TEXT_AREA,
+ .text_area = {
+ .color = color,
+ .area = area,
+ .max_size = max_size,
+ .num_chars = num_chars,
+ .text = text,
+ },
+ }
+ );
+}
+
+void draw_text_cursor(vec4_f32 color, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection) {
+ perform_graphics_request(
+ (Graphics_Context) { .disable_cache = 1, },
+ (Graphics_Request) {
+ .op = GRAPHICS_DRAW_TEXT_CURSOR,
+ .text_cursor = {
+ .color = color,
+ .area = area,
+ .max_size = max_size,
+ .num_chars = num_chars,
+ .text = text,
+ .cursor = cursor,
+ .selection = selection,
+ },
+ }
+ );
+}
+
// ================================================================
typedef struct {
@@ -1343,6 +1547,13 @@ static i64 _graphics_cache_gc_index = 0;
static void graphics_request_hash_(Blake2b_State *S, Graphics_Request req) {
switch (req.op) {
+ case GRAPHICS_FILL_TRIANGLES:
+ blake2b_update(S, (u8 *) &req.triangles.color, sizeof req.triangles.color);
+ blake2b_update(S, (u8 *) &req.triangles.area, sizeof req.triangles.area);
+ blake2b_update(S, (u8 *) &req.triangles.num_triangles, sizeof req.triangles.num_triangles);
+ blake2b_update(S, (u8 *) req.triangles.vertices, req.triangles.num_triangles * 3 * sizeof *req.triangles.vertices);
+ break;
+
case GRAPHICS_DRAW_PIXELS:
blake2b_update(S, (u8 *) &req.pixels.area, sizeof req.pixels.area);
blake2b_update(S, (u8 *) &req.pixels.src.width, sizeof req.pixels.src.width);
@@ -1423,6 +1634,13 @@ static Graphics_Request graphics_request_scaled_(Graphics_Request req, vec2 scal
scaled.triangle.vertices[2].y *= scale.y;
break;
+ case GRAPHICS_FILL_TRIANGLES:
+ scaled.triangles.area.x *= scale.x;
+ scaled.triangles.area.y *= scale.y;
+ scaled.triangles.area.width *= scale.x;
+ scaled.triangles.area.height *= scale.y;
+ break;
+
case GRAPHICS_FILL_QUAD:
scaled.quad.vertices[0].x *= scale.x;
scaled.quad.vertices[0].y *= scale.y;
@@ -1511,6 +1729,11 @@ static Graphics_Request graphics_request_moved_(Graphics_Request req, vec2 offse
moved.triangle.vertices[2].y += offset.y;
break;
+ case GRAPHICS_FILL_TRIANGLES:
+ moved.triangles.area.x += offset.x;
+ moved.triangles.area.y += offset.y;
+ break;
+
case GRAPHICS_FILL_QUAD:
moved.quad.vertices[0].x += offset.x;
moved.quad.vertices[0].y += offset.y;
@@ -1568,6 +1791,18 @@ static Box graphics_request_area_(Graphics_Request req) {
area.height = max3_(v[0].y, v[1].y, v[2].y) - area.y;
} break;
+ case GRAPHICS_FILL_TRIANGLES:
+ if (req.triangles.num_triangles > 0) {
+ f64 x0, y0, x1, y1;
+ triangles_area_(&x0, &y0, &x1, &y1, req.triangles.num_triangles, req.triangles.vertices);
+
+ area.x = x0;
+ area.y = y0;
+ area.width = x1 - x0;
+ area.height = y1 - y0;
+ }
+ break;
+
case GRAPHICS_FILL_QUAD:
case GRAPHICS_FILL_LINE: {
vec2 quad[4] = {0};
@@ -1635,6 +1870,16 @@ static void perform_graphics_request_to_buffer_(Pixel_Buffer dst, Graphics_Reque
);
break;
+ case GRAPHICS_FILL_TRIANGLES:
+ fill_triangles_to_buffer(
+ dst,
+ req.triangles.color,
+ req.triangles.area,
+ req.triangles.num_triangles,
+ req.triangles.vertices
+ );
+ break;
+
case GRAPHICS_FILL_QUAD:
fill_quad_to_buffer(
dst,
@@ -1738,7 +1983,7 @@ static void perform_graphics_request_cached_(Pixel_Buffer dst, Graphics_Request
}
if (slot == -1) {
- LOG_ERROR("Graphics cache collision.");
+ LOG_ERROR("Graphics cache overflow.");
slot = n;
}
@@ -1747,11 +1992,11 @@ static void perform_graphics_request_cached_(Pixel_Buffer dst, Graphics_Request
i64 num_pixels = norm.width * norm.height;
if (reset_slot) {
- s->occupied = 1;
- s->id = id;
- s->req = norm.req;
- s->width = norm.width;
- s->height = norm.height;
+ s->occupied = 1;
+ s->id = id;
+ s->req = norm.req;
+ s->width = norm.width;
+ s->height = norm.height;
if (num_pixels > s->buffer_len)
resize_dynamic_array_exact(&s->buffer_len, (void **) &s->buffer, sizeof *s->buffer, num_pixels);
@@ -1784,7 +2029,7 @@ static void perform_graphics_request_cached_(Pixel_Buffer dst, Graphics_Request
static void scale_and_perform_graphics_request_(Graphics_Context context, Graphics_Request req) {
Graphics_Request scaled = graphics_request_scaled_(req, context.scale);
- if (context.disable_antialiasing)
+ if (context.disable_cache)
perform_graphics_request_to_buffer_(context.dst, scaled);
else
perform_graphics_request_cached_(context.dst, scaled);