1 /**
2  * @file lv_canvas.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include <stdlib.h>
10 #include "lv_canvas.h"
11 #include "../lv_misc/lv_math.h"
12 #include "../lv_draw/lv_draw.h"
13 #include "../lv_core/lv_refr.h"
14 
15 #if LV_USE_CANVAS != 0
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 
21 /**********************
22  *      TYPEDEFS
23  **********************/
24 
25 /**********************
26  *  STATIC PROTOTYPES
27  **********************/
28 static lv_res_t lv_canvas_signal(lv_obj_t * canvas, lv_signal_t sign, void * param);
29 
30 /**********************
31  *  STATIC VARIABLES
32  **********************/
33 static lv_signal_cb_t ancestor_signal;
34 static lv_design_cb_t ancestor_design;
35 
36 /**********************
37  *      MACROS
38  **********************/
39 
40 /**********************
41  *   GLOBAL FUNCTIONS
42  **********************/
43 
44 /**
45  * Create a canvas object
46  * @param par pointer to an object, it will be the parent of the new canvas
47  * @param copy pointer to a canvas object, if not NULL then the new object will be copied from it
48  * @return pointer to the created canvas
49  */
lv_canvas_create(lv_obj_t * par,const lv_obj_t * copy)50 lv_obj_t * lv_canvas_create(lv_obj_t * par, const lv_obj_t * copy)
51 {
52     LV_LOG_TRACE("canvas create started");
53 
54     /*Create the ancestor of canvas*/
55     lv_obj_t * new_canvas = lv_img_create(par, copy);
56     lv_mem_assert(new_canvas);
57     if(new_canvas == NULL) return NULL;
58 
59     /*Allocate the canvas type specific extended data*/
60     lv_canvas_ext_t * ext = lv_obj_allocate_ext_attr(new_canvas, sizeof(lv_canvas_ext_t));
61     lv_mem_assert(ext);
62     if(ext == NULL) return NULL;
63     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_canvas);
64     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_canvas);
65 
66     /*Initialize the allocated 'ext' */
67     ext->dsc.header.always_zero = 0;
68     ext->dsc.header.cf          = LV_IMG_CF_TRUE_COLOR;
69     ext->dsc.header.h           = 0;
70     ext->dsc.header.w           = 0;
71     ext->dsc.data_size          = 0;
72     ext->dsc.data               = NULL;
73 
74     lv_img_set_src(new_canvas, &ext->dsc);
75 
76     /*The signal and design functions are not copied so set them here*/
77     lv_obj_set_signal_cb(new_canvas, lv_canvas_signal);
78 
79     /*Init the new canvas canvas*/
80     if(copy == NULL) {
81 
82     }
83     /*Copy an existing canvas*/
84     else {
85         /*Refresh the style with new signal function*/
86         lv_obj_refresh_style(new_canvas);
87     }
88 
89     LV_LOG_INFO("canvas created");
90 
91     return new_canvas;
92 }
93 
94 /*=====================
95  * Setter functions
96  *====================*/
97 
98 /**
99  * Set a buffer for the canvas.
100  * @param buf a buffer where the content of the canvas will be.
101  * The required size is (lv_img_color_format_get_px_size(cf) * w * h) / 8)
102  * It can be allocated with `lv_mem_alloc()` or
103  * it can be statically allocated array (e.g. static lv_color_t buf[100*50]) or
104  * it can be an address in RAM or external SRAM
105  * @param canvas pointer to a canvas object
106  * @param w width of the canvas
107  * @param h height of the canvas
108  * @param cf color format. The following formats are supported:
109  *      LV_IMG_CF_TRUE_COLOR, LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, LV_IMG_CF_INDEXES_1/2/4/8BIT
110  *
111  */
lv_canvas_set_buffer(lv_obj_t * canvas,void * buf,lv_coord_t w,lv_coord_t h,lv_img_cf_t cf)112 void lv_canvas_set_buffer(lv_obj_t * canvas, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
113 {
114     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
115 
116     ext->dsc.header.cf = cf;
117     ext->dsc.header.w  = w;
118     ext->dsc.header.h  = h;
119     ext->dsc.data      = buf;
120     ext->dsc.data_size = (lv_img_color_format_get_px_size(cf) * w * h) / 8;
121 
122     lv_img_set_src(canvas, &ext->dsc);
123 }
124 
125 /**
126  * Set the color of a pixel on the canvas
127  * @param canvas pointer to canvas object
128  * @param x x coordinate of the point to set
129  * @param y x coordinate of the point to set
130  * @param c color of the point
131  */
lv_canvas_set_px(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,lv_color_t c)132 void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c)
133 {
134     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
135 
136     lv_img_buf_set_px_color(&ext->dsc, x, y, c);
137     lv_obj_invalidate(canvas);
138 }
139 
140 /**
141  * Set the palette color of a canvas with index format. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
142  * @param canvas pointer to canvas object
143  * @param id the palette color to set:
144  *   - for `LV_IMG_CF_INDEXED1`: 0..1
145  *   - for `LV_IMG_CF_INDEXED2`: 0..3
146  *   - for `LV_IMG_CF_INDEXED4`: 0..15
147  *   - for `LV_IMG_CF_INDEXED8`: 0..255
148  * @param c the color to set
149  */
lv_canvas_set_palette(lv_obj_t * canvas,uint8_t id,lv_color_t c)150 void lv_canvas_set_palette(lv_obj_t * canvas, uint8_t id, lv_color_t c)
151 {
152     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
153 
154     lv_img_buf_set_palette(&ext->dsc, id, c);
155     lv_obj_invalidate(canvas);
156 }
157 
158 /**
159  * Set a style of a canvas.
160  * @param canvas pointer to canvas object
161  * @param type which style should be set
162  * @param style pointer to a style
163  */
lv_canvas_set_style(lv_obj_t * canvas,lv_canvas_style_t type,const lv_style_t * style)164 void lv_canvas_set_style(lv_obj_t * canvas, lv_canvas_style_t type, const lv_style_t * style)
165 {
166     switch(type) {
167         case LV_CANVAS_STYLE_MAIN: lv_img_set_style(canvas, LV_IMG_STYLE_MAIN, style); break;
168     }
169 }
170 
171 /*=====================
172  * Getter functions
173  *====================*/
174 
175 /**
176  * Get the color of a pixel on the canvas
177  * @param canvas
178  * @param x x coordinate of the point to set
179  * @param y x coordinate of the point to set
180  * @return color of the point
181  */
lv_canvas_get_px(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y)182 lv_color_t lv_canvas_get_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y)
183 {
184     lv_canvas_ext_t * ext    = lv_obj_get_ext_attr(canvas);
185     const lv_style_t * style = lv_canvas_get_style(canvas, LV_CANVAS_STYLE_MAIN);
186 
187     return lv_img_buf_get_px_color(&ext->dsc, x, y, style);
188 }
189 
190 /**
191  * Get the image of the canvas as a pointer to an `lv_img_dsc_t` variable.
192  * @param canvas pointer to a canvas object
193  * @return pointer to the image descriptor.
194  */
lv_canvas_get_img(lv_obj_t * canvas)195 lv_img_dsc_t * lv_canvas_get_img(lv_obj_t * canvas)
196 {
197     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
198 
199     return &ext->dsc;
200 }
201 
202 /**
203  * Get style of a canvas.
204  * @param canvas pointer to canvas object
205  * @param type which style should be get
206  * @return style pointer to the style
207  */
lv_canvas_get_style(const lv_obj_t * canvas,lv_canvas_style_t type)208 const lv_style_t * lv_canvas_get_style(const lv_obj_t * canvas, lv_canvas_style_t type)
209 {
210     const lv_style_t * style = NULL;
211 
212     switch(type) {
213         case LV_CANVAS_STYLE_MAIN: style = lv_img_get_style(canvas, LV_IMG_STYLE_MAIN); break;
214         default: style = NULL;
215     }
216 
217     return style;
218 }
219 
220 /*=====================
221  * Other functions
222  *====================*/
223 
224 /**
225  * Copy a buffer to the canvas
226  * @param canvas pointer to a canvas object
227  * @param to_copy buffer to copy. The color format has to match with the canvas's buffer color
228  * format
229  * @param w width of the buffer to copy
230  * @param h height of the buffer to copy
231  * @param x left side of the destination position
232  * @param y top side of the destination position
233  */
lv_canvas_copy_buf(lv_obj_t * canvas,const void * to_copy,lv_coord_t x,lv_coord_t y,lv_coord_t w,lv_coord_t h)234 void lv_canvas_copy_buf(lv_obj_t * canvas, const void * to_copy, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h)
235 {
236     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
237     if(x + w >= ext->dsc.header.w || y + h >= ext->dsc.header.h) {
238         LV_LOG_WARN("lv_canvas_copy_buf: x or y out of the canvas");
239         return;
240     }
241 
242     uint32_t px_size   = lv_img_color_format_get_px_size(ext->dsc.header.cf) >> 3;
243     uint32_t px        = ext->dsc.header.w * y * px_size + x * px_size;
244     uint8_t * to_copy8 = (uint8_t *)to_copy;
245     lv_coord_t i;
246     for(i = 0; i < h; i++) {
247         memcpy((void *)&ext->dsc.data[px], to_copy8, w * px_size);
248         px += ext->dsc.header.w * px_size;
249         to_copy8 += w * px_size;
250     }
251 }
252 
253 /**
254  * Rotate and image and store the result on a canvas.
255  * @param canvas pointer to a canvas object
256  * @param img pointer to an image descriptor.
257  *             Can be the image descriptor of an other canvas too (`lv_canvas_get_img()`).
258  * @param angle the angle of rotation (0..360);
259  * @param offset_x offset X to tell where to put the result data on destination canvas
260  * @param offset_y offset X to tell where to put the result data on destination canvas
261  * @param pivot_x pivot X of rotation. Relative to the source canvas
262  *                Set to `source width / 2` to rotate around the center
263  * @param pivot_y pivot Y of rotation. Relative to the source canvas
264  *                Set to `source height / 2` to rotate around the center
265  */
lv_canvas_rotate(lv_obj_t * canvas,lv_img_dsc_t * img,int16_t angle,lv_coord_t offset_x,lv_coord_t offset_y,int32_t pivot_x,int32_t pivot_y)266 void lv_canvas_rotate(lv_obj_t * canvas, lv_img_dsc_t * img, int16_t angle, lv_coord_t offset_x, lv_coord_t offset_y,
267                       int32_t pivot_x, int32_t pivot_y)
268 {
269     lv_canvas_ext_t * ext_dst = lv_obj_get_ext_attr(canvas);
270     const lv_style_t * style  = lv_canvas_get_style(canvas, LV_CANVAS_STYLE_MAIN);
271     int32_t sinma             = lv_trigo_sin(-angle);
272     int32_t cosma             = lv_trigo_sin(-angle + 90); /* cos */
273 
274     int32_t img_width   = img->header.w;
275     int32_t img_height  = img->header.h;
276     int32_t dest_width  = ext_dst->dsc.header.w;
277     int32_t dest_height = ext_dst->dsc.header.h;
278 
279     int32_t x;
280     int32_t y;
281     for(x = -offset_x; x < dest_width - offset_x; x++) {
282         for(y = -offset_y; y < dest_height - offset_y; y++) {
283             /*Get the target point relative coordinates to the pivot*/
284             int32_t xt = x - pivot_x;
285             int32_t yt = y - pivot_y;
286 
287             /*Get the source pixel from the upscaled image*/
288             int32_t xs = ((cosma * xt - sinma * yt) >> (LV_TRIGO_SHIFT - 8)) + pivot_x * 256;
289             int32_t ys = ((sinma * xt + cosma * yt) >> (LV_TRIGO_SHIFT - 8)) + pivot_y * 256;
290 
291             /*Get the integer part of the source pixel*/
292             int xs_int = xs >> 8;
293             int ys_int = ys >> 8;
294 
295             if(xs_int >= img_width)
296                 continue;
297             else if(xs_int < 0)
298                 continue;
299 
300             if(ys_int >= img_height)
301                 continue;
302             else if(ys_int < 0)
303                 continue;
304 
305             /*Get the fractional part of the source pixel*/
306             int xs_fract = xs & 0xff;
307             int ys_fract = ys & 0xff;
308 
309             /* If the fractional < 0x70 mix the source pixel with the left/top pixel
310              * If the fractional > 0x90 mix the source pixel with the right/bottom pixel
311              * In the 0x70..0x90 range use the unchanged source pixel */
312 
313             int xn;      /*x neightboor*/
314             lv_opa_t xr; /*x mix ratio*/
315             if(xs_fract < 0x70) {
316                 xn = xs_int - 1;
317                 xr = xs_fract * 2;
318             } else if(xs_fract > 0x90) {
319                 xn = xs_int + 1;
320                 xr = (0xFF - xs_fract) * 2;
321             } else {
322                 xn = xs_int;
323                 xr = 0xFF;
324             }
325 
326             /*Handle under/overflow*/
327             if(xn >= img_width)
328                 continue;
329             else if(xn < 0)
330                 continue;
331 
332             int yn;      /*y neightboor*/
333             lv_opa_t yr; /*y mix ratio*/
334             if(ys_fract < 0x70) {
335                 yn = ys_int - 1;
336                 yr = ys_fract * 2;
337             } else if(ys_fract > 0x90) {
338                 yn = ys_int + 1;
339                 yr = (0xFF - ys_fract) * 2;
340             } else {
341                 yn = ys_int;
342                 yr = 0xFF;
343             }
344 
345             /*Handle under/overflow*/
346             if(yn >= img_height)
347                 continue;
348             else if(yn < 0)
349                 continue;
350 
351             /*Get the mixture of the original source and the neightboor pixels in both directions*/
352             lv_color_t c_dest_int = lv_img_buf_get_px_color(img, xs_int, ys_int, style);
353 
354             if(lv_img_color_format_is_chroma_keyed(img->header.cf)) {
355                 lv_color_t ct = LV_COLOR_TRANSP;
356                 if(c_dest_int.full == ct.full) continue;
357             }
358 
359             lv_color_t c_dest_xn = lv_img_buf_get_px_color(img, xn, ys_int, style);
360             lv_color_t c_dest_yn = lv_img_buf_get_px_color(img, xs_int, yn, style);
361             lv_color_t x_dest    = lv_color_mix(c_dest_int, c_dest_xn, xr);
362             lv_color_t y_dest    = lv_color_mix(c_dest_int, c_dest_yn, yr);
363             lv_color_t color_res = lv_color_mix(x_dest, y_dest, LV_OPA_50);
364 
365             if(x + offset_x >= 0 && x + offset_x < dest_width && y + offset_y >= 0 && y + offset_y < dest_height) {
366                 /*If the image has no alpha channel just simple set the result color on the canvas*/
367                 if(lv_img_color_format_has_alpha(img->header.cf) == false) {
368                     lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, color_res);
369                 } else {
370                     /*Get result pixel opacity*/
371                     lv_opa_t opa_int = lv_img_buf_get_px_alpha(img, xs_int, ys_int);
372                     lv_opa_t opa_xn  = lv_img_buf_get_px_alpha(img, xn, ys_int);
373                     lv_opa_t opa_yn  = lv_img_buf_get_px_alpha(img, xs_int, yn);
374                     lv_opa_t opa_x   = (opa_int * xr + (opa_xn * (255 - xr))) >> 8;
375                     lv_opa_t opa_y   = (opa_int * yr + (opa_yn * (255 - yr))) >> 8;
376                     lv_opa_t opa_res = (opa_x + opa_y) / 2;
377                     if(opa_res <= LV_OPA_MIN) continue;
378 
379                     lv_color_t bg_color = lv_img_buf_get_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, style);
380 
381                     /*If the canvas has no alpha but the image has mix the image's color with
382                      * canvas*/
383                     if(lv_img_color_format_has_alpha(ext_dst->dsc.header.cf) == false) {
384                         if(opa_res < LV_OPA_MAX) color_res = lv_color_mix(color_res, bg_color, opa_res);
385                         lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, color_res);
386                     }
387                     /*Both the image and canvas has alpha channel. Some extra calculation is
388                        required*/
389                     else {
390                         lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y);
391                         /* Pick the foreground if it's fully opaque or the Background is fully
392                          * transparent*/
393                         if(opa_res >= LV_OPA_MAX || bg_opa <= LV_OPA_MIN) {
394                             lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, color_res);
395                             lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, opa_res);
396                         }
397                         /*Opaque background: use simple mix*/
398                         else if(bg_opa >= LV_OPA_MAX) {
399                             lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y,
400                                                     lv_color_mix(color_res, bg_color, opa_res));
401                         }
402                         /*Both colors have alpha. Expensive calculation need to be applied*/
403                         else {
404 
405                             /*Info:
406                              * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
407                             lv_opa_t opa_res_2 = 255 - ((uint16_t)((uint16_t)(255 - opa_res) * (255 - bg_opa)) >> 8);
408                             if(opa_res_2 == 0) {
409                                 opa_res_2 = 1; /*never happens, just to be sure*/
410                             }
411                             lv_opa_t ratio = (uint16_t)((uint16_t)opa_res * 255) / opa_res_2;
412 
413                             lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y,
414                                                     lv_color_mix(color_res, bg_color, ratio));
415                             lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, opa_res_2);
416                         }
417                     }
418                 }
419             }
420         }
421     }
422 
423     lv_obj_invalidate(canvas);
424 }
425 
426 /**
427  * Fill the canvas with color
428  * @param canvas pointer to a canvas
429  * @param color the background color
430  */
lv_canvas_fill_bg(lv_obj_t * canvas,lv_color_t color)431 void lv_canvas_fill_bg(lv_obj_t * canvas, lv_color_t color)
432 {
433     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
434 
435     uint32_t x = dsc->header.w * dsc->header.h;
436     uint32_t y;
437     for(y = 0; y < dsc->header.h; y++) {
438         for(x = 0; x < dsc->header.w; x++) {
439             lv_img_buf_set_px_color(dsc, x, y, color);
440         }
441     }
442 }
443 
444 /**
445  * Draw a rectangle on the canvas
446  * @param canvas pointer to a canvas object
447  * @param x left coordinate of the rectangle
448  * @param y top coordinate of the rectangle
449  * @param w width of the rectangle
450  * @param h height of the rectangle
451  * @param style style of the rectangle (`body` properties are used except `padding`)
452  */
lv_canvas_draw_rect(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,lv_coord_t w,lv_coord_t h,const lv_style_t * style)453 void lv_canvas_draw_rect(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h,
454                          const lv_style_t * style)
455 {
456     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
457 
458     /* Create a dummy display to fool the lv_draw function.
459      * It will think it draws to real screen. */
460     lv_area_t mask;
461     mask.x1 = 0;
462     mask.x2 = dsc->header.w - 1;
463     mask.y1 = 0;
464     mask.y2 = dsc->header.h - 1;
465 
466     lv_area_t coords;
467     coords.x1 = x;
468     coords.y1 = y;
469     coords.x2 = x + w - 1;
470     coords.y2 = y + h - 1;
471 
472     lv_disp_t disp;
473     memset(&disp, 0, sizeof(lv_disp_t));
474 
475     lv_disp_buf_t disp_buf;
476     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
477     lv_area_copy(&disp_buf.area, &mask);
478 
479     lv_disp_drv_init(&disp.driver);
480 
481     disp.driver.buffer  = &disp_buf;
482     disp.driver.hor_res = dsc->header.w;
483     disp.driver.ver_res = dsc->header.h;
484 
485     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
486     lv_color_t ctransp = LV_COLOR_TRANSP;
487     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
488         style->body.main_color.full == ctransp.full &&
489         style->body.grad_color.full == ctransp.full)
490     {
491         disp.driver.antialiasing = 0;
492     }
493 
494     lv_disp_t * refr_ori = lv_refr_get_disp_refreshing();
495     lv_refr_set_disp_refreshing(&disp);
496 
497     lv_draw_rect(&coords, &mask, style, LV_OPA_COVER);
498 
499     lv_refr_set_disp_refreshing(refr_ori);
500 }
501 
502 /**
503  * Draw a text on the canvas.
504  * @param canvas pointer to a canvas object
505  * @param x left coordinate of the text
506  * @param y top coordinate of the text
507  * @param max_w max width of the text. The text will be wrapped to fit into this size
508  * @param style style of the text (`text` properties are used)
509  * @param txt text to display
510  * @param align align of the text (`LV_LABEL_ALIGN_LEFT/RIGHT/CENTER`)
511  */
lv_canvas_draw_text(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,lv_coord_t max_w,const lv_style_t * style,const char * txt,lv_label_align_t align)512 void lv_canvas_draw_text(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t max_w, const lv_style_t * style,
513                          const char * txt, lv_label_align_t align)
514 {
515     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
516 
517     /* Create a dummy display to fool the lv_draw function.
518      * It will think it draws to real screen. */
519     lv_area_t mask;
520     mask.x1 = 0;
521     mask.x2 = dsc->header.w - 1;
522     mask.y1 = 0;
523     mask.y2 = dsc->header.h - 1;
524 
525     lv_area_t coords;
526     coords.x1 = x;
527     coords.y1 = y;
528     coords.x2 = x + max_w - 1;
529     coords.y2 = dsc->header.h - 1;
530 
531     lv_disp_t disp;
532     memset(&disp, 0, sizeof(lv_disp_t));
533 
534     lv_disp_buf_t disp_buf;
535     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
536     lv_area_copy(&disp_buf.area, &mask);
537 
538     lv_disp_drv_init(&disp.driver);
539 
540     disp.driver.buffer  = &disp_buf;
541     disp.driver.hor_res = dsc->header.w;
542     disp.driver.ver_res = dsc->header.h;
543 
544     lv_disp_t * refr_ori = lv_refr_get_disp_refreshing();
545     lv_refr_set_disp_refreshing(&disp);
546 
547     lv_txt_flag_t flag;
548     switch(align) {
549         case LV_LABEL_ALIGN_LEFT: flag = LV_TXT_FLAG_NONE; break;
550         case LV_LABEL_ALIGN_RIGHT: flag = LV_TXT_FLAG_RIGHT; break;
551         case LV_LABEL_ALIGN_CENTER: flag = LV_TXT_FLAG_CENTER; break;
552         default: flag = LV_TXT_FLAG_NONE; break;
553     }
554 
555     lv_draw_label(&coords, &mask, style, LV_OPA_COVER, txt, flag, NULL, LV_LABEL_TEXT_SEL_OFF, LV_LABEL_TEXT_SEL_OFF,
556                   NULL);
557 
558     lv_refr_set_disp_refreshing(refr_ori);
559 }
560 
561 /**
562  * Draw an image on the canvas
563  * @param canvas pointer to a canvas object
564  * @param src image source. Can be a pointer an `lv_img_dsc_t` variable or a path an image.
565  * @param style style of the image (`image` properties are used)
566  */
lv_canvas_draw_img(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,const void * src,const lv_style_t * style)567 void lv_canvas_draw_img(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, const void * src, const lv_style_t * style)
568 {
569     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
570 
571     /* Create a dummy display to fool the lv_draw function.
572      * It will think it draws to real screen. */
573     lv_area_t mask;
574     mask.x1 = 0;
575     mask.x2 = dsc->header.w - 1;
576     mask.y1 = 0;
577     mask.y2 = dsc->header.h - 1;
578 
579     lv_img_header_t header;
580     lv_res_t res = lv_img_decoder_get_info(src, &header);
581     if(res != LV_RES_OK) {
582         LV_LOG_WARN("lv_canvas_draw_img: Couldn't get the image data.");
583         return;
584     }
585 
586     lv_area_t coords;
587     coords.x1 = x;
588     coords.y1 = y;
589     coords.x2 = x + header.w - 1;
590     coords.y2 = y + header.h - 1;
591 
592     lv_disp_t disp;
593     memset(&disp, 0, sizeof(lv_disp_t));
594 
595     lv_disp_buf_t disp_buf;
596     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
597     lv_area_copy(&disp_buf.area, &mask);
598 
599     lv_disp_drv_init(&disp.driver);
600 
601     disp.driver.buffer  = &disp_buf;
602     disp.driver.hor_res = dsc->header.w;
603     disp.driver.ver_res = dsc->header.h;
604 
605     lv_disp_t * refr_ori = lv_refr_get_disp_refreshing();
606     lv_refr_set_disp_refreshing(&disp);
607 
608     lv_draw_img(&coords, &mask, src, style, LV_OPA_COVER);
609 
610     lv_refr_set_disp_refreshing(refr_ori);
611 }
612 
613 /**
614  * Draw a line on the canvas
615  * @param canvas pointer to a canvas object
616  * @param points point of the line
617  * @param point_cnt number of points
618  * @param style style of the line (`line` properties are used)
619  */
lv_canvas_draw_line(lv_obj_t * canvas,const lv_point_t * points,uint32_t point_cnt,const lv_style_t * style)620 void lv_canvas_draw_line(lv_obj_t * canvas, const lv_point_t * points, uint32_t point_cnt, const lv_style_t * style)
621 {
622     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
623 
624     /* Create a dummy display to fool the lv_draw function.
625      * It will think it draws to real screen. */
626     lv_area_t mask;
627     mask.x1 = 0;
628     mask.x2 = dsc->header.w - 1;
629     mask.y1 = 0;
630     mask.y2 = dsc->header.h - 1;
631 
632     lv_disp_t disp;
633     memset(&disp, 0, sizeof(lv_disp_t));
634 
635     lv_disp_buf_t disp_buf;
636     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
637     lv_area_copy(&disp_buf.area, &mask);
638 
639     lv_disp_drv_init(&disp.driver);
640 
641     disp.driver.buffer  = &disp_buf;
642     disp.driver.hor_res = dsc->header.w;
643     disp.driver.ver_res = dsc->header.h;
644 
645     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
646     lv_color_t ctransp = LV_COLOR_TRANSP;
647     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
648         style->body.main_color.full == ctransp.full &&
649         style->body.grad_color.full == ctransp.full)
650     {
651         disp.driver.antialiasing = 0;
652     }
653 
654     lv_disp_t * refr_ori = lv_refr_get_disp_refreshing();
655     lv_refr_set_disp_refreshing(&disp);
656 
657     uint32_t i;
658     for(i = 0; i < point_cnt - 1; i++) {
659         lv_draw_line(&points[i], &points[i + 1], &mask, style, LV_OPA_COVER);
660     }
661 
662     lv_refr_set_disp_refreshing(refr_ori);
663 }
664 
665 /**
666  * Draw a polygon on the canvas
667  * @param canvas pointer to a canvas object
668  * @param points point of the polygon
669  * @param point_cnt number of points
670  * @param style style of the polygon (`body.main_color` and `body.opa` is used)
671  */
lv_canvas_draw_polygon(lv_obj_t * canvas,const lv_point_t * points,uint32_t point_cnt,const lv_style_t * style)672 void lv_canvas_draw_polygon(lv_obj_t * canvas, const lv_point_t * points, uint32_t point_cnt, const lv_style_t * style)
673 {
674     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
675 
676     /* Create a dummy display to fool the lv_draw function.
677      * It will think it draws to real screen. */
678     lv_area_t mask;
679     mask.x1 = 0;
680     mask.x2 = dsc->header.w - 1;
681     mask.y1 = 0;
682     mask.y2 = dsc->header.h - 1;
683 
684     lv_disp_t disp;
685     memset(&disp, 0, sizeof(lv_disp_t));
686 
687     lv_disp_buf_t disp_buf;
688     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
689     lv_area_copy(&disp_buf.area, &mask);
690 
691     lv_disp_drv_init(&disp.driver);
692 
693     disp.driver.buffer  = &disp_buf;
694     disp.driver.hor_res = dsc->header.w;
695     disp.driver.ver_res = dsc->header.h;
696 
697     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
698     lv_color_t ctransp = LV_COLOR_TRANSP;
699     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
700         style->body.main_color.full == ctransp.full &&
701         style->body.grad_color.full == ctransp.full)
702     {
703         disp.driver.antialiasing = 0;
704     }
705 
706     lv_disp_t * refr_ori = lv_refr_get_disp_refreshing();
707     lv_refr_set_disp_refreshing(&disp);
708 
709     lv_draw_polygon(points, point_cnt, &mask, style, LV_OPA_COVER);
710 
711     lv_refr_set_disp_refreshing(refr_ori);
712 }
713 
714 /**
715  * Draw an arc on the canvas
716  * @param canvas pointer to a canvas object
717  * @param x origo x  of the arc
718  * @param y origo y of the arc
719  * @param r radius of the arc
720  * @param start_angle start angle in degrees
721  * @param end_angle end angle in degrees
722  * @param style style of the polygon (`body.main_color` and `body.opa` is used)
723  */
lv_canvas_draw_arc(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,lv_coord_t r,int32_t start_angle,int32_t end_angle,const lv_style_t * style)724 void lv_canvas_draw_arc(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t r, int32_t start_angle,
725                         int32_t end_angle, const lv_style_t * style)
726 {
727     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
728 
729     /* Create a dummy display to fool the lv_draw function.
730      * It will think it draws to real screen. */
731     lv_area_t mask;
732     mask.x1 = 0;
733     mask.x2 = dsc->header.w - 1;
734     mask.y1 = 0;
735     mask.y2 = dsc->header.h - 1;
736 
737     lv_disp_t disp;
738     memset(&disp, 0, sizeof(lv_disp_t));
739 
740     lv_disp_buf_t disp_buf;
741     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
742     lv_area_copy(&disp_buf.area, &mask);
743 
744     lv_disp_drv_init(&disp.driver);
745 
746     disp.driver.buffer  = &disp_buf;
747     disp.driver.hor_res = dsc->header.w;
748     disp.driver.ver_res = dsc->header.h;
749 
750     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
751     lv_color_t ctransp = LV_COLOR_TRANSP;
752     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
753         style->body.main_color.full == ctransp.full &&
754         style->body.grad_color.full == ctransp.full)
755     {
756         disp.driver.antialiasing = 0;
757     }
758 
759     lv_disp_t * refr_ori = lv_refr_get_disp_refreshing();
760     lv_refr_set_disp_refreshing(&disp);
761 
762     lv_draw_arc(x, y, r, &mask, start_angle, end_angle, style, LV_OPA_COVER);
763 
764     lv_refr_set_disp_refreshing(refr_ori);
765 }
766 
767 /**********************
768  *   STATIC FUNCTIONS
769  **********************/
770 
771 /**
772  * Signal function of the canvas
773  * @param canvas pointer to a canvas object
774  * @param sign a signal type from lv_signal_t enum
775  * @param param pointer to a signal specific variable
776  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
777  */
lv_canvas_signal(lv_obj_t * canvas,lv_signal_t sign,void * param)778 static lv_res_t lv_canvas_signal(lv_obj_t * canvas, lv_signal_t sign, void * param)
779 {
780     lv_res_t res;
781 
782     /* Include the ancient signal function */
783     res = ancestor_signal(canvas, sign, param);
784     if(res != LV_RES_OK) return res;
785 
786     if(sign == LV_SIGNAL_CLEANUP) {
787         /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
788     } else if(sign == LV_SIGNAL_GET_TYPE) {
789         lv_obj_type_t * buf = param;
790         uint8_t i;
791         for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
792             if(buf->type[i] == NULL) break;
793         }
794         buf->type[i] = "lv_canvas";
795     }
796 
797     return res;
798 }
799 
800 #endif
801