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