1 /**
2  * @file lv_canvas.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_canvas.h"
10 #include "../misc/lv_assert.h"
11 #include "../misc/lv_math.h"
12 #include "../draw/lv_draw.h"
13 #include "../core/lv_refr.h"
14 
15 #if LV_USE_CANVAS != 0
16 
17 #include "../draw/sw/lv_draw_sw.h"
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define MY_CLASS &lv_canvas_class
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static void lv_canvas_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
32 static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
33 static void init_fake_disp(lv_obj_t * canvas, lv_disp_t * disp, lv_disp_drv_t * drv, lv_area_t * clip_area);
34 static void deinit_fake_disp(lv_obj_t * canvas, lv_disp_t * disp);
35 
36 /**********************
37  *  STATIC VARIABLES
38  **********************/
39 const lv_obj_class_t lv_canvas_class = {
40     .constructor_cb = lv_canvas_constructor,
41     .destructor_cb = lv_canvas_destructor,
42     .instance_size = sizeof(lv_canvas_t),
43     .base_class = &lv_img_class
44 };
45 
46 /**********************
47  *      MACROS
48  **********************/
49 
50 /**********************
51  *   GLOBAL FUNCTIONS
52  **********************/
53 
lv_canvas_create(lv_obj_t * parent)54 lv_obj_t * lv_canvas_create(lv_obj_t * parent)
55 {
56     LV_LOG_INFO("begin");
57     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
58     lv_obj_class_init_obj(obj);
59     return obj;
60 }
61 
62 /*=====================
63  * Setter functions
64  *====================*/
65 
lv_canvas_set_buffer(lv_obj_t * obj,void * buf,lv_coord_t w,lv_coord_t h,lv_img_cf_t cf)66 void lv_canvas_set_buffer(lv_obj_t * obj, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf)
67 {
68     LV_ASSERT_OBJ(obj, MY_CLASS);
69     LV_ASSERT_NULL(buf);
70 
71     lv_canvas_t * canvas = (lv_canvas_t *)obj;
72 
73     canvas->dsc.header.cf = cf;
74     canvas->dsc.header.w  = w;
75     canvas->dsc.header.h  = h;
76     canvas->dsc.data      = buf;
77 
78     lv_img_set_src(obj, &canvas->dsc);
79 }
80 
lv_canvas_set_px_color(lv_obj_t * obj,lv_coord_t x,lv_coord_t y,lv_color_t c)81 void lv_canvas_set_px_color(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_color_t c)
82 {
83     LV_ASSERT_OBJ(obj, MY_CLASS);
84 
85     lv_canvas_t * canvas = (lv_canvas_t *)obj;
86 
87     lv_img_buf_set_px_color(&canvas->dsc, x, y, c);
88     lv_obj_invalidate(obj);
89 }
90 
lv_canvas_set_px_opa(lv_obj_t * obj,lv_coord_t x,lv_coord_t y,lv_opa_t opa)91 void lv_canvas_set_px_opa(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_opa_t opa)
92 {
93     LV_ASSERT_OBJ(obj, MY_CLASS);
94 
95     lv_canvas_t * canvas = (lv_canvas_t *)obj;
96 
97     lv_img_buf_set_px_alpha(&canvas->dsc, x, y, opa);
98     lv_obj_invalidate(obj);
99 }
100 
lv_canvas_set_palette(lv_obj_t * obj,uint8_t id,lv_color_t c)101 void lv_canvas_set_palette(lv_obj_t * obj, uint8_t id, lv_color_t c)
102 {
103     LV_ASSERT_OBJ(obj, MY_CLASS);
104 
105     lv_canvas_t * canvas = (lv_canvas_t *)obj;
106 
107     lv_img_buf_set_palette(&canvas->dsc, id, c);
108     lv_obj_invalidate(obj);
109 }
110 
111 /*=====================
112  * Getter functions
113  *====================*/
114 
lv_canvas_get_px(lv_obj_t * obj,lv_coord_t x,lv_coord_t y)115 lv_color_t lv_canvas_get_px(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
116 {
117     LV_ASSERT_OBJ(obj, MY_CLASS);
118 
119     lv_canvas_t * canvas = (lv_canvas_t *)obj;
120     lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);
121 
122     return lv_img_buf_get_px_color(&canvas->dsc, x, y, color);
123 }
124 
lv_canvas_get_img(lv_obj_t * obj)125 lv_img_dsc_t * lv_canvas_get_img(lv_obj_t * obj)
126 {
127     LV_ASSERT_OBJ(obj, MY_CLASS);
128 
129     lv_canvas_t * canvas = (lv_canvas_t *)obj;
130     return &canvas->dsc;
131 }
132 
133 /*=====================
134  * Other functions
135  *====================*/
136 
lv_canvas_copy_buf(lv_obj_t * obj,const void * to_copy,lv_coord_t x,lv_coord_t y,lv_coord_t w,lv_coord_t h)137 void lv_canvas_copy_buf(lv_obj_t * obj, const void * to_copy, lv_coord_t x, lv_coord_t y, lv_coord_t w, lv_coord_t h)
138 {
139     LV_ASSERT_OBJ(obj, MY_CLASS);
140     LV_ASSERT_NULL(to_copy);
141 
142     lv_canvas_t * canvas = (lv_canvas_t *)obj;
143 
144     if(x + w - 1 >= (lv_coord_t)canvas->dsc.header.w || y + h - 1 >= (lv_coord_t)canvas->dsc.header.h) {
145         LV_LOG_WARN("lv_canvas_copy_buf: x or y out of the canvas");
146         return;
147     }
148 
149     uint32_t px_size   = lv_img_cf_get_px_size(canvas->dsc.header.cf) >> 3;
150     uint32_t px        = canvas->dsc.header.w * y * px_size + x * px_size;
151     uint8_t * to_copy8 = (uint8_t *)to_copy;
152     lv_coord_t i;
153     for(i = 0; i < h; i++) {
154         lv_memcpy((void *)&canvas->dsc.data[px], to_copy8, w * px_size);
155         px += canvas->dsc.header.w * px_size;
156         to_copy8 += w * px_size;
157     }
158 }
159 
lv_canvas_transform(lv_obj_t * obj,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)160 void lv_canvas_transform(lv_obj_t * obj, lv_img_dsc_t * img, int16_t angle, uint16_t zoom, lv_coord_t offset_x,
161                          lv_coord_t offset_y,
162                          int32_t pivot_x, int32_t pivot_y, bool antialias)
163 {
164 #if LV_DRAW_COMPLEX
165     LV_ASSERT_OBJ(obj, MY_CLASS);
166     LV_ASSERT_NULL(img);
167 
168     lv_canvas_t * canvas = (lv_canvas_t *)obj;
169     lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);
170 
171     int32_t dest_width  = canvas->dsc.header.w;
172     int32_t dest_height = canvas->dsc.header.h;
173 
174     int32_t x;
175     int32_t y;
176     bool ret;
177 
178     lv_img_transform_dsc_t dsc;
179     dsc.cfg.angle = angle;
180     dsc.cfg.zoom = zoom;
181     dsc.cfg.src = img->data;
182     dsc.cfg.src_w = img->header.w;
183     dsc.cfg.src_h = img->header.h;
184     dsc.cfg.cf = img->header.cf;
185     dsc.cfg.pivot_x = pivot_x;
186     dsc.cfg.pivot_y = pivot_y;
187     dsc.cfg.color = color;
188     dsc.cfg.antialias = antialias;
189     _lv_img_buf_transform_init(&dsc);
190 
191     for(y = -offset_y; y < dest_height - offset_y; y++) {
192         for(x = -offset_x; x < dest_width - offset_x; x++) {
193 
194             ret = _lv_img_buf_transform(&dsc, x, y);
195 
196             if(ret == false) continue;
197 
198             if(x + offset_x >= 0 && x + offset_x < dest_width && y + offset_y >= 0 && y + offset_y < dest_height) {
199                 /*If the image has no alpha channel just simple set the result color on the canvas*/
200                 if(lv_img_cf_has_alpha(img->header.cf) == false) {
201                     lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.color);
202                 }
203                 else {
204                     lv_color_t bg_color = lv_img_buf_get_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.cfg.color);
205 
206                     /*If the canvas has no alpha but the image has mix the image's color with
207                      * canvas*/
208                     if(lv_img_cf_has_alpha(canvas->dsc.header.cf) == false) {
209                         if(dsc.res.opa < LV_OPA_MAX) dsc.res.color = lv_color_mix(dsc.res.color, bg_color, dsc.res.opa);
210                         lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.color);
211                     }
212                     /*Both the image and canvas has alpha channel. Some extra calculation is
213                        required*/
214                     else {
215                         lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&canvas->dsc, x + offset_x, y + offset_y);
216                         /*Pick the foreground if it's fully opaque or the Background is fully
217                          *transparent*/
218                         if(dsc.res.opa >= LV_OPA_MAX || bg_opa <= LV_OPA_MIN) {
219                             lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.color);
220                             lv_img_buf_set_px_alpha(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.opa);
221                         }
222                         /*Opaque background: use simple mix*/
223                         else if(bg_opa >= LV_OPA_MAX) {
224                             lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y,
225                                                     lv_color_mix(dsc.res.color, bg_color, dsc.res.opa));
226                         }
227                         /*Both colors have alpha. Expensive calculation need to be applied*/
228                         else {
229 
230                             /*Info:
231                              * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
232                             lv_opa_t opa_res_2 = 255 - ((uint16_t)((uint16_t)(255 - dsc.res.opa) * (255 - bg_opa)) >> 8);
233                             if(opa_res_2 == 0) {
234                                 opa_res_2 = 1; /*never happens, just to be sure*/
235                             }
236                             lv_opa_t ratio = (uint16_t)((uint16_t)dsc.res.opa * 255) / opa_res_2;
237 
238                             lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y,
239                                                     lv_color_mix(dsc.res.color, bg_color, ratio));
240                             lv_img_buf_set_px_alpha(&canvas->dsc, x + offset_x, y + offset_y, opa_res_2);
241                         }
242                     }
243                 }
244             }
245         }
246     }
247 
248     lv_obj_invalidate(obj);
249 #else
250     LV_UNUSED(obj);
251     LV_UNUSED(img);
252     LV_UNUSED(angle);
253     LV_UNUSED(zoom);
254     LV_UNUSED(offset_x);
255     LV_UNUSED(offset_y);
256     LV_UNUSED(pivot_x);
257     LV_UNUSED(pivot_y);
258     LV_UNUSED(antialias);
259     LV_LOG_WARN("Can't transform canvas with LV_DRAW_COMPLEX == 0");
260 #endif
261 }
262 
lv_canvas_blur_hor(lv_obj_t * obj,const lv_area_t * area,uint16_t r)263 void lv_canvas_blur_hor(lv_obj_t * obj, const lv_area_t * area, uint16_t r)
264 {
265     LV_ASSERT_OBJ(obj, MY_CLASS);
266 
267     if(r == 0) return;
268 
269     lv_canvas_t * canvas = (lv_canvas_t *)obj;
270 
271     lv_area_t a;
272     if(area) {
273         lv_area_copy(&a, area);
274         if(a.x1 < 0) a.x1 = 0;
275         if(a.y1 < 0) a.y1 = 0;
276         if(a.x2 > canvas->dsc.header.w - 1) a.x2 = canvas->dsc.header.w - 1;
277         if(a.y2 > canvas->dsc.header.h - 1) a.y2 = canvas->dsc.header.h - 1;
278     }
279     else {
280         a.x1 = 0;
281         a.y1 = 0;
282         a.x2 = canvas->dsc.header.w - 1;
283         a.y2 = canvas->dsc.header.h - 1;
284     }
285 
286     lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);
287 
288     uint16_t r_back = r / 2;
289     uint16_t r_front = r / 2;
290 
291     if((r & 0x1) == 0) r_back--;
292 
293     bool has_alpha = lv_img_cf_has_alpha(canvas->dsc.header.cf);
294 
295     lv_coord_t line_w = lv_img_buf_get_img_size(canvas->dsc.header.w, 1, canvas->dsc.header.cf);
296     uint8_t * line_buf = lv_mem_buf_get(line_w);
297 
298     lv_img_dsc_t line_img;
299     line_img.data = line_buf;
300     line_img.header.always_zero = 0;
301     line_img.header.w = canvas->dsc.header.w;
302     line_img.header.h = 1;
303     line_img.header.cf = canvas->dsc.header.cf;
304 
305     lv_coord_t x;
306     lv_coord_t y;
307     lv_coord_t x_safe;
308 
309     for(y = a.y1; y <= a.y2; y++) {
310         uint32_t asum = 0;
311         uint32_t rsum = 0;
312         uint32_t gsum = 0;
313         uint32_t bsum = 0;
314 
315         lv_color_t c;
316         lv_opa_t opa = LV_OPA_TRANSP;
317         lv_memcpy(line_buf, &canvas->dsc.data[y * line_w], line_w);
318 
319         for(x = a.x1 - r_back; x <= a.x1 + r_front; x++) {
320             x_safe = x < 0 ? 0 : x;
321             x_safe = x_safe > canvas->dsc.header.w - 1 ? canvas->dsc.header.w - 1 : x_safe;
322 
323             c = lv_img_buf_get_px_color(&line_img, x_safe, 0, color);
324             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
325 
326             rsum += c.ch.red;
327 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
328             gsum += (c.ch.green_h << 3) + c.ch.green_l;
329 #else
330             gsum += c.ch.green;
331 #endif
332             bsum += c.ch.blue;
333             if(has_alpha) asum += opa;
334         }
335 
336         /*Just to indicate that the px is visible*/
337         if(has_alpha == false) asum = LV_OPA_COVER;
338 
339         for(x = a.x1; x <= a.x2; x++) {
340 
341             if(asum) {
342                 c.ch.red = rsum / r;
343 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
344                 uint8_t gtmp = gsum / r;
345                 c.ch.green_h = gtmp >> 3;
346                 c.ch.green_l = gtmp & 0x7;
347 #else
348                 c.ch.green = gsum / r;
349 #endif
350                 c.ch.blue = bsum / r;
351                 if(has_alpha) opa = asum / r;
352 
353                 lv_img_buf_set_px_color(&canvas->dsc, x, y, c);
354             }
355             if(has_alpha) lv_img_buf_set_px_alpha(&canvas->dsc, x, y, opa);
356 
357             x_safe = x - r_back;
358             x_safe = x_safe < 0 ? 0 : x_safe;
359             c = lv_img_buf_get_px_color(&line_img, x_safe, 0, color);
360             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
361 
362             rsum -= c.ch.red;
363 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
364             gsum -= (c.ch.green_h << 3) + c.ch.green_l;
365 #else
366             gsum -= c.ch.green;
367 #endif
368             bsum -= c.ch.blue;
369             if(has_alpha) asum -= opa;
370 
371             x_safe = x + 1 + r_front;
372             x_safe = x_safe > canvas->dsc.header.w - 1 ? canvas->dsc.header.w - 1 : x_safe;
373             c = lv_img_buf_get_px_color(&line_img, x_safe, 0, lv_color_white());
374             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, x_safe, 0);
375 
376             rsum += c.ch.red;
377 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
378             gsum += (c.ch.green_h << 3) + c.ch.green_l;
379 #else
380             gsum += c.ch.green;
381 #endif
382             bsum += c.ch.blue;
383             if(has_alpha) asum += opa;
384         }
385     }
386     lv_obj_invalidate(obj);
387 
388     lv_mem_buf_release(line_buf);
389 }
390 
lv_canvas_blur_ver(lv_obj_t * obj,const lv_area_t * area,uint16_t r)391 void lv_canvas_blur_ver(lv_obj_t * obj, const lv_area_t * area, uint16_t r)
392 {
393     LV_ASSERT_OBJ(obj, MY_CLASS);
394 
395     if(r == 0) return;
396 
397     lv_canvas_t * canvas = (lv_canvas_t *)obj;
398 
399     lv_area_t a;
400     if(area) {
401         lv_area_copy(&a, area);
402         if(a.x1 < 0) a.x1 = 0;
403         if(a.y1 < 0) a.y1 = 0;
404         if(a.x2 > canvas->dsc.header.w - 1) a.x2 = canvas->dsc.header.w - 1;
405         if(a.y2 > canvas->dsc.header.h - 1) a.y2 = canvas->dsc.header.h - 1;
406     }
407     else {
408         a.x1 = 0;
409         a.y1 = 0;
410         a.x2 = canvas->dsc.header.w - 1;
411         a.y2 = canvas->dsc.header.h - 1;
412     }
413 
414     lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN);
415 
416     uint16_t r_back = r / 2;
417     uint16_t r_front = r / 2;
418 
419     if((r & 0x1) == 0) r_back--;
420 
421     bool has_alpha = lv_img_cf_has_alpha(canvas->dsc.header.cf);
422     lv_coord_t col_w = lv_img_buf_get_img_size(1, canvas->dsc.header.h, canvas->dsc.header.cf);
423     uint8_t * col_buf = lv_mem_buf_get(col_w);
424     lv_img_dsc_t line_img;
425 
426     line_img.data = col_buf;
427     line_img.header.always_zero = 0;
428     line_img.header.w = 1;
429     line_img.header.h = canvas->dsc.header.h;
430     line_img.header.cf = canvas->dsc.header.cf;
431 
432     lv_coord_t x;
433     lv_coord_t y;
434     lv_coord_t y_safe;
435 
436     for(x = a.x1; x <= a.x2; x++) {
437         uint32_t asum = 0;
438         uint32_t rsum = 0;
439         uint32_t gsum = 0;
440         uint32_t bsum = 0;
441 
442         lv_color_t c;
443         lv_opa_t opa = LV_OPA_COVER;
444 
445         for(y = a.y1 - r_back; y <= a.y1 + r_front; y++) {
446             y_safe = y < 0 ? 0 : y;
447             y_safe = y_safe > canvas->dsc.header.h - 1 ? canvas->dsc.header.h - 1 : y_safe;
448 
449             c = lv_img_buf_get_px_color(&canvas->dsc, x, y_safe, color);
450             if(has_alpha) opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, y_safe);
451 
452             lv_img_buf_set_px_color(&line_img, 0, y_safe, c);
453             if(has_alpha) lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa);
454 
455             rsum += c.ch.red;
456 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
457             gsum += (c.ch.green_h << 3) + c.ch.green_l;
458 #else
459             gsum += c.ch.green;
460 #endif
461             bsum += c.ch.blue;
462             if(has_alpha) asum += opa;
463         }
464 
465         /*Just to indicate that the px is visible*/
466         if(has_alpha == false) asum = LV_OPA_COVER;
467 
468         for(y = a.y1; y <= a.y2; y++) {
469             if(asum) {
470                 c.ch.red = rsum / r;
471 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
472                 uint8_t gtmp = gsum / r;
473                 c.ch.green_h = gtmp >> 3;
474                 c.ch.green_l = gtmp & 0x7;
475 #else
476                 c.ch.green = gsum / r;
477 #endif
478                 c.ch.blue = bsum / r;
479                 if(has_alpha) opa = asum / r;
480 
481                 lv_img_buf_set_px_color(&canvas->dsc, x, y, c);
482             }
483             if(has_alpha) lv_img_buf_set_px_alpha(&canvas->dsc, x, y, opa);
484 
485             y_safe = y - r_back;
486             y_safe = y_safe < 0 ? 0 : y_safe;
487             c = lv_img_buf_get_px_color(&line_img, 0, y_safe, color);
488             if(has_alpha) opa = lv_img_buf_get_px_alpha(&line_img, 0, y_safe);
489 
490             rsum -= c.ch.red;
491 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
492             gsum -= (c.ch.green_h << 3) + c.ch.green_l;
493 #else
494             gsum -= c.ch.green;
495 #endif
496             bsum -= c.ch.blue;
497             if(has_alpha) asum -= opa;
498 
499             y_safe = y + 1 + r_front;
500             y_safe = y_safe > canvas->dsc.header.h - 1 ? canvas->dsc.header.h - 1 : y_safe;
501 
502             c = lv_img_buf_get_px_color(&canvas->dsc, x, y_safe, color);
503             if(has_alpha) opa = lv_img_buf_get_px_alpha(&canvas->dsc, x, y_safe);
504 
505             lv_img_buf_set_px_color(&line_img, 0, y_safe, c);
506             if(has_alpha) lv_img_buf_set_px_alpha(&line_img, 0, y_safe, opa);
507 
508             rsum += c.ch.red;
509 #if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP
510             gsum += (c.ch.green_h << 3) + c.ch.green_l;
511 #else
512             gsum += c.ch.green;
513 #endif
514             bsum += c.ch.blue;
515             if(has_alpha) asum += opa;
516         }
517     }
518 
519     lv_obj_invalidate(obj);
520 
521     lv_mem_buf_release(col_buf);
522 }
523 
lv_canvas_fill_bg(lv_obj_t * canvas,lv_color_t color,lv_opa_t opa)524 void lv_canvas_fill_bg(lv_obj_t * canvas, lv_color_t color, lv_opa_t opa)
525 {
526     LV_ASSERT_OBJ(canvas, MY_CLASS);
527 
528     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
529 
530     if(dsc->header.cf == LV_IMG_CF_INDEXED_1BIT) {
531         uint32_t row_byte_cnt = (dsc->header.w + 7) >> 3;
532         /*+8 skip the palette*/
533         lv_memset((uint8_t *)dsc->data + 8, color.full ? 0xff : 0x00, row_byte_cnt * dsc->header.h);
534     }
535     else if(dsc->header.cf == LV_IMG_CF_ALPHA_1BIT) {
536         uint32_t row_byte_cnt = (dsc->header.w + 7) >> 3;
537         lv_memset((uint8_t *)dsc->data, opa > LV_OPA_50 ? 0xff : 0x00, row_byte_cnt * dsc->header.h);
538     }
539     else {
540         uint32_t x;
541         uint32_t y;
542         for(y = 0; y < dsc->header.h; y++) {
543             for(x = 0; x < dsc->header.w; x++) {
544                 lv_img_buf_set_px_color(dsc, x, y, color);
545                 lv_img_buf_set_px_alpha(dsc, x, y, opa);
546             }
547         }
548     }
549 
550     lv_obj_invalidate(canvas);
551 }
552 
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 * draw_dsc)553 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,
554                          const lv_draw_rect_dsc_t * draw_dsc)
555 {
556     LV_ASSERT_OBJ(canvas, MY_CLASS);
557 
558     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
559 
560     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
561         LV_LOG_WARN("lv_canvas_draw_rect: can't draw to LV_IMG_CF_INDEXED canvas");
562         return;
563     }
564 
565     /*Create a dummy display to fool the lv_draw function.
566      *It will think it draws to real screen.*/
567     lv_disp_t fake_disp;
568     lv_disp_drv_t driver;
569     lv_area_t clip_area;
570     init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
571 
572     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
573     _lv_refr_set_disp_refreshing(&fake_disp);
574 
575     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
576     lv_color_t ctransp = LV_COLOR_CHROMA_KEY;
577     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
578        draw_dsc->bg_color.full == ctransp.full) {
579         fake_disp.driver->antialiasing = 0;
580     }
581 
582     lv_area_t coords;
583     coords.x1 = x;
584     coords.y1 = y;
585     coords.x2 = x + w - 1;
586     coords.y2 = y + h - 1;
587 
588     lv_draw_rect(driver.draw_ctx, draw_dsc, &coords);
589 
590     _lv_refr_set_disp_refreshing(refr_ori);
591 
592     deinit_fake_disp(canvas, &fake_disp);
593 
594     lv_obj_invalidate(canvas);
595 }
596 
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 * draw_dsc,const char * txt)597 void lv_canvas_draw_text(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_coord_t max_w,
598                          lv_draw_label_dsc_t * draw_dsc, const char * txt)
599 {
600     LV_ASSERT_OBJ(canvas, MY_CLASS);
601 
602     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
603 
604     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
605         LV_LOG_WARN("lv_canvas_draw_text: can't draw to LV_IMG_CF_INDEXED canvas");
606         return;
607     }
608 
609     /*Create a dummy display to fool the lv_draw function.
610      *It will think it draws to real screen.*/
611     lv_disp_t fake_disp;
612     lv_disp_drv_t driver;
613     lv_area_t clip_area;
614     init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
615 
616     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
617     _lv_refr_set_disp_refreshing(&fake_disp);
618 
619     lv_area_t coords;
620     coords.x1 = x;
621     coords.y1 = y;
622     coords.x2 = x + max_w - 1;
623     coords.y2 = dsc->header.h - 1;
624     lv_draw_label(driver.draw_ctx, draw_dsc, &coords, txt, NULL);
625 
626     _lv_refr_set_disp_refreshing(refr_ori);
627 
628     deinit_fake_disp(canvas, &fake_disp);
629 
630     lv_obj_invalidate(canvas);
631 }
632 
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 * draw_dsc)633 void lv_canvas_draw_img(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, const void * src,
634                         const lv_draw_img_dsc_t * draw_dsc)
635 {
636     LV_ASSERT_OBJ(canvas, MY_CLASS);
637 
638     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
639 
640     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
641         LV_LOG_WARN("lv_canvas_draw_img: can't draw to LV_IMG_CF_INDEXED canvas");
642         return;
643     }
644 
645     lv_img_header_t header;
646     lv_res_t res = lv_img_decoder_get_info(src, &header);
647     if(res != LV_RES_OK) {
648         LV_LOG_WARN("lv_canvas_draw_img: Couldn't get the image data.");
649         return;
650     }
651     /*Create a dummy display to fool the lv_draw function.
652      *It will think it draws to real screen.*/
653     lv_disp_t fake_disp;
654     lv_disp_drv_t driver;
655     lv_area_t clip_area;
656     init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
657 
658     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
659     _lv_refr_set_disp_refreshing(&fake_disp);
660 
661     lv_area_t coords;
662     coords.x1 = x;
663     coords.y1 = y;
664     coords.x2 = x + header.w - 1;
665     coords.y2 = y + header.h - 1;
666 
667     lv_draw_img(driver.draw_ctx, draw_dsc, &coords, src);
668 
669     _lv_refr_set_disp_refreshing(refr_ori);
670 
671     deinit_fake_disp(canvas, &fake_disp);
672 
673     lv_obj_invalidate(canvas);
674 }
675 
lv_canvas_draw_line(lv_obj_t * canvas,const lv_point_t points[],uint32_t point_cnt,const lv_draw_line_dsc_t * draw_dsc)676 void lv_canvas_draw_line(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
677                          const lv_draw_line_dsc_t * draw_dsc)
678 {
679     LV_ASSERT_OBJ(canvas, MY_CLASS);
680 
681     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
682 
683     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
684         LV_LOG_WARN("lv_canvas_draw_line: can't draw to LV_IMG_CF_INDEXED canvas");
685         return;
686     }
687 
688     /*Create a dummy display to fool the lv_draw function.
689      *It will think it draws to real screen.*/
690     lv_disp_t fake_disp;
691     lv_disp_drv_t driver;
692     lv_area_t clip_area;
693     init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
694 
695     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
696     _lv_refr_set_disp_refreshing(&fake_disp);
697 
698 
699     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
700     lv_color_t ctransp = LV_COLOR_CHROMA_KEY;
701     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
702        draw_dsc->color.full == ctransp.full) {
703         fake_disp.driver->antialiasing = 0;
704     }
705 
706     uint32_t i;
707     for(i = 0; i < point_cnt - 1; i++) {
708         lv_draw_line(driver.draw_ctx, draw_dsc, &points[i], &points[i + 1]);
709     }
710 
711     _lv_refr_set_disp_refreshing(refr_ori);
712 
713     deinit_fake_disp(canvas, &fake_disp);
714 
715     lv_obj_invalidate(canvas);
716 }
717 
lv_canvas_draw_polygon(lv_obj_t * canvas,const lv_point_t points[],uint32_t point_cnt,const lv_draw_rect_dsc_t * draw_dsc)718 void lv_canvas_draw_polygon(lv_obj_t * canvas, const lv_point_t points[], uint32_t point_cnt,
719                             const lv_draw_rect_dsc_t * draw_dsc)
720 {
721     LV_ASSERT_OBJ(canvas, MY_CLASS);
722 
723     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
724 
725     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
726         LV_LOG_WARN("lv_canvas_draw_polygon: can't draw to LV_IMG_CF_INDEXED canvas");
727         return;
728     }
729 
730     /*Create a dummy display to fool the lv_draw function.
731      *It will think it draws to real screen.*/
732     lv_disp_t fake_disp;
733     lv_disp_drv_t driver;
734     lv_area_t clip_area;
735     init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
736 
737     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
738     _lv_refr_set_disp_refreshing(&fake_disp);
739 
740     /*Disable anti-aliasing if drawing with transparent color to chroma keyed canvas*/
741     lv_color_t ctransp = LV_COLOR_CHROMA_KEY;
742     if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED &&
743        draw_dsc->bg_color.full == ctransp.full) {
744         fake_disp.driver->antialiasing = 0;
745     }
746 
747     lv_draw_polygon(driver.draw_ctx, draw_dsc, points, point_cnt);
748 
749     _lv_refr_set_disp_refreshing(refr_ori);
750 
751     deinit_fake_disp(canvas, &fake_disp);
752 
753     lv_obj_invalidate(canvas);
754 }
755 
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_arc_dsc_t * draw_dsc)756 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,
757                         int32_t end_angle, const lv_draw_arc_dsc_t * draw_dsc)
758 {
759 #if LV_DRAW_COMPLEX
760     LV_ASSERT_OBJ(canvas, MY_CLASS);
761 
762     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
763 
764     if(dsc->header.cf >= LV_IMG_CF_INDEXED_1BIT && dsc->header.cf <= LV_IMG_CF_INDEXED_8BIT) {
765         LV_LOG_WARN("lv_canvas_draw_arc: can't draw to LV_IMG_CF_INDEXED canvas");
766         return;
767     }
768 
769     /*Create a dummy display to fool the lv_draw function.
770      *It will think it draws to real screen.*/
771     lv_disp_t fake_disp;
772     lv_disp_drv_t driver;
773     lv_area_t clip_area;
774     init_fake_disp(canvas, &fake_disp, &driver, &clip_area);
775 
776     lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing();
777     _lv_refr_set_disp_refreshing(&fake_disp);
778 
779     lv_point_t p = {x, y};
780     lv_draw_arc(driver.draw_ctx, draw_dsc, &p, r,  start_angle, end_angle);
781 
782     _lv_refr_set_disp_refreshing(refr_ori);
783 
784     deinit_fake_disp(canvas, &fake_disp);
785 
786     lv_obj_invalidate(canvas);
787 #else
788     LV_UNUSED(canvas);
789     LV_UNUSED(x);
790     LV_UNUSED(y);
791     LV_UNUSED(r);
792     LV_UNUSED(start_angle);
793     LV_UNUSED(end_angle);
794     LV_UNUSED(draw_dsc);
795     LV_LOG_WARN("Can't draw arc with LV_DRAW_COMPLEX == 0");
796 #endif
797 }
798 
799 /**********************
800  *   STATIC FUNCTIONS
801  **********************/
802 
lv_canvas_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)803 static void lv_canvas_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
804 {
805     LV_UNUSED(class_p);
806     LV_TRACE_OBJ_CREATE("begin");
807 
808     lv_canvas_t * canvas = (lv_canvas_t *)obj;
809 
810     canvas->dsc.header.always_zero = 0;
811     canvas->dsc.header.cf          = LV_IMG_CF_TRUE_COLOR;
812     canvas->dsc.header.h           = 0;
813     canvas->dsc.header.w           = 0;
814     canvas->dsc.data_size          = 0;
815     canvas->dsc.data               = NULL;
816 
817     lv_img_set_src(obj, &canvas->dsc);
818 
819     LV_TRACE_OBJ_CREATE("finished");
820 }
821 
lv_canvas_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)822 static void lv_canvas_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
823 {
824     LV_UNUSED(class_p);
825     LV_TRACE_OBJ_CREATE("begin");
826 
827     lv_canvas_t * canvas = (lv_canvas_t *)obj;
828     lv_img_cache_invalidate_src(&canvas->dsc);
829 }
830 
831 
init_fake_disp(lv_obj_t * canvas,lv_disp_t * disp,lv_disp_drv_t * drv,lv_area_t * clip_area)832 static void init_fake_disp(lv_obj_t * canvas, lv_disp_t * disp, lv_disp_drv_t * drv, lv_area_t * clip_area)
833 {
834     lv_img_dsc_t * dsc = lv_canvas_get_img(canvas);
835 
836     clip_area->x1 = 0;
837     clip_area->x2 = dsc->header.w - 1;
838     clip_area->y1 = 0;
839     clip_area->y2 = dsc->header.h - 1;
840 
841     /*Allocate the fake driver on the stack as the entire display doesn't outlive this function*/
842     lv_memset_00(disp, sizeof(lv_disp_t));
843     disp->driver = drv;
844 
845     lv_disp_drv_init(disp->driver);
846     disp->driver->hor_res = dsc->header.w;
847     disp->driver->ver_res = dsc->header.h;
848 
849     lv_draw_ctx_t * draw_ctx = lv_mem_alloc(sizeof(lv_draw_sw_ctx_t));
850     LV_ASSERT_MALLOC(draw_ctx);
851     if(draw_ctx == NULL)  return;
852     lv_draw_sw_init_ctx(drv, draw_ctx);
853     disp->driver->draw_ctx = draw_ctx;
854     draw_ctx->clip_area = clip_area;
855     draw_ctx->buf_area = clip_area;
856     draw_ctx->buf = (void *)dsc->data;
857 
858     lv_disp_drv_use_generic_set_px_cb(disp->driver, dsc->header.cf);
859 }
860 
deinit_fake_disp(lv_obj_t * canvas,lv_disp_t * disp)861 static void deinit_fake_disp(lv_obj_t * canvas, lv_disp_t * disp)
862 {
863     LV_UNUSED(canvas);
864     lv_draw_sw_deinit_ctx(disp->driver, disp->driver->draw_ctx);
865     lv_mem_free(disp->driver->draw_ctx);
866 }
867 
868 
869 
870 #endif
871