pngz
easy png io library that converts all images into 8 bit RGBA.
Loading...
Searching...
No Matches
pngz.c
Go to the documentation of this file.
1/* pngz.c
2 * samantha jane
3 * easy png library to handle png io
4 *
5 * normal use case:
6 *
7 * // load in an image and resave it elsewhere
8 * pngz z = {.path = "./img.png"};
9 * pngz_load(&z");
10 * pngz_save(&z");
11 * pngz_save_as(z, "./TEST.png");
12 * pngz_free(&z);
13 *
14 * sources:
15 * http://www.libpng.org/pub/png/libpng-manual.txt
16 * http://zarb.org/~gc/html/libpng.html
17 * https://gist.github.com/niw/5963798
18 *--------------------------------------------------------------------------80*/
19
20#include <pthread.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <errno.h>
24#include "pngz.h"
25
26
27/* alloc & free *-------------------------------------------------------------*/
28
29/**
30 * allocate a pixel buffer.
31 * every pixel will be represented as a struct
32 *
33 * @param rows number of rows to allocate
34 * @param cols number pixels per col to allocate
35 * @return exit code, setting errno on failure
36 */
37pixel** pngz_alloc_pixels(unsigned rows, unsigned cols) {
38 pixel** pixels;
39 errno = 0;
40 /* allocate rows to hold addresses */
41 pixels = malloc(sizeof(void *) * rows);
42 if (!pixels) {
43 fprintf(stderr, "ERROR > allocing pixel buffer rows.\n");
44 errno = ENOMEM;
45 return NULL;
46 }
47 /* allocate each row to hold pixels */
48 for (unsigned i = 0; i < rows; i++) {
49 pixels[i] = malloc(sizeof(pixel) * cols);
50 if (!pixels[i]) {
51 fprintf(stderr, "ERROR > allocing pixel buffer cols.\n");
52 errno = ENOMEM;
53 return NULL;
54 }
55 }
56 return pixels;
57}
58
59/**
60 * allocate raw pixel byte buffer.
61 *
62 * @param rows number of rows to allocate
63 * @param cols number bytes per row to allocate
64 * @return a ptr to buffer, returns null and sets errno on failure
65 */
66unsigned char** pngz_alloc_bytes(unsigned rows, unsigned cols) {
67 errno = 0;
68 /* allocate rows to hold addresses */
69 unsigned char** bytes = malloc(sizeof(void *) * rows);
70 if (!bytes) {
71 fprintf(stderr, "ERROR > allocing raw buffer rows.\n");
72 errno = ENOMEM;
73 return NULL;
74 }
75 for (unsigned i = 0; i < rows; i++) {
76 bytes[i] = malloc(sizeof(unsigned char) * cols);
77 if (!bytes[i]) {
78 fprintf(stderr, "ERROR > allocing raw buffer cols.\n");
79 errno = ENOMEM;
80 return NULL;
81 }
82 }
83 return bytes;
84}
85
86/**
87 * safely free pixel buffer.
88 *
89 * @param pixels 2D pixel ptr of size [rows][cols]
90 * @param rows number of rows to free
91 * @return exit code
92 */
93int pngz_free_pixels(pixel** pixels, unsigned rows) {
94 errno = 0;
95 int exit_code = 0;
96 for (unsigned i = 0; i < rows; i++) {
97 if (pixels[i]) {
98 free(pixels[i]);
99 pixels[i] = NULL;
100 } else {
101 fprintf(stderr, "ERROR > freeing pixel buffer cols.\n");
102 errno = EPERM;
103 exit_code = 1;
104 }
105 }
106 if (pixels) {
107 free(pixels);
108 pixels = NULL;
109 } else {
110 fprintf(stderr, "ERROR > freeing pixel buffer rows.\n");
111 errno = EPERM;
112 exit_code = 1;
113 }
114 return exit_code;
115}
116
117/**
118 * safely free pixel buffer.
119 *
120 * @param bytes 2D pixel bytes of size [rows][cols * 4]
121 * @param rows number of rows to free
122 * @return exit code
123 */
124int pngz_free_bytes(unsigned char** bytes, unsigned rows) {
125 int exit_code;
126 errno = 0;
127 exit_code = 0;
128 for (unsigned i = 0; i < rows; i++) {
129 if (bytes[i]) {
130 free(bytes[i]);
131 bytes[i] = NULL;
132 } else {
133 fprintf(stderr, "ERROR > freeing raw buffer cols.\n");
134 errno = EPERM;
135 exit_code = 1;
136 }
137 }
138 if (bytes) {
139 free(bytes);
140 bytes = NULL;
141 } else {
142 fprintf(stderr, "ERROR > freeing raw buffer rows.\n");
143 errno = EPERM;
144 exit_code = 1;
145 }
146 return exit_code;
147}
148
149/**
150 * free a pngz structs pixels.
151 *
152 * @param z pngz* with pixels loaded into memory
153 * @return exit code, error if you try to free a NULL ptr
154 */
156 return pngz_free_pixels(z->pixels, z->height);
157}
158
159/* loading and saving *-------------------------------------------------------*/
160
161/**
162 * pack pixels with bytes.
163 *
164 * @param bytes_src unpacked pixel byte source
165 * @param pixels_dest packed pixel destination
166 * @param rows rows of pixels
167 * @param cols cols of pixels (IN PIXELS NOT BYTES)
168 * @return exit code
169 **/
171 unsigned char** bytes_src, pixel** pixels_dest,
172 unsigned rows, unsigned cols
173) {
174 for (unsigned r = 0; r < rows; r++) {
175 for (unsigned c = 0; c < cols; c++) {
176 pixel p = {
177 .r = bytes_src[r][c * 4],
178 .g = bytes_src[r][(c * 4) + 1],
179 .b = bytes_src[r][(c * 4) + 2],
180 .a = bytes_src[r][(c * 4) + 3]
181 };
182 pixels_dest[r][c] = p;
183 }
184 }
185 return 0;
186}
187
188/**
189 * unpack pixels into raw byte ptrs.
190 *
191 * @param pixels_src packed pixel source
192 * @param bytes_dest unpacked byte destination
193 * @param rows rows of pixels
194 * @param cols cols of pixels (IN PIXELS NOT BYTES)
195 * @return exit code
196 **/
198 pixel** pixels_src, unsigned char** bytes_dest,
199 unsigned rows, unsigned cols
200) {
201 for (unsigned r = 0; r < rows; r++) {
202 for (unsigned c = 0; c < cols; c++) {
203 bytes_dest[r][c * 4] = pixels_src[r][c].r;
204 bytes_dest[r][(c * 4) + 1] = pixels_src[r][c].g;
205 bytes_dest[r][(c * 4) + 2] = pixels_src[r][c].b;
206 bytes_dest[r][(c * 4) + 3] = pixels_src[r][c].a;
207 }
208 }
209 return 0;
210}
211
212/** load a pngz from a path directly passed into the call,
213 * just indirectly calls the standard pngz_load()
214 *
215 * @param z pngz* easy png ptr to load into
216 * @param path char* path to read from
217 * @return exit code
218 */
219int pngz_load_from(pngz* z, char* path) {
220 z->path = path;
221 return pngz_load(z);
222}
223
224/**
225 * load a pngz object into memory
226 *
227 *
228 * @param z pngz* easy png ptr to load into
229 * @return exit code
230 */
232
233 errno = 0;
234
235 /* defaults */
236 z->path = z->path ? z->path : "default.png";
237
238 fprintf(stderr, "pngz loading png at '%s'\n", z->path);
239
240
241 /* open the file and read into info struct */
242 FILE *fp;
243 if(!(fp = fopen(z->path, "rb"))) {
244 fprintf(stderr, "ERROR > opening file.\n");
245 errno = EIO;
246 return 1;
247 }
248 png_structp png;
249 if (!(png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) {
250 fprintf(stderr, "ERROR > creating read struct.\n");
251 errno = EIO;
252 return 1;
253 }
254 png_infop info;
255 if (!(info = png_create_info_struct(png))) {
256 fprintf(stderr, "ERROR > creating png info.\n");
257 errno = EIO;
258 return 1;
259 }
260 if (setjmp(png_jmpbuf(png))) {
261 fprintf(stderr, "ERROR > io failed.\n");
262 errno = EIO;
263 return 1;
264 }
265
266 /* read in png info */
267 png_init_io(png, fp);
268 png_read_info(png, info);
269 unsigned width = png_get_image_width(png, info);
270 unsigned height = png_get_image_height(png, info);
271 unsigned char bit_depth = png_get_bit_depth(png, info);
272 unsigned char color_type = png_get_color_type(png, info);
273
274 /* convert contents into RGBA8 */
275 if (color_type == PNG_COLOR_TYPE_PALETTE) {
276 png_set_palette_to_rgb(png);
277 }
278 if (bit_depth == 16) {
279 png_set_strip_16(png);
280 }
281 if (bit_depth < 8) {
282 if (color_type == PNG_COLOR_TYPE_GRAY) {
283 png_set_expand_gray_1_2_4_to_8(png);
284 } else {
285 png_set_expand(png);
286 }
287 }
288 if (png_get_valid(png, info, PNG_INFO_tRNS)){
289 png_set_tRNS_to_alpha(png);
290 }
291 if (
292 color_type != PNG_COLOR_TYPE_RGBA ||
293 color_type != PNG_COLOR_TYPE_GRAY_ALPHA
294 ) {
295 png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
296 }
297 if (
298 color_type != PNG_COLOR_TYPE_GRAY ||
299 color_type != PNG_COLOR_TYPE_GRAY_ALPHA
300 ) {
301 png_set_gray_to_rgb(png);
302 }
303 png_read_update_info(png, info);
304
305 /* malloc pixel buffers */
306 unsigned char** byte_ptrs = pngz_alloc_bytes(height, width * 4);
307 z->pixels = pngz_alloc_pixels(height, width);
308 /* load into byte buffer, transfer to pixels */
309 png_read_image(png, byte_ptrs);
310 pngz_pack_pixels(byte_ptrs, z->pixels, height, width);
311 /* clean up */
312 pngz_free_bytes(byte_ptrs, height);
313 png_destroy_read_struct(&png, &info, NULL);
314 fclose(fp);
315
316 /* if we got here, pack referenced struct and return */
317 z->width = width;
318 z->height = height;
319 return 0;
320}
321
322/**
323 * write a png back out to file
324 *
325 * @param z pngz* easy png ptr to write to file
326 * @return exit code
327 */
329 return pngz_save_as(z, z.path);
330}
331
332/**
333 * write a png back out to file with a new name
334 *
335 * @param path char* file path to save to
336 * @param z pngz* easy png ptr to write to file
337 * @return exit code
338 */
339int pngz_save_as(pngz z, char* path) {
340
341 FILE *fp;
342 png_structp png;
343 png_infop info;
344
345 errno = 0;
346
347 /* open the file and write info struct */
348 if (!(fp = fopen(path, "wb"))) {
349 fprintf(stderr, "ERROR > opening file.\n");
350 errno = EIO;
351 return 1;
352 }
353
354 if (!(png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) {
355 fprintf(stderr, "ERROR > creating write struct.\n");
356 errno = EIO;
357 return 1;
358 }
359 if (!(info = png_create_info_struct(png))) {
360 fprintf(stderr, "ERROR > creating info struct.\n");
361 errno = EIO;
362 return 1;
363 }
364 if (setjmp(png_jmpbuf(png))) {
365 fprintf(stderr, "ERROR > io failed.\n");
366 errno = EIO;
367 return 1;
368 }
369
370 /* write to RGBA8 */
371 png_init_io(png, fp);
372 png_set_IHDR(
373 png,
374 info,
375 z.width, z.height,
376 8,
377 PNG_COLOR_TYPE_RGBA,
378 PNG_INTERLACE_NONE,
379 PNG_COMPRESSION_TYPE_DEFAULT,
380 PNG_FILTER_TYPE_DEFAULT
381 );
382 png_write_info(png, info);
383 unsigned char** bytes = pngz_alloc_bytes(z.height, z.width * 4);
384 pngz_unpack_pixels(z.pixels, bytes, z.height, z.width);
385 png_write_image(png, bytes);
386 png_write_end(png, NULL);
387
388 /* clean up */
389 pngz_free_bytes(bytes, z.height);
390 png_destroy_write_struct(&png, &info);
391 fclose(fp);
392 return 0;
393}
394
395/* printing functions *-------------------------------------------------------*/
396
397/**
398 * print a pngz ztruct contents.
399 *
400 * @param z pngz to print
401 * @return void
402 */
404 printf("PNGZ [\n");
405 printf(" rows x cols : %d x %d\n", z.rows, z.cols);
406 if (z.pixels) {
407 printf(" pixels : loaded\n");
408 } else {
409 printf(" pixels : empty\n");
410 }
411 printf("]\n");
412 return;
413}
414
415/**
416 * print out a single pixels rgba values in hex.
417 *
418 * @param p unsigned char* to the pixel to print
419 * @return void
420 *
421 */
423 printf(
424 "PIXEL [ #%02X%02X%02X%02X ]\n",
425 p.r, p.g, p.g, p.a
426 );
427 return;
428}
429
430/* private util function to indent */
431void print_indent(int indent) {
432 for (int i = 0; i < indent; i++) {
433 printf(" ");
434 }
435 return;
436}
437
438/**
439 * print a pngz ztruct contents.
440 *
441 * @param z pngz to print
442 * @param indent int for how much to indent this print
443 * @return void
444 */
445void pngz_print_indent(pngz z, int indent) {
446 print_indent(indent);
447 printf("PNGZ [\n");
448 print_indent(indent);
449 printf(" row x col : %d x %d\n", z.height, z.width);
450 print_indent(indent);
451 if (z.pixels) {
452 printf(" pixels : loaded\n");
453 } else {
454 printf(" pixels : empty\n");
455 }
456 print_indent(indent);
457 printf("]\n");
458 return;
459}
460
461/**
462 * print out a single pixels rgba values.
463 *
464 * @param p unsigned char* to the pixel to print
465 * @param indent int for how much to indent this print
466 * @return void
467 *
468 */
469void pngz_print_pixel_indent(pixel p, int indent) {
470 print_indent(indent);
471 printf(
472 "PIXEL [ #%02X%02X%02X%02X ]\n",
473 p.r, p.g, p.g, p.a
474 );
475 return;
476}
void print_indent(int indent)
Definition pngz.c:431
int pngz_unpack_pixels(pixel **pixels_src, unsigned char **bytes_dest, unsigned rows, unsigned cols)
unpack pixels into raw byte ptrs.
Definition pngz.c:197
int pngz_load(pngz *z)
load a pngz object into memory
Definition pngz.c:231
int pngz_free_bytes(unsigned char **bytes, unsigned rows)
safely free pixel buffer.
Definition pngz.c:124
unsigned char ** pngz_alloc_bytes(unsigned rows, unsigned cols)
allocate raw pixel byte buffer.
Definition pngz.c:66
int pngz_load_from(pngz *z, char *path)
load a pngz from a path directly passed into the call, just indirectly calls the standard pngz_load()
Definition pngz.c:219
void pngz_print_pixel_indent(pixel p, int indent)
print out a single pixels rgba values.
Definition pngz.c:469
int pngz_save(pngz z)
write a png back out to file
Definition pngz.c:328
void pngz_print_indent(pngz z, int indent)
print a pngz ztruct contents.
Definition pngz.c:445
int pngz_free(pngz *z)
free a pngz structs pixels.
Definition pngz.c:155
void pngz_print_pixel(pixel p)
print out a single pixels rgba values in hex.
Definition pngz.c:422
int pngz_free_pixels(pixel **pixels, unsigned rows)
safely free pixel buffer.
Definition pngz.c:93
pixel ** pngz_alloc_pixels(unsigned rows, unsigned cols)
allocate a pixel buffer.
Definition pngz.c:37
int pngz_pack_pixels(unsigned char **bytes_src, pixel **pixels_dest, unsigned rows, unsigned cols)
pack pixels with bytes.
Definition pngz.c:170
void pngz_print(pngz z)
print a pngz ztruct contents.
Definition pngz.c:403
int pngz_save_as(pngz z, char *path)
write a png back out to file with a new name
Definition pngz.c:339
pixel in RGBA8 format.
Definition pngz.h:13
unsigned char r
channel values
Definition pngz.h:15
unsigned char g
Definition pngz.h:15
unsigned char a
Definition pngz.h:15
easy png structure.
Definition pngz.h:23
char * path
default path to load from and save to
Definition pngz.h:25
unsigned cols
Definition pngz.h:34
unsigned rows
Definition pngz.h:29
unsigned height
Definition pngz.h:28
pixel ** pixels
[height][width] pixel buffer
Definition pngz.h:37
unsigned width
Definition pngz.h:33