1 /**
2 * @file lv_png.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "../../../lvgl.h"
10 #if LV_USE_PNG
11
12 #include "lv_png.h"
13 #include "lodepng.h"
14 #include <stdlib.h>
15
16 /*********************
17 * DEFINES
18 *********************/
19
20 /**********************
21 * TYPEDEFS
22 **********************/
23
24 /**********************
25 * STATIC PROTOTYPES
26 **********************/
27 static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
28 static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
29 static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
30 static void convert_color_depth(uint8_t * img, uint32_t px_cnt);
31
32 /**********************
33 * STATIC VARIABLES
34 **********************/
35
36 /**********************
37 * MACROS
38 **********************/
39
40 /**********************
41 * GLOBAL FUNCTIONS
42 **********************/
43
44 /**
45 * Register the PNG decoder functions in LVGL
46 */
lv_png_init(void)47 void lv_png_init(void)
48 {
49 lv_img_decoder_t * dec = lv_img_decoder_create();
50 lv_img_decoder_set_info_cb(dec, decoder_info);
51 lv_img_decoder_set_open_cb(dec, decoder_open);
52 lv_img_decoder_set_close_cb(dec, decoder_close);
53 }
54
55 /**********************
56 * STATIC FUNCTIONS
57 **********************/
58
59 /**
60 * Get info about a PNG image
61 * @param src can be file name or pointer to a C array
62 * @param header store the info here
63 * @return LV_RES_OK: no error; LV_RES_INV: can't get the info
64 */
decoder_info(struct _lv_img_decoder_t * decoder,const void * src,lv_img_header_t * header)65 static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
66 {
67 (void) decoder; /*Unused*/
68 lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/
69
70 /*If it's a PNG file...*/
71 if(src_type == LV_IMG_SRC_FILE) {
72 const char * fn = src;
73 if(strcmp(lv_fs_get_ext(fn), "png") == 0) { /*Check the extension*/
74
75 /* Read the width and height from the file. They have a constant location:
76 * [16..23]: width
77 * [24..27]: height
78 */
79 uint32_t size[2];
80 lv_fs_file_t f;
81 lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD);
82 if(res != LV_FS_RES_OK) return LV_RES_INV;
83
84 lv_fs_seek(&f, 16, LV_FS_SEEK_SET);
85
86 uint32_t rn;
87 lv_fs_read(&f, &size, 8, &rn);
88 lv_fs_close(&f);
89
90 if(rn != 8) return LV_RES_INV;
91
92 /*Save the data in the header*/
93 header->always_zero = 0;
94 header->cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
95 /*The width and height are stored in Big endian format so convert them to little endian*/
96 header->w = (lv_coord_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
97 header->h = (lv_coord_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
98
99 return LV_RES_OK;
100 }
101 }
102 /*If it's a PNG file in a C array...*/
103 else if(src_type == LV_IMG_SRC_VARIABLE) {
104 const lv_img_dsc_t * img_dsc = src;
105 const uint32_t data_size = img_dsc->data_size;
106 const uint32_t * size = ((uint32_t *)img_dsc->data) + 4;
107 const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
108 if(data_size < sizeof(magic)) return LV_RES_INV;
109 if(memcmp(magic, img_dsc->data, sizeof(magic))) return LV_RES_INV;
110 header->always_zero = 0;
111
112 if(img_dsc->header.cf) {
113 header->cf = img_dsc->header.cf; /*Save the color format*/
114 }
115 else {
116 header->cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
117 }
118
119 if(img_dsc->header.w) {
120 header->w = img_dsc->header.w; /*Save the image width*/
121 }
122 else {
123 header->w = (lv_coord_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
124 }
125
126 if(img_dsc->header.h) {
127 header->h = img_dsc->header.h; /*Save the color height*/
128 }
129 else {
130 header->h = (lv_coord_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
131 }
132
133 return LV_RES_OK;
134 }
135
136 return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/
137 }
138
139 /**
140 * Open a PNG image and return the decided image
141 * @param src can be file name or pointer to a C array
142 * @param style style of the image object (unused now but certain formats might use it)
143 * @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed
144 */
decoder_open(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)145 static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
146 {
147
148 (void) decoder; /*Unused*/
149 uint32_t error; /*For the return values of PNG decoder functions*/
150
151 uint8_t * img_data = NULL;
152
153 /*If it's a PNG file...*/
154 if(dsc->src_type == LV_IMG_SRC_FILE) {
155 const char * fn = dsc->src;
156 if(strcmp(lv_fs_get_ext(fn), "png") == 0) { /*Check the extension*/
157
158 /*Load the PNG file into buffer. It's still compressed (not decoded)*/
159 unsigned char * png_data; /*Pointer to the loaded data. Same as the original file just loaded into the RAM*/
160 size_t png_data_size; /*Size of `png_data` in bytes*/
161
162 error = lodepng_load_file(&png_data, &png_data_size, fn); /*Load the file*/
163 if(error) {
164 LV_LOG_WARN("error %" LV_PRIu32 ": %s\n", error, lodepng_error_text(error));
165 return LV_RES_INV;
166 }
167
168 /*Decode the PNG image*/
169 unsigned png_width; /*Will be the width of the decoded image*/
170 unsigned png_height; /*Will be the width of the decoded image*/
171
172 /*Decode the loaded image in ARGB8888 */
173 error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size);
174 lv_mem_free(png_data); /*Free the loaded file*/
175 if(error) {
176 if(img_data != NULL) {
177 lv_mem_free(img_data);
178 }
179 LV_LOG_WARN("error %" LV_PRIu32 ": %s\n", error, lodepng_error_text(error));
180 return LV_RES_INV;
181 }
182
183 /*Convert the image to the system's color depth*/
184 convert_color_depth(img_data, png_width * png_height);
185 dsc->img_data = img_data;
186 return LV_RES_OK; /*The image is fully decoded. Return with its pointer*/
187 }
188 }
189 /*If it's a PNG file in a C array...*/
190 else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
191 const lv_img_dsc_t * img_dsc = dsc->src;
192 unsigned png_width; /*No used, just required by he decoder*/
193 unsigned png_height; /*No used, just required by he decoder*/
194
195 /*Decode the image in ARGB8888 */
196 error = lodepng_decode32(&img_data, &png_width, &png_height, img_dsc->data, img_dsc->data_size);
197
198 if(error) {
199 if(img_data != NULL) {
200 lv_mem_free(img_data);
201 }
202 return LV_RES_INV;
203 }
204
205 /*Convert the image to the system's color depth*/
206 convert_color_depth(img_data, png_width * png_height);
207
208 dsc->img_data = img_data;
209 return LV_RES_OK; /*Return with its pointer*/
210 }
211
212 return LV_RES_INV; /*If not returned earlier then it failed*/
213 }
214
215 /**
216 * Free the allocated resources
217 */
decoder_close(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)218 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
219 {
220 LV_UNUSED(decoder); /*Unused*/
221 if(dsc->img_data) {
222 lv_mem_free((uint8_t *)dsc->img_data);
223 dsc->img_data = NULL;
224 }
225 }
226
227 /**
228 * If the display is not in 32 bit format (ARGB888) then covert the image to the current color depth
229 * @param img the ARGB888 image
230 * @param px_cnt number of pixels in `img`
231 */
convert_color_depth(uint8_t * img,uint32_t px_cnt)232 static void convert_color_depth(uint8_t * img, uint32_t px_cnt)
233 {
234 #if LV_COLOR_DEPTH == 32
235 lv_color32_t * img_argb = (lv_color32_t *)img;
236 lv_color_t c;
237 lv_color_t * img_c = (lv_color_t *) img;
238 uint32_t i;
239 for(i = 0; i < px_cnt; i++) {
240 c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue);
241 img_c[i].ch.red = c.ch.blue;
242 img_c[i].ch.blue = c.ch.red;
243 }
244 #elif LV_COLOR_DEPTH == 16
245 lv_color32_t * img_argb = (lv_color32_t *)img;
246 lv_color_t c;
247 uint32_t i;
248 for(i = 0; i < px_cnt; i++) {
249 c = lv_color_make(img_argb[i].ch.blue, img_argb[i].ch.green, img_argb[i].ch.red);
250 img[i * 3 + 2] = img_argb[i].ch.alpha;
251 img[i * 3 + 1] = c.full >> 8;
252 img[i * 3 + 0] = c.full & 0xFF;
253 }
254 #elif LV_COLOR_DEPTH == 8
255 lv_color32_t * img_argb = (lv_color32_t *)img;
256 lv_color_t c;
257 uint32_t i;
258 for(i = 0; i < px_cnt; i++) {
259 c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue);
260 img[i * 2 + 1] = img_argb[i].ch.alpha;
261 img[i * 2 + 0] = c.full;
262 }
263 #elif LV_COLOR_DEPTH == 1
264 lv_color32_t * img_argb = (lv_color32_t *)img;
265 uint8_t b;
266 uint32_t i;
267 for(i = 0; i < px_cnt; i++) {
268 b = img_argb[i].ch.red | img_argb[i].ch.green | img_argb[i].ch.blue;
269 img[i * 2 + 1] = img_argb[i].ch.alpha;
270 img[i * 2 + 0] = b > 128 ? 1 : 0;
271 }
272 #endif
273 }
274
275 #endif /*LV_USE_PNG*/
276