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_debug.h"
12 #include "../lv_misc/lv_math.h"
13 #include "../lv_draw/lv_draw.h"
14 #include "../lv_core/lv_refr.h"
15 #include "../lv_themes/lv_theme.h"
16 
17 #if LV_USE_CANVAS != 0
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define LV_OBJX_NAME "lv_canvas"
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static lv_res_t lv_canvas_signal(lv_obj_t * canvas, lv_signal_t sign, void * param);
32 static void set_set_px_cb(lv_disp_drv_t * disp_drv, lv_img_cf_t cf);
33 
34 static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x,
35                                     lv_coord_t y,
36                                     lv_color_t color, lv_opa_t opa);
37 
38 static void set_px_cb_alpha1(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
39                              lv_color_t color, lv_opa_t opa);
40 
41 static void set_px_cb_alpha2(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
42                              lv_color_t color, lv_opa_t opa);
43 
44 static void set_px_cb_alpha4(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
45                              lv_color_t color, lv_opa_t opa);
46 
47 
48 static void set_px_cb_alpha8(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
49                              lv_color_t color, lv_opa_t opa);
50 
51 static void set_px_alpha_generic(lv_img_dsc_t * d, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
52 
53 /**********************
54  *  STATIC VARIABLES
55  **********************/
56 static lv_signal_cb_t ancestor_signal;
57 static lv_design_cb_t ancestor_design;
58 
59 /**********************
60  *      MACROS
61  **********************/
62 
63 /**********************
64  *   GLOBAL FUNCTIONS
65  **********************/
66 
67 /**
68  * Create a canvas object
69  * @param par pointer to an object, it will be the parent of the new canvas
70  * @param copy pointer to a canvas object, if not NULL then the new object will be copied from it
71  * @return pointer to the created canvas
72  */
lv_canvas_create(lv_obj_t * par,const lv_obj_t * copy)73 lv_obj_t * lv_canvas_create(lv_obj_t * par, const lv_obj_t * copy)
74 {
75     LV_LOG_TRACE("canvas create started");
76 
77     /*Create the ancestor of canvas*/
78     lv_obj_t * new_canvas = lv_img_create(par, copy);
79     LV_ASSERT_MEM(new_canvas);
80     if(new_canvas == NULL) return NULL;
81 
82     /*Allocate the canvas type specific extended data*/
83     lv_canvas_ext_t * ext = lv_obj_allocate_ext_attr(new_canvas, sizeof(lv_canvas_ext_t));
84     LV_ASSERT_MEM(ext);
85     if(ext == NULL) {
86         lv_obj_del(new_canvas);
87         return NULL;
88     }
89 
90     if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_canvas);
91     if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_cb(new_canvas);
92 
93     /*Initialize the allocated 'ext' */
94     ext->dsc.header.always_zero = 0;
95     ext->dsc.header.cf          = LV_IMG_CF_TRUE_COLOR;
96     ext->dsc.header.h           = 0;
97     ext->dsc.header.w           = 0;
98     ext->dsc.data_size          = 0;
99     ext->dsc.data               = NULL;
100 
101     lv_img_set_src(new_canvas, &ext->dsc);
102 
103     /*The signal and design functions are not copied so set them here*/
104     lv_obj_set_signal_cb(new_canvas, lv_canvas_signal);
105 
106     /*Init the new canvas canvas*/
107     if(copy == NULL) {
108         lv_theme_apply(new_canvas, LV_THEME_CANVAS);
109     }
110     /*Copy an existing canvas*/
111     else {
112         /*Do not copy the image data because each canvas needs it's own buffer*/
113 
114     }
115 
116     LV_LOG_INFO("canvas created");
117 
118     return new_canvas;
119 }
120 
121 /*=====================
122  * Setter functions
123  *====================*/
124 
125 /**
126  * Set a buffer for the canvas.
127  * @param buf a buffer where the content of the canvas will be.
128  * The required size is (lv_img_color_format_get_px_size(cf) * w * h) / 8)
129  * It can be allocated with `lv_mem_alloc()` or
130  * it can be statically allocated array (e.g. static lv_color_t buf[100*50]) or
131  * it can be an address in RAM or external SRAM
132  * @param canvas pointer to a canvas object
133  * @param w width of the canvas
134  * @param h height of the canvas
135  * @param cf color format. The following formats are supported:
136  *      LV_IMG_CF_TRUE_COLOR, LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, LV_IMG_CF_INDEXES_1/2/4/8BIT
137  *
138  */
lv_canvas_set_buffer(lv_obj_t * canvas,void * buf,lv_coord_t w,lv_coord_t h,lv_img_cf_t cf)139 void lv_canvas_set_buffer(lv_obj_t * canvas, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
140 {
141     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
142     LV_ASSERT_NULL(buf);
143 
144     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
145 
146     ext->dsc.header.cf = cf;
147     ext->dsc.header.w  = w;
148     ext->dsc.header.h  = h;
149     ext->dsc.data      = buf;
150 
151     lv_img_set_src(canvas, &ext->dsc);
152 }
153 
154 /**
155  * Set the color of a pixel on the canvas
156  * @param canvas pointer to canvas object
157  * @param x x coordinate of the point to set
158  * @param y x coordinate of the point to set
159  * @param c color of the point
160  */
lv_canvas_set_px(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,lv_color_t c)161 void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c)
162 {
163     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
164 
165     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
166 
167     lv_img_buf_set_px_color(&ext->dsc, x, y, c);
168     lv_obj_invalidate(canvas);
169 }
170 
171 /**
172  * Set the palette color of a canvas with index format. Valid only for `LV_IMG_CF_INDEXED1/2/4/8`
173  * @param canvas pointer to canvas object
174  * @param id the palette color to set:
175  *   - for `LV_IMG_CF_INDEXED1`: 0..1
176  *   - for `LV_IMG_CF_INDEXED2`: 0..3
177  *   - for `LV_IMG_CF_INDEXED4`: 0..15
178  *   - for `LV_IMG_CF_INDEXED8`: 0..255
179  * @param c the color to set
180  */
lv_canvas_set_palette(lv_obj_t * canvas,uint8_t id,lv_color_t c)181 void lv_canvas_set_palette(lv_obj_t * canvas, uint8_t id, lv_color_t c)
182 {
183     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
184 
185     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
186 
187     lv_img_buf_set_palette(&ext->dsc, id, c);
188     lv_obj_invalidate(canvas);
189 }
190 
191 /*=====================
192  * Getter functions
193  *====================*/
194 
195 /**
196  * Get the color of a pixel on the canvas
197  * @param canvas
198  * @param x x coordinate of the point to set
199  * @param y x coordinate of the point to set
200  * @return color of the point
201  */
lv_canvas_get_px(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y)202 lv_color_t lv_canvas_get_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y)
203 {
204     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
205 
206     lv_canvas_ext_t * ext    = lv_obj_get_ext_attr(canvas);
207     lv_color_t color = lv_obj_get_style_image_recolor(canvas, LV_CANVAS_PART_MAIN);
208 
209     return lv_img_buf_get_px_color(&ext->dsc, x, y, color);
210 }
211 
212 /**
213  * Get the image of the canvas as a pointer to an `lv_img_dsc_t` variable.
214  * @param canvas pointer to a canvas object
215  * @return pointer to the image descriptor.
216  */
lv_canvas_get_img(lv_obj_t * canvas)217 lv_img_dsc_t * lv_canvas_get_img(lv_obj_t * canvas)
218 {
219     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
220 
221     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
222 
223     return &ext->dsc;
224 }
225 
226 /*=====================
227  * Other functions
228  *====================*/
229 
230 /**
231  * Copy a buffer to the canvas
232  * @param canvas pointer to a canvas object
233  * @param to_copy buffer to copy. The color format has to match with the canvas's buffer color
234  * format
235  * @param w width of the buffer to copy
236  * @param h height of the buffer to copy
237  * @param x left side of the destination position
238  * @param y top side of the destination position
239  */
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)240 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)
241 {
242     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
243     LV_ASSERT_NULL(to_copy);
244 
245     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
246     if(x + w >= (lv_coord_t)ext->dsc.header.w || y + h >= (lv_coord_t)ext->dsc.header.h) {
247         LV_LOG_WARN("lv_canvas_copy_buf: x or y out of the canvas");
248         return;
249     }
250 
251     uint32_t px_size   = lv_img_cf_get_px_size(ext->dsc.header.cf) >> 3;
252     uint32_t px        = ext->dsc.header.w * y * px_size + x * px_size;
253     uint8_t * to_copy8 = (uint8_t *)to_copy;
254     lv_coord_t i;
255     for(i = 0; i < h; i++) {
256         _lv_memcpy((void *)&ext->dsc.data[px], to_copy8, w * px_size);
257         px += ext->dsc.header.w * px_size;
258         to_copy8 += w * px_size;
259     }
260 }
261 
262 /**
263  * Transform and image and store the result on a canvas.
264  * @param canvas pointer to a canvas object to store the result of the transformation.
265  * @param img pointer to an image descriptor to transform.
266  *             Can be the image descriptor of an other canvas too (`lv_canvas_get_img()`).
267  * @param angle the angle of rotation (0..3600), 0.1 deg resolution
268  * @param zoom zoom factor (256 no zoom);
269  * @param offset_x offset X to tell where to put the result data on destination canvas
270  * @param offset_y offset X to tell where to put the result data on destination canvas
271  * @param pivot_x pivot X of rotation. Relative to the source canvas
272  *                Set to `source width / 2` to rotate around the center
273  * @param pivot_y pivot Y of rotation. Relative to the source canvas
274  *                Set to `source height / 2` to rotate around the center
275  * @param antialias apply anti-aliasing during the transformation. Looks better but slower.
276  */
lv_canvas_transform(lv_obj_t * canvas,lv_img_dsc_t * img,int16_t angle,uint16_t zoom,lv_coord_t offset_x,lv_coord_t offset_y,int32_t pivot_x,int32_t pivot_y,bool antialias)277 void lv_canvas_transform(lv_obj_t * canvas, lv_img_dsc_t * img, int16_t angle, uint16_t zoom, lv_coord_t offset_x,
278                          lv_coord_t offset_y,
279                          int32_t pivot_x, int32_t pivot_y, bool antialias)
280 {
281 #if LV_USE_IMG_TRANSFORM
282     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
283     LV_ASSERT_NULL(img);
284 
285     lv_canvas_ext_t * ext_dst = lv_obj_get_ext_attr(canvas);
286     lv_color_t color = lv_obj_get_style_image_recolor(canvas, LV_CANVAS_PART_MAIN);
287 
288     int32_t dest_width  = ext_dst->dsc.header.w;
289     int32_t dest_height = ext_dst->dsc.header.h;
290 
291     int32_t x;
292     int32_t y;
293     bool ret;
294 
295     lv_img_transform_dsc_t dsc;
296     dsc.cfg.angle = angle;
297     dsc.cfg.zoom = zoom;
298     dsc.cfg.src = img->data;
299     dsc.cfg.src_w = img->header.w;
300     dsc.cfg.src_h = img->header.h;
301     dsc.cfg.cf = img->header.cf;
302     dsc.cfg.pivot_x = pivot_x;
303     dsc.cfg.pivot_y = pivot_y;
304     dsc.cfg.color = color;
305     dsc.cfg.antialias = antialias;
306     _lv_img_buf_transform_init(&dsc);
307 
308     for(y = -offset_y; y < dest_height - offset_y; y++) {
309         for(x = -offset_x; x < dest_width - offset_x; x++) {
310 
311             ret = _lv_img_buf_transform(&dsc, x, y);
312 
313             if(ret == false) continue;
314 
315             if(x + offset_x >= 0 && x + offset_x < dest_width && y + offset_y >= 0 && y + offset_y < dest_height) {
316                 /*If the image has no alpha channel just simple set the result color on the canvas*/
317                 if(lv_img_cf_has_alpha(img->header.cf) == false) {
318                     lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res.color);
319                 }
320                 else {
321                     lv_color_t bg_color = lv_img_buf_get_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.cfg.color);
322 
323                     /*If the canvas has no alpha but the image has mix the image's color with
324                      * canvas*/
325                     if(lv_img_cf_has_alpha(ext_dst->dsc.header.cf) == false) {
326                         if(dsc.res.opa < LV_OPA_MAX) dsc.res.color = lv_color_mix(dsc.res.color, bg_color, dsc.res.opa);
327                         lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res.color);
328                     }
329                     /*Both the image and canvas has alpha channel. Some extra calculation is
330                        required*/
331                     else {
332                         lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y);
333                         /* Pick the foreground if it's fully opaque or the Background is fully
334                          * transparent*/
335                         if(dsc.res.opa >= LV_OPA_MAX || bg_opa <= LV_OPA_MIN) {
336                             lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res.color);
337                             lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, dsc.res.opa);
338                         }
339                         /*Opaque background: use simple mix*/
340                         else if(bg_opa >= LV_OPA_MAX) {
341                             lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y,
342                                                     lv_color_mix(dsc.res.color, bg_color, dsc.res.opa));
343                         }
344                         /*Both colors have alpha. Expensive calculation need to be applied*/
345                         else {
346 
347                             /*Info:
348                              * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
349                             lv_opa_t opa_res_2 = 255 - ((uint16_t)((uint16_t)(255 - dsc.res.opa) * (255 - bg_opa)) >> 8);
350                             if(opa_res_2 == 0) {
351                                 opa_res_2 = 1; /*never happens, just to be sure*/
352                             }
353                             lv_opa_t ratio = (uint16_t)((uint16_t)dsc.res.opa * 255) / opa_res_2;
354 
355                             lv_img_buf_set_px_color(&ext_dst->dsc, x + offset_x, y + offset_y,
356                                                     lv_color_mix(dsc.res.color, bg_color, ratio));
357                             lv_img_buf_set_px_alpha(&ext_dst->dsc, x + offset_x, y + offset_y, opa_res_2);
358                         }
359                     }
360                 }
361             }
362         }
363     }
364 
365     lv_obj_invalidate(canvas);
366 #else
367     LV_UNUSED(canvas);
368     LV_UNUSED(img);
369     LV_UNUSED(angle);
370     LV_UNUSED(zoom);
371     LV_UNUSED(offset_x);
372     LV_UNUSED(offset_y);
373     LV_UNUSED(pivot_x);
374     LV_UNUSED(pivot_y);
375     LV_UNUSED(antialias);
376     LV_LOG_WARN("LV_USE_IMG_TRANSFORM is disabled in lv_conf.h");
377 #endif
378 }
379 
380 
381 /**
382  * Apply horizontal blur on the canvas
383  * @param canvas pointer to a canvas object
384  * @param area the area to blur. If `NULL` the whole canvas will be blurred.
385  * @param r radius of the blur
386  */
lv_canvas_blur_hor(lv_obj_t * canvas,const lv_area_t * area,uint16_t r)387 void lv_canvas_blur_hor(lv_obj_t * canvas, const lv_area_t * area, uint16_t r)
388 {
389     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
390 
391     if(r == 0) return;
392 
393     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
394 
395     lv_area_t a;
396     if(area) {
397         lv_area_copy(&a, area);
398         if(a.x1 < 0) a.x1 = 0;
399         if(a.y1 < 0) a.y1 = 0;
400         if(a.x2 > ext->dsc.header.w - 1) a.x2 = ext->dsc.header.w - 1;
401         if(a.y2 > ext->dsc.header.h - 1) a.y2 = ext->dsc.header.h - 1;
402     }
403     else {
404         a.x1 = 0;
405         a.y1 = 0;
406         a.x2 = ext->dsc.header.w - 1;
407         a.y2 = ext->dsc.header.h - 1;
408     }
409 
410     lv_color_t color = lv_obj_get_style_image_recolor(canvas, LV_CANVAS_PART_MAIN);
411 
412     uint16_t r_back = r / 2;
413     uint16_t r_front = r / 2;
414 
415     if((r & 0x1) == 0) r_back--;
416 
417     bool has_alpha = lv_img_cf_has_alpha(ext->dsc.header.cf);
418 
419     lv_coord_t line_w = lv_img_buf_get_img_size(ext->dsc.header.w, 1, ext->dsc.header.cf);
420     uint8_t * line_buf = _lv_mem_buf_get(line_w);
421 
422     lv_img_dsc_t line_img;
423     line_img.data = line_buf;
424     line_img.header.always_zero = 0;
425     line_img.header.w = ext->dsc.header.w;
426     line_img.header.h = 1;
427     line_img.header.cf = ext->dsc.header.cf;
428 
429     lv_coord_t x;
430     lv_coord_t y;
431     lv_coord_t x_safe;
432 
433     for(y = a.y1; y <= a.y2; y++) {
434         uint32_t asum = 0;
435         uint32_t rsum = 0;
436         uint32_t gsum = 0;
437         uint32_t bsum = 0;
438 
439         lv_color_t c;
440         lv_opa_t opa = LV_OPA_TRANSP;
441         _lv_memcpy(line_buf, &ext->dsc.data[y * line_w], line_w);
442 
443 
444         for(x = a.x1 - r_back; x <= a.x1 + r_front; x++) {
445             x_safe = x < 0 ? 0 : x;
446             x_safe = x_safe > ext->dsc.header.w - 1 ? ext->dsc.header.w - 1 : x_safe;
447 
448             c = lv_img_buf_get_px_color(&line_img, x_safe, 0, color);
449             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
450 
451             rsum += c.ch.red;
452 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
453             gsum += (c.ch.green_h << 3) + c.ch.green_l;
454 #else
455             gsum += c.ch.green;
456 #endif
457             bsum += c.ch.blue;
458             if(has_alpha) asum += opa;
459         }
460 
461         /*Just to indicate that the px is visible*/
462         if(has_alpha == false) asum = LV_OPA_COVER;
463 
464         for(x = a.x1; x <= a.x2; x++) {
465 
466             if(asum) {
467                 c.ch.red = rsum / r;
468 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
469                 uint8_t gtmp = gsum / r;
470                 c.ch.green_h = gtmp >> 3;
471                 c.ch.green_l = gtmp & 0x7;
472 #else
473                 c.ch.green = gsum / r;
474 #endif
475                 c.ch.blue = bsum / r;
476                 if(has_alpha) opa = asum / r;
477 
478                 lv_img_buf_set_px_color(&ext->dsc, x, y, c);
479             }
480             if(has_alpha) lv_img_buf_set_px_alpha(&ext->dsc, x, y, opa);
481 
482             x_safe = x - r_back;
483             x_safe = x_safe < 0 ? 0 : x_safe;
484             c = lv_img_buf_get_px_color(&line_img, x_safe, 0, color);
485             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
486 
487             rsum -= c.ch.red;
488 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
489             gsum -= (c.ch.green_h << 3) + c.ch.green_l;
490 #else
491             gsum -= c.ch.green;
492 #endif
493             bsum -= c.ch.blue;
494             if(has_alpha) asum -= opa;
495 
496             x_safe = x + 1 + r_front;
497             x_safe = x_safe > ext->dsc.header.w - 1 ? ext->dsc.header.w - 1 : x_safe;
498             c = lv_img_buf_get_px_color(&line_img, x_safe, 0, LV_COLOR_RED);
499             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
500 
501             rsum += c.ch.red;
502 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
503             gsum += (c.ch.green_h << 3) + c.ch.green_l;
504 #else
505             gsum += c.ch.green;
506 #endif
507             bsum += c.ch.blue;
508             if(has_alpha) asum += opa;
509         }
510     }
511     lv_obj_invalidate(canvas);
512 
513     _lv_mem_buf_release(line_buf);
514 }
515 
516 
517 /**
518  * Apply vertical blur on the canvas
519  * @param canvas pointer to a canvas object
520  * @param area the area to blur. If `NULL` the whole canvas will be blurred.
521  * @param r radius of the blur
522  */
lv_canvas_blur_ver(lv_obj_t * canvas,const lv_area_t * area,uint16_t r)523 void lv_canvas_blur_ver(lv_obj_t * canvas, const lv_area_t * area, uint16_t r)
524 {
525     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
526 
527     if(r == 0) return;
528 
529     lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas);
530 
531     lv_area_t a;
532     if(area) {
533         lv_area_copy(&a, area);
534         if(a.x1 < 0) a.x1 = 0;
535         if(a.y1 < 0) a.y1 = 0;
536         if(a.x2 > ext->dsc.header.w - 1) a.x2 = ext->dsc.header.w - 1;
537         if(a.y2 > ext->dsc.header.h - 1) a.y2 = ext->dsc.header.h - 1;
538     }
539     else {
540         a.x1 = 0;
541         a.y1 = 0;
542         a.x2 = ext->dsc.header.w - 1;
543         a.y2 = ext->dsc.header.h - 1;
544     }
545 
546     lv_color_t color = lv_obj_get_style_image_recolor(canvas, LV_CANVAS_PART_MAIN);
547 
548     uint16_t r_back = r / 2;
549     uint16_t r_front = r / 2;
550 
551     if((r & 0x1) == 0) r_back--;
552 
553     bool has_alpha = lv_img_cf_has_alpha(ext->dsc.header.cf);
554     lv_coord_t col_w = lv_img_buf_get_img_size(1, ext->dsc.header.h, ext->dsc.header.cf);
555     uint8_t * col_buf = _lv_mem_buf_get(col_w);
556     lv_img_dsc_t line_img;
557 
558     line_img.data = col_buf;
559     line_img.header.always_zero = 0;
560     line_img.header.w = 1;
561     line_img.header.h = ext->dsc.header.h;
562     line_img.header.cf = ext->dsc.header.cf;
563 
564     lv_coord_t x;
565     lv_coord_t y;
566     lv_coord_t y_safe;
567 
568     for(x = a.x1; x <= a.x2; x++) {
569         uint32_t asum = 0;
570         uint32_t rsum = 0;
571         uint32_t gsum = 0;
572         uint32_t bsum = 0;
573 
574         lv_color_t c;
575         lv_opa_t opa = LV_OPA_COVER;
576 
577         for(y = a.y1 - r_back; y <= a.y1 + r_front; y++) {
578             y_safe = y < 0 ? 0 : y;
579             y_safe = y_safe > ext->dsc.header.h - 1 ? ext->dsc.header.h - 1 : y_safe;
580 
581             c = lv_img_buf_get_px_color(&ext->dsc, x, y_safe, color);
582             if(has_alpha) opa = lv_img_buf_get_px_alpha(&ext->dsc, x, y_safe);
583 
584             lv_img_buf_set_px_color(&line_img, 0, y_safe, c);
585             if(has_alpha) lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa);
586 
587             rsum += c.ch.red;
588 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
589             gsum += (c.ch.green_h << 3) + c.ch.green_l;
590 #else
591             gsum += c.ch.green;
592 #endif
593             bsum += c.ch.blue;
594             if(has_alpha) asum += opa;
595         }
596 
597         /*Just to indicate that the px is visible*/
598         if(has_alpha == false) asum = LV_OPA_COVER;
599 
600         for(y = a.y1; y <= a.y2; y++) {
601             if(asum) {
602                 c.ch.red = rsum / r;
603 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
604                 uint8_t gtmp = gsum / r;
605                 c.ch.green_h = gtmp >> 3;
606                 c.ch.green_l = gtmp & 0x7;
607 #else
608                 c.ch.green = gsum / r;
609 #endif
610                 c.ch.blue = bsum / r;
611                 if(has_alpha) opa = asum / r;
612 
613                 lv_img_buf_set_px_color(&ext->dsc, x, y, c);
614             }
615             if(has_alpha) lv_img_buf_set_px_alpha(&ext->dsc, x, y, opa);
616 
617             y_safe = y - r_back;
618             y_safe = y_safe < 0 ? 0 : y_safe;
619             c = lv_img_buf_get_px_color(&line_img, 0, y_safe, color);
620             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, 0, y_safe);
621 
622             rsum -= c.ch.red;
623 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
624             gsum -= (c.ch.green_h << 3) + c.ch.green_l;
625 #else
626             gsum -= c.ch.green;
627 #endif
628             bsum -= c.ch.blue;
629             if(has_alpha) asum -= opa;
630 
631             y_safe = y + 1 + r_front;
632             y_safe = y_safe > ext->dsc.header.h - 1 ? ext->dsc.header.h - 1 : y_safe;
633 
634             c = lv_img_buf_get_px_color(&ext->dsc, x, y_safe, color);
635             if(has_alpha) opa = lv_img_buf_get_px_alpha(&ext->dsc, x, y_safe);
636 
637             lv_img_buf_set_px_color(&line_img, 0, y_safe, c);
638             if(has_alpha) lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa);
639 
640             rsum += c.ch.red;
641 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
642             gsum += (c.ch.green_h << 3) + c.ch.green_l;
643 #else
644             gsum += c.ch.green;
645 #endif
646             bsum += c.ch.blue;
647             if(has_alpha) asum += opa;
648         }
649     }
650 
651     lv_obj_invalidate(canvas);
652 
653     _lv_mem_buf_release(col_buf);
654 }
655 
656 /**
657  * Fill the canvas with color
658  * @param canvas pointer to a canvas
659  * @param color the background color
660  * @param opa the desired opacity
661  */
lv_canvas_fill_bg(lv_obj_t * canvas,lv_color_t color,lv_opa_t opa)662 void lv_canvas_fill_bg(lv_obj_t * canvas, lv_color_t color, lv_opa_t opa)
663 {
664     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
665 
666     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
667 
668     if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
669         uint32_t row_byte_cnt = (dsc->header.w + 7) >> 3;
670         /*+8 skip the palette*/
671         _lv_memset((uint8_t *)dsc->data + 8, color.full ? 0xff : 0x00, row_byte_cnt * dsc->header.h);
672     }
673     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
674         uint32_t row_byte_cnt = (dsc->header.w + 7) >> 3;
675         _lv_memset((uint8_t *)dsc->data, opa > LV_OPA_50 ? 0xff : 0x00, row_byte_cnt * dsc->header.h);
676     }
677     else {
678         uint32_t x;
679         uint32_t y;
680         for(y = 0; y < dsc->header.h; y++) {
681             for(x = 0; x < dsc->header.w; x++) {
682                 lv_img_buf_set_px_color(dsc, x, y, color);
683                 lv_img_buf_set_px_alpha(dsc, x, y, opa);
684             }
685         }
686     }
687 
688     lv_obj_invalidate(canvas);
689 }
690 
691 /**
692  * Draw a rectangle on the canvas
693  * @param canvas pointer to a canvas object
694  * @param x left coordinate of the rectangle
695  * @param y top coordinate of the rectangle
696  * @param w width of the rectangle
697  * @param h height of the rectangle
698  * @param rect_dsc descriptor of the rectangle
699  */
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_draw_rect_dsc_t * rect_dsc)700 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,
701                          const lv_draw_rect_dsc_t * rect_dsc)
702 {
703     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
704 
705     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
706 
707     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
708         LV_LOG_WARN("lv_canvas_draw_rect: can't raw to LV_IMG_CF_INDEXED canvas");
709         return;
710     }
711 
712     /* Create a dummy display to fool the lv_draw function.
713      * It will think it draws to real screen. */
714     lv_area_t mask;
715     mask.x1 = 0;
716     mask.x2 = dsc->header.w - 1;
717     mask.y1 = 0;
718     mask.y2 = dsc->header.h - 1;
719 
720     lv_area_t coords;
721     coords.x1 = x;
722     coords.y1 = y;
723     coords.x2 = x + w - 1;
724     coords.y2 = y + h - 1;
725 
726     lv_disp_t disp;
727     _lv_memset_00(&disp, sizeof(lv_disp_t));
728 
729     lv_disp_buf_t disp_buf;
730     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
731     lv_area_copy(&disp_buf.area, &mask);
732 
733     lv_disp_drv_init(&disp.driver);
734 
735     disp.driver.buffer  = &disp_buf;
736     disp.driver.hor_res = dsc->header.w;
737     disp.driver.ver_res = dsc->header.h;
738 
739     set_set_px_cb(&disp.driver, dsc->header.cf);
740 
741 #if LV_ANTIALIAS
742 
743     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
744     lv_color_t ctransp = LV_COLOR_TRANSP;
745     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
746        rect_dsc->bg_color.full == ctransp.full) {
747         disp.driver.antialiasing = 0;
748     }
749 #endif
750 
751     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
752     _lv_refr_set_disp_refreshing(&disp);
753 
754     lv_draw_rect(&coords, &mask, rect_dsc);
755 
756     _lv_refr_set_disp_refreshing(refr_ori);
757 
758     lv_obj_invalidate(canvas);
759 }
760 
761 /**
762  * Draw a text on the canvas.
763  * @param canvas pointer to a canvas object
764  * @param x left coordinate of the text
765  * @param y top coordinate of the text
766  * @param max_w max width of the text. The text will be wrapped to fit into this size
767  * @param label_draw_dsc pointer to a valid label descriptor `lv_draw_label_dsc_t`
768  * @param txt text to display
769  * @param align align of the text (`LV_LABEL_ALIGN_LEFT/RIGHT/CENTER`)
770  */
lv_canvas_draw_text(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,lv_coord_t max_w,lv_draw_label_dsc_t * label_draw_dsc,const char * txt,lv_label_align_t align)771 void lv_canvas_draw_text(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t max_w,
772                          lv_draw_label_dsc_t * label_draw_dsc,
773                          const char * txt, lv_label_align_t align)
774 {
775     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
776 
777     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
778 
779     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
780         LV_LOG_WARN("lv_canvas_draw_text: can't raw to LV_IMG_CF_INDEXED canvas");
781         return;
782     }
783 
784     /* Create a dummy display to fool the lv_draw function.
785      * It will think it draws to real screen. */
786     lv_area_t mask;
787     mask.x1 = 0;
788     mask.x2 = dsc->header.w - 1;
789     mask.y1 = 0;
790     mask.y2 = dsc->header.h - 1;
791 
792     lv_area_t coords;
793     coords.x1 = x;
794     coords.y1 = y;
795     coords.x2 = x + max_w - 1;
796     coords.y2 = dsc->header.h - 1;
797 
798     lv_disp_t disp;
799     _lv_memset_00(&disp, sizeof(lv_disp_t));
800 
801     lv_disp_buf_t disp_buf;
802     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
803     lv_area_copy(&disp_buf.area, &mask);
804 
805     lv_disp_drv_init(&disp.driver);
806 
807     disp.driver.buffer  = &disp_buf;
808     disp.driver.hor_res = dsc->header.w;
809     disp.driver.ver_res = dsc->header.h;
810 
811     set_set_px_cb(&disp.driver, dsc->header.cf);
812 
813     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
814     _lv_refr_set_disp_refreshing(&disp);
815 
816     lv_txt_flag_t flag;
817     switch(align) {
818         case LV_LABEL_ALIGN_LEFT:
819             flag = LV_TXT_FLAG_NONE;
820             break;
821         case LV_LABEL_ALIGN_RIGHT:
822             flag = LV_TXT_FLAG_RIGHT;
823             break;
824         case LV_LABEL_ALIGN_CENTER:
825             flag = LV_TXT_FLAG_CENTER;
826             break;
827         default:
828             flag = LV_TXT_FLAG_NONE;
829             break;
830     }
831 
832     label_draw_dsc->flag = flag;
833 
834     lv_draw_label(&coords, &mask, label_draw_dsc, txt, NULL);
835 
836     _lv_refr_set_disp_refreshing(refr_ori);
837 
838     lv_obj_invalidate(canvas);
839 }
840 
841 /**
842  * Draw an image on the canvas
843  * @param canvas pointer to a canvas object
844  * @param src image source. Can be a pointer an `lv_img_dsc_t` variable or a path an image.
845  * @param img_draw_dsc pointer to a valid label descriptor `lv_draw_img_dsc_t`
846  */
lv_canvas_draw_img(lv_obj_t * canvas,lv_coord_t x,lv_coord_t y,const void * src,const lv_draw_img_dsc_t * img_draw_dsc)847 void lv_canvas_draw_img(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, const void * src,
848                         const lv_draw_img_dsc_t * img_draw_dsc)
849 {
850     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
851 
852     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
853 
854     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
855         LV_LOG_WARN("lv_canvas_draw_img: can't raw to LV_IMG_CF_INDEXED canvas");
856         return;
857     }
858 
859     /* Create a dummy display to fool the lv_draw function.
860      * It will think it draws to real screen. */
861     lv_area_t mask;
862     mask.x1 = 0;
863     mask.x2 = dsc->header.w - 1;
864     mask.y1 = 0;
865     mask.y2 = dsc->header.h - 1;
866 
867     lv_img_header_t header;
868     lv_res_t res = lv_img_decoder_get_info(src, &header);
869     if(res != LV_RES_OK) {
870         LV_LOG_WARN("lv_canvas_draw_img: Couldn't get the image data.");
871         return;
872     }
873 
874     lv_area_t coords;
875     coords.x1 = x;
876     coords.y1 = y;
877     coords.x2 = x + header.w - 1;
878     coords.y2 = y + header.h - 1;
879 
880     lv_disp_t disp;
881     _lv_memset_00(&disp, sizeof(lv_disp_t));
882 
883     lv_disp_buf_t disp_buf;
884     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
885     lv_area_copy(&disp_buf.area, &mask);
886 
887     lv_disp_drv_init(&disp.driver);
888 
889     disp.driver.buffer  = &disp_buf;
890     disp.driver.hor_res = dsc->header.w;
891     disp.driver.ver_res = dsc->header.h;
892 
893     set_set_px_cb(&disp.driver, dsc->header.cf);
894 
895     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
896     _lv_refr_set_disp_refreshing(&disp);
897 
898     lv_draw_img(&coords, &mask, src, img_draw_dsc);
899 
900     _lv_refr_set_disp_refreshing(refr_ori);
901 
902     lv_obj_invalidate(canvas);
903 }
904 
905 /**
906  * Draw a line on the canvas
907  * @param canvas pointer to a canvas object
908  * @param points point of the line
909  * @param point_cnt number of points
910  * @param line_draw_dsc pointer to an initialized `lv_draw_line_dsc_t` variable
911  */
lv_canvas_draw_line(lv_obj_t * canvas,const lv_point_t points[],uint32_t point_cnt,const lv_draw_line_dsc_t * line_draw_dsc)912 void lv_canvas_draw_line(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
913                          const lv_draw_line_dsc_t * line_draw_dsc)
914 {
915     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
916 
917     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
918 
919     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
920         LV_LOG_WARN("lv_canvas_draw_line: can't raw to LV_IMG_CF_INDEXED canvas");
921         return;
922     }
923     /* Create a dummy display to fool the lv_draw function.
924      * It will think it draws to real screen. */
925     lv_area_t mask;
926     mask.x1 = 0;
927     mask.x2 = dsc->header.w - 1;
928     mask.y1 = 0;
929     mask.y2 = dsc->header.h - 1;
930 
931     lv_disp_t disp;
932     _lv_memset_00(&disp, sizeof(lv_disp_t));
933 
934     lv_disp_buf_t disp_buf;
935     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
936     lv_area_copy(&disp_buf.area, &mask);
937 
938     lv_disp_drv_init(&disp.driver);
939 
940     disp.driver.buffer  = &disp_buf;
941     disp.driver.hor_res = dsc->header.w;
942     disp.driver.ver_res = dsc->header.h;
943 
944     set_set_px_cb(&disp.driver, dsc->header.cf);
945 
946 #if LV_ANTIALIAS
947     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
948     lv_color_t ctransp = LV_COLOR_TRANSP;
949     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
950        line_draw_dsc->color.full == ctransp.full) {
951         disp.driver.antialiasing = 0;
952     }
953 #endif
954 
955     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
956     _lv_refr_set_disp_refreshing(&disp);
957 
958     uint32_t i;
959     for(i = 0; i < point_cnt - 1; i++) {
960         lv_draw_line(&points[i], &points[i + 1], &mask, line_draw_dsc);
961     }
962 
963     _lv_refr_set_disp_refreshing(refr_ori);
964 
965     lv_obj_invalidate(canvas);
966 }
967 
968 /**
969  * Draw a polygon on the canvas
970  * @param canvas pointer to a canvas object
971  * @param points point of the polygon
972  * @param point_cnt number of points
973  * @param poly_draw_dsc pointer to an initialized `lv_draw_rect_dsc_t` variable
974  */
lv_canvas_draw_polygon(lv_obj_t * canvas,const lv_point_t points[],uint32_t point_cnt,const lv_draw_rect_dsc_t * poly_draw_dsc)975 void lv_canvas_draw_polygon(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
976                             const lv_draw_rect_dsc_t * poly_draw_dsc)
977 {
978     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
979 
980     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
981 
982     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
983         LV_LOG_WARN("lv_canvas_draw_polygon: can't raw to LV_IMG_CF_INDEXED canvas");
984         return;
985     }
986 
987     /* Create a dummy display to fool the lv_draw function.
988      * It will think it draws to real screen. */
989     lv_area_t mask;
990     mask.x1 = 0;
991     mask.x2 = dsc->header.w - 1;
992     mask.y1 = 0;
993     mask.y2 = dsc->header.h - 1;
994 
995     lv_disp_t disp;
996     _lv_memset_00(&disp, sizeof(lv_disp_t));
997 
998     lv_disp_buf_t disp_buf;
999     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
1000     lv_area_copy(&disp_buf.area, &mask);
1001 
1002     lv_disp_drv_init(&disp.driver);
1003 
1004     disp.driver.buffer  = &disp_buf;
1005     disp.driver.hor_res = dsc->header.w;
1006     disp.driver.ver_res = dsc->header.h;
1007 
1008     set_set_px_cb(&disp.driver, dsc->header.cf);
1009 
1010 #if LV_ANTIALIAS
1011     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
1012     lv_color_t ctransp = LV_COLOR_TRANSP;
1013     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
1014        poly_draw_dsc->bg_color.full == ctransp.full) {
1015         disp.driver.antialiasing = 0;
1016     }
1017 #endif
1018 
1019     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
1020     _lv_refr_set_disp_refreshing(&disp);
1021 
1022     lv_draw_polygon(points, point_cnt, &mask, poly_draw_dsc);
1023 
1024     _lv_refr_set_disp_refreshing(refr_ori);
1025 
1026     lv_obj_invalidate(canvas);
1027 }
1028 
1029 /**
1030  * Draw an arc on the canvas
1031  * @param canvas pointer to a canvas object
1032  * @param x origo x  of the arc
1033  * @param y origo y of the arc
1034  * @param r radius of the arc
1035  * @param start_angle start angle in degrees
1036  * @param end_angle end angle in degrees
1037  * @param arc_draw_dsc pointer to an initialized `lv_draw_line_dsc_t` variable
1038  */
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_draw_line_dsc_t * arc_draw_dsc)1039 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,
1040                         int32_t end_angle, const lv_draw_line_dsc_t * arc_draw_dsc)
1041 {
1042     LV_ASSERT_OBJ(canvas, LV_OBJX_NAME);
1043 
1044     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
1045 
1046     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
1047         LV_LOG_WARN("lv_canvas_draw_arc: can't raw to LV_IMG_CF_INDEXED canvas");
1048         return;
1049     }
1050 
1051     /* Create a dummy display to fool the lv_draw function.
1052      * It will think it draws to real screen. */
1053     lv_area_t mask;
1054     mask.x1 = 0;
1055     mask.x2 = dsc->header.w - 1;
1056     mask.y1 = 0;
1057     mask.y2 = dsc->header.h - 1;
1058 
1059     lv_disp_t disp;
1060     _lv_memset_00(&disp, sizeof(lv_disp_t));
1061 
1062     lv_disp_buf_t disp_buf;
1063     lv_disp_buf_init(&disp_buf, (void *)dsc->data, NULL, dsc->header.w * dsc->header.h);
1064     lv_area_copy(&disp_buf.area, &mask);
1065 
1066     lv_disp_drv_init(&disp.driver);
1067 
1068     disp.driver.buffer  = &disp_buf;
1069     disp.driver.hor_res = dsc->header.w;
1070     disp.driver.ver_res = dsc->header.h;
1071 
1072     set_set_px_cb(&disp.driver, dsc->header.cf);
1073 
1074 #if LV_ANTIALIAS
1075     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
1076     lv_color_t ctransp = LV_COLOR_TRANSP;
1077     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
1078        arc_draw_dsc->color.full == ctransp.full) {
1079         disp.driver.antialiasing = 0;
1080     }
1081 #endif
1082 
1083     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
1084     _lv_refr_set_disp_refreshing(&disp);
1085 
1086     lv_draw_arc(x, y, r,  start_angle, end_angle, &mask, arc_draw_dsc);
1087 
1088     _lv_refr_set_disp_refreshing(refr_ori);
1089 
1090     lv_obj_invalidate(canvas);
1091 }
1092 
1093 /**********************
1094  *   STATIC FUNCTIONS
1095  **********************/
1096 
1097 /**
1098  * Signal function of the canvas
1099  * @param canvas pointer to a canvas object
1100  * @param sign a signal type from lv_signal_t enum
1101  * @param param pointer to a signal specific variable
1102  * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
1103  */
lv_canvas_signal(lv_obj_t * canvas,lv_signal_t sign,void * param)1104 static lv_res_t lv_canvas_signal(lv_obj_t * canvas, lv_signal_t sign, void * param)
1105 {
1106     lv_res_t res;
1107 
1108     /* Include the ancient signal function */
1109     res = ancestor_signal(canvas, sign, param);
1110     if(res != LV_RES_OK) return res;
1111     if(sign == LV_SIGNAL_GET_TYPE) return lv_obj_handle_get_type_signal(param, LV_OBJX_NAME);
1112 
1113     if(sign == LV_SIGNAL_CLEANUP) {
1114         /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/
1115     }
1116 
1117     return res;
1118 }
1119 
set_set_px_cb(lv_disp_drv_t * disp_drv,lv_img_cf_t cf)1120 static void set_set_px_cb(lv_disp_drv_t * disp_drv, lv_img_cf_t cf)
1121 {
1122     switch(cf) {
1123         case LV_IMG_CF_TRUE_COLOR_ALPHA:
1124             disp_drv->set_px_cb = set_px_true_color_alpha;
1125             break;
1126         case LV_IMG_CF_ALPHA_1BIT:
1127             disp_drv->set_px_cb = set_px_cb_alpha1;
1128             break;
1129         case LV_IMG_CF_ALPHA_2BIT:
1130             disp_drv->set_px_cb = set_px_cb_alpha2;
1131             break;
1132         case LV_IMG_CF_ALPHA_4BIT:
1133             disp_drv->set_px_cb = set_px_cb_alpha4;
1134             break;
1135         case LV_IMG_CF_ALPHA_8BIT:
1136             disp_drv->set_px_cb = set_px_cb_alpha8;
1137             break;
1138         default:
1139             disp_drv->set_px_cb = NULL;
1140     }
1141 }
1142 
set_px_cb_alpha1(lv_disp_drv_t * disp_drv,uint8_t * buf,lv_coord_t buf_w,lv_coord_t x,lv_coord_t y,lv_color_t color,lv_opa_t opa)1143 static void set_px_cb_alpha1(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
1144                              lv_color_t color, lv_opa_t opa)
1145 {
1146     (void) disp_drv; /*Unused*/
1147 
1148     if(opa <= LV_OPA_MIN) return;
1149     lv_img_dsc_t d;
1150     d.data = buf;
1151     d.header.w = buf_w;
1152     d.header.cf = LV_IMG_CF_ALPHA_1BIT;
1153 
1154     set_px_alpha_generic(&d, x, y, color, opa);
1155 }
1156 
set_px_cb_alpha2(lv_disp_drv_t * disp_drv,uint8_t * buf,lv_coord_t buf_w,lv_coord_t x,lv_coord_t y,lv_color_t color,lv_opa_t opa)1157 static void set_px_cb_alpha2(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
1158                              lv_color_t color, lv_opa_t opa)
1159 {
1160     (void) disp_drv; /*Unused*/
1161 
1162     if(opa <= LV_OPA_MIN) return;
1163     lv_img_dsc_t d;
1164     d.data = buf;
1165     d.header.w = buf_w;
1166     d.header.cf = LV_IMG_CF_ALPHA_2BIT;
1167 
1168     set_px_alpha_generic(&d, x, y, color, opa);
1169 }
1170 
set_px_cb_alpha4(lv_disp_drv_t * disp_drv,uint8_t * buf,lv_coord_t buf_w,lv_coord_t x,lv_coord_t y,lv_color_t color,lv_opa_t opa)1171 static void set_px_cb_alpha4(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
1172                              lv_color_t color, lv_opa_t opa)
1173 {
1174     (void) disp_drv; /*Unused*/
1175 
1176     if(opa <= LV_OPA_MIN) return;
1177     lv_img_dsc_t d;
1178     d.data = buf;
1179     d.header.w = buf_w;
1180     d.header.cf = LV_IMG_CF_ALPHA_4BIT;
1181 
1182     set_px_alpha_generic(&d, x, y, color, opa);
1183 }
1184 
set_px_cb_alpha8(lv_disp_drv_t * disp_drv,uint8_t * buf,lv_coord_t buf_w,lv_coord_t x,lv_coord_t y,lv_color_t color,lv_opa_t opa)1185 static void set_px_cb_alpha8(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
1186                              lv_color_t color, lv_opa_t opa)
1187 {
1188     (void) disp_drv; /*Unused*/
1189 
1190     if(opa <= LV_OPA_MIN) return;
1191     lv_img_dsc_t d;
1192     d.data = buf;
1193     d.header.w = buf_w;
1194     d.header.cf = LV_IMG_CF_ALPHA_8BIT;
1195 
1196     set_px_alpha_generic(&d, x, y, color, opa);
1197 }
1198 
set_px_alpha_generic(lv_img_dsc_t * d,lv_coord_t x,lv_coord_t y,lv_color_t color,lv_opa_t opa)1199 static void set_px_alpha_generic(lv_img_dsc_t * d, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa)
1200 {
1201     d->header.always_zero = 0;
1202     d->header.h = LV_VER_RES_MAX;
1203 
1204     uint8_t br = lv_color_brightness(color);
1205     if(opa < LV_OPA_MAX) {
1206         uint8_t bg = lv_img_buf_get_px_alpha(d, x, y);
1207         br = (uint16_t)((uint16_t)br * opa + (bg * (255 - opa))) >> 8;
1208     }
1209 
1210     lv_img_buf_set_px_alpha(d, x, y, br);
1211 }
1212 
1213 
set_px_true_color_alpha(lv_disp_drv_t * disp_drv,uint8_t * buf,lv_coord_t buf_w,lv_coord_t x,lv_coord_t y,lv_color_t color,lv_opa_t opa)1214 static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x,
1215                                     lv_coord_t y,
1216                                     lv_color_t color, lv_opa_t opa)
1217 {
1218     (void) disp_drv; /*Unused*/
1219 
1220     if(opa <= LV_OPA_MIN) return;
1221     lv_img_dsc_t d;
1222     d.data = buf;
1223     d.header.always_zero = 0;
1224     d.header.h = LV_VER_RES_MAX;
1225     d.header.w = buf_w;
1226     d.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
1227 
1228     lv_color_t bg_color = lv_img_buf_get_px_color(&d, x, y, LV_COLOR_BLACK);
1229     lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&d, x, y);
1230 
1231     lv_opa_t res_opa;
1232     lv_color_t res_color;
1233 
1234     lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &res_opa);
1235 
1236 
1237     lv_img_buf_set_px_alpha(&d, x, y, res_opa);
1238     lv_img_buf_set_px_color(&d, x, y, res_color);
1239 }
1240 
1241 #endif
1242