/**
* A miminal use of libpng for UIUC's CS418 class.
*
* See uselibpng.h for usage examples.
*
* Written by Luther Tychonievich by consulting "PNG: The Definitive Guide" by Greg Roelofs , with a focus on brevity for the common case. Notably, I omitted the recommended setjmp/longjmp error handling entirely.
*/
#ifdef __cplusplus
extern "C" {
#endif
#include
#include
#include "uselibpng.h"
image_t *load_image(const char *filename) {
png_structp ps = NULL;
png_infop pi = NULL;
image_t *ans = NULL;
FILE *fp = NULL;
uint8_t header[8];
png_byte color = 0, depth = 0;
fp = fopen(filename, "rb");
if (!fp) goto fail1;
fread(header, 1, 8, fp);
if (png_sig_cmp(header, 0, 8)) goto fail2;
ps = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!ps) goto fail2;
pi = png_create_info_struct(ps);
if (!pi) goto fail3;
png_set_sig_bytes(ps, 8);
png_init_io(ps, fp);
png_set_keep_unknown_chunks(ps, 1, NULL, 0);
png_read_info(ps, pi);
ans = (image_t *)calloc(1, sizeof(image_t));
if (!ans) goto fail3;
ans->width = png_get_image_width(ps, pi);
ans->height = png_get_image_height(ps, pi);
color = png_get_color_type(ps, pi);
depth = png_get_bit_depth(ps, pi);
if (depth == 16) png_set_strip_16(ps);
if (color == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(ps);
if (color == PNG_COLOR_TYPE_GRAY && depth < 8) png_set_expand_gray_1_2_4_to_8(ps);
if (color == PNG_COLOR_TYPE_GRAY_ALPHA || color == PNG_COLOR_TYPE_GRAY) png_set_gray_to_rgb(ps);
if (png_get_valid(ps, pi, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(ps);
if (color == PNG_COLOR_TYPE_RGB || color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_PALETTE)
png_set_filler(ps, 0xFF, PNG_FILLER_AFTER); // set missing alpha to opaque
png_read_update_info(ps, pi);
ans->rgba = (pixel_t *)malloc(ans->width * ans->height * sizeof(pixel_t));
if (!ans->rgba) goto fail4;
for(int passes = png_set_interlace_handling(ps); passes >= 1; passes-=1) {
for(uint32_t i=0; iheight; i+=1) {
png_read_row(ps, (png_byte *)&(ans->rgba[ans->width*i]), NULL);
}
}
png_read_end(ps, NULL);
goto succeed;
fail4: free(ans);
succeed: fail3: png_destroy_read_struct(&ps, &pi, NULL);
fail2: fclose(fp);
fail1: return ans;
}
void save_image(image_t *img, const char *filename) {
png_structp ps = NULL;
png_infop pi = NULL;
FILE *out = NULL;
ps = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!ps) goto fail1;
pi = png_create_info_struct(ps);
if (!pi) goto fail2;
out = fopen(filename, "wb");
if (!out) goto fail2;
png_init_io(ps, out);
//png_set_compression_level(ps, Z_BEST_COMPRESSION);
png_set_IHDR(ps, pi, img->width, img->height,
8, // bits per channel
PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
png_write_info(ps, pi);
png_set_packing(ps);
for(uint32_t i=0; iheight; i+=1) {
png_write_row(ps, (png_byte *)&(img->rgba[img->width*i]));
}
png_write_end(ps, NULL);
fclose(out);
fail2: png_destroy_write_struct(&ps, &pi);
fail1: return;
}
image_t *new_image(uint32_t width, uint32_t height) {
image_t *data = (image_t *)malloc(sizeof(image_t));
if (!data) return NULL;
data->width = width;
data->height = height;
data->rgba = (pixel_t *)calloc(width*height, sizeof(pixel_t));
if (!data->rgba) { free(data); return NULL; }
else return data;
}
void free_image(image_t *img) {
if (!img) return;
if (img->rgba) free(img->rgba);
img->width = 0;
img->height = 0;
img->rgba = NULL;
free(img);
}
#ifdef __cplusplus
}
#endif