1 /**
2 * @file lv_draw_basic.c
3 *
4 */
5
6 #include "lv_draw_basic.h"
7
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <string.h>
11
12 #include "../lv_core/lv_refr.h"
13 #include "../lv_hal/lv_hal.h"
14 #include "../lv_font/lv_font.h"
15 #include "../lv_misc/lv_area.h"
16 #include "../lv_misc/lv_color.h"
17 #include "../lv_misc/lv_log.h"
18
19 #include <stddef.h>
20 #include "lv_draw.h"
21
22 /*********************
23 * INCLUDES
24 *********************/
25
26 /*********************
27 * DEFINES
28 *********************/
29
30 /*Always fill < 50 px with 'sw_color_fill' because of the hw. init overhead*/
31 #define VFILL_HW_ACC_SIZE_LIMIT 50
32
33 #ifndef LV_ATTRIBUTE_MEM_ALIGN
34 #define LV_ATTRIBUTE_MEM_ALIGN
35 #endif
36
37 /**********************
38 * TYPEDEFS
39 **********************/
40
41 /**********************
42 * STATIC PROTOTYPES
43 **********************/
44 static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
45 static void sw_color_fill(lv_color_t * mem, lv_coord_t mem_width, const lv_area_t * fill_area, lv_color_t color,
46 lv_opa_t opa);
47
48 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
49 static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa);
50 #endif
51
52 /**********************
53 * STATIC VARIABLES
54 **********************/
55
56 /**********************
57 * MACROS
58 **********************/
59
60 /**********************
61 * GLOBAL FUNCTIONS
62 **********************/
63
64 /**
65 * Put a pixel in the Virtual Display Buffer
66 * @param x pixel x coordinate
67 * @param y pixel y coordinate
68 * @param mask_p fill only on this mask (truncated to VDB area)
69 * @param color pixel color
70 * @param opa opacity of the area (0..255)
71 */
lv_draw_px(lv_coord_t x,lv_coord_t y,const lv_area_t * mask_p,lv_color_t color,lv_opa_t opa)72 void lv_draw_px(lv_coord_t x, lv_coord_t y, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa)
73 {
74
75 if(opa < LV_OPA_MIN) return;
76 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
77
78 /*Pixel out of the mask*/
79 if(x < mask_p->x1 || x > mask_p->x2 || y < mask_p->y1 || y > mask_p->y2) {
80 return;
81 }
82
83 lv_disp_t * disp = lv_refr_get_disp_refreshing();
84 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
85 uint32_t vdb_width = lv_area_get_width(&vdb->area);
86
87 /*Make the coordinates relative to VDB*/
88 x -= vdb->area.x1;
89 y -= vdb->area.y1;
90
91 if(disp->driver.set_px_cb) {
92 disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, x, y, color, opa);
93 } else {
94 bool scr_transp = false;
95 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
96 scr_transp = disp->driver.screen_transp;
97 #endif
98
99 lv_color_t * vdb_px_p = vdb->buf_act;
100 vdb_px_p += y * vdb_width + x;
101
102 if(scr_transp == false) {
103 if(opa == LV_OPA_COVER) {
104 *vdb_px_p = color;
105 } else {
106 *vdb_px_p = lv_color_mix(color, *vdb_px_p, opa);
107 }
108 } else {
109 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
110 *vdb_px_p = color_mix_2_alpha(*vdb_px_p, (*vdb_px_p).ch.alpha, color, opa);
111 #endif
112 }
113 }
114 }
115
116 /**
117 * Fill an area in the Virtual Display Buffer
118 * @param cords_p coordinates of the area to fill
119 * @param mask_p fill only o this mask (truncated to VDB area)
120 * @param color fill color
121 * @param opa opacity of the area (0..255)
122 */
lv_draw_fill(const lv_area_t * cords_p,const lv_area_t * mask_p,lv_color_t color,lv_opa_t opa)123 void lv_draw_fill(const lv_area_t * cords_p, const lv_area_t * mask_p, lv_color_t color, lv_opa_t opa)
124 {
125 if(opa < LV_OPA_MIN) return;
126 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
127
128 lv_area_t res_a;
129 bool union_ok;
130
131 /*Get the union of cord and mask*/
132 /* The mask is already truncated to the vdb size
133 * in 'lv_refr_area_with_vdb' function */
134 union_ok = lv_area_intersect(&res_a, cords_p, mask_p);
135
136 /*If there are common part of the three area then draw to the vdb*/
137 if(union_ok == false) {
138 return;
139 }
140
141 lv_disp_t * disp = lv_refr_get_disp_refreshing();
142 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
143
144 lv_area_t vdb_rel_a; /*Stores relative coordinates on vdb*/
145 vdb_rel_a.x1 = res_a.x1 - vdb->area.x1;
146 vdb_rel_a.y1 = res_a.y1 - vdb->area.y1;
147 vdb_rel_a.x2 = res_a.x2 - vdb->area.x1;
148 vdb_rel_a.y2 = res_a.y2 - vdb->area.y1;
149
150 lv_color_t * vdb_buf_tmp = vdb->buf_act;
151 uint32_t vdb_width = lv_area_get_width(&vdb->area);
152 /*Move the vdb_tmp to the first row*/
153 vdb_buf_tmp += vdb_width * vdb_rel_a.y1;
154
155 #if LV_USE_GPU
156 static LV_ATTRIBUTE_MEM_ALIGN lv_color_t color_array_tmp[LV_HOR_RES_MAX]; /*Used by 'lv_disp_mem_blend'*/
157 static lv_coord_t last_width = -1;
158
159 lv_coord_t w = lv_area_get_width(&vdb_rel_a);
160 /*Don't use hw. acc. for every small fill (because of the init overhead)*/
161 if(w < VFILL_HW_ACC_SIZE_LIMIT) {
162 sw_color_fill(vdb->buf_act, vdb_width, &vdb_rel_a, color, opa);
163 }
164 /*Not opaque fill*/
165 else if(opa == LV_OPA_COVER) {
166 /*Use hw fill if present*/
167 if(disp->driver.gpu_fill_cb) {
168 disp->driver.gpu_fill_cb(&disp->driver, vdb->buf_act, vdb_width, &vdb_rel_a, color);
169 }
170 /*Use hw blend if present and the area is not too small*/
171 else if(lv_area_get_height(&vdb_rel_a) > VFILL_HW_ACC_SIZE_LIMIT && disp->driver.gpu_blend_cb) {
172 /*Fill a one line sized buffer with a color and blend this later*/
173 if(color_array_tmp[0].full != color.full || last_width != w) {
174 uint16_t i;
175 for(i = 0; i < w; i++) {
176 color_array_tmp[i].full = color.full;
177 }
178 last_width = w;
179 }
180
181 /*Blend the filled line to every line VDB line-by-line*/
182 lv_coord_t row;
183 for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
184 disp->driver.gpu_blend_cb(&disp->driver, &vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
185 vdb_buf_tmp += vdb_width;
186 }
187
188 }
189 /*Else use sw fill if no better option*/
190 else {
191 sw_color_fill(vdb->buf_act, vdb_width, &vdb_rel_a, color, opa);
192 }
193
194 }
195 /*Fill with opacity*/
196 else {
197 /*Use hw blend if present*/
198 if(disp->driver.gpu_blend_cb) {
199 if(color_array_tmp[0].full != color.full || last_width != w) {
200 uint16_t i;
201 for(i = 0; i < w; i++) {
202 color_array_tmp[i].full = color.full;
203 }
204
205 last_width = w;
206 }
207 lv_coord_t row;
208 for(row = vdb_rel_a.y1; row <= vdb_rel_a.y2; row++) {
209 disp->driver.gpu_blend_cb(&disp->driver, &vdb_buf_tmp[vdb_rel_a.x1], color_array_tmp, w, opa);
210 vdb_buf_tmp += vdb_width;
211 }
212
213 }
214 /*Use sw fill with opa if no better option*/
215 else {
216 sw_color_fill(vdb->buf_act, vdb_width, &vdb_rel_a, color, opa);
217 }
218 }
219 #else
220 sw_color_fill(vdb->buf_act, vdb_width, &vdb_rel_a, color, opa);
221 #endif
222 }
223
224 /**
225 * Draw a letter in the Virtual Display Buffer
226 * @param pos_p left-top coordinate of the latter
227 * @param mask_p the letter will be drawn only on this area (truncated to VDB area)
228 * @param font_p pointer to font
229 * @param letter a letter to draw
230 * @param color color of letter
231 * @param opa opacity of letter (0..255)
232 */
lv_draw_letter(const lv_point_t * pos_p,const lv_area_t * mask_p,const lv_font_t * font_p,uint32_t letter,lv_color_t color,lv_opa_t opa)233 void lv_draw_letter(const lv_point_t * pos_p, const lv_area_t * mask_p, const lv_font_t * font_p, uint32_t letter,
234 lv_color_t color, lv_opa_t opa)
235 {
236 /*clang-format off*/
237 const uint8_t bpp1_opa_table[2] = {0, 255}; /*Opacity mapping with bpp = 1 (Just for compatibility)*/
238 const uint8_t bpp2_opa_table[4] = {0, 85, 170, 255}; /*Opacity mapping with bpp = 2*/
239 const uint8_t bpp4_opa_table[16] = {0, 17, 34, 51, /*Opacity mapping with bpp = 4*/
240 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255};
241 /*clang-format on*/
242
243 if(opa < LV_OPA_MIN) return;
244 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
245
246 if(font_p == NULL) {
247 LV_LOG_WARN("Font: character's bitmap not found");
248 return;
249 }
250
251 lv_font_glyph_dsc_t g;
252 bool g_ret = lv_font_get_glyph_dsc(font_p, &g, letter, '\0');
253 if(g_ret == false) return;
254
255 lv_coord_t pos_x = pos_p->x + g.ofs_x;
256 lv_coord_t pos_y = pos_p->y + (font_p->line_height - font_p->base_line) - g.box_h - g.ofs_y;
257
258 const uint8_t * bpp_opa_table;
259 uint8_t bitmask_init;
260 uint8_t bitmask;
261
262 switch(g.bpp) {
263 case 1:
264 bpp_opa_table = bpp1_opa_table;
265 bitmask_init = 0x80;
266 break;
267 case 2:
268 bpp_opa_table = bpp2_opa_table;
269 bitmask_init = 0xC0;
270 break;
271 case 4:
272 bpp_opa_table = bpp4_opa_table;
273 bitmask_init = 0xF0;
274 break;
275 case 8:
276 bpp_opa_table = NULL;
277 bitmask_init = 0xFF;
278 break; /*No opa table, pixel value will be used directly*/
279 default: return; /*Invalid bpp. Can't render the letter*/
280 }
281
282 const uint8_t * map_p = lv_font_get_glyph_bitmap(font_p, letter);
283
284 if(map_p == NULL) return;
285
286 /*If the letter is completely out of mask don't draw it */
287 if(pos_x + g.box_w < mask_p->x1 || pos_x > mask_p->x2 || pos_y + g.box_h < mask_p->y1 || pos_y > mask_p->y2) return;
288
289 lv_disp_t * disp = lv_refr_get_disp_refreshing();
290 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
291
292 lv_coord_t vdb_width = lv_area_get_width(&vdb->area);
293 lv_color_t * vdb_buf_tmp = vdb->buf_act;
294 lv_coord_t col, row;
295
296 uint8_t width_byte_scr = g.box_w >> 3; /*Width in bytes (on the screen finally) (e.g. w = 11 -> 2 bytes wide)*/
297 if(g.box_w & 0x7) width_byte_scr++;
298 uint16_t width_bit = g.box_w * g.bpp; /*Letter width in bits*/
299
300 /* Calculate the col/row start/end on the map*/
301 lv_coord_t col_start = pos_x >= mask_p->x1 ? 0 : mask_p->x1 - pos_x;
302 lv_coord_t col_end = pos_x + g.box_w <= mask_p->x2 ? g.box_w : mask_p->x2 - pos_x + 1;
303 lv_coord_t row_start = pos_y >= mask_p->y1 ? 0 : mask_p->y1 - pos_y;
304 lv_coord_t row_end = pos_y + g.box_h <= mask_p->y2 ? g.box_h : mask_p->y2 - pos_y + 1;
305
306 /*Set a pointer on VDB to the first pixel of the letter*/
307 vdb_buf_tmp += ((pos_y - vdb->area.y1) * vdb_width) + pos_x - vdb->area.x1;
308
309 /*If the letter is partially out of mask the move there on VDB*/
310 vdb_buf_tmp += (row_start * vdb_width) + col_start;
311
312 /*Move on the map too*/
313 uint32_t bit_ofs = (row_start * width_bit) + (col_start * g.bpp);
314 map_p += bit_ofs >> 3;
315
316 uint8_t letter_px;
317 lv_opa_t px_opa;
318 uint16_t col_bit;
319 col_bit = bit_ofs & 0x7; /* "& 0x7" equals to "% 8" just faster */
320
321 bool scr_transp = false;
322 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
323 scr_transp = disp->driver.screen_transp;
324 #endif
325
326 for(row = row_start; row < row_end; row++) {
327 bitmask = bitmask_init >> col_bit;
328 for(col = col_start; col < col_end; col++) {
329 letter_px = (*map_p & bitmask) >> (8 - col_bit - g.bpp);
330 if(letter_px != 0) {
331 if(opa == LV_OPA_COVER) {
332 px_opa = g.bpp == 8 ? letter_px : bpp_opa_table[letter_px];
333 } else {
334 px_opa = g.bpp == 8 ? (uint16_t)((uint16_t)letter_px * opa) >> 8
335 : (uint16_t)((uint16_t)bpp_opa_table[letter_px] * opa) >> 8;
336 }
337
338 if(disp->driver.set_px_cb) {
339 disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width,
340 (col + pos_x) - vdb->area.x1, (row + pos_y) - vdb->area.y1, color, px_opa);
341 } else if(vdb_buf_tmp->full != color.full) {
342 if(px_opa > LV_OPA_MAX)
343 *vdb_buf_tmp = color;
344 else if(px_opa > LV_OPA_MIN) {
345 if(scr_transp == false) {
346 *vdb_buf_tmp = lv_color_mix(color, *vdb_buf_tmp, px_opa);
347 } else {
348 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
349 *vdb_buf_tmp = color_mix_2_alpha(*vdb_buf_tmp, (*vdb_buf_tmp).ch.alpha, color, px_opa);
350 #endif
351 }
352 }
353 }
354 }
355
356 vdb_buf_tmp++;
357
358 if(col_bit < 8 - g.bpp) {
359 col_bit += g.bpp;
360 bitmask = bitmask >> g.bpp;
361 } else {
362 col_bit = 0;
363 bitmask = bitmask_init;
364 map_p++;
365 }
366 }
367 col_bit += ((g.box_w - col_end) + col_start) * g.bpp;
368
369 map_p += (col_bit >> 3);
370 col_bit = col_bit & 0x7;
371 vdb_buf_tmp += vdb_width - (col_end - col_start); /*Next row in VDB*/
372 }
373 }
374
375 /**
376 * Draw a color map to the display (image)
377 * @param cords_p coordinates the color map
378 * @param mask_p the map will drawn only on this area (truncated to VDB area)
379 * @param map_p pointer to a lv_color_t array
380 * @param opa opacity of the map
381 * @param chroma_keyed true: enable transparency of LV_IMG_LV_COLOR_TRANSP color pixels
382 * @param alpha_byte true: extra alpha byte is inserted for every pixel
383 * @param recolor mix the pixels with this color
384 * @param recolor_opa the intense of recoloring
385 */
lv_draw_map(const lv_area_t * cords_p,const lv_area_t * mask_p,const uint8_t * map_p,lv_opa_t opa,bool chroma_key,bool alpha_byte,lv_color_t recolor,lv_opa_t recolor_opa)386 void lv_draw_map(const lv_area_t * cords_p, const lv_area_t * mask_p, const uint8_t * map_p, lv_opa_t opa,
387 bool chroma_key, bool alpha_byte, lv_color_t recolor, lv_opa_t recolor_opa)
388 {
389
390 if(opa < LV_OPA_MIN) return;
391 if(opa > LV_OPA_MAX) opa = LV_OPA_COVER;
392
393 lv_area_t masked_a;
394 bool union_ok;
395
396 /*Get the union of map size and mask*/
397 /* The mask is already truncated to the vdb size
398 * in 'lv_refr_area_with_vdb' function */
399 union_ok = lv_area_intersect(&masked_a, cords_p, mask_p);
400
401 /*If there are common part of the three area then draw to the vdb*/
402 if(union_ok == false) return;
403
404 /*The pixel size in byte is different if an alpha byte is added too*/
405 uint8_t px_size_byte = alpha_byte ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t);
406
407 /*If the map starts OUT of the masked area then calc. the first pixel*/
408 lv_coord_t map_width = lv_area_get_width(cords_p);
409 if(cords_p->y1 < masked_a.y1) {
410 map_p += (uint32_t)map_width * ((masked_a.y1 - cords_p->y1)) * px_size_byte;
411 }
412 if(cords_p->x1 < masked_a.x1) {
413 map_p += (masked_a.x1 - cords_p->x1) * px_size_byte;
414 }
415
416 lv_disp_t * disp = lv_refr_get_disp_refreshing();
417 lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
418
419 /*Stores coordinates relative to the current VDB*/
420 masked_a.x1 = masked_a.x1 - vdb->area.x1;
421 masked_a.y1 = masked_a.y1 - vdb->area.y1;
422 masked_a.x2 = masked_a.x2 - vdb->area.x1;
423 masked_a.y2 = masked_a.y2 - vdb->area.y1;
424
425 lv_coord_t vdb_width = lv_area_get_width(&vdb->area);
426 lv_color_t * vdb_buf_tmp = vdb->buf_act;
427 vdb_buf_tmp += (uint32_t)vdb_width * masked_a.y1; /*Move to the first row*/
428 vdb_buf_tmp += (uint32_t)masked_a.x1; /*Move to the first col*/
429
430 lv_coord_t row;
431 lv_coord_t map_useful_w = lv_area_get_width(&masked_a);
432
433 bool scr_transp = false;
434 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
435 scr_transp = disp->driver.screen_transp;
436 #endif
437
438 /*The simplest case just copy the pixels into the VDB*/
439 if(chroma_key == false && alpha_byte == false && opa == LV_OPA_COVER && recolor_opa == LV_OPA_TRANSP) {
440
441 /*Use the custom VDB write function is exists*/
442 if(disp->driver.set_px_cb) {
443 lv_coord_t col;
444 for(row = masked_a.y1; row <= masked_a.y2; row++) {
445 for(col = 0; col < map_useful_w; col++) {
446 lv_color_t px_color = *((lv_color_t *)&map_p[(uint32_t)col * px_size_byte]);
447 disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, col + masked_a.x1, row,
448 px_color, opa);
449 }
450 map_p += map_width * px_size_byte; /*Next row on the map*/
451 }
452 }
453 /*Normal native VDB*/
454 else {
455 for(row = masked_a.y1; row <= masked_a.y2; row++) {
456 #if LV_USE_GPU
457 if(disp->driver.gpu_blend_cb == false) {
458 sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
459 } else {
460 disp->driver.gpu_blend_cb(&disp->driver, vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
461 }
462 #else
463 sw_mem_blend(vdb_buf_tmp, (lv_color_t *)map_p, map_useful_w, opa);
464 #endif
465 map_p += map_width * px_size_byte; /*Next row on the map*/
466 vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
467 }
468 }
469 }
470
471 /*In the other cases every pixel need to be checked one-by-one*/
472 else {
473
474 lv_coord_t col;
475 lv_color_t last_img_px = LV_COLOR_BLACK;
476 lv_color_t recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
477 for(row = masked_a.y1; row <= masked_a.y2; row++) {
478 for(col = 0; col < map_useful_w; col++) {
479 lv_opa_t opa_result = opa;
480 uint8_t * px_color_p = (uint8_t *)&map_p[(uint32_t)col * px_size_byte];
481 lv_color_t px_color;
482
483 /*Calculate with the pixel level alpha*/
484 if(alpha_byte) {
485 #if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1
486 px_color.full = px_color_p[0];
487 #elif LV_COLOR_DEPTH == 16
488 /*Because of Alpha byte 16 bit color can start on odd address which can cause
489 * crash*/
490 px_color.full = px_color_p[0] + (px_color_p[1] << 8);
491 #elif LV_COLOR_DEPTH == 32
492 px_color = *((lv_color_t *)px_color_p);
493 #endif
494 lv_opa_t px_opa = *(px_color_p + LV_IMG_PX_SIZE_ALPHA_BYTE - 1);
495 if(px_opa == LV_OPA_TRANSP)
496 continue;
497 else if(px_opa != LV_OPA_COVER)
498 opa_result = (uint32_t)((uint32_t)px_opa * opa_result) >> 8;
499 } else {
500 px_color = *((lv_color_t *)px_color_p);
501 }
502
503 /*Handle chroma key*/
504 if(chroma_key && px_color.full == disp->driver.color_chroma_key.full) continue;
505
506 /*Re-color the pixel if required*/
507 if(recolor_opa != LV_OPA_TRANSP) {
508 if(last_img_px.full != px_color.full) { /*Minor acceleration: calculate only for
509 new colors (save the last)*/
510 last_img_px = px_color;
511 recolored_px = lv_color_mix(recolor, last_img_px, recolor_opa);
512 }
513 /*Handle custom VDB write is present*/
514 if(disp->driver.set_px_cb) {
515 disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, col + masked_a.x1,
516 row, recolored_px, opa_result);
517 }
518 /*Normal native VDB write*/
519 else {
520 if(opa_result == LV_OPA_COVER)
521 vdb_buf_tmp[col].full = recolored_px.full;
522 else
523 vdb_buf_tmp[col] = lv_color_mix(recolored_px, vdb_buf_tmp[col], opa_result);
524 }
525 } else {
526 /*Handle custom VDB write is present*/
527 if(disp->driver.set_px_cb) {
528 disp->driver.set_px_cb(&disp->driver, (uint8_t *)vdb->buf_act, vdb_width, col + masked_a.x1,
529 row, px_color, opa_result);
530 }
531 /*Normal native VDB write*/
532 else {
533
534 if(opa_result == LV_OPA_COVER)
535 vdb_buf_tmp[col] = px_color;
536 else {
537 if(scr_transp == false) {
538 vdb_buf_tmp[col] = lv_color_mix(px_color, vdb_buf_tmp[col], opa_result);
539 } else {
540 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
541 vdb_buf_tmp[col] = color_mix_2_alpha(vdb_buf_tmp[col], vdb_buf_tmp[col].ch.alpha,
542 px_color, opa_result);
543 #endif
544 }
545 }
546 }
547 }
548 }
549
550 map_p += map_width * px_size_byte; /*Next row on the map*/
551 vdb_buf_tmp += vdb_width; /*Next row on the VDB*/
552 }
553 }
554 }
555
556 /**********************
557 * STATIC FUNCTIONS
558 **********************/
559
560 /**
561 * Blend pixels to destination memory using opacity
562 * @param dest a memory address. Copy 'src' here.
563 * @param src pointer to pixel map. Copy it to 'dest'.
564 * @param length number of pixels in 'src'
565 * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
566 */
sw_mem_blend(lv_color_t * dest,const lv_color_t * src,uint32_t length,lv_opa_t opa)567 static void sw_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
568 {
569 if(opa == LV_OPA_COVER) {
570 memcpy(dest, src, length * sizeof(lv_color_t));
571 } else {
572 uint32_t col;
573 for(col = 0; col < length; col++) {
574 dest[col] = lv_color_mix(src[col], dest[col], opa);
575 }
576 }
577 }
578
579 /**
580 * Fill an area with a color
581 * @param mem a memory address. Considered to a rectangular window according to 'mem_area'
582 * @param mem_width width of the 'mem' buffer
583 * @param fill_area coordinates of an area to fill. Relative to 'mem_area'.
584 * @param color fill color
585 * @param opa opacity (0, LV_OPA_TRANSP: transparent ... 255, LV_OPA_COVER, fully cover)
586 */
sw_color_fill(lv_color_t * mem,lv_coord_t mem_width,const lv_area_t * fill_area,lv_color_t color,lv_opa_t opa)587 static void sw_color_fill(lv_color_t * mem, lv_coord_t mem_width, const lv_area_t * fill_area, lv_color_t color,
588 lv_opa_t opa)
589 {
590 /*Set all row in vdb to the given color*/
591 lv_coord_t row;
592 lv_coord_t col;
593
594 lv_disp_t * disp = lv_refr_get_disp_refreshing();
595 if(disp->driver.set_px_cb) {
596 for(col = fill_area->x1; col <= fill_area->x2; col++) {
597 for(row = fill_area->y1; row <= fill_area->y2; row++) {
598 disp->driver.set_px_cb(&disp->driver, (uint8_t *)mem, mem_width, col, row, color, opa);
599 }
600 }
601 } else {
602 mem += fill_area->y1 * mem_width; /*Go to the first row*/
603
604 /*Run simpler function without opacity*/
605 if(opa == LV_OPA_COVER) {
606
607 /*Fill the first row with 'color'*/
608 for(col = fill_area->x1; col <= fill_area->x2; col++) {
609 mem[col] = color;
610 }
611
612 /*Copy the first row to all other rows*/
613 lv_color_t * mem_first = &mem[fill_area->x1];
614 lv_coord_t copy_size = (fill_area->x2 - fill_area->x1 + 1) * sizeof(lv_color_t);
615 mem += mem_width;
616
617 for(row = fill_area->y1 + 1; row <= fill_area->y2; row++) {
618 memcpy(&mem[fill_area->x1], mem_first, copy_size);
619 mem += mem_width;
620 }
621 }
622 /*Calculate with alpha too*/
623 else {
624 bool scr_transp = false;
625 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
626 scr_transp = disp->driver.screen_transp;
627 #endif
628
629 lv_color_t bg_tmp = LV_COLOR_BLACK;
630 lv_color_t opa_tmp = lv_color_mix(color, bg_tmp, opa);
631 for(row = fill_area->y1; row <= fill_area->y2; row++) {
632 for(col = fill_area->x1; col <= fill_area->x2; col++) {
633 if(scr_transp == false) {
634 /*If the bg color changed recalculate the result color*/
635 if(mem[col].full != bg_tmp.full) {
636 bg_tmp = mem[col];
637 opa_tmp = lv_color_mix(color, bg_tmp, opa);
638 }
639
640 mem[col] = opa_tmp;
641
642 } else {
643 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
644 mem[col] = color_mix_2_alpha(mem[col], mem[col].ch.alpha, color, opa);
645 #endif
646 }
647 }
648 mem += mem_width;
649 }
650 }
651 }
652 }
653
654 #if LV_COLOR_DEPTH == 32 && LV_COLOR_SCREEN_TRANSP
655 /**
656 * Mix two colors. Both color can have alpha value. It requires ARGB888 colors.
657 * @param bg_color background color
658 * @param bg_opa alpha of the background color
659 * @param fg_color foreground color
660 * @param fg_opa alpha of the foreground color
661 * @return the mixed color. the alpha channel (color.alpha) contains the result alpha
662 */
color_mix_2_alpha(lv_color_t bg_color,lv_opa_t bg_opa,lv_color_t fg_color,lv_opa_t fg_opa)663 static inline lv_color_t color_mix_2_alpha(lv_color_t bg_color, lv_opa_t bg_opa, lv_color_t fg_color, lv_opa_t fg_opa)
664 {
665 /* Pick the foreground if it's fully opaque or the Background is fully transparent*/
666 if(fg_opa > LV_OPA_MAX || bg_opa <= LV_OPA_MIN) {
667 fg_color.ch.alpha = fg_opa;
668 return fg_color;
669 }
670 /*Transparent foreground: use the Background*/
671 else if(fg_opa <= LV_OPA_MIN) {
672 return bg_color;
673 }
674 /*Opaque background: use simple mix*/
675 else if(bg_opa >= LV_OPA_MAX) {
676 return lv_color_mix(fg_color, bg_color, fg_opa);
677 }
678 /*Both colors have alpha. Expensive calculation need to be applied*/
679 else {
680 /*Save the parameters and the result. If they will be asked again don't compute again*/
681 static lv_opa_t fg_opa_save = 0;
682 static lv_opa_t bg_opa_save = 0;
683 static lv_color_t fg_color_save = {{0}};
684 static lv_color_t bg_color_save = {{0}};
685 static lv_color_t c = {{0}};
686
687 if(fg_opa != fg_opa_save || bg_opa != bg_opa_save || fg_color.full != fg_color_save.full ||
688 bg_color.full != bg_color_save.full) {
689 fg_opa_save = fg_opa;
690 bg_opa_save = bg_opa;
691 fg_color_save.full = fg_color.full;
692 bg_color_save.full = bg_color.full;
693 /*Info:
694 * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
695 lv_opa_t alpha_res = 255 - ((uint16_t)((uint16_t)(255 - fg_opa) * (255 - bg_opa)) >> 8);
696 if(alpha_res == 0) {
697 while(1)
698 ;
699 }
700 lv_opa_t ratio = (uint16_t)((uint16_t)fg_opa * 255) / alpha_res;
701 c = lv_color_mix(fg_color, bg_color, ratio);
702 c.ch.alpha = alpha_res;
703 }
704 return c;
705 }
706 }
707 #endif
708