1 /**
2 * @file lv_draw_img.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_draw_img.h"
10 #include "lv_img_cache.h"
11 #include "../hal/lv_hal_disp.h"
12 #include "../misc/lv_log.h"
13 #include "../core/lv_refr.h"
14 #include "../misc/lv_mem.h"
15 #include "../misc/lv_math.h"
16
17 /*********************
18 * DEFINES
19 *********************/
20
21 /**********************
22 * TYPEDEFS
23 **********************/
24
25 /**********************
26 * STATIC PROTOTYPES
27 **********************/
28 LV_ATTRIBUTE_FAST_MEM static lv_res_t decode_and_draw(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
29 const lv_area_t * coords, const void * src);
30
31 static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg);
32 static void draw_cleanup(_lv_img_cache_entry_t * cache);
33
34 /**********************
35 * STATIC VARIABLES
36 **********************/
37
38 /**********************
39 * MACROS
40 **********************/
41
42 /**********************
43 * GLOBAL FUNCTIONS
44 **********************/
45
lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)46 void lv_draw_img_dsc_init(lv_draw_img_dsc_t * dsc)
47 {
48 lv_memset_00(dsc, sizeof(lv_draw_img_dsc_t));
49 dsc->recolor = lv_color_black();
50 dsc->opa = LV_OPA_COVER;
51 dsc->zoom = LV_IMG_ZOOM_NONE;
52 dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
53 }
54
55 /**
56 * Draw an image
57 * @param coords the coordinates of the image
58 * @param mask the image will be drawn only in this area
59 * @param src pointer to a lv_color_t array which contains the pixels of the image
60 * @param dsc pointer to an initialized `lv_draw_img_dsc_t` variable
61 */
lv_draw_img(lv_draw_ctx_t * draw_ctx,const lv_draw_img_dsc_t * dsc,const lv_area_t * coords,const void * src)62 void lv_draw_img(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords, const void * src)
63 {
64 if(src == NULL) {
65 LV_LOG_WARN("Image draw: src is NULL");
66 show_error(draw_ctx, coords, "No\ndata");
67 return;
68 }
69
70 if(dsc->opa <= LV_OPA_MIN) return;
71
72 lv_res_t res;
73 if(draw_ctx->draw_img) {
74 res = draw_ctx->draw_img(draw_ctx, dsc, coords, src);
75 }
76 else {
77 res = decode_and_draw(draw_ctx, dsc, coords, src);
78 }
79
80 if(res == LV_RES_INV) {
81 LV_LOG_WARN("Image draw error");
82 show_error(draw_ctx, coords, "No\ndata");
83 return;
84 }
85 }
86
87 /**
88 * Get the pixel size of a color format in bits
89 * @param cf a color format (`LV_IMG_CF_...`)
90 * @return the pixel size in bits
91 */
lv_img_cf_get_px_size(lv_img_cf_t cf)92 uint8_t lv_img_cf_get_px_size(lv_img_cf_t cf)
93 {
94 uint8_t px_size = 0;
95
96 switch(cf) {
97 case LV_IMG_CF_UNKNOWN:
98 case LV_IMG_CF_RAW:
99 px_size = 0;
100 break;
101 case LV_IMG_CF_TRUE_COLOR:
102 case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
103 px_size = LV_COLOR_SIZE;
104 break;
105 case LV_IMG_CF_TRUE_COLOR_ALPHA:
106 px_size = LV_IMG_PX_SIZE_ALPHA_BYTE << 3;
107 break;
108 case LV_IMG_CF_INDEXED_1BIT:
109 case LV_IMG_CF_ALPHA_1BIT:
110 px_size = 1;
111 break;
112 case LV_IMG_CF_INDEXED_2BIT:
113 case LV_IMG_CF_ALPHA_2BIT:
114 px_size = 2;
115 break;
116 case LV_IMG_CF_INDEXED_4BIT:
117 case LV_IMG_CF_ALPHA_4BIT:
118 px_size = 4;
119 break;
120 case LV_IMG_CF_INDEXED_8BIT:
121 case LV_IMG_CF_ALPHA_8BIT:
122 px_size = 8;
123 break;
124 default:
125 px_size = 0;
126 break;
127 }
128
129 return px_size;
130 }
131
132 /**
133 * Check if a color format is chroma keyed or not
134 * @param cf a color format (`LV_IMG_CF_...`)
135 * @return true: chroma keyed; false: not chroma keyed
136 */
lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)137 bool lv_img_cf_is_chroma_keyed(lv_img_cf_t cf)
138 {
139 bool is_chroma_keyed = false;
140
141 switch(cf) {
142 case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED:
143 case LV_IMG_CF_RAW_CHROMA_KEYED:
144 is_chroma_keyed = true;
145 break;
146
147 default:
148 is_chroma_keyed = false;
149 break;
150 }
151
152 return is_chroma_keyed;
153 }
154
155 /**
156 * Check if a color format has alpha channel or not
157 * @param cf a color format (`LV_IMG_CF_...`)
158 * @return true: has alpha channel; false: doesn't have alpha channel
159 */
lv_img_cf_has_alpha(lv_img_cf_t cf)160 bool lv_img_cf_has_alpha(lv_img_cf_t cf)
161 {
162 bool has_alpha = false;
163
164 switch(cf) {
165 case LV_IMG_CF_TRUE_COLOR_ALPHA:
166 case LV_IMG_CF_RAW_ALPHA:
167 case LV_IMG_CF_INDEXED_1BIT:
168 case LV_IMG_CF_INDEXED_2BIT:
169 case LV_IMG_CF_INDEXED_4BIT:
170 case LV_IMG_CF_INDEXED_8BIT:
171 case LV_IMG_CF_ALPHA_1BIT:
172 case LV_IMG_CF_ALPHA_2BIT:
173 case LV_IMG_CF_ALPHA_4BIT:
174 case LV_IMG_CF_ALPHA_8BIT:
175 has_alpha = true;
176 break;
177 default:
178 has_alpha = false;
179 break;
180 }
181
182 return has_alpha;
183 }
184
185 /**
186 * Get the type of an image source
187 * @param src pointer to an image source:
188 * - pointer to an 'lv_img_t' variable (image stored internally and compiled into the code)
189 * - a path to a file (e.g. "S:/folder/image.bin")
190 * - or a symbol (e.g. LV_SYMBOL_CLOSE)
191 * @return type of the image source LV_IMG_SRC_VARIABLE/FILE/SYMBOL/UNKNOWN
192 */
lv_img_src_get_type(const void * src)193 lv_img_src_t lv_img_src_get_type(const void * src)
194 {
195 lv_img_src_t img_src_type = LV_IMG_SRC_UNKNOWN;
196
197 if(src == NULL) return img_src_type;
198 const uint8_t * u8_p = src;
199
200 /*The first byte shows the type of the image source*/
201 if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
202 img_src_type = LV_IMG_SRC_FILE; /*If it's an ASCII character then it's file name*/
203 }
204 else if(u8_p[0] >= 0x80) {
205 img_src_type = LV_IMG_SRC_SYMBOL; /*Symbols begins after 0x7F*/
206 }
207 else {
208 img_src_type = LV_IMG_SRC_VARIABLE; /*`lv_img_dsc_t` is draw to the first byte < 0x20*/
209 }
210
211 if(LV_IMG_SRC_UNKNOWN == img_src_type) {
212 LV_LOG_WARN("lv_img_src_get_type: unknown image type");
213 }
214
215 return img_src_type;
216 }
217
lv_draw_img_decoded(lv_draw_ctx_t * draw_ctx,const lv_draw_img_dsc_t * dsc,const lv_area_t * coords,const uint8_t * map_p,lv_img_cf_t color_format)218 void lv_draw_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
219 const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format)
220 {
221 if(draw_ctx->draw_img_decoded == NULL) return;
222
223 draw_ctx->draw_img_decoded(draw_ctx, dsc, coords, map_p, color_format);
224 }
225
226 /**********************
227 * STATIC FUNCTIONS
228 **********************/
229
decode_and_draw(lv_draw_ctx_t * draw_ctx,const lv_draw_img_dsc_t * draw_dsc,const lv_area_t * coords,const void * src)230 LV_ATTRIBUTE_FAST_MEM static lv_res_t decode_and_draw(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
231 const lv_area_t * coords, const void * src)
232 {
233 if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;
234
235 _lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);
236
237 if(cdsc == NULL) return LV_RES_INV;
238
239
240 lv_img_cf_t cf;
241 if(lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED;
242 else if(lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
243 else cf = LV_IMG_CF_TRUE_COLOR;
244
245 if(cdsc->dec_dsc.error_msg != NULL) {
246 LV_LOG_WARN("Image draw error");
247
248 show_error(draw_ctx, coords, cdsc->dec_dsc.error_msg);
249 }
250 /*The decoder could open the image and gave the entire uncompressed image.
251 *Just draw it!*/
252 else if(cdsc->dec_dsc.img_data) {
253 lv_area_t map_area_rot;
254 lv_area_copy(&map_area_rot, coords);
255 if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) {
256 int32_t w = lv_area_get_width(coords);
257 int32_t h = lv_area_get_height(coords);
258
259 _lv_img_buf_get_transformed_area(&map_area_rot, w, h, draw_dsc->angle, draw_dsc->zoom, &draw_dsc->pivot);
260
261 map_area_rot.x1 += coords->x1;
262 map_area_rot.y1 += coords->y1;
263 map_area_rot.x2 += coords->x1;
264 map_area_rot.y2 += coords->y1;
265 }
266
267 lv_area_t clip_com; /*Common area of mask and coords*/
268 bool union_ok;
269 union_ok = _lv_area_intersect(&clip_com, draw_ctx->clip_area, &map_area_rot);
270 /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
271 if(union_ok == false) {
272 draw_cleanup(cdsc);
273 return LV_RES_OK;
274 }
275
276 const lv_area_t * clip_area_ori = draw_ctx->clip_area;
277 draw_ctx->clip_area = &clip_com;
278 lv_draw_img_decoded(draw_ctx, draw_dsc, coords, cdsc->dec_dsc.img_data, cf);
279 draw_ctx->clip_area = clip_area_ori;
280 }
281 /*The whole uncompressed image is not available. Try to read it line-by-line*/
282 else {
283 lv_area_t mask_com; /*Common area of mask and coords*/
284 bool union_ok;
285 union_ok = _lv_area_intersect(&mask_com, draw_ctx->clip_area, coords);
286 /*Out of mask. There is nothing to draw so the image is drawn successfully.*/
287 if(union_ok == false) {
288 draw_cleanup(cdsc);
289 return LV_RES_OK;
290 }
291
292 int32_t width = lv_area_get_width(&mask_com);
293
294 uint8_t * buf = lv_mem_buf_get(lv_area_get_width(&mask_com) *
295 LV_IMG_PX_SIZE_ALPHA_BYTE); /*+1 because of the possible alpha byte*/
296
297 const lv_area_t * clip_area_ori = draw_ctx->clip_area;
298 lv_area_t line;
299 lv_area_copy(&line, &mask_com);
300 lv_area_set_height(&line, 1);
301 int32_t x = mask_com.x1 - coords->x1;
302 int32_t y = mask_com.y1 - coords->y1;
303 int32_t row;
304 lv_res_t read_res;
305 for(row = mask_com.y1; row <= mask_com.y2; row++) {
306 lv_area_t mask_line;
307 union_ok = _lv_area_intersect(&mask_line, clip_area_ori, &line);
308 if(union_ok == false) continue;
309
310 read_res = lv_img_decoder_read_line(&cdsc->dec_dsc, x, y, width, buf);
311 if(read_res != LV_RES_OK) {
312 lv_img_decoder_close(&cdsc->dec_dsc);
313 LV_LOG_WARN("Image draw can't read the line");
314 lv_mem_buf_release(buf);
315 draw_cleanup(cdsc);
316 draw_ctx->clip_area = clip_area_ori;
317 return LV_RES_INV;
318 }
319
320 draw_ctx->clip_area = &mask_line;
321 lv_draw_img_decoded(draw_ctx, draw_dsc, &line, buf, cf);
322 line.y1++;
323 line.y2++;
324 y++;
325 }
326 draw_ctx->clip_area = clip_area_ori;
327 lv_mem_buf_release(buf);
328 }
329
330 draw_cleanup(cdsc);
331 return LV_RES_OK;
332 }
333
334
show_error(lv_draw_ctx_t * draw_ctx,const lv_area_t * coords,const char * msg)335 static void show_error(lv_draw_ctx_t * draw_ctx, const lv_area_t * coords, const char * msg)
336 {
337 lv_draw_rect_dsc_t rect_dsc;
338 lv_draw_rect_dsc_init(&rect_dsc);
339 rect_dsc.bg_color = lv_color_white();
340 lv_draw_rect(draw_ctx, &rect_dsc, coords);
341
342 lv_draw_label_dsc_t label_dsc;
343 lv_draw_label_dsc_init(&label_dsc);
344 lv_draw_label(draw_ctx, &label_dsc, coords, msg, NULL);
345 }
346
draw_cleanup(_lv_img_cache_entry_t * cache)347 static void draw_cleanup(_lv_img_cache_entry_t * cache)
348 {
349 /*Automatically close images with no caching*/
350 #if LV_IMG_CACHE_DEF_SIZE == 0
351 lv_img_decoder_close(&cache->dec_dsc);
352 #else
353 LV_UNUSED(cache);
354 #endif
355 }
356