A C library for creating and drawing images using the PPM image format.

init

Clay f44b80bc

+3
.gitignore
··· 1 + .out 2 + *.ppm 3 + .vscode
+7
Makefile
··· 1 + all: default 2 + 3 + build: 4 + gcc -o .out main.c ppm.c -lm 5 + 6 + clean: 7 + rm .out *.ppm
+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
··· 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
··· 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