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