summaryrefslogtreecommitdiff
path: root/src/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/image.c')
-rw-r--r--src/image.c94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/image.c b/src/image.c
new file mode 100644
index 0000000..e05353c
--- /dev/null
+++ b/src/image.c
@@ -0,0 +1,94 @@
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <png.h>
+#include "log.h"
+#include "image.h"
+
+#define HEADER_BYTES 8
+
+void image_load(struct image *image, const char *filename)
+{
+ FILE *fp = fopen(filename, "rb");
+ uint8_t header[HEADER_BYTES];
+ if (!fp) {
+ log_error("Couldn't open '%s': %s.\n",
+ filename, strerror(errno));
+ return;
+ }
+ if (fread(header, 1, HEADER_BYTES, fp) != HEADER_BYTES) {
+ log_error("Failed to read '%s': %s.\n",
+ filename, strerror(errno));
+ fclose(fp);
+ return;
+ }
+ if (png_sig_cmp(header, 0, HEADER_BYTES)) {
+ log_error("'%s' isn't a PNG file.\n", filename);
+ fclose(fp);
+ return;
+ }
+
+ png_structp png_ptr = png_create_read_struct(
+ PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (!png_ptr) {
+ log_error("Couldn't create PNG read struct.\n");
+ fclose(fp);
+ return;
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ fclose(fp);
+ log_error("Couldn't create PNG info struct.\n");
+ return;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)) != 0) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(fp);
+ log_error("Couldn't setjmp for libpng.\n");
+ return;
+ }
+
+ png_init_io(png_ptr, fp);
+ png_set_sig_bytes(png_ptr, HEADER_BYTES);
+ png_read_info(png_ptr, info_ptr);
+
+ uint32_t width = png_get_image_width(png_ptr, info_ptr);
+ uint32_t height = png_get_image_height(png_ptr, info_ptr);
+ //uint8_t color_type = png_get_color_type(png_ptr, info_ptr);
+ //uint8_t bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+
+ png_set_expand(png_ptr);
+ png_set_gray_to_rgb(png_ptr);
+ png_set_filler(png_ptr, 0xFFu, PNG_FILLER_AFTER);
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ uint32_t row_bytes = png_get_rowbytes(png_ptr, info_ptr);
+ /* Guard against integer overflow */
+ if (height > PNG_SIZE_MAX / row_bytes) {
+ png_error(png_ptr, "image_data buffer would be too large");
+ }
+
+ png_bytep buffer = malloc(height * row_bytes);
+ png_bytepp row_pointers = calloc(height, sizeof(png_bytep));
+ for (uint32_t y = 0; y < height; y++) {
+ row_pointers[y] = &buffer[y * row_bytes];
+ }
+
+ png_read_image(png_ptr, row_pointers);
+ png_read_end(png_ptr, NULL);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ free(row_pointers);
+ fclose(fp);
+
+ image->width = width;
+ image->height = height;
+ image->buffer = buffer;
+}