1 /**
2  * @file lv_img.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_image_private.h"
10 #include "../../misc/lv_area_private.h"
11 #include "../../draw/lv_draw_image_private.h"
12 #include "../../draw/lv_draw_private.h"
13 #include "../../core/lv_obj_event_private.h"
14 #include "../../core/lv_obj_class_private.h"
15 #if LV_USE_IMAGE != 0
16 
17 #include "../../stdlib/lv_string.h"
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define MY_CLASS (&lv_image_class)
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static void lv_image_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
32 static void lv_image_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
33 static void lv_image_event(const lv_obj_class_t * class_p, lv_event_t * e);
34 static void draw_image(lv_event_t * e);
35 static void scale_update(lv_obj_t * obj, int32_t scale_x, int32_t scale_y);
36 static void update_align(lv_obj_t * obj);
37 #if LV_USE_OBJ_PROPERTY
38     static void lv_image_set_pivot_helper(lv_obj_t * obj, lv_point_t * pivot);
39     static lv_point_t lv_image_get_pivot_helper(lv_obj_t * obj);
40 #endif
41 
42 #if LV_USE_OBJ_PROPERTY
43 static const lv_property_ops_t properties[] = {
44     {
45         .id = LV_PROPERTY_IMAGE_SRC,
46         .setter = lv_image_set_src,
47         .getter = lv_image_get_src,
48     },
49     {
50         .id = LV_PROPERTY_IMAGE_OFFSET_X,
51         .setter = lv_image_set_offset_x,
52         .getter = lv_image_get_offset_x,
53     },
54     {
55         .id = LV_PROPERTY_IMAGE_OFFSET_Y,
56         .setter = lv_image_set_offset_y,
57         .getter = lv_image_get_offset_y,
58     },
59     {
60         .id = LV_PROPERTY_IMAGE_ROTATION,
61         .setter = lv_image_set_rotation,
62         .getter = lv_image_get_rotation,
63     },
64     {
65         .id = LV_PROPERTY_IMAGE_PIVOT,
66         .setter = lv_image_set_pivot_helper,
67         .getter = lv_image_get_pivot_helper,
68     },
69     {
70         .id = LV_PROPERTY_IMAGE_SCALE,
71         .setter = lv_image_set_scale,
72         .getter = lv_image_get_scale,
73     },
74     {
75         .id = LV_PROPERTY_IMAGE_SCALE_X,
76         .setter = lv_image_set_scale_x,
77         .getter = lv_image_get_scale_x,
78     },
79     {
80         .id = LV_PROPERTY_IMAGE_SCALE_Y,
81         .setter = lv_image_set_scale_y,
82         .getter = lv_image_get_scale_y,
83     },
84     {
85         .id = LV_PROPERTY_IMAGE_BLEND_MODE,
86         .setter = lv_image_set_blend_mode,
87         .getter = lv_image_get_blend_mode,
88     },
89     {
90         .id = LV_PROPERTY_IMAGE_ANTIALIAS,
91         .setter = lv_image_set_antialias,
92         .getter = lv_image_get_antialias,
93     },
94     {
95         .id = LV_PROPERTY_IMAGE_INNER_ALIGN,
96         .setter = lv_image_set_inner_align,
97         .getter = lv_image_get_inner_align,
98     },
99 };
100 #endif
101 
102 /**********************
103  *  STATIC VARIABLES
104  **********************/
105 const lv_obj_class_t lv_image_class = {
106     .constructor_cb = lv_image_constructor,
107     .destructor_cb = lv_image_destructor,
108     .event_cb = lv_image_event,
109     .width_def = LV_SIZE_CONTENT,
110     .height_def = LV_SIZE_CONTENT,
111     .instance_size = sizeof(lv_image_t),
112     .base_class = &lv_obj_class,
113     .name = "image",
114 #if LV_USE_OBJ_PROPERTY
115     .prop_index_start = LV_PROPERTY_IMAGE_START,
116     .prop_index_end = LV_PROPERTY_IMAGE_END,
117     .properties = properties,
118     .properties_count = sizeof(properties) / sizeof(properties[0]),
119 
120 #if LV_USE_OBJ_PROPERTY_NAME
121     .property_names = lv_image_property_names,
122     .names_count = sizeof(lv_image_property_names) / sizeof(lv_property_name_t),
123 #endif
124 
125 #endif
126 };
127 
128 /**********************
129  *      MACROS
130  **********************/
131 
132 /**********************
133  *   GLOBAL FUNCTIONS
134  **********************/
135 
lv_image_create(lv_obj_t * parent)136 lv_obj_t * lv_image_create(lv_obj_t * parent)
137 {
138     LV_LOG_INFO("begin");
139     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
140     lv_obj_class_init_obj(obj);
141     return obj;
142 }
143 
144 /*=====================
145  * Setter functions
146  *====================*/
147 
lv_image_set_src(lv_obj_t * obj,const void * src)148 void lv_image_set_src(lv_obj_t * obj, const void * src)
149 {
150     LV_ASSERT_OBJ(obj, MY_CLASS);
151 
152     lv_obj_invalidate(obj);
153 
154     lv_image_src_t src_type = lv_image_src_get_type(src);
155     lv_image_t * img = (lv_image_t *)obj;
156 
157 #if LV_USE_LOG && LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO
158     switch(src_type) {
159         case LV_IMAGE_SRC_FILE:
160             LV_LOG_TRACE("`LV_IMAGE_SRC_FILE` type found");
161             break;
162         case LV_IMAGE_SRC_VARIABLE:
163             LV_LOG_TRACE("`LV_IMAGE_SRC_VARIABLE` type found");
164             break;
165         case LV_IMAGE_SRC_SYMBOL:
166             LV_LOG_TRACE("`LV_IMAGE_SRC_SYMBOL` type found");
167             break;
168         default:
169             LV_LOG_WARN("unknown type");
170     }
171 #endif
172 
173     /*If the new source type is unknown free the memories of the old source*/
174     if(src_type == LV_IMAGE_SRC_UNKNOWN) {
175         LV_LOG_WARN("unknown image type");
176         if(img->src_type == LV_IMAGE_SRC_SYMBOL || img->src_type == LV_IMAGE_SRC_FILE) {
177             lv_free((void *)img->src);
178         }
179         img->src      = NULL;
180         img->src_type = LV_IMAGE_SRC_UNKNOWN;
181         return;
182     }
183 
184     lv_image_header_t header;
185     lv_result_t res = lv_image_decoder_get_info(src, &header);
186     if(res != LV_RESULT_OK) {
187 #if LV_USE_LOG
188         char buf[24];
189         LV_LOG_WARN("failed to get image info: %s",
190                     src_type == LV_IMAGE_SRC_FILE ? (const char *)src : (lv_snprintf(buf, sizeof(buf), "%p", src), buf));
191 #endif /*LV_USE_LOG*/
192         return;
193     }
194 
195     /*Save the source*/
196     if(src_type == LV_IMAGE_SRC_VARIABLE) {
197         /*If memory was allocated because of the previous `src_type` then free it*/
198         if(img->src_type == LV_IMAGE_SRC_FILE || img->src_type == LV_IMAGE_SRC_SYMBOL) {
199             lv_free((void *)img->src);
200         }
201         img->src = src;
202     }
203     else if(src_type == LV_IMAGE_SRC_FILE || src_type == LV_IMAGE_SRC_SYMBOL) {
204         /*If the new and the old src are the same then it was only a refresh.*/
205         if(img->src != src) {
206             const void * old_src = NULL;
207             /*If memory was allocated because of the previous `src_type` then save its pointer and free after allocation.
208              *It's important to allocate first to be sure the new data will be on a new address.
209              *Else `img_cache` wouldn't see the change in source.*/
210             if(img->src_type == LV_IMAGE_SRC_FILE || img->src_type == LV_IMAGE_SRC_SYMBOL) {
211                 old_src = img->src;
212             }
213             char * new_str = lv_strdup(src);
214             LV_ASSERT_MALLOC(new_str);
215             if(new_str == NULL) return;
216             img->src = new_str;
217 
218             if(old_src) lv_free((void *)old_src);
219         }
220     }
221 
222     if(src_type == LV_IMAGE_SRC_SYMBOL) {
223         /*`lv_image_dsc_get_info` couldn't set the width and height of a font so set it here*/
224         const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);
225         int32_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);
226         int32_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);
227         lv_point_t size;
228         lv_text_get_size(&size, src, font, letter_space, line_space, LV_COORD_MAX, LV_TEXT_FLAG_NONE);
229         header.w = size.x;
230         header.h = size.y;
231     }
232 
233     img->src_type = src_type;
234     img->w        = header.w;
235     img->h        = header.h;
236     img->cf       = header.cf;
237 
238     lv_obj_refresh_self_size(obj);
239 
240     update_align(obj);
241 
242     /*Provide enough room for the rotated corners*/
243     if(img->rotation || img->scale_x != LV_SCALE_NONE || img->scale_y != LV_SCALE_NONE) {
244         lv_obj_refresh_ext_draw_size(obj);
245     }
246 
247     lv_obj_invalidate(obj);
248 }
249 
lv_image_set_offset_x(lv_obj_t * obj,int32_t x)250 void lv_image_set_offset_x(lv_obj_t * obj, int32_t x)
251 {
252     LV_ASSERT_OBJ(obj, MY_CLASS);
253 
254     lv_image_t * img = (lv_image_t *)obj;
255 
256     img->offset.x = x;
257     lv_obj_invalidate(obj);
258 }
259 
lv_image_set_offset_y(lv_obj_t * obj,int32_t y)260 void lv_image_set_offset_y(lv_obj_t * obj, int32_t y)
261 {
262     LV_ASSERT_OBJ(obj, MY_CLASS);
263 
264     lv_image_t * img = (lv_image_t *)obj;
265 
266     img->offset.y = y;
267     lv_obj_invalidate(obj);
268 }
269 
lv_image_set_rotation(lv_obj_t * obj,int32_t angle)270 void lv_image_set_rotation(lv_obj_t * obj, int32_t angle)
271 {
272     LV_ASSERT_OBJ(obj, MY_CLASS);
273 
274     lv_image_t * img = (lv_image_t *)obj;
275     if(img->align > LV_IMAGE_ALIGN_AUTO_TRANSFORM) {
276         angle = 0;
277     }
278     else {
279         while(angle >= 3600) angle -= 3600;
280         while(angle < 0) angle += 3600;
281     }
282 
283     if((uint32_t)angle == img->rotation) return;
284 
285     lv_obj_update_layout(obj);  /*Be sure the object's size is calculated*/
286     int32_t w = lv_obj_get_width(obj);
287     int32_t h = lv_obj_get_height(obj);
288     lv_area_t a;
289     lv_point_t pivot_px;
290     lv_image_get_pivot(obj, &pivot_px);
291     lv_image_buf_get_transformed_area(&a, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
292     a.x1 += obj->coords.x1;
293     a.y1 += obj->coords.y1;
294     a.x2 += obj->coords.x1;
295     a.y2 += obj->coords.y1;
296     lv_obj_invalidate_area(obj, &a);
297 
298     img->rotation = angle;
299 
300     /* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate
301      * the whole ext draw area */
302     lv_display_t * disp = lv_obj_get_display(obj);
303     lv_display_enable_invalidation(disp, false);
304     lv_obj_refresh_ext_draw_size(obj);
305     lv_display_enable_invalidation(disp, true);
306 
307     lv_image_buf_get_transformed_area(&a, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
308     a.x1 += obj->coords.x1;
309     a.y1 += obj->coords.y1;
310     a.x2 += obj->coords.x1;
311     a.y2 += obj->coords.y1;
312     lv_obj_invalidate_area(obj, &a);
313 }
314 
lv_image_set_pivot(lv_obj_t * obj,int32_t x,int32_t y)315 void lv_image_set_pivot(lv_obj_t * obj, int32_t x, int32_t y)
316 {
317     LV_ASSERT_OBJ(obj, MY_CLASS);
318 
319     lv_image_t * img = (lv_image_t *)obj;
320     if(img->align > LV_IMAGE_ALIGN_AUTO_TRANSFORM) {
321         x = 0;
322         y = 0;
323     }
324 
325     if(img->pivot.x == x && img->pivot.y == y) return;
326 
327     lv_obj_update_layout(obj);  /*Be sure the object's size is calculated*/
328     int32_t w = lv_obj_get_width(obj);
329     int32_t h = lv_obj_get_height(obj);
330     lv_area_t a;
331     lv_point_t pivot_px;
332     lv_image_get_pivot(obj, &pivot_px);
333     lv_image_buf_get_transformed_area(&a, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
334     a.x1 += obj->coords.x1;
335     a.y1 += obj->coords.y1;
336     a.x2 += obj->coords.x1;
337     a.y2 += obj->coords.y1;
338     lv_obj_invalidate_area(obj, &a);
339 
340     lv_point_set(&img->pivot, x, y);
341 
342     /* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate
343      * the whole ext draw area */
344     lv_display_t * disp = lv_obj_get_display(obj);
345     lv_display_enable_invalidation(disp, false);
346     lv_obj_refresh_ext_draw_size(obj);
347     lv_display_enable_invalidation(disp, true);
348 
349     lv_image_get_pivot(obj, &pivot_px);
350     lv_image_buf_get_transformed_area(&a, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
351     a.x1 += obj->coords.x1;
352     a.y1 += obj->coords.y1;
353     a.x2 += obj->coords.x1;
354     a.y2 += obj->coords.y1;
355     lv_obj_invalidate_area(obj, &a);
356 }
357 
lv_image_set_scale(lv_obj_t * obj,uint32_t zoom)358 void lv_image_set_scale(lv_obj_t * obj, uint32_t zoom)
359 {
360     LV_ASSERT_OBJ(obj, MY_CLASS);
361 
362     lv_image_t * img = (lv_image_t *)obj;
363 
364     /*If scale is set internally, do no overwrite it*/
365     if(img->align > LV_IMAGE_ALIGN_AUTO_TRANSFORM) return;
366 
367     if(zoom == img->scale_x && zoom == img->scale_y) return;
368 
369     if(zoom == 0) zoom = 1;
370 
371     scale_update(obj, zoom, zoom);
372 }
373 
lv_image_set_scale_x(lv_obj_t * obj,uint32_t zoom)374 void lv_image_set_scale_x(lv_obj_t * obj, uint32_t zoom)
375 {
376     LV_ASSERT_OBJ(obj, MY_CLASS);
377 
378     lv_image_t * img = (lv_image_t *)obj;
379 
380     /*If scale is set internally, do no overwrite it*/
381     if(img->align > LV_IMAGE_ALIGN_AUTO_TRANSFORM) return;
382 
383     if(zoom == img->scale_x) return;
384 
385     if(zoom == 0) zoom = 1;
386 
387     scale_update(obj, zoom, img->scale_y);
388 }
389 
lv_image_set_scale_y(lv_obj_t * obj,uint32_t zoom)390 void lv_image_set_scale_y(lv_obj_t * obj, uint32_t zoom)
391 {
392     LV_ASSERT_OBJ(obj, MY_CLASS);
393 
394     lv_image_t * img = (lv_image_t *)obj;
395 
396     /*If scale is set internally, do no overwrite it*/
397     if(img->align > LV_IMAGE_ALIGN_AUTO_TRANSFORM) return;
398 
399     if(zoom == img->scale_y) return;
400 
401     if(zoom == 0) zoom = 1;
402 
403     scale_update(obj, img->scale_x, zoom);
404 }
405 
lv_image_set_blend_mode(lv_obj_t * obj,lv_blend_mode_t blend_mode)406 void lv_image_set_blend_mode(lv_obj_t * obj, lv_blend_mode_t blend_mode)
407 {
408     LV_ASSERT_OBJ(obj, MY_CLASS);
409 
410     lv_image_t * img = (lv_image_t *)obj;
411 
412     /*If scale is set internally, do no overwrite it*/
413     if(img->blend_mode == blend_mode) return;
414 
415     img->blend_mode = blend_mode;
416 
417     lv_obj_invalidate(obj);
418 }
419 
lv_image_set_antialias(lv_obj_t * obj,bool antialias)420 void lv_image_set_antialias(lv_obj_t * obj, bool antialias)
421 {
422     LV_ASSERT_OBJ(obj, MY_CLASS);
423 
424     lv_image_t * img = (lv_image_t *)obj;
425     if(antialias == img->antialias) return;
426 
427     img->antialias = antialias;
428     lv_obj_invalidate(obj);
429 }
430 
lv_image_set_inner_align(lv_obj_t * obj,lv_image_align_t align)431 void lv_image_set_inner_align(lv_obj_t * obj, lv_image_align_t align)
432 {
433     LV_ASSERT_OBJ(obj, MY_CLASS);
434 
435     lv_image_t * img = (lv_image_t *)obj;
436     if(align == img->align) return;
437 
438     /*If we're removing STRETCH, reset the scale*/
439     if(img->align == LV_IMAGE_ALIGN_STRETCH) {
440         lv_image_set_scale(obj, LV_SCALE_NONE);
441     }
442 
443     img->align = align;
444     update_align(obj);
445 
446     lv_obj_invalidate(obj);
447 }
448 
lv_image_set_bitmap_map_src(lv_obj_t * obj,const lv_image_dsc_t * src)449 void lv_image_set_bitmap_map_src(lv_obj_t * obj, const lv_image_dsc_t * src)
450 {
451     LV_ASSERT_OBJ(obj, MY_CLASS);
452     lv_image_t * img = (lv_image_t *)obj;
453     img->bitmap_mask_src = src;
454     lv_obj_invalidate(obj);
455 }
456 
457 /*=====================
458  * Getter functions
459  *====================*/
460 
lv_image_get_src(lv_obj_t * obj)461 const void * lv_image_get_src(lv_obj_t * obj)
462 {
463     LV_ASSERT_OBJ(obj, MY_CLASS);
464 
465     lv_image_t * img = (lv_image_t *)obj;
466 
467     return img->src;
468 }
469 
lv_image_get_offset_x(lv_obj_t * obj)470 int32_t lv_image_get_offset_x(lv_obj_t * obj)
471 {
472     LV_ASSERT_OBJ(obj, MY_CLASS);
473 
474     lv_image_t * img = (lv_image_t *)obj;
475 
476     return img->offset.x;
477 }
478 
lv_image_get_offset_y(lv_obj_t * obj)479 int32_t lv_image_get_offset_y(lv_obj_t * obj)
480 {
481     LV_ASSERT_OBJ(obj, MY_CLASS);
482 
483     lv_image_t * img = (lv_image_t *)obj;
484 
485     return img->offset.y;
486 }
487 
lv_image_get_rotation(lv_obj_t * obj)488 int32_t lv_image_get_rotation(lv_obj_t * obj)
489 {
490     LV_ASSERT_OBJ(obj, MY_CLASS);
491 
492     lv_image_t * img = (lv_image_t *)obj;
493 
494     return img->rotation;
495 }
496 
lv_image_get_pivot(lv_obj_t * obj,lv_point_t * pivot)497 void lv_image_get_pivot(lv_obj_t * obj, lv_point_t * pivot)
498 {
499     LV_ASSERT_OBJ(obj, MY_CLASS);
500 
501     lv_image_t * img = (lv_image_t *)obj;
502 
503     pivot->x = lv_pct_to_px(img->pivot.x, img->w);
504     pivot->y = lv_pct_to_px(img->pivot.y, img->h);
505 }
506 
lv_image_get_scale(lv_obj_t * obj)507 int32_t lv_image_get_scale(lv_obj_t * obj)
508 {
509     LV_ASSERT_OBJ(obj, MY_CLASS);
510 
511     lv_image_t * img = (lv_image_t *)obj;
512 
513     return img->scale_x;
514 }
515 
lv_image_get_scale_x(lv_obj_t * obj)516 int32_t lv_image_get_scale_x(lv_obj_t * obj)
517 {
518     LV_ASSERT_OBJ(obj, MY_CLASS);
519 
520     lv_image_t * img = (lv_image_t *)obj;
521 
522     return img->scale_x;
523 }
524 
lv_image_get_scale_y(lv_obj_t * obj)525 int32_t lv_image_get_scale_y(lv_obj_t * obj)
526 {
527     LV_ASSERT_OBJ(obj, MY_CLASS);
528 
529     lv_image_t * img = (lv_image_t *)obj;
530 
531     return img->scale_y;
532 }
533 
lv_image_get_blend_mode(lv_obj_t * obj)534 lv_blend_mode_t lv_image_get_blend_mode(lv_obj_t * obj)
535 {
536     LV_ASSERT_OBJ(obj, MY_CLASS);
537 
538     lv_image_t * img = (lv_image_t *)obj;
539 
540     return img->blend_mode;
541 }
542 
lv_image_get_antialias(lv_obj_t * obj)543 bool lv_image_get_antialias(lv_obj_t * obj)
544 {
545     LV_ASSERT_OBJ(obj, MY_CLASS);
546 
547     lv_image_t * img = (lv_image_t *)obj;
548 
549     return img->antialias ? true : false;
550 }
551 
lv_image_get_inner_align(lv_obj_t * obj)552 lv_image_align_t lv_image_get_inner_align(lv_obj_t * obj)
553 {
554     LV_ASSERT_OBJ(obj, MY_CLASS);
555 
556     lv_image_t * img = (lv_image_t *)obj;
557 
558     return img->align;
559 }
560 
lv_image_get_bitmap_map_src(lv_obj_t * obj)561 const lv_image_dsc_t * lv_image_get_bitmap_map_src(lv_obj_t * obj)
562 {
563     LV_ASSERT_OBJ(obj, MY_CLASS);
564 
565     lv_image_t * img = (lv_image_t *)obj;
566 
567     return img->bitmap_mask_src;
568 }
569 
570 /**********************
571  *   STATIC FUNCTIONS
572  **********************/
573 
lv_image_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)574 static void lv_image_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
575 {
576     LV_UNUSED(class_p);
577     LV_TRACE_OBJ_CREATE("begin");
578 
579     lv_image_t * img = (lv_image_t *)obj;
580 
581     img->src       = NULL;
582     img->src_type  = LV_IMAGE_SRC_UNKNOWN;
583     img->cf        = LV_COLOR_FORMAT_UNKNOWN;
584     img->w         = lv_obj_get_width(obj);
585     img->h         = lv_obj_get_height(obj);
586     img->rotation     = 0;
587     img->scale_x      = LV_SCALE_NONE;
588     img->scale_y      = LV_SCALE_NONE;
589     img->antialias = LV_COLOR_DEPTH > 8 ? 1 : 0;
590     lv_point_set(&img->offset, 0, 0);
591     lv_point_set(&img->pivot, LV_PCT(50), LV_PCT(50)); /*Default pivot to image center*/
592     img->align     = LV_IMAGE_ALIGN_CENTER;
593 
594     lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICKABLE);
595     lv_obj_add_flag(obj, LV_OBJ_FLAG_ADV_HITTEST);
596 
597     LV_TRACE_OBJ_CREATE("finished");
598 }
599 
lv_image_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)600 static void lv_image_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
601 {
602     LV_UNUSED(class_p);
603     lv_image_t * img = (lv_image_t *)obj;
604     if(img->src_type == LV_IMAGE_SRC_FILE || img->src_type == LV_IMAGE_SRC_SYMBOL) {
605         lv_free((void *)img->src);
606         img->src      = NULL;
607         img->src_type = LV_IMAGE_SRC_UNKNOWN;
608     }
609 }
610 
lv_image_event(const lv_obj_class_t * class_p,lv_event_t * e)611 static void lv_image_event(const lv_obj_class_t * class_p, lv_event_t * e)
612 {
613     LV_UNUSED(class_p);
614 
615     lv_event_code_t code = lv_event_get_code(e);
616 
617     /*Call the ancestor's event handler*/
618     lv_result_t res = lv_obj_event_base(MY_CLASS, e);
619     if(res != LV_RESULT_OK) return;
620 
621     lv_obj_t * obj = lv_event_get_current_target(e);
622     lv_image_t * img = (lv_image_t *)obj;
623     lv_point_t pivot_px;
624     lv_image_get_pivot(obj, &pivot_px);
625 
626     if(code == LV_EVENT_STYLE_CHANGED) {
627         /*Refresh the file name to refresh the symbol text size*/
628         if(img->src_type == LV_IMAGE_SRC_SYMBOL) {
629             lv_image_set_src(obj, img->src);
630         }
631         else {
632             /*With transformation it might change*/
633             lv_obj_refresh_ext_draw_size(obj);
634         }
635     }
636     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
637 
638         int32_t * s = lv_event_get_param(e);
639 
640         /*If the image has angle provide enough room for the rotated corners*/
641         if(img->rotation || img->scale_x != LV_SCALE_NONE || img->scale_y != LV_SCALE_NONE) {
642             lv_area_t a;
643             int32_t w = lv_obj_get_width(obj);
644             int32_t h = lv_obj_get_height(obj);
645             lv_image_buf_get_transformed_area(&a, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
646             *s = LV_MAX(*s, -a.x1);
647             *s = LV_MAX(*s, -a.y1);
648             *s = LV_MAX(*s, a.x2 - w);
649             *s = LV_MAX(*s, a.y2 - h);
650         }
651     }
652     else if(code == LV_EVENT_HIT_TEST) {
653         lv_hit_test_info_t * info = lv_event_get_param(e);
654 
655         /*If the object is exactly image sized (not cropped, not mosaic) and transformed
656          *perform hit test on its transformed area*/
657         if(img->w == lv_obj_get_width(obj) && img->h == lv_obj_get_height(obj) &&
658            (img->scale_x != LV_SCALE_NONE || img->scale_y != LV_SCALE_NONE ||
659             img->rotation != 0 || img->pivot.x != img->w / 2 || img->pivot.y != img->h / 2)) {
660 
661             int32_t w = lv_obj_get_width(obj);
662             int32_t h = lv_obj_get_height(obj);
663             lv_area_t coords;
664             lv_image_buf_get_transformed_area(&coords, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
665             coords.x1 += obj->coords.x1;
666             coords.y1 += obj->coords.y1;
667             coords.x2 += obj->coords.x1;
668             coords.y2 += obj->coords.y1;
669 
670             info->res = lv_area_is_point_on(&coords, info->point, 0);
671         }
672         else {
673             lv_area_t a;
674             lv_obj_get_click_area(obj, &a);
675             info->res = lv_area_is_point_on(&a, info->point, 0);
676         }
677     }
678     else if(code == LV_EVENT_GET_SELF_SIZE) {
679         lv_point_t * p = lv_event_get_param(e);
680         p->x = img->w;
681         p->y = img->h;
682     }
683     else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
684         draw_image(e);
685     }
686 }
687 
draw_image(lv_event_t * e)688 static void draw_image(lv_event_t * e)
689 {
690     lv_event_code_t code = lv_event_get_code(e);
691     lv_obj_t * obj = lv_event_get_current_target(e);
692     lv_image_t * img = (lv_image_t *)obj;
693     if(code == LV_EVENT_COVER_CHECK) {
694         lv_cover_check_info_t * info = lv_event_get_param(e);
695         if(info->res == LV_COVER_RES_MASKED) return;
696         if(img->src_type == LV_IMAGE_SRC_UNKNOWN || img->src_type == LV_IMAGE_SRC_SYMBOL) {
697             info->res = LV_COVER_RES_NOT_COVER;
698             return;
699         }
700 
701         /*Non true color format might have "holes"*/
702         if(lv_color_format_has_alpha(img->cf)) {
703             info->res = LV_COVER_RES_NOT_COVER;
704             return;
705         }
706 
707         /*With not LV_OPA_COVER images can't cover an area */
708         if(lv_obj_get_style_image_opa(obj, LV_PART_MAIN) != LV_OPA_COVER) {
709             info->res = LV_COVER_RES_NOT_COVER;
710             return;
711         }
712 
713         if(img->rotation != 0) {
714             info->res = LV_COVER_RES_NOT_COVER;
715             return;
716         }
717 
718         if(img->scale_x == LV_SCALE_NONE && img->scale_y == LV_SCALE_NONE) {
719             if(lv_area_is_in(info->area, &obj->coords, 0) == false) {
720                 info->res = LV_COVER_RES_NOT_COVER;
721                 return;
722             }
723         }
724         else {
725             lv_area_t a;
726             lv_point_t pivot_px;
727             lv_image_get_pivot(obj, &pivot_px);
728             lv_image_buf_get_transformed_area(&a, lv_obj_get_width(obj), lv_obj_get_height(obj), 0, img->scale_x, img->scale_y,
729                                               &pivot_px);
730             a.x1 += obj->coords.x1;
731             a.y1 += obj->coords.y1;
732             a.x2 += obj->coords.x1;
733             a.y2 += obj->coords.y1;
734 
735             if(lv_area_is_in(info->area, &a, 0) == false) {
736                 info->res = LV_COVER_RES_NOT_COVER;
737                 return;
738             }
739         }
740         if(img->bitmap_mask_src) {
741             info->res = LV_COVER_RES_NOT_COVER;
742             return;
743         }
744     }
745     else if(code == LV_EVENT_DRAW_MAIN) {
746 
747         if(img->h == 0 || img->w == 0) return;
748         if(img->scale_x == 0 || img->scale_y == 0) return;
749 
750         lv_layer_t * layer = lv_event_get_layer(e);
751 
752         if(img->src_type == LV_IMAGE_SRC_FILE || img->src_type == LV_IMAGE_SRC_VARIABLE) {
753             lv_draw_image_dsc_t draw_dsc;
754             lv_draw_image_dsc_init(&draw_dsc);
755             draw_dsc.base.layer = layer;
756             lv_obj_init_draw_image_dsc(obj, LV_PART_MAIN, &draw_dsc);
757 
758             lv_area_t clip_area_ori = layer->_clip_area;
759 
760             lv_image_get_pivot(obj, &draw_dsc.pivot);
761             draw_dsc.scale_x = img->scale_x;
762             draw_dsc.scale_y = img->scale_y;
763             draw_dsc.rotation = img->rotation;
764             draw_dsc.antialias = img->antialias;
765             draw_dsc.blend_mode = img->blend_mode;
766             draw_dsc.bitmap_mask_src = img->bitmap_mask_src;
767             draw_dsc.src = img->src;
768 
769             lv_area_set(&draw_dsc.image_area, obj->coords.x1,
770                         obj->coords.y1,
771                         obj->coords.x1 + img->w - 1,
772                         obj->coords.y1 + img->h - 1);
773 
774             draw_dsc.clip_radius = lv_obj_get_style_radius(obj, LV_PART_MAIN);
775 
776             lv_area_t coords;
777             if(img->align < LV_IMAGE_ALIGN_AUTO_TRANSFORM) {
778                 lv_area_align(&obj->coords, &draw_dsc.image_area, img->align, img->offset.x, img->offset.y);
779                 coords = draw_dsc.image_area;
780             }
781             else if(img->align == LV_IMAGE_ALIGN_TILE) {
782                 lv_area_intersect(&layer->_clip_area, &layer->_clip_area, &obj->coords);
783                 lv_area_move(&draw_dsc.image_area, img->offset.x, img->offset.y);
784 
785                 lv_area_move(&draw_dsc.image_area,
786                              ((layer->_clip_area.x1 - draw_dsc.image_area.x1 - (img->w - 1)) / img->w) * img->w,
787                              ((layer->_clip_area.y1 - draw_dsc.image_area.y1 - (img->h - 1)) / img->h) * img->h);
788                 coords = layer->_clip_area;
789                 draw_dsc.tile = 1;
790             }
791             else {
792                 coords = draw_dsc.image_area;
793             }
794 
795             lv_draw_image(layer, &draw_dsc, &coords);
796             layer->_clip_area = clip_area_ori;
797 
798         }
799         else if(img->src_type == LV_IMAGE_SRC_SYMBOL) {
800             lv_draw_label_dsc_t label_dsc;
801             lv_draw_label_dsc_init(&label_dsc);
802             label_dsc.base.layer = layer;
803             lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, &label_dsc);
804             label_dsc.text = img->src;
805             lv_draw_label(layer, &label_dsc, &obj->coords);
806         }
807         else if(img->src == NULL) {
808             /*Do not need to draw image when src is NULL*/
809             LV_LOG_WARN("image source is NULL");
810         }
811         else {
812             /*Trigger the error handler of image draw*/
813             LV_LOG_WARN("image source type is unknown");
814         }
815     }
816 }
817 
scale_update(lv_obj_t * obj,int32_t scale_x,int32_t scale_y)818 static void scale_update(lv_obj_t * obj, int32_t scale_x, int32_t scale_y)
819 {
820     lv_image_t * img = (lv_image_t *)obj;
821 
822     lv_obj_update_layout(obj);  /*Be sure the object's size is calculated*/
823     int32_t w = lv_obj_get_width(obj);
824     int32_t h = lv_obj_get_height(obj);
825     lv_area_t a;
826     lv_point_t pivot_px;
827     lv_image_get_pivot(obj, &pivot_px);
828     lv_image_buf_get_transformed_area(&a, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
829     a.x1 += obj->coords.x1 - 1;
830     a.y1 += obj->coords.y1 - 1;
831     a.x2 += obj->coords.x1 + 1;
832     a.y2 += obj->coords.y1 + 1;
833     lv_obj_invalidate_area(obj, &a);
834 
835     img->scale_x = scale_x;
836     img->scale_y = scale_y;
837 
838     /* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate
839      * the whole ext draw area */
840     lv_display_t * disp = lv_obj_get_display(obj);
841     lv_display_enable_invalidation(disp, false);
842     lv_obj_refresh_ext_draw_size(obj);
843     lv_display_enable_invalidation(disp, true);
844 
845     lv_image_buf_get_transformed_area(&a, w, h, img->rotation, img->scale_x, img->scale_y, &pivot_px);
846     a.x1 += obj->coords.x1 - 1;
847     a.y1 += obj->coords.y1 - 1;
848     a.x2 += obj->coords.x1 + 1;
849     a.y2 += obj->coords.y1 + 1;
850     lv_obj_invalidate_area(obj, &a);
851 }
852 
update_align(lv_obj_t * obj)853 static void update_align(lv_obj_t * obj)
854 {
855     lv_image_t * img = (lv_image_t *)obj;
856     if(img->align == LV_IMAGE_ALIGN_STRETCH) {
857         lv_image_set_rotation(obj, 0);
858         lv_image_set_pivot(obj, 0, 0);
859         if(img->w != 0 && img->h != 0) {
860             lv_obj_update_layout(obj);
861             int32_t scale_x = lv_obj_get_width(obj) * LV_SCALE_NONE / img->w;
862             int32_t scale_y = lv_obj_get_height(obj) * LV_SCALE_NONE / img->h;
863             scale_update(obj, scale_x, scale_y);
864         }
865     }
866     else if(img->align == LV_IMAGE_ALIGN_TILE) {
867         lv_image_set_rotation(obj, 0);
868         lv_image_set_pivot(obj, 0, 0);
869         scale_update(obj, LV_SCALE_NONE, LV_SCALE_NONE);
870 
871     }
872 }
873 
874 #if LV_USE_OBJ_PROPERTY
lv_image_set_pivot_helper(lv_obj_t * obj,lv_point_t * pivot)875 static void lv_image_set_pivot_helper(lv_obj_t * obj, lv_point_t * pivot)
876 {
877     lv_image_set_pivot(obj, pivot->x, pivot->y);
878 }
879 
lv_image_get_pivot_helper(lv_obj_t * obj)880 static lv_point_t lv_image_get_pivot_helper(lv_obj_t * obj)
881 {
882     lv_point_t pivot;
883     lv_image_get_pivot(obj, &pivot);
884     return pivot;
885 }
886 #endif
887 
888 #endif
889