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