1 #include "core/png_read.h"
2
3 #include "core/dir.h"
4 #include "core/file.h"
5 #include "core/log.h"
6 #include "graphics/color.h"
7
8 #include "png.h"
9
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #define BYTES_PER_PIXEL 4
14
15 static struct {
16 png_structp png_ptr;
17 png_infop info_ptr;
18 FILE *fp;
19 } data;
20
unload_png(void)21 static void unload_png(void)
22 {
23 png_destroy_read_struct(&data.png_ptr, &data.info_ptr, 0);
24 if (data.fp) {
25 file_close(data.fp);
26 data.fp = 0;
27 }
28 }
29
load_png(const char * path)30 static int load_png(const char *path)
31 {
32 unload_png();
33 png_byte header[8];
34 data.fp = file_open_asset(path, "rb");
35 if (!data.fp) {
36 log_error("Unable to open png file", path, 0);
37 return 0;
38 }
39 size_t bytes_read = fread(header, 1, 8, data.fp);
40 if (bytes_read != 8 || png_sig_cmp(header, 0, 8)) {
41 log_error("Invalid png file", path, 0);
42 unload_png();
43 return 0;
44 }
45
46 data.png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
47 if (!data.png_ptr) {
48 log_error("Unable to create a png struct", 0, 0);
49 unload_png();
50 return 0;
51 }
52 data.info_ptr = png_create_info_struct(data.png_ptr);
53 if (!data.info_ptr) {
54 log_error("Unable to create a png struct", 0, 0);
55 unload_png();
56 return 0;
57 }
58
59 if (setjmp(png_jmpbuf(data.png_ptr))) {
60 log_error("Unable to read png information", 0, 0);
61 unload_png();
62 return 0;
63 }
64 png_init_io(data.png_ptr, data.fp);
65 png_set_sig_bytes(data.png_ptr, 8);
66 png_read_info(data.png_ptr, data.info_ptr);
67 return 1;
68 }
69
png_get_image_size(const char * path,int * width,int * height)70 int png_get_image_size(const char *path, int *width, int *height)
71 {
72 *width = 0;
73 *height = 0;
74 if (!load_png(path)) {
75 return 0;
76 }
77 *width = png_get_image_width(data.png_ptr, data.info_ptr);
78 *height = png_get_image_height(data.png_ptr, data.info_ptr);
79 unload_png();
80
81 return 1;
82 }
83
png_read(const char * path,color_t * pixels,int width,int height)84 int png_read(const char *path, color_t *pixels, int width, int height)
85 {
86 if (!load_png(path)) {
87 return 0;
88 }
89 png_bytep row = 0;
90 if (setjmp(png_jmpbuf(data.png_ptr))) {
91 log_error("Unable to read png file", 0, 0);
92 free(row);
93 unload_png();
94 return 0;
95 }
96 png_set_gray_to_rgb(data.png_ptr);
97 png_set_filler(data.png_ptr, 0xFF, PNG_FILLER_AFTER);
98 png_set_expand(data.png_ptr);
99 png_set_strip_16(data.png_ptr);
100 if (png_set_interlace_handling(data.png_ptr) != 1) {
101 log_info("The image has interlacing and therefore will not open correctly", 0, 0);
102 }
103 png_read_update_info(data.png_ptr, data.info_ptr);
104
105 int image_width = png_get_image_width(data.png_ptr, data.info_ptr);
106 int image_height = png_get_image_height(data.png_ptr, data.info_ptr);
107 int width_padding = 0;
108
109 if (width > image_width) {
110 width_padding = width - image_width;
111 width = image_width;
112 }
113 if (height > image_height) {
114 height = image_height;
115 }
116
117 row = malloc(sizeof(png_byte) * image_width * BYTES_PER_PIXEL);
118 if (!row) {
119 log_error("Unable to load png file. Out of memory", 0, 0);
120 unload_png();
121 return 0;
122 }
123 color_t *dst = pixels;
124 for (int y = 0; y < height; ++y) {
125 png_read_row(data.png_ptr, row, 0);
126 png_bytep src = row;
127 for (int x = 0; x < width; ++x) {
128 *dst = ((color_t) * (src + 0)) << COLOR_BITSHIFT_RED;
129 *dst |= ((color_t) * (src + 1)) << COLOR_BITSHIFT_GREEN;
130 *dst |= ((color_t) * (src + 2)) << COLOR_BITSHIFT_BLUE;
131 *dst |= ((color_t) * (src + 3)) << COLOR_BITSHIFT_ALPHA;
132 dst++;
133 src += BYTES_PER_PIXEL;
134 }
135 dst += width_padding;
136 }
137 free(row);
138 unload_png();
139 return 1;
140 }
141