diff options
author | quou <quou@disroot.org> | 2024-06-30 18:24:01 +1000 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-06-30 18:27:11 +1000 |
commit | 39100e7292d3ee12d387fddfa0f0d7b712e31e1c (patch) | |
tree | 28f26b19de857868aeb9ecf23a7fecfc170addf9 |
initial commit.
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | 3de.c | 45 | ||||
-rw-r--r-- | Makefile | 32 | ||||
-rw-r--r-- | config.h | 7 | ||||
-rw-r--r-- | error.h | 11 | ||||
-rw-r--r-- | maths.c | 115 | ||||
-rw-r--r-- | maths.h | 17 | ||||
-rw-r--r-- | memory.c | 222 | ||||
-rw-r--r-- | memory.h | 56 | ||||
-rw-r--r-- | plat.c | 413 | ||||
-rw-r--r-- | plat.h | 58 | ||||
-rw-r--r-- | rect.c | 46 | ||||
-rw-r--r-- | rect.h | 11 | ||||
-rw-r--r-- | render.c | 207 | ||||
-rw-r--r-- | render.h | 48 |
15 files changed, 1292 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60455eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +tags +*.d +*.o +3de @@ -0,0 +1,45 @@ +#include "config.h" +#include "memory.h" +#include "plat.h" +#include "render.h" + +#include <stdio.h> + +int entrypoint(int argc, const char** argv, Arena* a) { + App* app; + Renderer r; + Heap h; + FPS f; + char buf[32]; + Colour blue = make_aliceblue(); + (void)argc; + (void)argv; + init_heap( + &h, + arena_alloc(a, app_memory_size), + app_memory_size + ); + app = new_app(&h, 640, 480, "3D Engine"); + if (!app) return app->err; + init_fps(&f, 20); + while (app->o) { + fps_begin(&f); + while (f.now >= f.next) { + app_begin(app); + ren_begin(&r, app->fb, app->w, app->h); + ren_clear(&r); + sprintf(buf, "FPS: %d", app->fps); + ren_text(&r, blue, 3, 3, buf); + sprintf(buf, "CAP: %d", f.fps); + ren_text(&r, blue, 3, 13, buf); + sprintf(buf, "MOUSE: %d, %d", app->mx, app->my); + ren_text(&r, blue, 3, 23, buf); + ren_end(&r); + app_end(app); + fps_update(&f); + } + fps_end(&f); + } + deinit_app(app); + return 0; +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8b774cd --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +compiler = gcc +linker = gcc +cflags = -I./ -g -DDEBUG -Dplat_x11 -Dplat_x86 \ +-Dplat_posix -Dallocation_default_alignment=8 \ +-Wall -Wextra -pedantic -std=c90 +lflags = -lX11 +target = 3de + +sources = \ + 3de.c \ + maths.c \ + memory.c \ + plat.c \ + rect.c \ + render.c + +objects = $(sources:%.c=%.o) + +all: $(target) + +$(objects): %.o : %.c + $(compiler) -MMD -MF $(basename $@).d $(cflags) -o $@ -c $< + +$(target): $(objects) + $(linker) $(objects) -o $@ $(lflags) + +clean: + rm *.d + rm *.o + rm $(target) + +-include $(sources:%.c=%.d) diff --git a/config.h b/config.h new file mode 100644 index 0000000..96094dd --- /dev/null +++ b/config.h @@ -0,0 +1,7 @@ +#ifndef config_h +#define config_h + +#define memory_size (1024 * 1024 * 32) +#define app_memory_size (1024 * 1024 * 16) + +#endif @@ -0,0 +1,11 @@ +#ifndef error_h +#define error_h + +typedef enum { + error_none, + error_assertion_failed, + error_platform_error, + error_out_of_memory +} Error; + +#endif @@ -0,0 +1,115 @@ +#include "maths.h" + +#define sin_table_count 256 + +static const int sin_table[sin_table_count] = { + 0, 25, 50, 75, + 100, 125, 150, 175, + 200, 224, 249, 273, + 297, 321, 345, 369, + 392, 415, 438, 460, + 483, 505, 526, 548, + 569, 590, 610, 630, + 650, 669, 688, 706, + 724, 742, 759, 775, + 792, 807, 822, 837, + 851, 865, 878, 891, + 903, 915, 926, 936, + 946, 955, 964, 972, + 980, 987, 993, 999, + 1004, 1009, 1013, 1016, + 1019, 1021, 1023, 1024, + 1024, 1024, 1023, 1021, + 1019, 1016, 1013, 1009, + 1004, 999, 993, 987, + 980, 972, 964, 955, + 946, 936, 926, 915, + 903, 891, 878, 865, + 851, 837, 822, 807, + 792, 775, 759, 742, + 724, 706, 688, 669, + 650, 630, 610, 590, + 569, 548, 526, 505, + 483, 460, 438, 415, + 392, 369, 345, 321, + 297, 273, 249, 224, + 200, 175, 150, 125, + 100, 75, 50, 25, + 0, -25, -50, -75, + -100, -125, -150, -175, + -200, -224, -249, -273, + -297, -321, -345, -369, + -392, -415, -438, -460, + -483, -505, -526, -548, + -569, -590, -610, -630, + -650, -669, -688, -706, + -724, -742, -759, -775, + -792, -807, -822, -837, + -851, -865, -878, -891, + -903, -915, -926, -936, + -946, -955, -964, -972, + -980, -987, -993, -999, + -1004, -1009, -1013, -1016, + -1019, -1021, -1023, -1024, + -1024, -1024, -1023, -1021, + -1019, -1016, -1013, -1009, + -1004, -999, -993, -987, + -980, -972, -964, -955, + -946, -936, -926, -915, + -903, -891, -878, -865, + -851, -837, -822, -807, + -792, -775, -759, -742, + -724, -706, -688, -669, + -650, -630, -610, -590, + -569, -548, -526, -505, + -483, -460, -438, -415, + -392, -369, -345, -321, + -297, -273, -249, -224, + -200, -175, -150, -125, + -100, -75, -50, -25 +}; + +int fsqrt(int n) { + int lo, hi, mid, i, mm; + if (n <= 0) { return -1; } + lo = mini(1, n); + hi = maxi(1, n); + while ((100 << fbits * lo * lo) >> fbits * 2) + lo *= (10 << fbits) >> fbits; + while ((5 * hi * hi) >> fbits * 2 > n) + hi *= (1 << fbits) / 10; + for (i = 0; i < 100; i++) { + mid = ((lo + hi) * (1 << fbits)) / (2 << fbits); + mm = mid * mid >> fbits; + if (mm == n) return mid; + if (mm > n) hi = mid; + else lo = mid; + } + return mid; +} + +int absolute(int x) { + return x >= 0 ? x : -x; +} + +int fsin(int t) { + int index, sign; + sign = t >= 0 ? 1 : -1; + t = absolute(t); + index = (t * (sin_table_count) / ((1 << fbits) * 2)); + index = index % sin_table_count; + return (sign * sin_table[index]) / 2; +} + +int fcos(int t) { + return fsin((1 << fbits) / 2 - t); +} + +int ftan(int t) { + return (fsin(t) << fbits) / fcos(t); +} + +int flerp(int a, int b, int t) { + return a + ((t * (b - a)) >> fbits); +} + @@ -0,0 +1,17 @@ +#ifndef maths_h +#define maths_h + +#define mini(a_, b_) ((a_) < (b_) ? (a_) : (b_)) +#define maxi(a_, b_) ((a_) > (b_) ? (a_) : (b_)) +#define clamp(v_, min_, max_) (maxi(min_, mini(max_, v_))) + +#define fbits 9 + +int absolute(int x); +int fsin(int t); +int fcos(int t); +int ftan(int t); +int flerp(int a, int b, int t); +int fsqrt(int v); + +#endif diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..2d7e566 --- /dev/null +++ b/memory.c @@ -0,0 +1,222 @@ +#include "memory.h" +#include "plat.h" +#include <stdint.h> +#include <stddef.h> + +int aligned(void* p, int a) { + return (uintptr_t)p % a == 0; +} + +int align_size(int s, int a) { + return (s + (a - 1)) & -a; +} + +static uintptr_t align_address( + uintptr_t ad, + size_t al +) { + size_t m; + m = al - 1; + return (ad + m) & ~m; +} + +void init_arena( + Arena* a, + void* mem, + int size +) { + a->buf = mem; + a->size = size; + a->ptr = 0; +} + +void clear_arena(Arena* a) { + a->ptr = 0; +} + +void* imp_arena_alloc( + Arena* a, + int size +) { + assert(a->ptr + size < a->size); + return &a->buf[a->ptr += size]; +} + +void* arena_alloc( + Arena* a, + int size +) { + return arena_alloc_aligned( + a, + size, + allocation_default_alignment + ); +} + +void* arena_alloc_aligned( + Arena* a, + int size, + int align +) { + void* p; + p = imp_arena_alloc( + a, + size + ); + return (void*)align_address((uintptr_t)p, align); +} + +void init_heap( + Heap* h, + void* mem, + int size +) { + int* fb; + assert(aligned(mem, 4)); + assert(size > 8); + h->buf = mem; + h->size = size; + h->blocks = 1; + fb = (int*)h->buf; + fb[0] = size; +} + +void* imp2_heap_alloc( + Heap* h, + int size +) { + int o, i; + int hs = sizeof(int); + int as = align_size(size + hs, hs); + int f = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + if (as == bs) { + phdr[0] |= 1; + return phdr + 1; + } else { + int ns = bs - as; + if (ns > hs) { + int* nhdr = &phdr[ns / 4]; + phdr[0] = ns; + nhdr[0] = as | f; + h->blocks++; + return &nhdr[1]; + } + } + } else + o += bs; + } + return 0; +} + +void* imp_heap_alloc(Heap* h, int s) { + void* p = imp2_heap_alloc(h, s); + if (!p) { + heap_defrag(h); + p = imp2_heap_alloc(h, s); + } + return p; +} + +void imp_heap_free(Heap* h, void* p) { + assert((char*)p > h->buf); + assert((char*)p < h->buf + h->size); + (void)h; + ((int*)p)[-1] &= (unsigned)-1 >> 1; +} + +void* heap_alloc_aligned( + Heap* h, + int size, + int align +) { + unsigned char* p, * a; + ptrdiff_t shift; + size += (int)align; + p = imp_heap_alloc(h, size); + if (!p) { return 0; } + a = (unsigned char*)align_address((uintptr_t)p, align); + a += align * (unsigned)(p == a); + shift = a - p; + a[-1] = shift & 0xff; + return a; +} + +void heap_free_aligned(Heap* h, void* p) { + unsigned char* a; + ptrdiff_t shift; + a = p; + shift = a[-1]; + shift += 256 * shift == 0; + a -= shift; + imp_heap_free(h, a); +} + +void heap_defrag(Heap* h) { + int i, o, mtc; + int f = ~((unsigned)-1 >> 1); + for (i = o = mtc = 0; i < h->blocks; i++) { + int* phdr = (int*)&h->buf[o]; + int hdr = *phdr, bs, m, mc; + assert(aligned(phdr, sizeof hdr)); + bs = hdr & ~f; + if (~hdr & f) { + for ( + m = bs, mc = 0, i++; + i < h->blocks; + i++, mc++ + ) { + int mhdr = *(int*)&h->buf[o + m]; + if (~mhdr & f) + m += mhdr & ~f; + else + break; + } + i--; + bs = m; + phdr[0] = bs; + mtc += mc; + } + o += bs; + } + h->blocks -= mtc; +} + +void* heap_alloc( + Heap* h, + int size +) { + return heap_alloc_aligned( + h, + size, + allocation_default_alignment + ); +} + +void heap_free( + Heap* h, + void* p +) { + heap_free_aligned(h, p); +} + +/* +void print_blocks(Heap* h) { + int i, o; + int fb = ~((unsigned)-1 >> 1); + for (i = o = 0; i < h->blocks; i++) { + int b = *(int*)&h->buf[o]; + int bs = b & ~fb; + int f = ~b & fb; + printf("%s %d\n", f? "free": " ", bs); + o += bs; + } + assert(o == h->size); +} +*/ + diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..1dc5021 --- /dev/null +++ b/memory.h @@ -0,0 +1,56 @@ +#ifndef memory_h +#define memory_h + +int aligned(void* p, int a); +int align_size(int s, int a); + +typedef struct { + char* buf; + int size, ptr; +} Arena; + +void init_arena( + Arena* a, + void* mem, + int size +); +void clear_arena(Arena* a); +void* arena_alloc( + Arena* a, + int size +); +void* arena_alloc_aligned( + Arena* a, + int size, + int align +); + +typedef struct { + char* buf; + int size, blocks; +} Heap; + +void init_heap( + Heap* h, + void* mem, + int size +); +void* heap_alloc( + Heap* h, + int size +); +void* heap_alloc_aligned( + Heap* h, + int size, + int align +); +void heap_free_aligned( + Heap* h, + void* p +); +void heap_free( + Heap* h, + void* p +); +void heap_defrag(Heap* h); +#endif @@ -0,0 +1,413 @@ +#include "config.h" +#include "error.h" +#include "memory.h" +#include "plat.h" + +#ifdef plat_posix + +#define _POSIX_SOURCE +#define _GNU_SOURCE +#include <stdarg.h> +#include <stdio.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> + +extern int isatty(int); +extern int fileno(FILE*); + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +) { + if (!val) { + print_err( + "%d:%s: Assertion failed: %s.\n", + line, + file, + expr + ); + pbreak(error_assertion_failed); + return 0; + } + return 1; +} + +void print(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); +} + +void print_err(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;31m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void print_war(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[31;35m"); + } + + vfprintf(stderr, fmt, args); + + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[0m"); + } + + va_end(args); +} + +void pbreak(Error code) { +#if defined(DEBUG) && defined(plat_x86) + __asm__("int3;"); + (void)code; +#else + exit(code); +#endif +} + +static clockid_t global_clock; +static unsigned long global_freq; + +void init_timer(void) { + global_clock = CLOCK_REALTIME; + global_freq = 1000000000; +#if defined(_POSIX_MONOTONIC_CLOCK) + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + global_clock = CLOCK_MONOTONIC; + } +#endif +} + +unsigned long get_timer(void) { + struct timespec ts; + clock_gettime(global_clock, &ts); + return + (unsigned long)ts.tv_sec * global_freq + + (unsigned long)ts.tv_nsec; +} + +void sleep_ns(unsigned long ns) { + struct timespec t = { 0 }; + t.tv_nsec = ns; + nanosleep(&t, &t); +} + +void init_fps(FPS* f, int mpf) { + f->now = get_timer(); + f->next = f->now; + f->mpf = mpf; + f->pt = 0; + f->ct = -1; +} + +void fps_begin(FPS* f) { + f->now = get_timer(); +} + +void fps_end(FPS* f) { + unsigned long ts = f->next - f->now; + if (ts > 0) { + sleep_ns(ts); + } +} + +void fps_update(FPS* f) { + f->next += f->mpf * 1000000; + f->pt = f->ct; + f->ct = get_timer(); + f->fps = 1000000000 / (f->ct - f->pt); +} + +#include <stdlib.h> + +extern int entrypoint(int, const char**, Arena*); + +int main(int argc, const char** argv) { + Arena a; + void* mem = malloc(memory_size); + if (!mem) { + print_err("Out of memory.\n"); + return error_out_of_memory; + } + init_timer(); + init_arena( + &a, + mem, + memory_size + ); + return entrypoint(argc, argv, &a); +} + +#endif + +#ifdef plat_x11 + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <stdlib.h> + +typedef struct { + Display* d; + Window wi; + GC gc; + int w, h; + unsigned long begin, end; + XImage* bb; + Colour* bbp; + Atom wm_delete; +} App_Internal; + +void init_rendering( + App* a, + App_Internal* i, + XWindowAttributes* wa +) { + Colour* p, * fb; + int w = i->w = wa->width; + int h = i->h = wa->height; + int tw = (w + a->s) / a->s; + int th = (h + a->s) / a->s; + a->w = tw; + a->h = th; + if (i->bb) { + heap_free(a->heap, a->fb); + XDestroyImage(i->bb); + } + p = malloc(sizeof *p * w * h); + fb = heap_alloc(a->heap, sizeof *p * tw * th); + if (!p || !fb) { + print_err("Out of memory.\n"); + pbreak(error_out_of_memory); + } + a->fb = fb; + i->bb = XCreateImage( + i->d, + wa->visual, + wa->depth, + ZPixmap, + 0, + (char*)p, + w, + h, + 32, + w * sizeof *p + ); + if (!i->bb) { + print_err("Failed to create X11 backbuffer.\n"); + pbreak(error_platform_error); + } + i->bbp = p; +} + +void init_window(App* a, App_Internal* i, const char* n) { + Window root, w; + Display* d; + XWindowAttributes wa; + unsigned rm, bm; + i->bb = 0; + i->bbp = 0; + d = i->d; + root = DefaultRootWindow(d); + w = XCreateSimpleWindow( + d, + root, + 0, + 0, + a->w * a->s, + a->h * a->s, + 0, + WhitePixel(d, 0), + BlackPixel(d, 0) + ); + i->wi = w; + i->wm_delete = XInternAtom( + d, + "WM_DELETE_WINDOW", + 0 + ); + XSetWMProtocols(d, w, &i->wm_delete, 1); + XStoreName(d, w, n); + XSelectInput( + d, + w, + ExposureMask | + KeyPressMask | + KeyReleaseMask | + PointerMotionMask | + ButtonPressMask | + ButtonReleaseMask + ); + XMapRaised(d, w); + i->gc = XCreateGC(d, w, 0, 0); + if (!i->gc) { + print_err("Failed to create X graphics context.\n"); + pbreak(error_platform_error); + } + XGetWindowAttributes(d, w, &wa); + if (wa.depth != 24 && wa.depth != 32) { + print_err("Only true colour displays are supported.\n"); + pbreak(error_platform_error); + } + rm = wa.visual->red_mask & 0x1000000; + bm = wa.visual->blue_mask & 0xffffff; + if ((rm == 0xff && bm == 0xff0000)) { + print_war("Detected BGR. Colours will look fucked.\n"); + } + init_rendering(a, i, &wa); +} + +App* new_app(Heap* mem, int w, int h, const char* n) { + App* a; + App_Internal* i; + a = heap_alloc(mem, sizeof *a + sizeof *i); + i = (App_Internal*)(&a[1]); + a->heap = mem; + a->s = 2; + a->o = 0; + a->err = error_none; + a->w = w; + a->h = h; + i->d = XOpenDisplay(0); + if (!i->d) { + print_err("Failed to open X11 display.\n"); + pbreak(error_platform_error); + } + init_window(a, i, n); + a->o = 1; + return a; +} + +void deinit_app(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + XDestroyImage(i->bb); + XFreeGC(i->d, i->gc); + XDestroyWindow(i->d, i->wi); + XCloseDisplay(i->d); + heap_free(a->heap, a); +} + +void app_begin(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + Display* d = i->d; + Window w = i->wi; + i->begin = get_timer(); + while (XPending(d)) { + XEvent e; + XWindowAttributes wa; + int nw, nh; + XNextEvent(d, &e); + switch (e.type) { + case ClientMessage: + if ( + (Atom)e.xclient.data.l[0] == + i->wm_delete + ) { + a->o = 0; + } + break; + case Expose: + XGetWindowAttributes(d, w, &wa); + nw = wa.width; + nh = wa.height; + if ( + !i->bb || + nw != i->w || + nh != i->h + ) { + init_rendering(a, i, &wa); + } + break; + case MotionNotify: + a->mx = e.xmotion.x / a->s; + a->my = e.xmotion.y / a->s; + break; + default: + break; + } + } +} + +void app_rencpy( + App* a, + App_Internal* i, + int x, + int y, + int w, + int h +) { + int rat_x, rat_y; + int rrat_x, rrat_y; + int sx, sy; + int ix, iy, sw, sh, dw, dh; + int dsx, dsy; + int dex, dey; + int pidx, fbits = 9; + Colour* dst; + const Colour* src; + unsigned char t; + sw = a->w; + sh = a->h; + dw = i->w; + dh = i->h; + rat_x = (sw << fbits * 2) / (dw << fbits); + rat_y = (sh << fbits * 2) / (dh << fbits); + rrat_x = (dw << fbits * 2) / (sw << fbits); + rrat_y = (dh << fbits * 2) / (sh << fbits); + dsx = (x * rrat_x) >> fbits; + dsy = (y * rrat_y) >> fbits; + dex = ((x + w) * rrat_x) >> fbits; + dey = ((y + h) * rrat_y) >> fbits; + dst = i->bbp; + src = a->fb; + for (iy = dsy; iy < dey; iy++) { + sy = (rat_y * (iy << fbits)) >> (fbits * 2); + for (ix = dsx; ix < dex; ix++) { + sx = (rat_x * (ix << fbits)) >> (fbits * 2); + pidx = sx + sy * sw; + dst = &i->bbp[ix + iy * dw]; + *dst = src[pidx]; + t = dst->r; + dst->r = dst->b; + dst->b = t; + } + } + XPutImage( + i->d, + i->wi, + i->gc, + i->bb, + dsx, dsy, + dsx, dsy, + dex - dsx, dey - dsy + ); +} + +void app_end(App* a) { + App_Internal* i = (App_Internal*)(&a[1]); + app_rencpy(a, i, 0, 0, a->w, a->h); + i->end = get_timer(); + a->fps = 1000000000 / (i->end - i->begin); +} + +#endif @@ -0,0 +1,58 @@ +#ifndef plat_h +#define plat_h + +#include "error.h" +#include "memory.h" +#include "render.h" + +#ifdef assert +#undef assert +#endif + +#define assert(expr) \ + imp_assert( \ + expr, \ + #expr, \ + __FILE__, \ + __LINE__ \ + ) + +int imp_assert( + int val, + const char* expr, + const char* file, + int line +); + +void print(const char* fmt, ...); +void print_err(const char* fmt, ...); +void print_war(const char* fmt, ...); +void pbreak(Error code); + +typedef struct { + int mpf; + int fps; + unsigned long pt, ct; + unsigned long now, next; +} FPS; + +void init_fps(FPS* f, int mpf); +void fps_begin(FPS* f); +void fps_end(FPS* f); +void fps_update(FPS* f); + +typedef struct { + int w, h, s, o, mpf; + int fps; + int mx, my; + Error err; + Colour* fb; + Heap* heap; +} App; + +App* new_app(Heap* mem, int w, int h, const char* n); +void deinit_app(App* a); +void app_begin(App* a); +void app_end(App* a); + +#endif @@ -0,0 +1,46 @@ +#include "maths.h" +#include "rect.h" + +Rect* rect_clip(Rect* r, const Rect* c) { + int n; + int x = r->x, y = r->y; + if ((n = c->x - r->x) > 0) { + r->w -= n; + r->x += n; + } + if ((n = c->y - r->y) > 0) { + r->h -= n; + r->y += n; + } + if ((n = x + r->w - (c->x + c->w)) > 0) + r->w -= n; + if ((n = y + r->h - (c->y + c->h)) > 0) + r->h -= n; + return r; +} + +Rect* rect_clips(Rect* r, Rect* s, const Rect* c) { + int n; + int x = r->x, y = r->y; + if ((n = c->x - r->x) > 0) { + r->w -= n; + r->x += n; + s->w -= n; + s->x += n; + } + if ((n = c->y - r->y) > 0) { + r->h -= n; + r->y += n; + s->h -= n; + s->y += n; + } + if ((n = x + r->w - (c->x + c->w)) > 0) { + r->w -= n; + s->w -= n; + } + if ((n = y + r->h - (c->y + c->h)) > 0) { + r->h -= n; + s->h -= n; + } + return r; +} @@ -0,0 +1,11 @@ +#ifndef rect_h +#define rect_h + +typedef struct { + int x, y, w, h; +} Rect; + +Rect* rect_clip(Rect* r, const Rect* c); +Rect* rect_clips(Rect* r, Rect* s, const Rect* c); + +#endif diff --git a/render.c b/render.c new file mode 100644 index 0000000..74da997 --- /dev/null +++ b/render.c @@ -0,0 +1,207 @@ +#include "render.h" + +static int font_w = 960; +static unsigned font_data[] = { + 0x00000000, 0x00003000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0cc0c000, 0x8000301b, 0x60601803, 0x00000000, 0x20000000, + 0x0780c078, 0x83f0e01e, 0xe0783f07, 0x60000001, 0x1e018000, + 0x07e0c078, 0xe3f87e1f, 0xe0cc1e0f, 0x1e638f01, 0x1f0c6318, + 0x07e1f07e, 0xc330fc1e, 0x30cc318c, 0x021e0fc3, 0x000101e0, + 0x00e00000, 0x0000e000, 0xc00e0007, 0x3c038c00, 0x00000000, + 0x00000000, 0x00001000, 0x00000000, 0x30380000, 0x0000c070, + 0x0cc1e000, 0xc630f81b, 0xc0301806, 0x00000cc0, 0x30000000, + 0x0cc0f0cc, 0xc030f033, 0x30cc330c, 0x30000003, 0x33030000, + 0x8cc1e0cc, 0xc230cc33, 0xc0cc3308, 0x0c630600, 0x318ce3b8, + 0x0cc318cc, 0xc330b433, 0x30cc318c, 0x06060cc3, 0x00038180, + 0x00c00018, 0x8000c000, 0x000c000d, 0x30030000, 0x00000000, + 0x00000000, 0x00001800, 0x00000000, 0x300c0000, 0x0409e0c0, + 0x8481e000, 0xc3300c3f, 0x80180c06, 0x000c0781, 0x18000000, + 0x0c00c0ec, 0xc1f0d830, 0x30cc1800, 0x180c0303, 0x30060000, + 0x8cc330ec, 0xc0318c01, 0xc0cc3180, 0x0c330600, 0x318de3f8, + 0x0cc318cc, 0xc3303003, 0x3078318c, 0x0c060643, 0x0006c180, + 0x07c0f018, 0x81e0f81e, 0xf06c3701, 0x30330f00, 0x1e07c1f8, + 0x0ee37076, 0xc330fc3e, 0x30c6318c, 0x300c0fc3, 0x0e0f20c0, + 0x0001e000, 0x8180781b, 0x80180013, 0x000c1fe1, 0x0c000000, + 0x0600c0dc, 0xc300cc1c, 0xe0780c07, 0x0c0c0303, 0x180c03f0, + 0x87c330ec, 0xc1f18c01, 0xc0fc0187, 0x0c1f0600, 0x318fe358, + 0x07c3187c, 0xc330300e, 0xe030358c, 0x18060301, 0x000c6180, + 0x0cc18030, 0xc330cc33, 0xc0dc1987, 0x301b0c00, 0x330cc358, + 0x0dc198cc, 0xc3301803, 0x306c358c, 0x00060643, 0x1b060180, + 0x0000c000, 0xc0c0c01b, 0x8018001e, 0x003f0781, 0x060003f0, + 0x0300c0cc, 0xc301fc30, 0x00cc0c0c, 0x0c000003, 0x0c0c0000, + 0x8cc3f00c, 0xc0318c01, 0xc0cc3980, 0x0c330660, 0x318f6318, + 0x06c3980c, 0xc3303018, 0xc078358c, 0x30060180, 0x00000180, + 0x0cc1f000, 0x83f0cc03, 0xc0cc1981, 0x301f0c00, 0x330cc358, + 0x00c198cc, 0xc330181e, 0x3038358c, 0x300c0303, 0x318000c0, + 0x80000000, 0xc6607c3f, 0xc030000c, 0x300c0cc0, 0x03030000, + 0x0180c0cc, 0xc330c033, 0x30cc0c0c, 0x180c0303, 0x000603f0, + 0x8cc3308c, 0xc230cc33, 0xc0cc3300, 0x8c630660, 0x318e6318, + 0x0cc1f00c, 0x83303033, 0xc0cc1f07, 0x600609c0, 0x00000180, + 0x0cc19800, 0x8030cc33, 0xc0cc1f01, 0x30330cc0, 0x330cc358, + 0x00c198cc, 0x8330d830, 0xe06c1f07, 0x300c0981, 0x318000c0, + 0x0000c000, 0x8630301b, 0x6060001b, 0x300c0000, 0x01830000, + 0x0fc3f078, 0x81e0c01e, 0xe0780c07, 0x300c0301, 0x0c030000, + 0x07e33078, 0xe3f87e1f, 0xe0cc3e01, 0xfe6383c1, 0x1f0c6318, + 0x0ce3c01e, 0x01e0781e, 0xe0cc1b03, 0xc01e0fc1, 0x000001e0, + 0x07e3f000, 0xc1e0f81e, 0xf0ce1803, 0xfc338783, 0x1e0cc318, + 0x01e1f07c, 0x06e0701f, 0x80c61b03, 0x30380fc1, 0x3f800070, + 0x0000c000, 0x0000301b, 0x00000000, 0x18000000, 0x00800000, + 0x00000000, 0x00000000, 0x00000000, 0x60060000, 0x00018000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x7f800000, + 0x00000000, 0x00000000, 0x00000f00, 0x00000000, 0x00000000, + 0x0001800c, 0x00000000, 0xf0000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0003c01e, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +Colour make_colour(unsigned rgb, unsigned char a) { + Colour r; + r.r = (unsigned char)(rgb >> 16); + r.g = (unsigned char)(rgb >> 8); + r.b = (unsigned char)rgb; + r.a = a; + return r; +} + +Colour make_red(void) { + return make_colour(0xff0000, 0xff); +} + +Colour make_green(void) { + return make_colour(0x00ff00, 0xff); +} + +Colour make_blue(void) { + return make_colour(0x0000ff, 0xff); +} + +Colour make_cyan(void) { + return make_colour(0x00ffff, 0xff); +} + +Colour make_pink(void) { + return make_colour(0xff00ff, 0xff); +} + +Colour make_yellow(void) { + return make_colour(0xffff00, 0xff); +} + +Colour make_aliceblue(void) { + return make_colour(0xf0f8ff, 0xff); +} + +Colour blend(Colour dst, Colour src) { + int ima; + ima = 0xff - src.a; + dst.r = (unsigned char)(((src.r * src.a) + (dst.r * ima)) >> 8); + dst.g = (unsigned char)(((src.g * src.a) + (dst.g * ima)) >> 8); + dst.b = (unsigned char)(((src.b * src.a) + (dst.b * ima)) >> 8); + return dst; +} + +Colour blend_mod(Colour dst, Colour src, Colour mod) { + int ima; + src.a = (src.a * mod.a) >> 8; + ima = 0xff - src.a; + dst.r = (unsigned char)(((src.r * mod.r * src.a) >> 16) + ((dst.r * ima) >> 8)); + dst.g = (unsigned char)(((src.g * mod.g * src.a) >> 16) + ((dst.g * ima) >> 8)); + dst.b = (unsigned char)(((src.b * mod.b * src.a) >> 16) + ((dst.b * ima) >> 8)); + return dst; +} + + +void ren_begin(Renderer* r, Colour* t, int w, int h) { + r->t = t; + r->w = w; + r->h = h; + r->clip.x = 0; + r->clip.y = 0; + r->clip.w = w; + r->clip.h = h; +} + +void ren_end(Renderer* r) { + (void)r; +} + +void ren_clear(Renderer* r) { + Colour* d = r->t, b = { 0 }; + int i = 0, e = r->w * r->h; + for (i = 0; i < e; i++, d++) + *d = b; +} + +void ren_clearc(Renderer* r, Colour c) { + Colour* d = r->t; + int i = 0, e = r->w * r->h; + for (i = 0; i < e; i++, d++) + *d = c; +} + +void ren_clip(Renderer* r, const Rect* c) { + r->clip = *c; +} + +void ren_point(Renderer* r, Colour c, int x, int y) { + if (x < 0) return; + if (y < 0) return; + if (x >= r->w) return; + if (y >= r->h) return; + r->t[x + y * r->w] = c; +} + +void ren_char( + Renderer* r, + Colour c, + int x, + int y, + char ch +) { + int i, j, k, l, ex, ey, s; + Colour* dst; + Rect re = { 0, 0, 10, 10 }; + Rect sub = { 0, 0, 10, 10 }; + re.x = x; + re.y = y; + sub.x = (ch - ' ') * 10; + rect_clips(&re, &sub, &r->clip); + ex = re.x + re.w; + ey = re.y + re.h; + dst = r->t + (re.x + re.y * r->w); + s = r->w - re.w; + for (j = re.y, l = sub.y; j < ey; j++, l++) { + for (i = re.x, k = sub.x; i < ex; i++, k++) { + int si = k + l * font_w; + unsigned bits = font_data[si / 32]; + int bit = bits & (1 << si % 32); + if (bit) + *dst = blend(*dst, c); + dst++; + } + dst += s; + } +} + +void ren_text( + Renderer* r, + Colour c, + int x, + int y, + const char* t +) { + const char* s; + for (s = t; *s; s++, x += 10) + ren_char(r, c, x, y, *s); +} + diff --git a/render.h b/render.h new file mode 100644 index 0000000..36bafe4 --- /dev/null +++ b/render.h @@ -0,0 +1,48 @@ +#ifndef render_h +#define render_h + +#include "rect.h" + +typedef struct { + unsigned char r, g, b, a; +} Colour; + +Colour make_colour(unsigned rgb, unsigned char a); +Colour make_red(void); +Colour make_green(void); +Colour make_blue(void); +Colour make_cyan(void); +Colour make_pink(void); +Colour make_yellow(void); +Colour make_aliceblue(void); +Colour blend(Colour dst, Colour src); +Colour blend_mod(Colour dst, Colour src, Colour mod); + +typedef struct { + Colour* t; + int w, h; + Rect clip; +} Renderer; + +void ren_begin(Renderer* r, Colour* t, int w, int h); +void ren_end(Renderer* r); +void ren_clear(Renderer* r); +void ren_clearc(Renderer* r, Colour c); +void ren_clip(Renderer* r, const Rect* c); +void ren_point(Renderer* r, Colour c, int x, int y); +void ren_char( + Renderer* r, + Colour c, + int x, + int y, + char ch +); +void ren_text( + Renderer* r, + Colour c, + int x, + int y, + const char* t +); + +#endif |