Skip to content

Instantly share code, notes, and snippets.

@tobyhinloopen
Created July 20, 2023 08:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tobyhinloopen/c19f3553f81246b8ca48711415b499b4 to your computer and use it in GitHub Desktop.
Save tobyhinloopen/c19f3553f81246b8ca48711415b499b4 to your computer and use it in GitHub Desktop.
#include "image_data_png.h"
#include <png.h>
#include <stdio.h>
#include <stdlib.h>
void image_data_png_read(image_data_t *image_data, const char *filename) {
FILE *fp = fopen(filename, "rb");
png_byte bit_depth;
png_byte color_type;
unsigned int y;
unsigned int x;
png_structp png_ptr =
png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) abort();
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) abort();
if (setjmp(png_jmpbuf(png_ptr))) abort();
png_init_io(png_ptr, fp);
png_read_info(png_ptr, info_ptr);
const unsigned int width = png_get_image_width(png_ptr, info_ptr);
const unsigned int height = png_get_image_height(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
/* Read any color_type into 8bit depth, RGBA format. */
/* See http://www.libpng.org/pub/png/libpng-manual.txt */
if (bit_depth == 16) png_set_strip_16(png_ptr);
if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr);
/* PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png_ptr);
/* These color_type don't have an alpha channel then fill it with 0xff. */
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
png_read_update_info(png_ptr, info_ptr);
png_bytep *row_pointers =
(png_bytep *)malloc(sizeof(row_pointers[0]) * height);
for (y = 0; y < height; y++) {
row_pointers[y] = (png_byte *)malloc(png_get_rowbytes(png_ptr, info_ptr));
}
png_read_image(png_ptr, row_pointers);
fclose(fp);
image_data_resize(image_data, width, height);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
png_byte *rgba = row_pointers[y] + x * 4;
image_data_put3(image_data, x, y, rgba[0], rgba[1], rgba[2]);
}
free(row_pointers[y]);
}
free(row_pointers);
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
}
void image_data_png_write(image_data_t *image_data, const char *filename) {
// SOURCE: https://www.lemoda.net/c/write-png/
FILE *fp;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
size_t x, y;
png_byte **row_pointers = NULL;
/* The following number is set by trial and error only. I cannot
see where it it is documented in the libpng manual.
*/
int pixel_size = 3;
int depth = 8;
fp = fopen(filename, "wb");
if (!fp) abort();
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) abort();
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) abort();
/* Set up error handling. */
if (setjmp(png_jmpbuf(png_ptr))) abort();
/* Set image attributes. */
png_set_IHDR(png_ptr, info_ptr, image_data->width, image_data->height, depth,
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
/* Initialize rows of PNG. */
row_pointers =
png_malloc(png_ptr, image_data->height * sizeof(row_pointers[0]));
for (y = 0; y < image_data->height; y++) {
png_byte *row =
png_malloc(png_ptr, sizeof(uint8_t) * image_data->width * pixel_size);
row_pointers[y] = row;
for (x = 0; x < image_data->width; x++) {
rgb_t *pixel = image_data_at(image_data, x, y);
*row++ = pixel->r;
*row++ = pixel->g;
*row++ = pixel->b;
}
}
/* Write the image data to "fp". */
png_init_io(png_ptr, fp);
png_set_rows(png_ptr, info_ptr, row_pointers);
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
for (y = 0; y < image_data->height; y++) {
png_free(png_ptr, row_pointers[y]);
}
png_free(png_ptr, row_pointers);
png_destroy_write_struct(&png_ptr, &info_ptr);
}
void executable_bitmap_png_read(executable_bitmap_t *executable_bitmap,
const char *filename) {
image_data_png_read(&executable_bitmap->image_data, filename);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment