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