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