Add PPM simple image implementation

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-22 23:55:57 +0100
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-01-22 23:55:57 +0100
Commit 91ab3ae0147712b549e4ec086818f847fd23e12f (patch)
-rw-r--r-- README.md 27
-rw-r--r-- examples/ppm.c 8
-rw-r--r-- nonstd.h 16
-rw-r--r-- tests.c 10
4 files changed, 44 insertions, 17 deletions
diff --git a/README.md b/README.md
...
19
- **Memory Arena**: Simple block-based arena allocator for bulk memory management.
19
- **Memory Arena**: Simple block-based arena allocator for bulk memory management.
20
- **File I/O**: Helper functions to read and write entire files with a single call.
20
- **File I/O**: Helper functions to read and write entire files with a single call.
21
- **Logging**: Simple, leveled logging with ANSI colors and timestamps.
21
- **Logging**: Simple, leveled logging with ANSI colors and timestamps.
  
22
- **Canvas & PPM**: Simple 2D drawing API with PPM (ASCII) import/export.
22
  
23
  
23
## Installation
24
## Installation
24
  
25
  
...
182
  
183
  
183
// Environment variable override supported:
184
// Environment variable override supported:
184
// LOG_LEVEL=0 (ERROR) ... 3 (DEBUG)
185
// LOG_LEVEL=0 (ERROR) ... 3 (DEBUG)
  
186
```
  
187
  
  
188
### 7. Canvas & PPM Images
  
189
  
  
190
Create simple 2D images, draw shapes, and save to PPM (ASCII) format.
  
191
  
  
192
```c
  
193
// Initialize a 400x400 canvas
  
194
Canvas canvas = ppm_init(400, 400);
  
195
  
  
196
// Use predefined colors or HEX/RGB macros
  
197
ppm_fill(&canvas, COLOR_HEX(0x1a1a1a));
  
198
  
  
199
// Draw basic shapes
  
200
ppm_draw_rect(&canvas, 50, 50, 100, 100, COLOR_RED);
  
201
ppm_draw_circle(&canvas, 250, 100, 40, COLOR_BLUE);
  
202
ppm_draw_line(&canvas, 200, 200, 350, 350, COLOR_GREEN);
  
203
ppm_draw_triangle(&canvas, 50, 350, 150, 350, 100, 250, COLOR_YELLOW);
  
204
  
  
205
// Save to file
  
206
if (ppm_save(&canvas, "output.ppm")) {
  
207
    printf("Image saved successfully!\n");
  
208
}
  
209
  
  
210
// Clean up
  
211
ppm_free(&canvas);
185
```
212
```
186
  
213
  
187
## Testing
214
## Testing
...
diff --git a/examples/ppm.c b/examples/ppm.c
...
10
	ppm_fill(&img, COLOR_HEX(0x1a1a1a));
10
	ppm_fill(&img, COLOR_HEX(0x1a1a1a));
11
  
11
  
12
	// Draw some shapes
12
	// Draw some shapes
13
	ppm_draw_rect(&img, 50, 50, 100, 100, CLR_RED);
13
	ppm_draw_rect(&img, 50, 50, 100, 100, COLOR_RED);
14
	ppm_draw_circle(&img, 250, 100, 40, CLR_BLUE);
14
	ppm_draw_circle(&img, 250, 100, 40, COLOR_BLUE);
15
	ppm_draw_triangle(&img, 50, 350, 150, 350, 100, 250, CLR_YELLOW);
15
	ppm_draw_triangle(&img, 50, 350, 150, 350, 100, 250, COLOR_YELLOW);
16
	ppm_draw_line(&img, 200, 200, 350, 350, CLR_GREEN);
16
	ppm_draw_line(&img, 200, 200, 350, 350, COLOR_GREEN);
17
  
17
  
18
	// Random colors and macros
18
	// Random colors and macros
19
	ppm_draw_rect(&img, 200, 250, 50, 80, COLOR_RGB(255, 165, 0));
19
	ppm_draw_rect(&img, 200, 250, 50, 80, COLOR_RGB(255, 165, 0));
...
diff --git a/nonstd.h b/nonstd.h
...
263
#define COLOR_RGB(r, g, b) ((Color){(u8)(r), (u8)(g), (u8)(b)})
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)})
264
#define COLOR_HEX(hex) ((Color){(u8)(((hex) >> 16) & 0xFF), (u8)(((hex) >> 8) & 0xFF), (u8)((hex) & 0xFF)})
265
  
265
  
266
#define CLR_BLACK COLOR_RGB(0, 0, 0)
266
#define COLOR_BLACK COLOR_RGB(0, 0, 0)
267
#define CLR_WHITE COLOR_RGB(255, 255, 255)
267
#define COLOR_WHITE COLOR_RGB(255, 255, 255)
268
#define CLR_RED COLOR_RGB(255, 0, 0)
268
#define COLOR_RED COLOR_RGB(255, 0, 0)
269
#define CLR_GREEN COLOR_RGB(0, 255, 0)
269
#define COLOR_GREEN COLOR_RGB(0, 255, 0)
270
#define CLR_BLUE COLOR_RGB(0, 0, 255)
270
#define COLOR_BLUE COLOR_RGB(0, 0, 255)
271
#define CLR_YELLOW COLOR_RGB(255, 255, 0)
271
#define COLOR_YELLOW COLOR_RGB(255, 255, 0)
272
#define CLR_MAGENTA COLOR_RGB(255, 0, 255)
272
#define COLOR_MAGENTA COLOR_RGB(255, 0, 255)
273
#define CLR_CYAN COLOR_RGB(0, 255, 255)
273
#define COLOR_CYAN COLOR_RGB(0, 255, 255)
274
  
274
  
275
NONSTD_DEF Canvas ppm_init(u32 width, u32 height);
275
NONSTD_DEF Canvas ppm_init(u32 width, u32 height);
276
NONSTD_DEF void ppm_free(Canvas *img);
276
NONSTD_DEF void ppm_free(Canvas *img);
...
diff --git a/tests.c b/tests.c
...
974
	Canvas img = ppm_init(100, 100);
974
	Canvas img = ppm_init(100, 100);
975
  
975
  
976
	// Test fill
976
	// Test fill
977
	ppm_fill(&img, CLR_RED);
977
	ppm_fill(&img, COLOR_RED);
978
	Color c1 = ppm_get_pixel(&img, 0, 0);
978
	Color c1 = ppm_get_pixel(&img, 0, 0);
979
	mu_assert_int_eq(255, c1.r);
979
	mu_assert_int_eq(255, c1.r);
980
	mu_assert_int_eq(0, c1.g);
980
	mu_assert_int_eq(0, c1.g);
981
  
981
  
982
	// Test rect
982
	// Test rect
983
	ppm_fill(&img, CLR_BLACK);
983
	ppm_fill(&img, COLOR_BLACK);
984
	ppm_draw_rect(&img, 10, 10, 20, 20, CLR_WHITE);
984
	ppm_draw_rect(&img, 10, 10, 20, 20, COLOR_WHITE);
985
	mu_assert_int_eq(255, ppm_get_pixel(&img, 10, 10).r);
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
986
	mu_assert_int_eq(0, ppm_get_pixel(&img, 15, 15).r); // Inside should be black
987
  
987
  
988
	// Test line
988
	// Test line
989
	ppm_fill(&img, CLR_BLACK);
989
	ppm_fill(&img, COLOR_BLACK);
990
	ppm_draw_line(&img, 0, 0, 10, 10, CLR_GREEN);
990
	ppm_draw_line(&img, 0, 0, 10, 10, COLOR_GREEN);
991
	mu_assert_int_eq(255, ppm_get_pixel(&img, 5, 5).g);
991
	mu_assert_int_eq(255, ppm_get_pixel(&img, 5, 5).g);
992
  
992
  
993
	// Test hexagon/color macros
993
	// Test hexagon/color macros
...