1 /**
2  * @file lv_libpng.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../draw/lv_image_decoder_private.h"
10 #include "../../../lvgl.h"
11 #if LV_USE_LIBPNG
12 
13 #include "lv_libpng.h"
14 #include <png.h>
15 #include <string.h>
16 #include "../../core/lv_global.h"
17 
18 /*********************
19  *      DEFINES
20  *********************/
21 
22 #define DECODER_NAME    "PNG"
23 
24 #define image_cache_draw_buf_handlers &(LV_GLOBAL_DEFAULT()->image_cache_draw_buf_handlers)
25 
26 /**********************
27  *      TYPEDEFS
28  **********************/
29 
30 /**********************
31  *  STATIC PROTOTYPES
32  **********************/
33 static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * src, lv_image_header_t * header);
34 static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
35 static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
36 static lv_draw_buf_t * decode_png(lv_image_decoder_dsc_t * dsc);
37 
38 /**********************
39  *  STATIC VARIABLES
40  **********************/
41 
42 /**********************
43  *      MACROS
44  **********************/
45 
46 /**********************
47  *   GLOBAL FUNCTIONS
48  **********************/
49 
50 /**
51  * Register the PNG decoder functions in LVGL
52  */
lv_libpng_init(void)53 void lv_libpng_init(void)
54 {
55     lv_image_decoder_t * dec = lv_image_decoder_create();
56     lv_image_decoder_set_info_cb(dec, decoder_info);
57     lv_image_decoder_set_open_cb(dec, decoder_open);
58     lv_image_decoder_set_close_cb(dec, decoder_close);
59 
60     dec->name = DECODER_NAME;
61 }
62 
lv_libpng_deinit(void)63 void lv_libpng_deinit(void)
64 {
65     lv_image_decoder_t * dec = NULL;
66     while((dec = lv_image_decoder_get_next(dec)) != NULL) {
67         if(dec->info_cb == decoder_info) {
68             lv_image_decoder_delete(dec);
69             break;
70         }
71     }
72 }
73 
74 /**********************
75  *   STATIC FUNCTIONS
76  **********************/
77 
78 /**
79  * Get info about a PNG image
80  * @param dsc can be file name or pointer to a C array
81  * @param header store the info here
82  * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info
83  */
decoder_info(lv_image_decoder_t * decoder,lv_image_decoder_dsc_t * dsc,lv_image_header_t * header)84 static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc, lv_image_header_t * header)
85 {
86     LV_UNUSED(decoder); /*Unused*/
87 
88     lv_image_src_t src_type = dsc->src_type;          /*Get the source type*/
89 
90     if(src_type == LV_IMAGE_SRC_FILE || src_type == LV_IMAGE_SRC_VARIABLE) {
91         uint32_t * size;
92         static const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
93         uint8_t buf[24];
94 
95         /*If it's a PNG file...*/
96         if(src_type == LV_IMAGE_SRC_FILE) {
97             /* Read the width and height from the file. They have a constant location:
98             * [16..19]: width
99             * [20..23]: height
100             */
101             uint32_t rn;
102             lv_fs_read(&dsc->file, buf, sizeof(buf), &rn);
103 
104             if(rn != sizeof(buf)) return LV_RESULT_INVALID;
105 
106             if(lv_memcmp(buf, magic, sizeof(magic)) != 0) return LV_RESULT_INVALID;
107 
108             size = (uint32_t *)&buf[16];
109         }
110         /*If it's a PNG file in a  C array...*/
111         else {
112             const lv_image_dsc_t * img_dsc = dsc->src;
113             const uint32_t data_size = img_dsc->data_size;
114             size = ((uint32_t *)img_dsc->data) + 4;
115 
116             if(data_size < sizeof(magic)) return LV_RESULT_INVALID;
117             if(lv_memcmp(img_dsc->data, magic, sizeof(magic)) != 0) return LV_RESULT_INVALID;
118         }
119 
120         /*Save the data in the header*/
121         header->cf = LV_COLOR_FORMAT_ARGB8888;
122         /*The width and height are stored in Big endian format so convert them to little endian*/
123         header->w = (int32_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);
124         header->h = (int32_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);
125 
126         return LV_RESULT_OK;
127     }
128 
129     return LV_RESULT_INVALID;         /*If didn't succeeded earlier then it's an error*/
130 }
131 
132 /**
133  * Open a PNG image and return the decided image
134  * @param decoder pointer to the decoder
135  * @param dsc     pointer to the decoder descriptor
136  * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't open the image
137  */
decoder_open(lv_image_decoder_t * decoder,lv_image_decoder_dsc_t * dsc)138 static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
139 {
140     LV_UNUSED(decoder); /*Unused*/
141 
142     LV_PROFILER_DECODER_BEGIN_TAG("lv_libpng_decoder_open");
143 
144     lv_draw_buf_t * decoded;
145     decoded = decode_png(dsc);
146 
147     if(decoded == NULL) {
148         LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
149         return LV_RESULT_INVALID;
150     }
151 
152     lv_draw_buf_t * adjusted = lv_image_decoder_post_process(dsc, decoded);
153     if(adjusted == NULL) {
154         lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
155         LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
156         return LV_RESULT_INVALID;
157     }
158 
159     /*The adjusted draw buffer is newly allocated.*/
160     if(adjusted != decoded) {
161         lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
162         decoded = adjusted;
163     }
164 
165     dsc->decoded = decoded;
166 
167     if(dsc->args.no_cache) {
168         LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
169         return LV_RESULT_OK;
170     }
171 
172     /*If the image cache is disabled, just return the decoded image*/
173     if(!lv_image_cache_is_enabled()) {
174         LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
175         return LV_RESULT_OK;
176     }
177 
178     /*Add the decoded image to the cache*/
179     lv_image_cache_data_t search_key;
180     search_key.src_type = dsc->src_type;
181     search_key.src = dsc->src;
182     search_key.slot.size = decoded->data_size;
183 
184     lv_cache_entry_t * entry = lv_image_decoder_add_to_cache(decoder, &search_key, decoded, NULL);
185 
186     if(entry == NULL) {
187         lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
188         LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
189         return LV_RESULT_INVALID;
190     }
191     dsc->cache_entry = entry;
192 
193     LV_PROFILER_DECODER_END_TAG("lv_libpng_decoder_open");
194     return LV_RESULT_OK;     /*The image is fully decoded. Return with its pointer*/
195 }
196 
197 /**
198  * Free the allocated resources
199  */
decoder_close(lv_image_decoder_t * decoder,lv_image_decoder_dsc_t * dsc)200 static void decoder_close(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc)
201 {
202     LV_UNUSED(decoder); /*Unused*/
203 
204     if(dsc->args.no_cache ||
205        !lv_image_cache_is_enabled()) lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, (lv_draw_buf_t *)dsc->decoded);
206 }
207 
alloc_file(const char * filename,uint32_t * size)208 static uint8_t * alloc_file(const char * filename, uint32_t * size)
209 {
210     uint8_t * data = NULL;
211     lv_fs_file_t f;
212     uint32_t data_size;
213     uint32_t rn;
214     lv_fs_res_t res;
215 
216     *size = 0;
217 
218     res = lv_fs_open(&f, filename, LV_FS_MODE_RD);
219     if(res != LV_FS_RES_OK) {
220         LV_LOG_WARN("can't open %s", filename);
221         return NULL;
222     }
223 
224     res = lv_fs_seek(&f, 0, LV_FS_SEEK_END);
225     if(res != LV_FS_RES_OK) {
226         goto failed;
227     }
228 
229     res = lv_fs_tell(&f, &data_size);
230     if(res != LV_FS_RES_OK) {
231         goto failed;
232     }
233 
234     res = lv_fs_seek(&f, 0, LV_FS_SEEK_SET);
235     if(res != LV_FS_RES_OK) {
236         goto failed;
237     }
238 
239     /*Read file to buffer*/
240     data = lv_malloc(data_size);
241     if(data == NULL) {
242         LV_LOG_WARN("malloc failed for data");
243         goto failed;
244     }
245 
246     res = lv_fs_read(&f, data, data_size, &rn);
247 
248     if(res == LV_FS_RES_OK && rn == data_size) {
249         *size = rn;
250     }
251     else {
252         LV_LOG_WARN("read file failed");
253         lv_free(data);
254         data = NULL;
255     }
256 
257 failed:
258     lv_fs_close(&f);
259 
260     return data;
261 }
262 
decode_png(lv_image_decoder_dsc_t * dsc)263 static lv_draw_buf_t * decode_png(lv_image_decoder_dsc_t * dsc)
264 {
265     int ret;
266     uint8_t * png_data;
267     uint32_t png_data_size;
268     /*Prepare png_image*/
269     png_image image;
270     lv_memzero(&image, sizeof(image));
271     image.version = PNG_IMAGE_VERSION;
272 
273     if(dsc->src_type == LV_IMAGE_SRC_FILE) {
274         png_data = alloc_file(dsc->src, &png_data_size);
275         if(png_data == NULL) {
276             LV_LOG_WARN("can't load file: %s", (const char *)dsc->src);
277             return NULL;
278         }
279     }
280     else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE) {
281         const lv_image_dsc_t * img_dsc = dsc->src;
282         png_data = (uint8_t *)img_dsc->data;
283         png_data_size = img_dsc->data_size;
284     }
285     else
286         return NULL;
287 
288     /*Ready to read file*/
289     ret = png_image_begin_read_from_memory(&image, png_data, png_data_size);
290     if(!ret) {
291         LV_LOG_ERROR("png read failed: %d", ret);
292         if(dsc->src_type == LV_IMAGE_SRC_FILE)
293             lv_free(png_data);
294         return NULL;
295     }
296 
297     lv_color_format_t cf;
298     if(dsc->args.use_indexed && (image.format & PNG_FORMAT_FLAG_COLORMAP)) {
299         cf = LV_COLOR_FORMAT_I8;
300         image.format = PNG_FORMAT_BGRA_COLORMAP;
301     }
302     else {
303         cf = LV_COLOR_FORMAT_ARGB8888;
304         image.format = PNG_FORMAT_BGRA;
305     }
306 
307     /*Alloc image buffer*/
308     lv_draw_buf_t * decoded;
309     decoded = lv_draw_buf_create_ex(image_cache_draw_buf_handlers, image.width, image.height, cf, LV_STRIDE_AUTO);
310     if(decoded == NULL) {
311 
312         if(dsc->src_type == LV_IMAGE_SRC_FILE) {
313             LV_LOG_ERROR("alloc PNG_IMAGE_SIZE(%" LV_PRIu32 ") failed: %s", (uint32_t)PNG_IMAGE_SIZE(image),
314                          (const char *)dsc->src);
315             lv_free(png_data);
316         }
317         else if(dsc->src_type == LV_IMAGE_SRC_VARIABLE)
318             LV_LOG_ERROR("alloc PNG_IMAGE_SIZE(%" LV_PRIu32 ")", (uint32_t)PNG_IMAGE_SIZE(image));
319 
320         png_image_free(&image);
321         return NULL;
322     }
323 
324     void * palette = decoded->data;
325     void * map = decoded->data + LV_COLOR_INDEXED_PALETTE_SIZE(cf) * sizeof(lv_color32_t);
326 
327     /*Start decoding*/
328     ret = png_image_finish_read(&image, NULL, map, decoded->header.stride, palette);
329     png_image_free(&image);
330     if(dsc->src_type == LV_IMAGE_SRC_FILE)
331         lv_free(png_data);
332     if(!ret) {
333         LV_LOG_ERROR("png decode failed: %s", image.message);
334         lv_draw_buf_destroy_user(image_cache_draw_buf_handlers, decoded);
335         return NULL;
336     }
337 
338     return decoded;
339 }
340 
341 #endif /*LV_USE_LIBPNG*/
342