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(&fn[strlen(fn) - 3], "png")) {              /*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             lv_fs_seek(&f, 16, LV_FS_SEEK_SET);
84             uint32_t rn;
85             lv_fs_read(&f, &size, 8, &rn);
86             if(rn != 8) return LV_RES_INV;
87             lv_fs_close(&f);
88             /*Save the data in the header*/
89             header->always_zero = 0;
90             header->cf = LV_IMG_CF_RAW_ALPHA;
91             /*The width and height are stored in Big endian format so convert them to little endian*/
92             header->w = (lv_coord_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
93             header->h = (lv_coord_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
94 
95             return LV_RES_OK;
96         }
97     }
98     /*If it's a PNG file in a  C array...*/
99     else if(src_type == LV_IMG_SRC_VARIABLE) {
100         const lv_img_dsc_t * img_dsc = src;
101         const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
102         if(memcmp(magic, img_dsc->data, sizeof(magic))) return LV_RES_INV;
103         header->always_zero = 0;
104         header->cf = img_dsc->header.cf;       /*Save the color format*/
105         header->w = img_dsc->header.w;         /*Save the color width*/
106         header->h = img_dsc->header.h;         /*Save the color height*/
107         return LV_RES_OK;
108     }
109 
110     return LV_RES_INV;         /*If didn't succeeded earlier then it's an error*/
111 }
112 
113 
114 /**
115  * Open a PNG image and return the decided image
116  * @param src can be file name or pointer to a C array
117  * @param style style of the image object (unused now but certain formats might use it)
118  * @return pointer to the decoded image or  `LV_IMG_DECODER_OPEN_FAIL` if failed
119  */
decoder_open(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)120 static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
121 {
122 
123     (void) decoder; /*Unused*/
124     uint32_t error;                 /*For the return values of PNG decoder functions*/
125 
126     uint8_t * img_data = NULL;
127 
128     /*If it's a PNG file...*/
129     if(dsc->src_type == LV_IMG_SRC_FILE) {
130         const char * fn = dsc->src;
131 
132         if(!strcmp(&fn[strlen(fn) - 3], "png")) {              /*Check the extension*/
133 
134             /*Load the PNG file into buffer. It's still compressed (not decoded)*/
135             unsigned char * png_data;      /*Pointer to the loaded data. Same as the original file just loaded into the RAM*/
136             size_t png_data_size;          /*Size of `png_data` in bytes*/
137 
138             error = lodepng_load_file(&png_data, &png_data_size, fn);   /*Load the file*/
139             if(error) {
140                 LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error));
141                 return LV_RES_INV;
142             }
143 
144             /*Decode the PNG image*/
145             uint32_t png_width;             /*Will be the width of the decoded image*/
146             uint32_t png_height;            /*Will be the width of the decoded image*/
147 
148             /*Decode the loaded image in ARGB8888 */
149             error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size);
150             lv_mem_free(png_data); /*Free the loaded file*/
151             if(error) {
152                 LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error));
153                 return LV_RES_INV;
154             }
155 
156             /*Convert the image to the system's color depth*/
157             convert_color_depth(img_data,  png_width * png_height);
158             dsc->img_data = img_data;
159             return LV_RES_OK;     /*The image is fully decoded. Return with its pointer*/
160         }
161     }
162     /*If it's a PNG file in a  C array...*/
163     else if(dsc->src_type == LV_IMG_SRC_VARIABLE) {
164         const lv_img_dsc_t * img_dsc = dsc->src;
165         uint32_t png_width;             /*No used, just required by he decoder*/
166         uint32_t png_height;            /*No used, just required by he decoder*/
167 
168         /*Decode the image in ARGB8888 */
169         error = lodepng_decode32(&img_data, &png_width, &png_height, img_dsc->data, img_dsc->data_size);
170 
171         if(error) {
172             return LV_RES_INV;
173         }
174 
175         /*Convert the image to the system's color depth*/
176         convert_color_depth(img_data,  png_width * png_height);
177 
178         dsc->img_data = img_data;
179         return LV_RES_OK;     /*Return with its pointer*/
180     }
181 
182     return LV_RES_INV;    /*If not returned earlier then it failed*/
183 }
184 
185 /**
186  * Free the allocated resources
187  */
decoder_close(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)188 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
189 {
190     LV_UNUSED(decoder); /*Unused*/
191     if(dsc->img_data) {
192         lv_mem_free((uint8_t *)dsc->img_data);
193         dsc->img_data = NULL;
194     }
195 }
196 
197 /**
198  * If the display is not in 32 bit format (ARGB888) then covert the image to the current color depth
199  * @param img the ARGB888 image
200  * @param px_cnt number of pixels in `img`
201  */
convert_color_depth(uint8_t * img,uint32_t px_cnt)202 static void convert_color_depth(uint8_t * img, uint32_t px_cnt)
203 {
204 #if LV_COLOR_DEPTH == 32
205     lv_color32_t * img_argb = (lv_color32_t *)img;
206     lv_color_t c;
207     lv_color_t * img_c = (lv_color_t *) img;
208     uint32_t i;
209     for(i = 0; i < px_cnt; i++) {
210         c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue);
211         img_c[i].ch.red = c.ch.blue;
212         img_c[i].ch.blue = c.ch.red;
213     }
214 #elif LV_COLOR_DEPTH == 16
215     lv_color32_t * img_argb = (lv_color32_t *)img;
216     lv_color_t c;
217     uint32_t i;
218     for(i = 0; i < px_cnt; i++) {
219         c = lv_color_make(img_argb[i].ch.blue, img_argb[i].ch.green, img_argb[i].ch.red);
220         img[i * 3 + 2] = img_argb[i].ch.alpha;
221         img[i * 3 + 1] = c.full >> 8;
222         img[i * 3 + 0] = c.full & 0xFF;
223     }
224 #elif LV_COLOR_DEPTH == 8
225     lv_color32_t * img_argb = (lv_color32_t *)img;
226     lv_color_t c;
227     uint32_t i;
228     for(i = 0; i < px_cnt; i++) {
229         c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue);
230         img[i * 2 + 1] = img_argb[i].ch.alpha;
231         img[i * 2 + 0] = c.full;
232     }
233 #elif LV_COLOR_DEPTH == 1
234     lv_color32_t * img_argb = (lv_color32_t *)img;
235     uint8_t b;
236     uint32_t i;
237     for(i = 0; i < px_cnt; i++) {
238         b = img_argb[i].ch.red | img_argb[i].ch.green | img_argb[i].ch.blue;
239         img[i * 2 + 1] = img_argb[i].ch.alpha;
240         img[i * 2 + 0] = b > 128 ? 1 : 0;
241     }
242 #endif
243 }
244 
245 #endif /*LV_USE_PNG*/
246 
247 
248