1// nonstd.h
2// A collection of useful functions and macros.
3// This library is licensed under the BSD 2-Clause License.
4//
5// This file provides both the interface and the implementation.
6// To instantiate the implementation,
7// #define NONSTD_IMPLEMENTATION
8// before including this file.
9
10#ifdef NONSTD_IMPLEMENTATION
11#ifndef _POSIX_C_SOURCE
12#define _POSIX_C_SOURCE 200809L
13#endif
14#endif
15
16#ifndef NONSTD_H
17#define NONSTD_H
18
19#include <stdarg.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/time.h>
26#include <time.h>
27#include <unistd.h>
28
29#ifndef NONSTD_DEF
30#ifdef NONSTD_STATIC
31#define NONSTD_DEF static
32#else
33#define NONSTD_DEF extern
34#endif
35#endif
36
37typedef int8_t i8;
38typedef uint8_t u8;
39typedef int16_t i16;
40typedef uint16_t u16;
41typedef int32_t i32;
42typedef uint32_t u32;
43typedef int64_t i64;
44typedef uint64_t u64;
45typedef unsigned int uint;
46typedef unsigned long ulong;
47typedef intptr_t isize;
48typedef uintptr_t usize;
49typedef char c8;
50
51#define countof(a) (sizeof(a) / sizeof((a)[0]))
52
53#define static_foreach(type, var, array) \
54 for (size_t _i_##var = 0, _n_##var = countof(array); \
55 _i_##var < _n_##var && ((var) = (array)[_i_##var], 1); ++_i_##var)
56
57#define ALLOC(type, n) ((type *)safe_malloc(sizeof(type), (n)))
58#define REALLOC(ptr, type, n) ((type *)safe_realloc((ptr), sizeof(type), (n)))
59#define FREE(ptr) \
60 do { \
61 free(ptr); \
62 ptr = NULL; \
63 } while (0)
64
65NONSTD_DEF void *safe_malloc(size_t item_size, size_t count);
66NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count);
67
68#ifndef MIN
69#define MIN(a, b) ((a) < (b) ? (a) : (b))
70#endif
71#ifndef MAX
72#define MAX(a, b) ((a) > (b) ? (a) : (b))
73#endif
74#define CLAMP(x, lo, hi) (MIN((hi), MAX((lo), (x))))
75
76// From https://github.com/tsoding/nob.h/blob/e2c9a46f01d052ab740140e74453665dc3334832/nob.h#L205-L206.
77#define UNUSED(value) (void)(value)
78#define TODO(message) \
79 do { \
80 TraceLog(LOG_ERROR, "%s:%d: TODO: %s", __FILE__, __LINE__, message); \
81 abort(); \
82 } while (0)
83#define UNREACHABLE(message) \
84 do { \
85 TraceLog(LOG_ERROR, "%s:%d: UNREACHABLE: %s", __FILE__, __LINE__, message); \
86 abort(); \
87 } while (0)
88
89#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
90#define STATIC_ASSERT(expr, msg) _Static_assert((expr), msg)
91#else
92#define STATIC_ASSERT(expr, msg) \
93 typedef char static_assertion_##msg[(expr) ? 1 : -1]
94#endif
95
96// String view - read-only, non-owning reference to a string
97typedef struct {
98 const char *data;
99 size_t length;
100} stringv;
101
102NONSTD_DEF stringv sv_from_cstr(const char *s);
103NONSTD_DEF stringv sv_from_parts(const char *data, size_t length);
104NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end);
105NONSTD_DEF int sv_equals(stringv a, stringv b);
106NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix);
107NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix);
108
109// String builder - owning, mutable, dynamically growing string buffer
110typedef struct {
111 char *data;
112 size_t length;
113 size_t capacity;
114} stringb;
115
116NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap);
117NONSTD_DEF void sb_free(stringb *sb);
118NONSTD_DEF void sb_ensure(stringb *sb, size_t additional);
119NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s);
120NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv);
121NONSTD_DEF void sb_append_char(stringb *sb, char c);
122NONSTD_DEF stringv sb_as_sv(const stringb *sb);
123
124// Slice - generic non-owning view into an array
125// Usage: SLICE_DEF(int); slice(int) view = ...;
126#define SLICE_DEF(T) \
127 typedef struct { \
128 T *data; \
129 size_t length; \
130 } slice_##T
131
132#define slice(T) slice_##T
133
134#define make_slice(T, ptr, len) ((slice(T)){.data = (ptr), .length = (len)})
135
136#define array_as_slice(T, arr) \
137 ((slice(T)){.data = (arr).data, .length = (arr).length})
138
139// Dynamic array - generic type-safe growable array using macros
140// Usage: array(int) numbers; array_init(numbers);
141#define array(T) \
142 struct { \
143 T *data; \
144 size_t length; \
145 size_t capacity; \
146 }
147
148#define array_init(arr) \
149 do { \
150 (arr).capacity = 0; \
151 (arr).data = NULL; \
152 (arr).length = 0; \
153 } while (0)
154
155#define array_init_cap(arr, initial_cap) \
156 do { \
157 (arr).capacity = (initial_cap) ? (initial_cap) : 16; \
158 (arr).data = ALLOC(__typeof__(*(arr).data), (arr).capacity); \
159 (arr).length = 0; \
160 } while (0)
161
162#define array_free(arr) \
163 do { \
164 FREE((arr).data); \
165 (arr).length = 0; \
166 (arr).capacity = 0; \
167 } while (0)
168
169#define array_ensure(arr, additional) \
170 do { \
171 size_t _needed = (arr).length + (additional); \
172 if (_needed > (arr).capacity) { \
173 size_t _new_cap = (arr).capacity ? (arr).capacity : 16; \
174 while (_new_cap < _needed) { \
175 if (_new_cap > SIZE_MAX / 2) { \
176 _new_cap = SIZE_MAX; \
177 break; \
178 } \
179 _new_cap *= 2; \
180 } \
181 if (_new_cap < _needed) { /* Overflow or OOM */ \
182 break; \
183 } \
184 void *_new_data = \
185 safe_realloc((arr).data, sizeof(*(arr).data), _new_cap); \
186 if (_new_data) { \
187 (arr).data = _new_data; \
188 (arr).capacity = _new_cap; \
189 } \
190 } \
191 } while (0)
192
193#define array_push(arr, value) \
194 do { \
195 array_ensure((arr), 1); \
196 if ((arr).length < (arr).capacity) { \
197 (arr).data[(arr).length++] = (value); \
198 } \
199 } while (0)
200
201#define array_pop(arr) ((arr).length > 0 ? (arr).data[--(arr).length] : 0)
202
203#define array_get(arr, index) ((arr).data[index])
204
205#define array_set(arr, index, value) \
206 do { \
207 if ((index) < (arr).length) { \
208 (arr).data[index] = (value); \
209 } \
210 } while (0)
211
212#define array_insert(arr, index, value) \
213 do { \
214 if ((index) <= (arr).length) { \
215 array_ensure((arr), 1); \
216 if ((arr).length < (arr).capacity) { \
217 for (size_t _i = (arr).length; _i > (index); --_i) { \
218 (arr).data[_i] = (arr).data[_i - 1]; \
219 } \
220 (arr).data[index] = (value); \
221 (arr).length++; \
222 } \
223 } \
224 } while (0)
225
226#define array_remove(arr, index) \
227 do { \
228 if ((index) < (arr).length) { \
229 for (size_t _i = (index); _i < (arr).length - 1; ++_i) { \
230 (arr).data[_i] = (arr).data[_i + 1]; \
231 } \
232 (arr).length--; \
233 } \
234 } while (0)
235
236#define array_clear(arr) \
237 do { \
238 (arr).length = 0; \
239 } while (0)
240
241#define array_reserve(arr, new_capacity) \
242 do { \
243 if ((new_capacity) > (arr).capacity) { \
244 void *_new_data = \
245 safe_realloc((arr).data, sizeof(*(arr).data), (new_capacity)); \
246 if (_new_data) { \
247 (arr).data = _new_data; \
248 (arr).capacity = (new_capacity); \
249 } \
250 } \
251 } while (0)
252
253#define array_foreach(arr, var) \
254 for (size_t _i_##var = 0; \
255 _i_##var < (arr).length && ((var) = (arr).data[_i_##var], 1); \
256 ++_i_##var)
257
258#define array_foreach_idx(arr, var, index) \
259 for (size_t index = 0; \
260 index < (arr).length && ((var) = (arr).data[index], 1); ++index)
261
262// Arena - block-based memory allocator
263typedef struct {
264 char *ptr;
265 char *end;
266 array(char *) blocks;
267} Arena;
268
269#define ARENA_DEFAULT_BLOCK_SIZE (4096)
270
271NONSTD_DEF Arena arena_make(void);
272NONSTD_DEF void arena_grow(Arena *a, size_t min_size);
273NONSTD_DEF void *arena_alloc(Arena *a, size_t size);
274NONSTD_DEF void arena_free(Arena *a);
275
276// Image - simple RGB image structure
277typedef struct {
278 u8 r, g, b;
279} Color;
280
281typedef struct {
282 u32 width;
283 u32 height;
284 Color *pixels;
285} Canvas;
286
287#define COLOR_RGB(r, g, b) ((Color){(u8)(r), (u8)(g), (u8)(b)})
288#define COLOR_HEX(hex) ((Color){(u8)(((hex) >> 16) & 0xFF), (u8)(((hex) >> 8) & 0xFF), (u8)((hex) & 0xFF)})
289
290#define COLOR_BLACK COLOR_RGB(0, 0, 0)
291#define COLOR_WHITE COLOR_RGB(255, 255, 255)
292#define COLOR_RED COLOR_RGB(255, 0, 0)
293#define COLOR_GREEN COLOR_RGB(0, 255, 0)
294#define COLOR_BLUE COLOR_RGB(0, 0, 255)
295#define COLOR_YELLOW COLOR_RGB(255, 255, 0)
296#define COLOR_MAGENTA COLOR_RGB(255, 0, 255)
297#define COLOR_CYAN COLOR_RGB(0, 255, 255)
298
299NONSTD_DEF Canvas ppm_init(u32 width, u32 height);
300NONSTD_DEF void ppm_free(Canvas *img);
301NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color);
302NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y);
303NONSTD_DEF int ppm_save(const Canvas *img, const char *filename);
304NONSTD_DEF Canvas ppm_read(const char *filename);
305NONSTD_DEF void ppm_fill(Canvas *canvas, Color color);
306NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color);
307NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color);
308NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 x, i32 y, i32 r, Color color);
309NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color);
310
311// File I/O helpers
312NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size);
313NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size);
314NONSTD_DEF stringb read_entire_file_sb(const char *filepath);
315NONSTD_DEF int write_file_sv(const char *filepath, stringv sv);
316NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb);
317
318#ifdef NONSTD_IMPLEMENTATION
319
320NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) {
321 if (count != 0 && item_size > SIZE_MAX / count) {
322 return NULL;
323 }
324 return malloc(item_size * count);
325}
326
327NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count) {
328 if (count != 0 && item_size > SIZE_MAX / count) {
329 return NULL;
330 }
331 return realloc(ptr, item_size * count);
332}
333
334// String View Implementation
335
336NONSTD_DEF stringv sv_from_cstr(const char *s) {
337 return (stringv){.data = s, .length = s ? strlen(s) : 0};
338}
339
340NONSTD_DEF stringv sv_from_parts(const char *data, size_t length) {
341 return (stringv){.data = data, .length = length};
342}
343
344NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end) {
345 if (start > sv.length) {
346 start = sv.length;
347 }
348 if (end > sv.length) {
349 end = sv.length;
350 }
351 if (start > end) {
352 start = end;
353 }
354 return (stringv){.data = sv.data + start, .length = end - start};
355}
356
357NONSTD_DEF int sv_equals(stringv a, stringv b) {
358 return a.length == b.length && (a.length == 0 || memcmp(a.data, b.data, a.length) == 0);
359}
360
361NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix) {
362 return sv.length >= prefix.length && memcmp(sv.data, prefix.data, prefix.length) == 0;
363}
364
365NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix) {
366 return sv.length >= suffix.length && memcmp(sv.data + sv.length - suffix.length, suffix.data, suffix.length) == 0;
367}
368
369// String Builder Implementation
370
371NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap) {
372 sb->capacity = initial_cap ? initial_cap : 16;
373 sb->data = ALLOC(char, sb->capacity);
374 sb->length = 0;
375 if (sb->data) {
376 sb->data[0] = '\0';
377 }
378}
379
380NONSTD_DEF void sb_free(stringb *sb) {
381 FREE(sb->data);
382 sb->length = 0;
383 sb->capacity = 0;
384}
385
386NONSTD_DEF void sb_ensure(stringb *sb, size_t additional) {
387 size_t needed = sb->length + additional + 1;
388 size_t new_cap = sb->capacity;
389
390 if (needed > new_cap) {
391 while (new_cap < needed) {
392 if (new_cap > SIZE_MAX / 2) {
393 new_cap = SIZE_MAX;
394 break;
395 }
396 new_cap *= 2;
397 }
398 if (new_cap < needed)
399 return; // Overflow
400
401 char *new_data = safe_realloc(sb->data, sizeof(char), new_cap);
402 if (new_data) {
403 sb->data = new_data;
404 sb->capacity = new_cap;
405 }
406 }
407}
408
409NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s) {
410 if (!s) {
411 return;
412 }
413 size_t slength = strlen(s);
414 sb_ensure(sb, slength);
415 if (sb->length + slength + 1 <= sb->capacity) {
416 memcpy(sb->data + sb->length, s, slength);
417 sb->length += slength;
418 sb->data[sb->length] = '\0';
419 }
420}
421
422NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv) {
423 if (!sv.data || sv.length == 0) {
424 return;
425 }
426 sb_ensure(sb, sv.length);
427 if (sb->length + sv.length + 1 <= sb->capacity) {
428 memcpy(sb->data + sb->length, sv.data, sv.length);
429 sb->length += sv.length;
430 sb->data[sb->length] = '\0';
431 }
432}
433
434NONSTD_DEF void sb_append_char(stringb *sb, char c) {
435 sb_ensure(sb, 1);
436 if (sb->length + 2 <= sb->capacity) {
437 sb->data[sb->length++] = c;
438 sb->data[sb->length] = '\0';
439 }
440}
441
442NONSTD_DEF stringv sb_as_sv(const stringb *sb) {
443 return (stringv){.data = sb->data, .length = sb->length};
444}
445
446NONSTD_DEF Arena arena_make(void) {
447 Arena a = {0};
448 array_init(a.blocks);
449 return a;
450}
451
452// Arena Implementation
453
454NONSTD_DEF void arena_grow(Arena *a, size_t min_size) {
455 size_t size = MAX(ARENA_DEFAULT_BLOCK_SIZE, min_size);
456 char *block = ALLOC(char, size);
457 a->ptr = block;
458 a->end = block + size;
459 array_push(a->blocks, block);
460}
461
462NONSTD_DEF void *arena_alloc(Arena *a, size_t size) {
463 // Align to 8 bytes basically
464 size_t align = sizeof(void *);
465 uintptr_t current = (uintptr_t)a->ptr;
466 uintptr_t aligned = (current + align - 1) & ~(align - 1);
467 uintptr_t end = (uintptr_t)a->end;
468
469 // Check for overflow (aligned wrapped around) or out of bounds (aligned >= end)
470 // or not enough space ((end - aligned) < size)
471 if (aligned < current || aligned >= end || (end - aligned) < size) {
472 arena_grow(a, size);
473 current = (uintptr_t)a->ptr;
474 aligned = (current + align - 1) & ~(align - 1);
475 end = (uintptr_t)a->end;
476 }
477
478 // Double check after grow (in case grow failed or size is just too huge)
479 if (aligned < current || aligned >= end || (end - aligned) < size) {
480 return NULL;
481 }
482
483 a->ptr = (char *)(aligned + size);
484 return (void *)aligned;
485}
486
487NONSTD_DEF void arena_free(Arena *a) {
488 char *block;
489 array_foreach(a->blocks, block) { FREE(block); }
490 array_free(a->blocks);
491 a->ptr = NULL;
492 a->end = NULL;
493}
494
495// File I/O Implementation
496
497NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size) {
498 FILE *f = fopen(filepath, "rb");
499 if (!f) {
500 return NULL;
501 }
502
503 fseek(f, 0, SEEK_END);
504 long length = ftell(f);
505 fseek(f, 0, SEEK_SET);
506
507 if (length < 0) {
508 fclose(f);
509 return NULL;
510 }
511
512 size_t size = (size_t)length;
513 char *buffer = ALLOC(char, size + 1);
514 if (!buffer) {
515 fclose(f);
516 return NULL;
517 }
518
519 size_t read = fread(buffer, 1, size, f);
520 fclose(f);
521
522 if (read != size) {
523 FREE(buffer);
524 return NULL;
525 }
526
527 buffer[size] = '\0';
528 if (out_size) {
529 *out_size = size;
530 }
531
532 return buffer;
533}
534
535NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size) {
536 FILE *f = fopen(filepath, "wb");
537 if (!f) {
538 return 0;
539 }
540
541 size_t written = fwrite(data, 1, size, f);
542 fclose(f);
543
544 return written == size;
545}
546
547NONSTD_DEF stringb read_entire_file_sb(const char *filepath) {
548 size_t size = 0;
549 char *data = read_entire_file(filepath, &size);
550 stringb sb = {0};
551 if (data) {
552 sb.data = data;
553 sb.length = size;
554 sb.capacity = size + 1;
555 }
556 return sb;
557}
558
559NONSTD_DEF int write_file_sv(const char *filepath, stringv sv) {
560 return write_entire_file(filepath, sv.data, sv.length);
561}
562
563NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb) {
564 return write_entire_file(filepath, sb->data, sb->length);
565}
566
567// PPM Image Implementation
568
569NONSTD_DEF Canvas ppm_init(u32 width, u32 height) {
570 Canvas img = {0};
571 img.width = width;
572 img.height = height;
573 img.pixels = ALLOC(Color, width * height);
574 if (img.pixels) {
575 memset(img.pixels, 0, sizeof(Color) * width * height);
576 }
577 return img;
578}
579
580NONSTD_DEF void ppm_free(Canvas *img) {
581 if (img->pixels) {
582 FREE(img->pixels);
583 }
584 img->width = 0;
585 img->height = 0;
586}
587
588NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color) {
589 if (x < img->width && y < img->height) {
590 img->pixels[y * img->width + x] = color;
591 }
592}
593
594NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y) {
595 if (x < img->width && y < img->height) {
596 return img->pixels[y * img->width + x];
597 }
598 return (Color){0, 0, 0};
599}
600
601NONSTD_DEF int ppm_save(const Canvas *img, const char *filename) {
602 FILE *f = fopen(filename, "w");
603 if (!f) {
604 return 0;
605 }
606
607 fprintf(f, "P3\n%u %u\n255\n", img->width, img->height);
608 for (u32 y = 0; y < img->height; ++y) {
609 for (u32 x = 0; x < img->width; ++x) {
610 Color c = ppm_get_pixel(img, x, y);
611 fprintf(f, "%d %d %d ", c.r, c.g, c.b);
612 }
613 fprintf(f, "\n");
614 }
615
616 fclose(f);
617 return 1;
618}
619
620NONSTD_DEF Canvas ppm_read(const char *filename) {
621 Canvas img = {0};
622 FILE *f = fopen(filename, "r");
623 if (!f) {
624 return img;
625 }
626
627 char magic[3];
628 if (fscanf(f, "%2s", magic) != 1 || strcmp(magic, "P3") != 0) {
629 fclose(f);
630 return img;
631 }
632
633 u32 w, h, max_val;
634 if (fscanf(f, "%u %u %u", &w, &h, &max_val) != 3) {
635 fclose(f);
636 return img;
637 }
638
639 img = ppm_init(w, h);
640 if (!img.pixels) {
641 fclose(f);
642 return img;
643 }
644
645 for (u32 i = 0; i < w * h; ++i) {
646 int r, g, b;
647 if (fscanf(f, "%d %d %d", &r, &g, &b) != 3) {
648 ppm_free(&img);
649 fclose(f);
650 return (Canvas){0};
651 }
652 img.pixels[i] = (Color){(u8)r, (u8)g, (u8)b};
653 }
654
655 fclose(f);
656 return img;
657}
658
659NONSTD_DEF void ppm_fill(Canvas *canvas, Color color) {
660 for (u32 i = 0; i < canvas->width * canvas->height; ++i) {
661 canvas->pixels[i] = color;
662 }
663}
664
665NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color) {
666 if (w == 0 || h == 0) {
667 return;
668 }
669 for (u32 i = x; i < x + w; ++i) {
670 ppm_set_pixel(canvas, i, y, color);
671 ppm_set_pixel(canvas, i, y + h - 1, color);
672 }
673 for (u32 j = y; j < y + h; ++j) {
674 ppm_set_pixel(canvas, x, j, color);
675 ppm_set_pixel(canvas, x + w - 1, j, color);
676 }
677}
678
679NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color) {
680 i32 dx = abs(x1 - x0);
681 i32 dy = -abs(y1 - y0);
682 i32 sx = x0 < x1 ? 1 : -1;
683 i32 sy = y0 < y1 ? 1 : -1;
684 i32 err = dx + dy;
685
686 while (1) {
687 ppm_set_pixel(canvas, (u32)x0, (u32)y0, color);
688 if (x0 == x1 && y0 == y1) {
689 break;
690 }
691
692 i32 e2 = 2 * err;
693 if (e2 >= dy) {
694 err += dy;
695 x0 += sx;
696 }
697 if (e2 <= dx) {
698 err += dx;
699 y0 += sy;
700 }
701 }
702}
703
704NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 xm, i32 ym, i32 r, Color color) {
705 i32 x = -r, y = 0, err = 2 - 2 * r;
706 do {
707 ppm_set_pixel(canvas, (u32)(xm - x), (u32)(ym + y), color);
708 ppm_set_pixel(canvas, (u32)(xm - y), (u32)(ym - x), color);
709 ppm_set_pixel(canvas, (u32)(xm + x), (u32)(ym - y), color);
710 ppm_set_pixel(canvas, (u32)(xm + y), (u32)(ym + x), color);
711 r = err;
712 if (r <= y) {
713 err += ++y * 2 + 1;
714 }
715 if (r > x || err > y) {
716 err += ++x * 2 + 1;
717 }
718 } while (x < 0);
719}
720
721NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color) {
722 ppm_draw_line(canvas, x0, y0, x1, y1, color);
723 ppm_draw_line(canvas, x1, y1, x2, y2, color);
724 ppm_draw_line(canvas, x2, y2, x0, y0, color);
725}
726
727#endif // NONSTD_IMPLEMENTATION
728
729#endif // NONSTD_H
730
731/*
732BSD 2-Clause License
733
734Copyright (c) 2026, Mitja Felicijan <mitja.felicijan@gmail.com>
735
736Redistribution and use in source and binary forms, with or without
737modification, are permitted provided that the following conditions are met:
738
7391. Redistributions of source code must retain the above copyright notice, this
740 list of conditions and the following disclaimer.
741
7422. Redistributions in binary form must reproduce the above copyright notice,
743 this list of conditions and the following disclaimer in the documentation
744 and/or other materials provided with the distribution.
745
746THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
747AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
748IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
749DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
750FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
751DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
752SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
753CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
754OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
755OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
756*/