Add PPM simple image implementation

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-22 23:53:24 +0100
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-22 23:53:24 +0100
Commit 3c62d3d54244731b641952bd7e93edbab16d5d79 (patch)
-rw-r--r-- examples/.gitignore 4
-rw-r--r-- examples/Makefile 7
-rw-r--r-- examples/logging.c 32
-rw-r--r-- examples/ppm.c 29
-rw-r--r-- nonstd.h 209
-rw-r--r-- tests.c 97
6 files changed, 355 insertions, 23 deletions
diff --git a/examples/.gitignore b/examples/.gitignore
  
1
# Build artifacts
1
stringv
2
stringv
2
stringb
3
stringb
3
foreach
4
foreach
...
6
arena
7
arena
7
files
8
files
8
logging
9
logging
  
10
ppm
9
  
11
  
  
12
# Generated artifacts
  
13
*.ppm
diff --git a/examples/Makefile b/examples/Makefile
...
5
LDFLAGS =
5
LDFLAGS =
6
  
6
  
7
# Example targets
7
# Example targets
8
EXAMPLES = foreach stringv stringb array slice arena files logging
8
EXAMPLES = foreach stringv stringb array slice arena files logging ppm
9
  
9
  
10
# Default target
10
# Default target
11
all: $(EXAMPLES)
11
all: $(EXAMPLES)
...
35
logging: logging.c
35
logging: logging.c
36
	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
36
	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
37
  
37
  
  
38
ppm: ppm.c
  
39
	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
  
40
  
38
# Run all examples
41
# Run all examples
39
run: all
42
run: all
40
	@echo "\n=== Running stringv ===\n"
43
	@echo "\n=== Running stringv ===\n"
...
53
	@./files
56
	@./files
54
	@echo "\n=== Running logging ===\n"
57
	@echo "\n=== Running logging ===\n"
55
	@./logging
58
	@./logging
  
59
	@echo "\n=== Running ppm ===\n"
  
60
	@./ppm
56
  
61
  
57
# Clean build artifacts
62
# Clean build artifacts
58
clean:
63
clean:
...
diff --git a/examples/logging.c b/examples/logging.c
...
2
#include "../nonstd.h"
2
#include "../nonstd.h"
3
  
3
  
4
int main(void) {
4
int main(void) {
5
    // Default level is LOG_INFO
5
	// Default level is LOG_INFO
6
    LOG_INFO_MSG("This is an info message: %d", 42);
6
	LOG_INFO_MSG("This is an info message: %d", 42);
7
    LOG_DEBUG_MSG("This debug message will NOT be shown by default");
7
	LOG_DEBUG_MSG("This debug message will NOT be shown by default");
8
  
8
  
9
    // Change level to LOG_DEBUG
9
	// Change level to LOG_DEBUG
10
    set_log_level(LOG_DEBUG);
10
	set_log_level(LOG_DEBUG);
11
    LOG_DEBUG_MSG("Now debug messages are shown: %s", "hello");
11
	LOG_DEBUG_MSG("Now debug messages are shown: %s", "hello");
12
  
12
  
13
    // Warnings and Errors
13
	// Warnings and Errors
14
    LOG_WARN_MSG("This is a warning!");
14
	LOG_WARN_MSG("This is a warning!");
15
    LOG_ERROR_MSG("This is an error!");
15
	LOG_ERROR_MSG("This is an error!");
16
  
16
  
17
    // Environment variable override test
17
	// Environment variable override test
18
    // You can set LOG_LEVEL=1 (WARN) etc.
18
	// You can set LOG_LEVEL=1 (WARN) etc.
19
    LogLevel env_level = get_log_level_from_env();
19
	LogLevel env_level = get_log_level_from_env();
20
    if (env_level != LOG_DEBUG) {
20
	if (env_level != LOG_DEBUG) {
21
        printf("Environment overrides level to: %d\n", env_level);
21
		printf("Environment overrides level to: %d\n", env_level);
22
    }
22
	}
23
  
23
  
24
    return 0;
24
	return 0;
25
}
25
}
diff --git a/examples/ppm.c b/examples/ppm.c
  
1
#define NONSTD_IMPLEMENTATION
  
2
#include "../nonstd.h"
  
3
  
  
4
int main() {
  
5
	u32 width = 400;
  
6
	u32 height = 400;
  
7
	Canvas img = ppm_init(width, height);
  
8
  
  
9
	// Background
  
10
	ppm_fill(&img, COLOR_HEX(0x1a1a1a));
  
11
  
  
12
	// Draw some shapes
  
13
	ppm_draw_rect(&img, 50, 50, 100, 100, CLR_RED);
  
14
	ppm_draw_circle(&img, 250, 100, 40, CLR_BLUE);
  
15
	ppm_draw_triangle(&img, 50, 350, 150, 350, 100, 250, CLR_YELLOW);
  
16
	ppm_draw_line(&img, 200, 200, 350, 350, CLR_GREEN);
  
17
  
  
18
	// Random colors and macros
  
19
	ppm_draw_rect(&img, 200, 250, 50, 80, COLOR_RGB(255, 165, 0));
  
20
  
  
21
	if (ppm_save(&img, "example.ppm")) {
  
22
		printf("Image saved to example.ppm\n");
  
23
	} else {
  
24
		printf("Failed to save image\n");
  
25
	}
  
26
  
  
27
	ppm_free(&img);
  
28
	return 0;
  
29
}
diff --git a/nonstd.h b/nonstd.h
...
7
#ifndef NONSTD_H
7
#ifndef NONSTD_H
8
#define NONSTD_H
8
#define NONSTD_H
9
  
9
  
  
10
#include <stdarg.h>
10
#include <stddef.h>
11
#include <stddef.h>
11
#include <stdint.h>
12
#include <stdint.h>
12
#include <stdio.h>
13
#include <stdio.h>
13
#include <stdlib.h>
14
#include <stdlib.h>
14
#include <string.h>
15
#include <string.h>
15
#include <stdarg.h>
16
#include <sys/time.h>
16
#include <time.h>
17
#include <time.h>
17
#include <sys/time.h>
  
18
#include <unistd.h>
18
#include <unistd.h>
19
  
19
  
20
#ifndef NONSTD_DEF
20
#ifndef NONSTD_DEF
...
249
NONSTD_DEF void *arena_alloc(Arena *a, size_t size);
249
NONSTD_DEF void *arena_alloc(Arena *a, size_t size);
250
NONSTD_DEF void arena_free(Arena *a);
250
NONSTD_DEF void arena_free(Arena *a);
251
  
251
  
  
252
// Image - simple RGB image structure
  
253
typedef struct {
  
254
	u8 r, g, b;
  
255
} Color;
  
256
  
  
257
typedef struct {
  
258
	u32 width;
  
259
	u32 height;
  
260
	Color *pixels;
  
261
} Canvas;
  
262
  
  
263
#define COLOR_RGB(r, g, b) ((Color){(u8)(r), (u8)(g), (u8)(b)})
  
264
#define COLOR_HEX(hex) ((Color){(u8)(((hex) >> 16) & 0xFF), (u8)(((hex) >> 8) & 0xFF), (u8)((hex) & 0xFF)})
  
265
  
  
266
#define CLR_BLACK COLOR_RGB(0, 0, 0)
  
267
#define CLR_WHITE COLOR_RGB(255, 255, 255)
  
268
#define CLR_RED COLOR_RGB(255, 0, 0)
  
269
#define CLR_GREEN COLOR_RGB(0, 255, 0)
  
270
#define CLR_BLUE COLOR_RGB(0, 0, 255)
  
271
#define CLR_YELLOW COLOR_RGB(255, 255, 0)
  
272
#define CLR_MAGENTA COLOR_RGB(255, 0, 255)
  
273
#define CLR_CYAN COLOR_RGB(0, 255, 255)
  
274
  
  
275
NONSTD_DEF Canvas ppm_init(u32 width, u32 height);
  
276
NONSTD_DEF void ppm_free(Canvas *img);
  
277
NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color);
  
278
NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y);
  
279
NONSTD_DEF int ppm_save(const Canvas *img, const char *filename);
  
280
NONSTD_DEF Canvas ppm_read(const char *filename);
  
281
NONSTD_DEF void ppm_fill(Canvas *canvas, Color color);
  
282
NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color);
  
283
NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color);
  
284
NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 x, i32 y, i32 r, Color color);
  
285
NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color);
  
286
  
252
// File I/O helpers
287
// File I/O helpers
253
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size);
288
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size);
254
NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size);
289
NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size);
...
297
	return realloc(ptr, item_size * count);
332
	return realloc(ptr, item_size * count);
298
}
333
}
299
  
334
  
  
335
// String View Implementation
  
336
  
300
NONSTD_DEF stringv sv_from_cstr(const char *s) {
337
NONSTD_DEF stringv sv_from_cstr(const char *s) {
301
	return (stringv){.data = s, .length = s ? strlen(s) : 0};
338
	return (stringv){.data = s, .length = s ? strlen(s) : 0};
302
}
339
}
...
329
NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix) {
366
NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix) {
330
	return sv.length >= suffix.length && memcmp(sv.data + sv.length - suffix.length, suffix.data, suffix.length) == 0;
367
	return sv.length >= suffix.length && memcmp(sv.data + sv.length - suffix.length, suffix.data, suffix.length) == 0;
331
}
368
}
  
369
  
  
370
// String Builder Implementation
332
  
371
  
333
NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap) {
372
NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap) {
334
	sb->capacity = initial_cap ? initial_cap : 16;
373
	sb->capacity = initial_cap ? initial_cap : 16;
...
411
	return a;
450
	return a;
412
}
451
}
413
  
452
  
  
453
// Arena Implementation
  
454
  
414
NONSTD_DEF void arena_grow(Arena *a, size_t min_size) {
455
NONSTD_DEF void arena_grow(Arena *a, size_t min_size) {
415
	size_t size = MAX(ARENA_DEFAULT_BLOCK_SIZE, min_size);
456
	size_t size = MAX(ARENA_DEFAULT_BLOCK_SIZE, min_size);
416
	char *block = ALLOC(char, size);
457
	char *block = ALLOC(char, size);
...
451
	a->ptr = NULL;
492
	a->ptr = NULL;
452
	a->end = NULL;
493
	a->end = NULL;
453
}
494
}
  
495
  
  
496
// File I/O Implementation
454
  
497
  
455
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size) {
498
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size) {
456
	FILE *f = fopen(filepath, "rb");
499
	FILE *f = fopen(filepath, "rb");
...
571
	const char *reset = isatty(fileno(stream)) ? COLOR_RESET : "";
614
	const char *reset = isatty(fileno(stream)) ? COLOR_RESET : "";
572
  
615
  
573
	const char *log_format = "%s[%s.%03d] [%-5s] ";
616
	const char *log_format = "%s[%s.%03d] [%-5s] ";
574
	fprintf(stream, log_format, color, time_str, (int)(tv.tv_usec / 1000),
617
	fprintf(stream, log_format, color, time_str, (int)(tv.tv_usec / 1000), level_strings[level]);
575
			level_strings[level]);
  
576
  
618
  
577
	va_list args;
619
	va_list args;
578
	va_start(args, format);
620
	va_start(args, format);
...
581
  
623
  
582
	fprintf(stream, "%s\n", reset);
624
	fprintf(stream, "%s\n", reset);
583
	fflush(stream);
625
	fflush(stream);
  
626
}
  
627
  
  
628
// PPM Image Implementation
  
629
  
  
630
NONSTD_DEF Canvas ppm_init(u32 width, u32 height) {
  
631
	Canvas img = {0};
  
632
	img.width = width;
  
633
	img.height = height;
  
634
	img.pixels = ALLOC(Color, width * height);
  
635
	if (img.pixels) {
  
636
		memset(img.pixels, 0, sizeof(Color) * width * height);
  
637
	}
  
638
	return img;
  
639
}
  
640
  
  
641
NONSTD_DEF void ppm_free(Canvas *img) {
  
642
	if (img->pixels) {
  
643
		FREE(img->pixels);
  
644
	}
  
645
	img->width = 0;
  
646
	img->height = 0;
  
647
}
  
648
  
  
649
NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color) {
  
650
	if (x < img->width && y < img->height) {
  
651
		img->pixels[y * img->width + x] = color;
  
652
	}
  
653
}
  
654
  
  
655
NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y) {
  
656
	if (x < img->width && y < img->height) {
  
657
		return img->pixels[y * img->width + x];
  
658
	}
  
659
	return (Color){0, 0, 0};
  
660
}
  
661
  
  
662
NONSTD_DEF int ppm_save(const Canvas *img, const char *filename) {
  
663
	FILE *f = fopen(filename, "w");
  
664
	if (!f) {
  
665
		return 0;
  
666
	}
  
667
  
  
668
	fprintf(f, "P3\n%u %u\n255\n", img->width, img->height);
  
669
	for (u32 y = 0; y < img->height; ++y) {
  
670
		for (u32 x = 0; x < img->width; ++x) {
  
671
			Color c = ppm_get_pixel(img, x, y);
  
672
			fprintf(f, "%d %d %d ", c.r, c.g, c.b);
  
673
		}
  
674
		fprintf(f, "\n");
  
675
	}
  
676
  
  
677
	fclose(f);
  
678
	return 1;
  
679
}
  
680
  
  
681
NONSTD_DEF Canvas ppm_read(const char *filename) {
  
682
	Canvas img = {0};
  
683
	FILE *f = fopen(filename, "r");
  
684
	if (!f) {
  
685
		return img;
  
686
	}
  
687
  
  
688
	char magic[3];
  
689
	if (fscanf(f, "%2s", magic) != 1 || strcmp(magic, "P3") != 0) {
  
690
		fclose(f);
  
691
		return img;
  
692
	}
  
693
  
  
694
	u32 w, h, max_val;
  
695
	if (fscanf(f, "%u %u %u", &w, &h, &max_val) != 3) {
  
696
		fclose(f);
  
697
		return img;
  
698
	}
  
699
  
  
700
	img = ppm_init(w, h);
  
701
	if (!img.pixels) {
  
702
		fclose(f);
  
703
		return img;
  
704
	}
  
705
  
  
706
	for (u32 i = 0; i < w * h; ++i) {
  
707
		int r, g, b;
  
708
		if (fscanf(f, "%d %d %d", &r, &g, &b) != 3) {
  
709
			ppm_free(&img);
  
710
			fclose(f);
  
711
			return (Canvas){0};
  
712
		}
  
713
		img.pixels[i] = (Color){(u8)r, (u8)g, (u8)b};
  
714
	}
  
715
  
  
716
	fclose(f);
  
717
	return img;
  
718
}
  
719
  
  
720
NONSTD_DEF void ppm_fill(Canvas *canvas, Color color) {
  
721
	for (u32 i = 0; i < canvas->width * canvas->height; ++i) {
  
722
		canvas->pixels[i] = color;
  
723
	}
  
724
}
  
725
  
  
726
NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color) {
  
727
	if (w == 0 || h == 0)
  
728
		return;
  
729
	for (u32 i = x; i < x + w; ++i) {
  
730
		ppm_set_pixel(canvas, i, y, color);
  
731
		ppm_set_pixel(canvas, i, y + h - 1, color);
  
732
	}
  
733
	for (u32 j = y; j < y + h; ++j) {
  
734
		ppm_set_pixel(canvas, x, j, color);
  
735
		ppm_set_pixel(canvas, x + w - 1, j, color);
  
736
	}
  
737
}
  
738
  
  
739
NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color) {
  
740
	i32 dx = abs(x1 - x0);
  
741
	i32 dy = -abs(y1 - y0);
  
742
	i32 sx = x0 < x1 ? 1 : -1;
  
743
	i32 sy = y0 < y1 ? 1 : -1;
  
744
	i32 err = dx + dy;
  
745
  
  
746
	while (1) {
  
747
		ppm_set_pixel(canvas, (u32)x0, (u32)y0, color);
  
748
		if (x0 == x1 && y0 == y1) {
  
749
			break;
  
750
		}
  
751
  
  
752
		i32 e2 = 2 * err;
  
753
		if (e2 >= dy) {
  
754
			err += dy;
  
755
			x0 += sx;
  
756
		}
  
757
		if (e2 <= dx) {
  
758
			err += dx;
  
759
			y0 += sy;
  
760
		}
  
761
	}
  
762
}
  
763
  
  
764
NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 xm, i32 ym, i32 r, Color color) {
  
765
	i32 x = -r, y = 0, err = 2 - 2 * r;
  
766
	do {
  
767
		ppm_set_pixel(canvas, (u32)(xm - x), (u32)(ym + y), color);
  
768
		ppm_set_pixel(canvas, (u32)(xm - y), (u32)(ym - x), color);
  
769
		ppm_set_pixel(canvas, (u32)(xm + x), (u32)(ym - y), color);
  
770
		ppm_set_pixel(canvas, (u32)(xm + y), (u32)(ym + x), color);
  
771
		r = err;
  
772
		if (r <= y) {
  
773
			err += ++y * 2 + 1;
  
774
		}
  
775
		if (r > x || err > y) {
  
776
			err += ++x * 2 + 1;
  
777
		}
  
778
	} while (x < 0);
  
779
}
  
780
  
  
781
NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color) {
  
782
	ppm_draw_line(canvas, x0, y0, x1, y1, color);
  
783
	ppm_draw_line(canvas, x1, y1, x2, y2, color);
  
784
	ppm_draw_line(canvas, x2, y2, x0, y0, color);
584
}
785
}
585
  
786
  
586
#endif // NONSTD_IMPLEMENTATION
787
#endif // NONSTD_IMPLEMENTATION
diff --git a/tests.c b/tests.c
...
864
  
864
  
865
	set_log_level(LOG_WARN);
865
	set_log_level(LOG_WARN);
866
  
866
  
867
	log_message(tmp, LOG_INFO, "Info message");   // Should not be logged
867
	log_message(tmp, LOG_INFO, "Info message");	  // Should not be logged
868
	log_message(tmp, LOG_ERROR, "Error message"); // Should be logged
868
	log_message(tmp, LOG_ERROR, "Error message"); // Should be logged
869
  
869
  
870
	rewind(tmp);
870
	rewind(tmp);
...
910
	mu_check(strstr(buffer, "Test 123 format") != NULL);
910
	mu_check(strstr(buffer, "Test 123 format") != NULL);
911
	mu_check(strstr(buffer, "[INFO ]") != NULL);
911
	mu_check(strstr(buffer, "[INFO ]") != NULL);
912
	// Check timestamp format roughly (YYYY-MM-DD)
912
	// Check timestamp format roughly (YYYY-MM-DD)
913
	mu_check(strstr(buffer, "20") != NULL); 
913
	mu_check(strstr(buffer, "20") != NULL);
914
  
914
  
915
	fclose(tmp);
915
	fclose(tmp);
916
}
916
}
917
  
917
  
  
918
// Image tests
  
919
MU_TEST(test_ppm_init_free) {
  
920
	Canvas img = ppm_init(100, 100);
  
921
	mu_assert_int_eq(100, (int)img.width);
  
922
	mu_assert_int_eq(100, (int)img.height);
  
923
	mu_check(img.pixels != NULL);
  
924
	ppm_free(&img);
  
925
	mu_assert_int_eq(0, (int)img.width);
  
926
	mu_assert_int_eq(0, (int)img.height);
  
927
	mu_check(img.pixels == NULL);
  
928
}
  
929
  
  
930
MU_TEST(test_ppm_set_get_pixel) {
  
931
	Canvas img = ppm_init(10, 10);
  
932
	Color c = {255, 128, 64};
  
933
	ppm_set_pixel(&img, 5, 5, c);
  
934
	Color got = ppm_get_pixel(&img, 5, 5);
  
935
	mu_assert_int_eq(255, got.r);
  
936
	mu_assert_int_eq(128, got.g);
  
937
	mu_assert_int_eq(64, got.b);
  
938
  
  
939
	// Test out of bounds (should return black)
  
940
	got = ppm_get_pixel(&img, 100, 100);
  
941
	mu_assert_int_eq(0, got.r);
  
942
  
  
943
	ppm_free(&img);
  
944
}
  
945
  
  
946
MU_TEST(test_ppm_save_read) {
  
947
	Canvas img = ppm_init(10, 10);
  
948
	for (u32 y = 0; y < 10; ++y) {
  
949
		for (u32 x = 0; x < 10; ++x) {
  
950
			ppm_set_pixel(&img, x, y, (Color){(u8)(x * 20), (u8)(y * 20), 100});
  
951
		}
  
952
	}
  
953
  
  
954
	const char *tmp_ppm = "test_image.ppm";
  
955
	mu_check(ppm_save(&img, tmp_ppm));
  
956
  
  
957
	Canvas read = ppm_read(tmp_ppm);
  
958
	mu_assert_int_eq((int)img.width, (int)read.width);
  
959
	mu_assert_int_eq((int)img.height, (int)read.height);
  
960
	mu_check(read.pixels != NULL);
  
961
  
  
962
	for (u32 i = 0; i < 100; ++i) {
  
963
		mu_assert_int_eq(img.pixels[i].r, read.pixels[i].r);
  
964
		mu_assert_int_eq(img.pixels[i].g, read.pixels[i].g);
  
965
		mu_assert_int_eq(img.pixels[i].b, read.pixels[i].b);
  
966
	}
  
967
  
  
968
	ppm_free(&img);
  
969
	ppm_free(&read);
  
970
	remove(tmp_ppm);
  
971
}
  
972
  
  
973
MU_TEST(test_ppm_draw_helpers) {
  
974
	Canvas img = ppm_init(100, 100);
  
975
  
  
976
	// Test fill
  
977
	ppm_fill(&img, CLR_RED);
  
978
	Color c1 = ppm_get_pixel(&img, 0, 0);
  
979
	mu_assert_int_eq(255, c1.r);
  
980
	mu_assert_int_eq(0, c1.g);
  
981
  
  
982
	// Test rect
  
983
	ppm_fill(&img, CLR_BLACK);
  
984
	ppm_draw_rect(&img, 10, 10, 20, 20, CLR_WHITE);
  
985
	mu_assert_int_eq(255, ppm_get_pixel(&img, 10, 10).r);
  
986
	mu_assert_int_eq(0, ppm_get_pixel(&img, 15, 15).r); // Inside should be black
  
987
  
  
988
	// Test line
  
989
	ppm_fill(&img, CLR_BLACK);
  
990
	ppm_draw_line(&img, 0, 0, 10, 10, CLR_GREEN);
  
991
	mu_assert_int_eq(255, ppm_get_pixel(&img, 5, 5).g);
  
992
  
  
993
	// Test hexagon/color macros
  
994
	Color hex = COLOR_HEX(0x112233);
  
995
	mu_assert_int_eq(0x11, hex.r);
  
996
	mu_assert_int_eq(0x22, hex.g);
  
997
	mu_assert_int_eq(0x33, hex.b);
  
998
  
  
999
	ppm_free(&img);
  
1000
}
  
1001
  
918
// Test suites
1002
// Test suites
919
MU_TEST_SUITE(test_suite_stringv) {
1003
MU_TEST_SUITE(test_suite_stringv) {
920
	printf("\n[String View Tests]\n");
1004
	printf("\n[String View Tests]\n");
...
1025
	RUN_TEST_WITH_NAME(test_logging_format);
1109
	RUN_TEST_WITH_NAME(test_logging_format);
1026
}
1110
}
1027
  
1111
  
  
1112
MU_TEST_SUITE(test_suite_image) {
  
1113
	printf("\n[Image Tests]\n");
  
1114
	RUN_TEST_WITH_NAME(test_ppm_init_free);
  
1115
	RUN_TEST_WITH_NAME(test_ppm_set_get_pixel);
  
1116
	RUN_TEST_WITH_NAME(test_ppm_save_read);
  
1117
	RUN_TEST_WITH_NAME(test_ppm_draw_helpers);
  
1118
}
  
1119
  
1028
int main(int argc, char *argv[]) {
1120
int main(int argc, char *argv[]) {
1029
	(void)argc;
1121
	(void)argc;
1030
	(void)argv;
1122
	(void)argv;
...
1038
	MU_RUN_SUITE(test_suite_arena);
1130
	MU_RUN_SUITE(test_suite_arena);
1039
	MU_RUN_SUITE(test_suite_files);
1131
	MU_RUN_SUITE(test_suite_files);
1040
	MU_RUN_SUITE(test_suite_logging);
1132
	MU_RUN_SUITE(test_suite_logging);
  
1133
	MU_RUN_SUITE(test_suite_image);
1041
  
1134
  
1042
	MU_REPORT();
1135
	MU_REPORT();
1043
  
1136
  
...