+7
Makefile
+7
Makefile
+3
README.md
+3
README.md
···
1
+
# Portable Pixel Map Library
2
+
3
+
I originally made this because I was watching a [video](https://youtu.be/dCqIKDo_Hkc?si=F_H5uxOoQ06eCzkw) from [Jacob Sorber](https://www.youtube.com/@JacobSorber) that show PPM as a very easy to use image format for beginners. I've been interested in making more things in C and I thought this would be a fairly easy exercise at creating my own PPM library.
+199
ppm.c
+199
ppm.c
···
1
+
#include "ppm.h"
2
+
#include <inttypes.h>
3
+
#include <math.h>
4
+
#include <stdint.h>
5
+
#include <stdio.h>
6
+
#include <stdlib.h>
7
+
8
+
ppm_image *ppm_create(uint64_t height, uint64_t width) {
9
+
return ppm_create_with_background_color(height, width, PPM_BLACK);
10
+
}
11
+
12
+
ppm_image *ppm_create_with_background_color(uint64_t height, uint64_t width,
13
+
ppm_rgb background_color) {
14
+
ppm_image *image = (ppm_image *)malloc(sizeof(ppm_image));
15
+
image->height = height;
16
+
image->width = width;
17
+
18
+
ppm_rgb *data = (ppm_rgb *)malloc(sizeof(ppm_rgb) * width * height);
19
+
20
+
for (uint64_t y = 0; y < height; y++) {
21
+
for (uint64_t x = 0; x < width; x++) {
22
+
uint64_t position = (y * width) + x;
23
+
24
+
data[position] = background_color;
25
+
}
26
+
}
27
+
28
+
image->data = data;
29
+
30
+
return image;
31
+
}
32
+
33
+
int ppm_destroy(ppm_image *image) {
34
+
free(image->data);
35
+
free(image);
36
+
37
+
return EXIT_SUCCESS;
38
+
}
39
+
40
+
int ppm_is_in_bounds(ppm_image *image, uint64_t x, uint64_t y) {
41
+
if (x <= 0)
42
+
return EXIT_FAILURE;
43
+
44
+
if (x > image->width)
45
+
return EXIT_FAILURE;
46
+
47
+
if (y <= 0)
48
+
return EXIT_FAILURE;
49
+
50
+
if (y > image->height)
51
+
return EXIT_FAILURE;
52
+
53
+
return EXIT_SUCCESS;
54
+
}
55
+
56
+
int ppm_to_ppm3_file(ppm_image *image, const char *file_path) {
57
+
FILE *file = fopen(file_path, "w");
58
+
59
+
fprintf(file, "P3\n");
60
+
fprintf(file, "%" PRIu64 " %" PRIu64 "\n", image->height, image->width);
61
+
fprintf(file, "255\n");
62
+
63
+
for (uint64_t y = 0; y < image->height; y++) {
64
+
for (uint64_t x = 0; x < image->width; x++) {
65
+
uint64_t position = (y * image->width) + x;
66
+
67
+
ppm_rgb rgb = image->data[position];
68
+
fprintf(file, "%" PRIu8 " %" PRIu8 " %" PRIu8, rgb.r, rgb.g, rgb.b);
69
+
70
+
if (x != image->width - 1) {
71
+
fprintf(file, " ");
72
+
}
73
+
}
74
+
75
+
if (y != image->height - 1) {
76
+
fprintf(file, "\n");
77
+
}
78
+
}
79
+
80
+
fclose(file);
81
+
82
+
return EXIT_SUCCESS;
83
+
}
84
+
85
+
int ppm_to_ppm6_file(ppm_image *image, const char *file_path) {
86
+
FILE *file = fopen(file_path, "w");
87
+
88
+
fprintf(file, "P6\n");
89
+
fprintf(file, "%" PRIu64 " %" PRIu64 "\n", image->height, image->width);
90
+
fprintf(file, "255\n");
91
+
92
+
for (uint64_t y = 0; y < image->height; y++) {
93
+
for (uint64_t x = 0; x < image->width; x++) {
94
+
uint64_t position = (y * image->width) + x;
95
+
96
+
ppm_rgb rgb = image->data[position];
97
+
fprintf(file, "%c%c%c", rgb.r, rgb.g, rgb.b);
98
+
}
99
+
}
100
+
101
+
fclose(file);
102
+
103
+
return EXIT_SUCCESS;
104
+
}
105
+
106
+
int ppm_draw_point(ppm_image *image, uint64_t x, uint64_t y, ppm_rgb color) {
107
+
if (ppm_is_in_bounds(image, x, y) == EXIT_FAILURE)
108
+
return EXIT_FAILURE;
109
+
110
+
uint64_t position = ((y - 1) * image->width) + x - 1;
111
+
112
+
image->data[position] = color;
113
+
114
+
return EXIT_SUCCESS;
115
+
}
116
+
117
+
int ppm_draw_circle(ppm_image *image, int x, int y, uint64_t radius,
118
+
ppm_rgb color) {
119
+
for (int local_y = 0; local_y <= radius * 2; local_y++) {
120
+
for (int local_x = 0; local_x <= radius * 2; local_x++) {
121
+
int local_centered_x = local_x - radius;
122
+
int local_centered_y = local_y - radius;
123
+
124
+
uint64_t length = sqrt((local_centered_x * local_centered_x) +
125
+
(local_centered_y * local_centered_y));
126
+
127
+
if (length > radius)
128
+
continue;
129
+
130
+
uint64_t global_x = local_x + x - radius;
131
+
uint64_t global_y = local_y + y - radius;
132
+
133
+
ppm_draw_point(image, global_x, global_y, color);
134
+
}
135
+
}
136
+
137
+
return EXIT_SUCCESS;
138
+
}
139
+
140
+
int ppm_draw_rectangle(ppm_image *image, int x, int y, uint64_t height,
141
+
uint64_t width, ppm_rgb color) {
142
+
for (int local_y = 0; local_y <= height; local_y++) {
143
+
for (int local_x = 0; local_x <= width; local_x++) {
144
+
145
+
uint64_t global_x = local_x + x;
146
+
uint64_t global_y = local_y + y;
147
+
148
+
ppm_draw_point(image, global_x, global_y, color);
149
+
}
150
+
}
151
+
152
+
return EXIT_SUCCESS;
153
+
}
154
+
155
+
/**
156
+
* Draw a line using Bresenham's line algorithm.
157
+
*/
158
+
int ppm_draw_line(ppm_image *image, int x1, int y1, int x2, int y2,
159
+
ppm_rgb color) {
160
+
161
+
int dx = x2 - x1;
162
+
int dy = y2 - y1;
163
+
164
+
int d = 2 * dy - dx;
165
+
166
+
int y = y1;
167
+
168
+
for (int x = x1; x < x2; x++) {
169
+
ppm_draw_point(image, x, y, color);
170
+
171
+
if (d > 0) {
172
+
y++;
173
+
d -= 2 * dx;
174
+
}
175
+
176
+
d += 2 * dy;
177
+
}
178
+
179
+
return EXIT_SUCCESS;
180
+
}
181
+
182
+
int ppm_clear(ppm_image *image) {
183
+
ppm_clear_with_background_color(image, PPM_BLACK);
184
+
185
+
return EXIT_SUCCESS;
186
+
}
187
+
188
+
int ppm_clear_with_background_color(ppm_image *image,
189
+
ppm_rgb background_color) {
190
+
for (uint64_t y = 0; y < image->height; y++) {
191
+
for (uint64_t x = 0; x < image->width; x++) {
192
+
uint64_t position = (y * image->width) + x;
193
+
194
+
image->data[position] = background_color;
195
+
}
196
+
}
197
+
198
+
return EXIT_SUCCESS;
199
+
}
+43
ppm.h
+43
ppm.h
···
1
+
/**
2
+
* PPM library for cr
3
+
*/
4
+
5
+
#ifndef PPM_H
6
+
#define PPM_H
7
+
#include <stdint.h>
8
+
9
+
typedef struct {
10
+
uint8_t r;
11
+
uint8_t g;
12
+
uint8_t b;
13
+
} ppm_rgb;
14
+
15
+
#define PPM_RED (ppm_rgb){255, 0, 0}
16
+
#define PPM_GREEN (ppm_rgb){0, 255, 0}
17
+
#define PPM_BLUE (ppm_rgb){0, 0, 255}
18
+
#define PPM_WHITE (ppm_rgb){255, 255, 255}
19
+
#define PPM_BLACK (ppm_rgb){0, 0, 0}
20
+
21
+
typedef struct {
22
+
uint64_t width;
23
+
uint64_t height;
24
+
ppm_rgb *data;
25
+
} ppm_image;
26
+
27
+
ppm_image *ppm_create(uint64_t height, uint64_t width);
28
+
ppm_image *ppm_create_with_background_color(uint64_t height, uint64_t width,
29
+
ppm_rgb background_color);
30
+
int ppm_destroy(ppm_image *image);
31
+
int ppm_to_ppm3_file(ppm_image *image, const char *file_path);
32
+
int ppm_to_ppm6_file(ppm_image *image, const char *file_path);
33
+
int ppm_draw_point(ppm_image *image, uint64_t x, uint64_t y, ppm_rgb color);
34
+
int ppm_draw_circle(ppm_image *image, int x, int y, uint64_t radius,
35
+
ppm_rgb color);
36
+
int ppm_draw_rectangle(ppm_image *image, int x, int y, uint64_t height,
37
+
uint64_t width, ppm_rgb color);
38
+
int ppm_draw_line(ppm_image *image, int x1, int y1, int x2, int y2,
39
+
ppm_rgb color);
40
+
int ppm_clear(ppm_image *image);
41
+
int ppm_clear_with_background_color(ppm_image *image, ppm_rgb background_color);
42
+
43
+
#endif