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