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