libdraw

Minimal window and drawing library
git clone http://git.omkov.net/libdraw
Log | Tree | Refs | Download

AuthorJakob Wakeling <[email protected]>
Date2024-03-11 09:03:59
Commite9e986772888d197ec72f63cf80d0a7778702ca9
Parent0db58180873b28d900e54d2db51566fcc05ab1d5

Improve circle drawing algorithm

Diffstat

M examples/draw.c | 10 +++++-----
M src/draw.c | 48 +++++++++++++++++++++++++++++++++++++++---------
M src/draw.h | 12 +++++++-----
A src/util/util.h | 12 ++++++++++++
M src/x11/draw.c | 30 ++++++++++++++++++++++++++++--

5 files changed, 91 insertions, 21 deletions

diff --git a/examples/draw.c b/examples/draw.c
index 47b7289..a434f25 100644
--- a/examples/draw.c
+++ b/examples/draw.c
@@ -5,7 +5,7 @@ int main() {
 	draw_window *window; int32_t w = 800, h = 600;
 	if (draw_window_init(&window, w, h, "Draw", 0) == -1) { return -1; }
 
-	bool left = false, right = false, draw = false;
+	bool left = false, right = false;
 	uint32_t colour = 0xffff0000;
 
 	draw_buffer *buf = draw_buffer_init(w, h);
@@ -27,12 +27,12 @@ int main() {
 			if (e.mouse.direction == DRAW_RELEASE) { right = false; }
 		}
 
-		if (left) { draw_buffer_rect(buf, (point){e.mouse.x, e.mouse.y}, (rect){-5, -5, 6, 6}, colour); }
-		if (right) { draw_buffer_rect(buf, (point){e.mouse.x, e.mouse.y}, (rect){-5, -5, 6, 6}, 0xff000000); }
-		if ((left || right) && !draw) { goto redraw; /*draw_window_repaint(window);*/ }
+		if (left) { draw_buffer_circle(buf, (point){e.mouse.x, e.mouse.y}, 6, colour, true); }
+		if (right) { draw_buffer_circle(buf, (point){e.mouse.x, e.mouse.y}, 6, 0xff000000, true); }
+		if (left || right) { goto redraw; }
 	} break;
 	case DRAW_EVENT_PAINT: redraw: {
-		draw_window_draw(window, (point){0, 0}, buf, buf->bounds); draw = false;
+		draw_window_draw(window, (point){0, 0}, buf, buf->bounds);
 	} break;
 	case DRAW_EVENT_RESIZE: {
 		w = e.resize.w; h = e.resize.h;
diff --git a/src/draw.c b/src/draw.c
index f236fb5..7e135fa 100644
--- a/src/draw.c
+++ b/src/draw.c
@@ -2,10 +2,15 @@
 // All rights reserved.
 
 #include "draw.h"
+#include "util/util.h"
 
 #include <stdlib.h>
 
-bool draw_debug = false;
+#define SET(B, X, Y, C) (B->buf[((Y) * B->bounds.w) + (X)] = C)
+#define IS_BOUND(B, X, Y) ((X) >= (B)->bounds.x && (X) < (B)->bounds.w && (Y) >= (B)->bounds.y && (Y) < (B)->bounds.h)
+#define SET_BOUND(B, X, Y, C) (IS_BOUND(B, X, Y) ? SET(B, X, Y, C) : 0)
+
+bool draw_debug = false, draw_debug_verbose = false;
 
 draw_buffer *draw_buffer_init(int32_t w, int32_t h) {
 	draw_buffer *buf = calloc(1, sizeof(draw_buffer)); if (buf == NULL) { return NULL; }
@@ -30,19 +35,39 @@ void draw_buffer_clear(draw_buffer *buf, uint32_t colour) {
 }
 
 void draw_buffer_rect(draw_buffer *buf, point p, rect r, uint32_t colour) {
-	for (int32_t x = p.x + r.x; x < p.x + r.w; x += 1) for (int32_t y = p.y + r.y; y < p.y + r.h; y += 1) {
-		if (x >= 0 && x < buf->bounds.w && y >= 0 && y < buf->bounds.h) {
-			buf->buf[(y * buf->bounds.w) + x] = colour;
-		}
-	}
+	int32_t x0 = CLAMP(p.x + r.x, buf->bounds.x, buf->bounds.w), y0 = CLAMP(p.y + r.y, buf->bounds.y, buf->bounds.h);
+	int32_t x1 = CLAMP(p.x + r.w, buf->bounds.x, buf->bounds.w), y1 = CLAMP(p.y + r.h, buf->bounds.y, buf->bounds.h);
+	for (; x0 < x1; x0 += 1) for (int32_t y = y0; y < y1; y += 1) { buf->buf[(y * buf->bounds.w) + x0] = colour; }
 }
 
-void draw_buffer_circle(draw_buffer *buf, point p, int32_t r, uint32_t colour) {
-	for (int32_t x = -r; x < r; x += 1) for (int32_t y = -r; y < r; y += 1) {
-		if ((x * x) + (y * y) < (r * r)) {
-			if (p.x + x >= 0 && p.x + x < buf->bounds.w && p.y + y >= 0 && p.y + y < buf->bounds.h) {
-				buf->buf[((p.y + y) * buf->bounds.w) + (p.x + x)] = colour;
+void draw_buffer_circle(draw_buffer *buf, point p, int32_t r, uint32_t colour, bool fill) {
+	int32_t x = 0, y = r, d = 3 - (2 * r);
+
+	while (x <= y) {
+		if (fill) {
+			for (int32_t i = -x; i <= x; i++) {
+				SET_BOUND(buf, p.x + i, p.y + y, colour);
+				SET_BOUND(buf, p.x + i, p.y - y, colour);
+			}
+
+			for (int32_t i = -y; i <= y; i++) {
+				SET_BOUND(buf, p.x + i, p.y + x, colour);
+				SET_BOUND(buf, p.x + i, p.y - x, colour);
 			}
 		}
+		else {
+			SET_BOUND(buf, p.x + x, p.y + y, colour);
+			SET_BOUND(buf, p.x + x, p.y - y, colour);
+			SET_BOUND(buf, p.x - x, p.y + y, colour);
+			SET_BOUND(buf, p.x - x, p.y - y, colour);
+			SET_BOUND(buf, p.x + y, p.y + x, colour);
+			SET_BOUND(buf, p.x + y, p.y - x, colour);
+			SET_BOUND(buf, p.x - y, p.y + x, colour);
+			SET_BOUND(buf, p.x - y, p.y - x, colour);
+		}
+
+		x += 1;
+		if (d < 0) { d += (4 * x) + 6; }
+		else { d += (4 * (x - y)) + 10; y -= 1; }
 	}
 }
diff --git a/src/draw.h b/src/draw.h
index c7777d6..7adbe23 100644
--- a/src/draw.h
+++ b/src/draw.h
@@ -1,8 +1,8 @@
 // Copyright (C) 2024, Jakob Wakeling
 // All rights reserved.
 
-#ifndef DRAW_DRAW_H_RNC24Y6G
-#define DRAW_DRAW_H_RNC24Y6G
+#ifndef LIBDRAW_DRAW_H_RNC24Y6G
+#define LIBDRAW_DRAW_H_RNC24Y6G
 
 #include "keys.h"
 
@@ -38,6 +38,8 @@ typedef enum {
 	DRAW_EVENT_PAINT,
 	DRAW_EVENT_MOVE,
 	DRAW_EVENT_RESIZE,
+	DRAW_EVENT_HIDE,
+	DRAW_EVENT_SHOW,
 } draw_event_k;
 
 typedef enum { DRAW_PRESS, DRAW_RELEASE, DRAW_REPEAT } draw_direction;
@@ -64,7 +66,7 @@ typedef struct { int32_t x, y; } point;
 typedef struct { int32_t x, y, w, h; } rect;
 typedef struct { rect bounds; uint32_t *buf; } draw_buffer;
 
-extern bool draw_debug;
+extern bool draw_debug, draw_debug_verbose;
 
 extern int draw_window_init(draw_window **window, int32_t w, int32_t h, const char *title, uint32_t flags);
 extern void draw_window_free(draw_window **window);
@@ -82,6 +84,6 @@ extern void draw_buffer_copy(draw_buffer *dst, draw_buffer *src, point dp, rect
 
 extern void draw_buffer_clear(draw_buffer *buf, uint32_t colour);
 extern void draw_buffer_rect(draw_buffer *buf, point p, rect r, uint32_t colour);
-extern void draw_buffer_circle(draw_buffer *buf, point p, int32_t r, uint32_t colour);
+extern void draw_buffer_circle(draw_buffer *buf, point p, int32_t r, uint32_t colour, bool fill);
 
-#endif // DRAW_DRAW_H_RNC24Y6G
+#endif // LIBDRAW_DRAW_H_RNC24Y6G
diff --git a/src/util/util.h b/src/util/util.h
new file mode 100644
index 0000000..3f622fd
--- /dev/null
+++ b/src/util/util.h
@@ -0,0 +1,12 @@
+// Copyright (C) 2024, Jakob Wakeling
+// All rights reserved.
+
+#ifndef LIBDRAW_UTIL_UTIL_H_9VY05T8I
+#define LIBDRAW_UTIL_UTIL_H_9VY05T8I
+
+#define MIN(a, b) ((a < b) ? a : b)
+#define MAX(a, b) ((a > b) ? a : b)
+#define CLAMP(x, a, b) (MIN(MAX(x, a), b))
+#define IS_CLAMPED(x, a, b) ((x) >= (a) && (x) <= (b))
+
+#endif // LIBDRAW_UTIL_UTIL_H_9VY05T8I
diff --git a/src/x11/draw.c b/src/x11/draw.c
index d1607db..6d52c17 100644
--- a/src/x11/draw.c
+++ b/src/x11/draw.c
@@ -49,7 +49,7 @@ int draw_window_init(draw_window **window, int32_t w, int32_t h, const char *tit
 
 	uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
 	uint32_t values[4] = {
-		0xff000000, 0,
+		0x00000000, 0x00000000,
 		XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS |
 		XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
 		XCB_EVENT_MASK_BUTTON_MOTION
@@ -135,18 +135,24 @@ draw_event draw_window_event(draw_window *window) {
 	switch (_e->response_type  & ~0x80) {
 		case XCB_KEY_PRESS: {
 			xcb_key_press_event_t *e = (xcb_key_press_event_t *)_e;
+			if (draw_debug_verbose) { printf("KeyPress (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
+
 			event = (draw_event){ .kind = DRAW_EVENT_KEY, .key = {
 				.code = keymap_find(window, e->detail), .modifiers = mod(e->state), .direction = DRAW_PRESS,
 			}};
 		} break;
 		case XCB_KEY_RELEASE: {
 			xcb_key_release_event_t *e = (xcb_key_release_event_t *)_e;
+			if (draw_debug_verbose) { printf("KeyRelease (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
+
 			event = (draw_event){ .kind = DRAW_EVENT_KEY, .key = {
 				.code = keymap_find(window, e->detail), .modifiers = mod(e->state), .direction = DRAW_RELEASE,
 			}};
 		} break;
 		case XCB_BUTTON_PRESS: {
 			xcb_button_press_event_t *e = (xcb_button_press_event_t *)_e;
+			if (draw_debug_verbose) { printf("ButtonPress (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
+
 			event = (draw_event){ .kind = DRAW_EVENT_MOUSE, .mouse = {
 				.x = e->event_x, .y = e->event_y, .button = e->detail, .modifiers = mod(e->state),
 				.direction = DRAW_PRESS,
@@ -154,6 +160,8 @@ draw_event draw_window_event(draw_window *window) {
 		} break;
 		case XCB_BUTTON_RELEASE: {
 			xcb_button_release_event_t *e = (xcb_button_release_event_t *)_e;
+			if (draw_debug_verbose) { printf("ButtonRelease (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
+
 			event = (draw_event){ .kind = DRAW_EVENT_MOUSE, .mouse = {
 				.x = e->event_x, .y = e->event_y, .button = e->detail, .modifiers = mod(e->state),
 				.direction = DRAW_RELEASE,
@@ -161,6 +169,8 @@ draw_event draw_window_event(draw_window *window) {
 		} break;
 		case XCB_MOTION_NOTIFY: {
 			xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *)_e;
+			if (draw_debug_verbose) { printf("MotionNotify (%d, %d)\n", e->event_x, e->event_y); }
+
 			event = (draw_event){ .kind = DRAW_EVENT_MOUSE, .mouse = {
 				.x = e->event_x, .y = e->event_y, .button = e->detail, .modifiers = mod(e->state),
 				.direction = DRAW_REPEAT,
@@ -168,22 +178,34 @@ draw_event draw_window_event(draw_window *window) {
 		} break;
 		case XCB_EXPOSE: {
 			xcb_expose_event_t *e = (xcb_expose_event_t *)_e;
+			if (draw_debug) { printf("Expose (%d, %d, %d, %d)\n", e->x, e->y, e->width, e->height); }
+
 			event = (draw_event){ .kind = DRAW_EVENT_PAINT, .paint = { e->x, e->y, e->width, e->height }};
-			if (draw_debug) { printf("Exposed %d, %d, %d, %d\n", e->x, e->y, e->width, e->height); }
+		} break;
+		case XCB_UNMAP_NOTIFY: {
+			xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *)_e;
+			if (draw_debug) { printf("UnmapNotify\n"); }
+			event = (draw_event){ .kind = DRAW_EVENT_HIDE };
+		} break;
+		case XCB_MAP_NOTIFY: {
+			xcb_map_notify_event_t *e = (xcb_map_notify_event_t *)_e;
+			if (draw_debug) { printf("MapNotify\n"); }
+			event = (draw_event){ .kind = DRAW_EVENT_SHOW };
 		} break;
 		case XCB_CONFIGURE_NOTIFY: {
 			xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *)_e;
+			if (draw_debug) { printf("ConfigureNotify (%d, %d, %d, %d)\n", e->x, e->y, e->width, e->height); }
 
 			if (e->width != window->w || e->height != window->h) {
 				window->w = e->width; window->h = e->height;
 				event = (draw_event){ .kind = DRAW_EVENT_RESIZE, .resize = { e->width, e->height }};
-				if (draw_debug) { printf("Resized to %d, %d\n", e->width, e->height); }
 			}
 		} break;
 		case XCB_CLIENT_MESSAGE: {
 			xcb_client_message_event_t *e = (xcb_client_message_event_t *)_e;
 			switch (e->type) {
 			case ATOM_REPAINT: {
+				if (draw_debug) { printf("ClientMessage (REPAINT)\n"); }
 				event = (draw_event){ .kind = DRAW_EVENT_PAINT, .paint = { 0, 0, window->w, window->h }};
 			} break;
 			}