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