1 /**
2  * @file lv_draw_img.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_image_private.h"
10 #include "../misc/lv_area_private.h"
11 #include "lv_image_decoder_private.h"
12 #include "lv_draw_private.h"
13 #include "../display/lv_display.h"
14 #include "../misc/lv_log.h"
15 #include "../misc/lv_math.h"
16 #include "../core/lv_refr.h"
17 #include "../stdlib/lv_mem.h"
18 #include "../stdlib/lv_string.h"
19 
20 /*********************
21  *      DEFINES
22  *********************/
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 
32 static void img_decode_and_draw(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
33                                 lv_image_decoder_dsc_t * decoder_dsc, lv_area_t * relative_decoded_area,
34                                 const lv_area_t * img_area, const lv_area_t * clipped_img_area,
35                                 lv_draw_image_core_cb draw_core_cb);
36 
37 /**********************
38  *  STATIC VARIABLES
39  **********************/
40 
41 /**********************
42  *      MACROS
43  **********************/
44 
45 /**********************
46  *   GLOBAL FUNCTIONS
47  **********************/
48 
lv_draw_image_dsc_init(lv_draw_image_dsc_t * dsc)49 void lv_draw_image_dsc_init(lv_draw_image_dsc_t * dsc)
50 {
51     lv_memzero(dsc, sizeof(lv_draw_image_dsc_t));
52     dsc->recolor = lv_color_black();
53     dsc->opa = LV_OPA_COVER;
54     dsc->scale_x = LV_SCALE_NONE;
55     dsc->scale_y = LV_SCALE_NONE;
56     dsc->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
57     dsc->image_area.x2 = LV_COORD_MIN;   /*Indicate invalid area by default by setting a negative size*/
58     dsc->base.dsc_size = sizeof(lv_draw_image_dsc_t);
59 }
60 
lv_draw_task_get_image_dsc(lv_draw_task_t * task)61 lv_draw_image_dsc_t * lv_draw_task_get_image_dsc(lv_draw_task_t * task)
62 {
63     return task->type == LV_DRAW_TASK_TYPE_IMAGE ? (lv_draw_image_dsc_t *)task->draw_dsc : NULL;
64 }
65 
lv_draw_layer(lv_layer_t * layer,const lv_draw_image_dsc_t * dsc,const lv_area_t * coords)66 void lv_draw_layer(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords)
67 {
68     if(dsc->scale_x <= 0 || dsc->scale_y <= 0) {
69         /* NOT draw if scale is negative or zero */
70         return;
71     }
72 
73     LV_PROFILER_DRAW_BEGIN;
74 
75     lv_draw_task_t * t = lv_draw_add_task(layer, coords);
76 
77     t->draw_dsc = lv_malloc(sizeof(*dsc));
78     LV_ASSERT_MALLOC(t->draw_dsc);
79     lv_memcpy(t->draw_dsc, dsc, sizeof(*dsc));
80     t->type = LV_DRAW_TASK_TYPE_LAYER;
81     t->state = LV_DRAW_TASK_STATE_WAITING;
82 
83     lv_image_buf_get_transformed_area(&t->_real_area, lv_area_get_width(coords), lv_area_get_height(coords),
84                                       dsc->rotation, dsc->scale_x, dsc->scale_y, &dsc->pivot);
85     lv_area_move(&t->_real_area, coords->x1, coords->y1);
86 
87     lv_layer_t * layer_to_draw = (lv_layer_t *)dsc->src;
88     layer_to_draw->all_tasks_added = true;
89 
90     lv_draw_finalize_task_creation(layer, t);
91 
92     LV_PROFILER_DRAW_END;
93 }
94 
lv_draw_image(lv_layer_t * layer,const lv_draw_image_dsc_t * dsc,const lv_area_t * coords)95 void lv_draw_image(lv_layer_t * layer, const lv_draw_image_dsc_t * dsc, const lv_area_t * coords)
96 {
97     if(dsc->src == NULL) {
98         LV_LOG_WARN("Image draw: src is NULL");
99         return;
100     }
101     if(dsc->opa <= LV_OPA_MIN) return;
102 
103     if(dsc->scale_x <= 0 || dsc->scale_y <= 0) {
104         /* NOT draw if scale is negative or zero */
105         return;
106     }
107 
108     LV_PROFILER_DRAW_BEGIN;
109 
110     lv_draw_image_dsc_t * new_image_dsc = lv_malloc(sizeof(*dsc));
111     LV_ASSERT_MALLOC(new_image_dsc);
112     lv_memcpy(new_image_dsc, dsc, sizeof(*dsc));
113     lv_result_t res = lv_image_decoder_get_info(new_image_dsc->src, &new_image_dsc->header);
114     if(res != LV_RESULT_OK) {
115         LV_LOG_WARN("Couldn't get info about the image");
116         lv_free(new_image_dsc);
117         LV_PROFILER_DRAW_END;
118         return;
119     }
120 
121     lv_draw_task_t * t = lv_draw_add_task(layer, coords);
122     t->draw_dsc = new_image_dsc;
123     t->type = LV_DRAW_TASK_TYPE_IMAGE;
124 
125     lv_image_buf_get_transformed_area(&t->_real_area, lv_area_get_width(coords), lv_area_get_height(coords),
126                                       dsc->rotation, dsc->scale_x, dsc->scale_y, &dsc->pivot);
127     lv_area_move(&t->_real_area, coords->x1, coords->y1);
128 
129     lv_draw_finalize_task_creation(layer, t);
130     LV_PROFILER_DRAW_END;
131 }
132 
lv_image_src_get_type(const void * src)133 lv_image_src_t lv_image_src_get_type(const void * src)
134 {
135     if(src == NULL) return LV_IMAGE_SRC_UNKNOWN;
136     const uint8_t * u8_p = src;
137 
138     /*The first byte shows the type of the image source*/
139     if(u8_p[0] >= 0x20 && u8_p[0] <= 0x7F) {
140         return LV_IMAGE_SRC_FILE; /*If it's an ASCII character then it's file name*/
141     }
142     else if(u8_p[0] >= 0x80) {
143         return LV_IMAGE_SRC_SYMBOL; /*Symbols begins after 0x7F*/
144     }
145     else {
146         return LV_IMAGE_SRC_VARIABLE; /*`lv_image_dsc_t` is draw to the first byte < 0x20*/
147     }
148 }
149 
lv_draw_image_normal_helper(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_area_t * coords,lv_draw_image_core_cb draw_core_cb)150 void lv_draw_image_normal_helper(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
151                                  const lv_area_t * coords, lv_draw_image_core_cb draw_core_cb)
152 {
153     if(draw_core_cb == NULL) {
154         LV_LOG_WARN("draw_core_cb is NULL");
155         return;
156     }
157 
158     lv_area_t draw_area;
159     lv_area_copy(&draw_area, coords);
160     if(draw_dsc->rotation || draw_dsc->scale_x != LV_SCALE_NONE || draw_dsc->scale_y != LV_SCALE_NONE) {
161         int32_t w = lv_area_get_width(coords);
162         int32_t h = lv_area_get_height(coords);
163 
164         lv_image_buf_get_transformed_area(&draw_area, w, h, draw_dsc->rotation, draw_dsc->scale_x, draw_dsc->scale_y,
165                                           &draw_dsc->pivot);
166 
167         draw_area.x1 += coords->x1;
168         draw_area.y1 += coords->y1;
169         draw_area.x2 += coords->x1;
170         draw_area.y2 += coords->y1;
171     }
172 
173     lv_area_t clipped_img_area;
174     if(!lv_area_intersect(&clipped_img_area, &draw_area, draw_unit->clip_area)) {
175         return;
176     }
177 
178     lv_image_decoder_dsc_t decoder_dsc;
179     lv_result_t res = lv_image_decoder_open(&decoder_dsc, draw_dsc->src, NULL);
180     if(res != LV_RESULT_OK) {
181         LV_LOG_ERROR("Failed to open image");
182         return;
183     }
184 
185     img_decode_and_draw(draw_unit, draw_dsc, &decoder_dsc, NULL, coords, &clipped_img_area, draw_core_cb);
186 
187     lv_image_decoder_close(&decoder_dsc);
188 }
189 
lv_draw_image_tiled_helper(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,const lv_area_t * coords,lv_draw_image_core_cb draw_core_cb)190 void lv_draw_image_tiled_helper(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
191                                 const lv_area_t * coords, lv_draw_image_core_cb draw_core_cb)
192 {
193     if(draw_core_cb == NULL) {
194         LV_LOG_WARN("draw_core_cb is NULL");
195         return;
196     }
197 
198     lv_image_decoder_dsc_t decoder_dsc;
199     lv_result_t res = lv_image_decoder_open(&decoder_dsc, draw_dsc->src, NULL);
200     if(res != LV_RESULT_OK) {
201         LV_LOG_ERROR("Failed to open image");
202         return;
203     }
204 
205     int32_t img_w = draw_dsc->header.w;
206     int32_t img_h = draw_dsc->header.h;
207 
208     lv_area_t tile_area;
209     if(lv_area_get_width(&draw_dsc->image_area) >= 0) {
210         tile_area = draw_dsc->image_area;
211     }
212     else {
213         tile_area = *coords;
214     }
215     lv_area_set_width(&tile_area, img_w);
216     lv_area_set_height(&tile_area, img_h);
217 
218     int32_t tile_x_start = tile_area.x1;
219 
220     lv_area_t relative_decoded_area = {
221         .x1 = LV_COORD_MIN,
222         .y1 = LV_COORD_MIN,
223         .x2 = LV_COORD_MIN,
224         .y2 = LV_COORD_MIN,
225     };
226 
227     while(tile_area.y1 <= coords->y2) {
228         while(tile_area.x1 <= coords->x2) {
229 
230             lv_area_t clipped_img_area;
231             if(lv_area_intersect(&clipped_img_area, &tile_area, coords)) {
232                 img_decode_and_draw(draw_unit, draw_dsc, &decoder_dsc, &relative_decoded_area, &tile_area, &clipped_img_area,
233                                     draw_core_cb);
234             }
235 
236             tile_area.x1 += img_w;
237             tile_area.x2 += img_w;
238         }
239 
240         tile_area.y1 += img_h;
241         tile_area.y2 += img_h;
242         tile_area.x1 = tile_x_start;
243         tile_area.x2 = tile_x_start + img_w - 1;
244     }
245 
246     lv_image_decoder_close(&decoder_dsc);
247 }
248 
lv_image_buf_get_transformed_area(lv_area_t * res,int32_t w,int32_t h,int32_t angle,uint16_t scale_x,uint16_t scale_y,const lv_point_t * pivot)249 void lv_image_buf_get_transformed_area(lv_area_t * res, int32_t w, int32_t h, int32_t angle,
250                                        uint16_t scale_x, uint16_t scale_y, const lv_point_t * pivot)
251 {
252     if(angle == 0 && scale_x == LV_SCALE_NONE && scale_y == LV_SCALE_NONE) {
253         res->x1 = 0;
254         res->y1 = 0;
255         res->x2 = w - 1;
256         res->y2 = h - 1;
257         return;
258     }
259 
260     lv_point_t p[4] = {
261         {0, 0},
262         {w, 0},
263         {0, h},
264         {w, h},
265     };
266     lv_point_transform(&p[0], angle, scale_x, scale_y, pivot, true);
267     lv_point_transform(&p[1], angle, scale_x, scale_y, pivot, true);
268     lv_point_transform(&p[2], angle, scale_x, scale_y, pivot, true);
269     lv_point_transform(&p[3], angle, scale_x, scale_y, pivot, true);
270     res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x);
271     res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) - 1;
272     res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y);
273     res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) - 1;
274 }
275 
276 /**********************
277  *   STATIC FUNCTIONS
278  **********************/
279 
img_decode_and_draw(lv_draw_unit_t * draw_unit,const lv_draw_image_dsc_t * draw_dsc,lv_image_decoder_dsc_t * decoder_dsc,lv_area_t * relative_decoded_area,const lv_area_t * img_area,const lv_area_t * clipped_img_area,lv_draw_image_core_cb draw_core_cb)280 static void img_decode_and_draw(lv_draw_unit_t * draw_unit, const lv_draw_image_dsc_t * draw_dsc,
281                                 lv_image_decoder_dsc_t * decoder_dsc, lv_area_t * relative_decoded_area,
282                                 const lv_area_t * img_area, const lv_area_t * clipped_img_area,
283                                 lv_draw_image_core_cb draw_core_cb)
284 {
285     lv_draw_image_sup_t sup;
286     sup.alpha_color = draw_dsc->recolor;
287     sup.palette = decoder_dsc->palette;
288     sup.palette_size = decoder_dsc->palette_size;
289 
290     /*The whole image is available, just draw it*/
291     if(decoder_dsc->decoded && (relative_decoded_area == NULL || relative_decoded_area->x1 == LV_COORD_MIN)) {
292         draw_core_cb(draw_unit, draw_dsc, decoder_dsc, &sup, img_area, clipped_img_area);
293     }
294     /*Draw in smaller pieces*/
295     else {
296         lv_area_t relative_full_area_to_decode = *clipped_img_area;
297         lv_area_move(&relative_full_area_to_decode, -img_area->x1, -img_area->y1);
298         lv_area_t tmp;
299         if(relative_decoded_area == NULL) relative_decoded_area = &tmp;
300         relative_decoded_area->x1 = LV_COORD_MIN;
301         relative_decoded_area->y1 = LV_COORD_MIN;
302         relative_decoded_area->x2 = LV_COORD_MIN;
303         relative_decoded_area->y2 = LV_COORD_MIN;
304         lv_result_t res = LV_RESULT_OK;
305 
306         while(res == LV_RESULT_OK) {
307             res = lv_image_decoder_get_area(decoder_dsc, &relative_full_area_to_decode, relative_decoded_area);
308 
309             lv_area_t absolute_decoded_area = *relative_decoded_area;
310             lv_area_move(&absolute_decoded_area, img_area->x1, img_area->y1);
311             if(res == LV_RESULT_OK) {
312                 /*Limit draw area to the current decoded area and draw the image*/
313                 lv_area_t clipped_img_area_sub;
314                 if(lv_area_intersect(&clipped_img_area_sub, clipped_img_area, &absolute_decoded_area)) {
315                     draw_core_cb(draw_unit, draw_dsc, decoder_dsc, &sup,
316                                  &absolute_decoded_area, &clipped_img_area_sub);
317                 }
318             }
319         }
320     }
321 }
322