Author | Jakob Wakeling <[email protected]> |
Date | 2024-03-11 09:03:59 |
Commit | e9e986772888d197ec72f63cf80d0a7778702ca9 |
Parent | 0db58180873b28d900e54d2db51566fcc05ab1d5 |
Improve circle drawing algorithm
Diffstat
M | examples/draw.c | | | 10 | +++++----- |
M | src/draw.c | | | 47 | ++++++++++++++++++++++++++++++++++++----------- |
M | src/draw.h | | | 12 | +++++++----- |
A | src/util/util.h | | | 12 | ++++++++++++ |
M | src/x11/draw.c | | | 28 | +++++++++++++++++++++++++--- |
5 files changed, 85 insertions, 24 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; }