1 /**
2  * @file lv_draw_blend.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_draw_blend.h"
10 #include "lv_img_decoder.h"
11 #include "../lv_misc/lv_math.h"
12 #include "../lv_hal/lv_hal_disp.h"
13 #include "../lv_core/lv_refr.h"
14 
15 #include "../lv_gpu/lv_gpu_stm32_dma2d.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 #define GPU_SIZE_LIMIT      240
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /**********************
27  *  STATIC PROTOTYPES
28  **********************/
29 
30 static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
31                         lv_color_t color, lv_opa_t opa,
32                         const lv_opa_t * mask, lv_draw_mask_res_t mask_res);
33 
34 LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_color_t * disp_buf,
35                                               const lv_area_t * draw_area,
36                                               lv_color_t color, lv_opa_t opa,
37                                               const lv_opa_t * mask, lv_draw_mask_res_t mask_res);
38 
39 #if LV_USE_BLEND_MODES
40 static void fill_blended(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
41                          lv_color_t color, lv_opa_t opa,
42                          const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode);
43 #endif
44 
45 static void map_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
46                        const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa,
47                        const lv_opa_t * mask, lv_draw_mask_res_t mask_res);
48 
49 LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_color_t * disp_buf,
50                                              const lv_area_t * draw_area,
51                                              const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa,
52                                              const lv_opa_t * mask, lv_draw_mask_res_t mask_res);
53 
54 #if LV_USE_BLEND_MODES
55 static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
56                         const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa,
57                         const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode);
58 
59 static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
60 static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa);
61 #endif
62 
63 /**********************
64  *  STATIC VARIABLES
65  **********************/
66 
67 #if LV_USE_GPU || LV_USE_GPU_STM32_DMA2D
68     LV_ATTRIBUTE_DMA static lv_color_t blend_buf[LV_HOR_RES_MAX];
69 #endif
70 
71 /**********************
72  *      MACROS
73  **********************/
74 
75 
76 #define FILL_NORMAL_MASK_PX(out_x,  color)                                                          \
77     if(*mask_tmp_x) {          \
78         if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[out_x] = color;                                 \
79         else disp_buf_first[out_x] = lv_color_mix(color, disp_buf_first[out_x], *mask_tmp_x);            \
80     }                                                                                               \
81     mask_tmp_x++;
82 
83 
84 #define FILL_NORMAL_MASK_PX_SCR_TRANSP(out_x,  color)                                               \
85     if(*mask_tmp_x) {          \
86         if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[out_x] = color;                                 \
87         else if(disp->driver.screen_transp) lv_color_mix_with_alpha(disp_buf_first[out_x], disp_buf_first[out_x].ch.alpha,              \
88                                                                         color, *mask_tmp_x, &disp_buf_first[out_x], &disp_buf_first[out_x].ch.alpha);           \
89         else disp_buf_first[out_x] = lv_color_mix(color, disp_buf_first[out_x], *mask_tmp_x);            \
90     }                                                                                                      \
91     mask_tmp_x++;
92 
93 
94 #define MAP_NORMAL_MASK_PX(x)                                                          \
95     if(*mask_tmp_x) {          \
96         if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[x] = map_buf_first[x];                                 \
97         else disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], *mask_tmp_x);            \
98     }                                                                                               \
99     mask_tmp_x++;
100 
101 #define MAP_NORMAL_MASK_PX_SCR_TRANSP(x)                        \
102     if(*mask_tmp_x) {          \
103         if(*mask_tmp_x == LV_OPA_COVER) disp_buf_first[x] = map_buf_first[x];                                 \
104         else if(disp->driver.screen_transp) lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha,              \
105                                                                         map_buf_first[x], *mask_tmp_x, &disp_buf_first[x], &disp_buf_first[x].ch.alpha);                  \
106         else disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], *mask_tmp_x);            \
107     }                                                                                               \
108     mask_tmp_x++;
109 
110 /**********************
111  *   GLOBAL FUNCTIONS
112  **********************/
113 
114 
115 /**
116  * Fill and area in the display buffer.
117  * @param clip_area clip the fill to this area  (absolute coordinates)
118  * @param fill_area fill this area  (absolute coordinates) (should be clipped)
119  * @param color fill color
120  * @param mask a mask to apply on the fill (uint8_t array with 0x00..0xff values).
121  *             Relative to fill area but its width is truncated to clip area.
122  * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
123  *                 LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
124  *                 LV_MASK_RES_CHANGED: the mask has mixed values
125  * @param opa overall opacity in 0x00..0xff range
126  * @param mode blend mode from `lv_blend_mode_t`
127  */
_lv_blend_fill(const lv_area_t * clip_area,const lv_area_t * fill_area,lv_color_t color,lv_opa_t * mask,lv_draw_mask_res_t mask_res,lv_opa_t opa,lv_blend_mode_t mode)128 LV_ATTRIBUTE_FAST_MEM void _lv_blend_fill(const lv_area_t * clip_area, const lv_area_t * fill_area,
129                                           lv_color_t color, lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_opa_t opa,
130                                           lv_blend_mode_t mode)
131 {
132     /*Do not draw transparent things*/
133     if(opa < LV_OPA_MIN) return;
134     if(mask_res == LV_DRAW_MASK_RES_TRANSP) return;
135 
136     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
137     lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
138     const lv_area_t * disp_area = &vdb->area;
139     lv_color_t * disp_buf = vdb->buf_act;
140 
141     if(disp->driver.gpu_wait_cb) disp->driver.gpu_wait_cb(&disp->driver);
142 
143     /* Get clipped fill area which is the real draw area.
144      * It is always the same or inside `fill_area` */
145     lv_area_t draw_area;
146     bool is_common;
147     is_common = _lv_area_intersect(&draw_area, clip_area, fill_area);
148     if(!is_common) return;
149 
150     /* Now `draw_area` has absolute coordinates.
151      * Make it relative to `disp_area` to simplify draw to `disp_buf`*/
152     draw_area.x1 -= disp_area->x1;
153     draw_area.y1 -= disp_area->y1;
154     draw_area.x2 -= disp_area->x1;
155     draw_area.y2 -= disp_area->y1;
156 
157     /*Round the values in the mask if anti-aliasing is disabled*/
158 #if LV_ANTIALIAS
159     if(mask && disp->driver.antialiasing == 0)
160 #else
161     if(mask)
162 #endif
163     {
164         int32_t mask_w = lv_area_get_width(&draw_area);
165         int32_t i;
166         for(i = 0; i < mask_w; i++)  mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP;
167     }
168 
169     if(disp->driver.set_px_cb) {
170         fill_set_px(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res);
171     }
172     else if(mode == LV_BLEND_MODE_NORMAL) {
173         fill_normal(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res);
174     }
175 #if LV_USE_BLEND_MODES
176     else {
177         fill_blended(disp_area, disp_buf, &draw_area, color, opa, mask, mask_res, mode);
178     }
179 #endif
180 }
181 
182 /**
183  * Copy a map (image) to a display buffer.
184  * @param clip_area clip the map to this area (absolute coordinates)
185  * @param map_area area of the image  (absolute coordinates)
186  * @param map_buf a pixels of the map (image)
187  * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
188  *                Relative to map area but its width is truncated to clip area.
189  * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
190  *                 LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
191  *                 LV_MASK_RES_CHANGED: the mask has mixed values
192  * @param opa  overall opacity in 0x00..0xff range
193  * @param mode blend mode from `lv_blend_mode_t`
194  */
_lv_blend_map(const lv_area_t * clip_area,const lv_area_t * map_area,const lv_color_t * map_buf,lv_opa_t * mask,lv_draw_mask_res_t mask_res,lv_opa_t opa,lv_blend_mode_t mode)195 LV_ATTRIBUTE_FAST_MEM void _lv_blend_map(const lv_area_t * clip_area, const lv_area_t * map_area,
196                                          const lv_color_t * map_buf,
197                                          lv_opa_t * mask, lv_draw_mask_res_t mask_res,
198                                          lv_opa_t opa, lv_blend_mode_t mode)
199 {
200     /*Do not draw transparent things*/
201     if(opa < LV_OPA_MIN) return;
202     if(mask_res == LV_DRAW_MASK_RES_TRANSP) return;
203 
204     /* Get clipped fill area which is the real draw area.
205      * It is always the same or inside `fill_area` */
206     lv_area_t draw_area;
207     bool is_common;
208     is_common = _lv_area_intersect(&draw_area, clip_area, map_area);
209     if(!is_common) return;
210 
211     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
212     lv_disp_buf_t * vdb = lv_disp_get_buf(disp);
213     const lv_area_t * disp_area = &vdb->area;
214     lv_color_t * disp_buf = vdb->buf_act;
215 
216     if(disp->driver.gpu_wait_cb) disp->driver.gpu_wait_cb(&disp->driver);
217 
218     /* Now `draw_area` has absolute coordinates.
219      * Make it relative to `disp_area` to simplify draw to `disp_buf`*/
220     draw_area.x1 -= disp_area->x1;
221     draw_area.y1 -= disp_area->y1;
222     draw_area.x2 -= disp_area->x1;
223     draw_area.y2 -= disp_area->y1;
224 
225     /*Round the values in the mask if anti-aliasing is disabled*/
226 #if LV_ANTIALIAS
227     if(mask && disp->driver.antialiasing == 0)
228 #else
229     if(mask)
230 #endif
231     {
232         int32_t mask_w = lv_area_get_width(&draw_area);
233         int32_t i;
234         for(i = 0; i < mask_w; i++)  mask[i] = mask[i] > 128 ? LV_OPA_COVER : LV_OPA_TRANSP;
235     }
236     if(disp->driver.set_px_cb) {
237         map_set_px(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res);
238     }
239     else if(mode == LV_BLEND_MODE_NORMAL) {
240         map_normal(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res);
241     }
242 #if LV_USE_BLEND_MODES
243     else {
244         map_blended(disp_area, disp_buf, &draw_area, map_area, map_buf, opa, mask, mask_res, mode);
245     }
246 #endif
247 }
248 
249 
250 /**********************
251  *   STATIC FUNCTIONS
252  **********************/
253 
fill_set_px(const lv_area_t * disp_area,lv_color_t * disp_buf,const lv_area_t * draw_area,lv_color_t color,lv_opa_t opa,const lv_opa_t * mask,lv_draw_mask_res_t mask_res)254 static void fill_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
255                         lv_color_t color, lv_opa_t opa,
256                         const lv_opa_t * mask, lv_draw_mask_res_t mask_res)
257 {
258 
259     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
260 
261     /*Get the width of the `disp_area` it will be used to go to the next line*/
262     int32_t disp_w = lv_area_get_width(disp_area);
263 
264     int32_t x;
265     int32_t y;
266 
267     if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
268         for(y = draw_area->y1; y <= draw_area->y2; y++) {
269             for(x = draw_area->x1; x <= draw_area->x2; x++) {
270                 disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, color, opa);
271             }
272         }
273     }
274     else {
275         /* The mask is relative to the clipped area.
276          * In the cycles below mask will be indexed from `draw_area.x1`
277          * but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
278         const lv_opa_t * mask_tmp = mask - draw_area->x1;
279 
280         /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
281         int32_t draw_area_w = lv_area_get_width(draw_area);
282 
283         for(y = draw_area->y1; y <= draw_area->y2; y++) {
284             for(x = draw_area->x1; x <= draw_area->x2; x++) {
285                 if(mask_tmp[x]) {
286                     disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, color,
287                                            (uint32_t)((uint32_t)opa * mask_tmp[x]) >> 8);
288                 }
289             }
290             mask_tmp += draw_area_w;
291         }
292     }
293 }
294 
295 /**
296  * Fill an area with a color
297  * @param disp_area the current display area (destination area)
298  * @param disp_buf destination buffer
299  * @param draw_area fill this area (relative to `disp_area`)
300  * @param color fill color
301  * @param opa overall opacity in 0x00..0xff range
302  * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
303  *                It fits into draw_area.
304  * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
305  *                 LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
306  *                 LV_MASK_RES_CHANGED: the mask has mixed values
307  */
fill_normal(const lv_area_t * disp_area,lv_color_t * disp_buf,const lv_area_t * draw_area,lv_color_t color,lv_opa_t opa,const lv_opa_t * mask,lv_draw_mask_res_t mask_res)308 LV_ATTRIBUTE_FAST_MEM static void fill_normal(const lv_area_t * disp_area, lv_color_t * disp_buf,
309                                               const lv_area_t * draw_area,
310                                               lv_color_t color, lv_opa_t opa,
311                                               const lv_opa_t * mask, lv_draw_mask_res_t mask_res)
312 {
313 
314 #if LV_USE_GPU || LV_COLOR_SCREEN_TRANSP
315     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
316 #endif
317 
318     /*Get the width of the `disp_area` it will be used to go to the next line*/
319     int32_t disp_w = lv_area_get_width(disp_area);
320 
321     int32_t draw_area_w = lv_area_get_width(draw_area);
322     int32_t draw_area_h = lv_area_get_height(draw_area);
323 
324     /*Create a temp. disp_buf which always point to the first pixel of the destination area*/
325     lv_color_t * disp_buf_first = disp_buf + disp_w * draw_area->y1 + draw_area->x1;
326 
327     int32_t x;
328     int32_t y;
329 
330     /*Simple fill (maybe with opacity), no masking*/
331     if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
332         if(opa > LV_OPA_MAX) {
333 #if LV_USE_GPU
334             if(disp->driver.gpu_fill_cb && lv_area_get_size(draw_area) > GPU_SIZE_LIMIT) {
335                 disp->driver.gpu_fill_cb(&disp->driver, disp_buf, disp_w, draw_area, color);
336                 return;
337             }
338 #endif
339 
340 #if LV_USE_GPU_STM32_DMA2D
341             if(lv_area_get_size(draw_area) >= 240) {
342                 lv_gpu_stm32_dma2d_fill(disp_buf_first, disp_w, color, draw_area_w, draw_area_h);
343                 return;
344             }
345 #endif
346             /*Software rendering*/
347             for(y = 0; y < draw_area_h; y++) {
348                 lv_color_fill(disp_buf_first, color, draw_area_w);
349                 disp_buf_first += disp_w;
350             }
351         }
352         /*No mask with opacity*/
353         else {
354 #if LV_USE_GPU
355             if(disp->driver.gpu_blend_cb && lv_area_get_size(draw_area) > GPU_SIZE_LIMIT) {
356                 for(x = 0; x < draw_area_w ; x++) blend_buf[x].full = color.full;
357 
358                 for(y = draw_area->y1; y <= draw_area->y2; y++) {
359                     disp->driver.gpu_blend_cb(&disp->driver, disp_buf_first, blend_buf, draw_area_w, opa);
360                     disp_buf_first += disp_w;
361                 }
362                 return;
363             }
364 #endif
365 
366 
367 #if LV_USE_GPU_STM32_DMA2D
368             if(lv_area_get_size(draw_area) >= 240) {
369                 if(blend_buf[0].full != color.full) lv_color_fill(blend_buf, color, LV_HOR_RES_MAX);
370 
371                 lv_coord_t line_h = LV_HOR_RES_MAX / draw_area_w;
372                 for(y = 0; y <= draw_area_h - line_h; y += line_h) {
373                     lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, blend_buf, opa, draw_area_w, draw_area_w, line_h);
374                     disp_buf_first += disp_w * line_h;
375                 }
376 
377                 if(y != draw_area_h) {
378                     lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, blend_buf, opa, draw_area_w, draw_area_w, draw_area_h - y);
379                 }
380 
381                 return;
382             }
383 #endif
384             lv_color_t last_dest_color = LV_COLOR_BLACK;
385             lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa);
386 
387             uint16_t color_premult[3];
388             lv_color_premult(color, opa, color_premult);
389             lv_opa_t opa_inv = 255 - opa;
390 
391             for(y = 0; y < draw_area_h; y++) {
392                 for(x = 0; x < draw_area_w; x++) {
393                     if(last_dest_color.full != disp_buf_first[x].full) {
394                         last_dest_color = disp_buf_first[x];
395 
396 #if LV_COLOR_SCREEN_TRANSP
397                         if(disp->driver.screen_transp) {
398                             lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, color, opa, &last_res_color,
399                                                     &last_res_color.ch.alpha);
400                         }
401                         else
402 #endif
403                         {
404                             last_res_color = lv_color_mix_premult(color_premult, disp_buf_first[x], opa_inv);
405                         }
406                     }
407                     disp_buf_first[x] = last_res_color;
408                 }
409                 disp_buf_first += disp_w;
410             }
411         }
412     }
413     /*Masked*/
414     else {
415         /*DMA2D could be used here but it's much slower than software rendering*/
416 #if LV_USE_GPU_STM32_DMA2D && 0
417         if(lv_area_get_size(draw_area) > 240) {
418             lv_gpu_stm32_dma2d_fill_mask(disp_buf_first, disp_w, color, mask, opa, draw_area_w, draw_area_h);
419             return;
420         }
421 #endif
422 
423 
424         /*Buffer the result color to avoid recalculating the same color*/
425         lv_color_t last_dest_color;
426         lv_color_t last_res_color;
427         lv_opa_t last_mask = LV_OPA_TRANSP;
428         last_dest_color.full = disp_buf_first[0].full;
429         last_res_color.full = disp_buf_first[0].full;
430 
431         int32_t x_end4 = draw_area_w - 4;
432 
433         /*Only the mask matters*/
434         if(opa > LV_OPA_MAX) {
435             for(y = 0; y < draw_area_h; y++) {
436                 const lv_opa_t * mask_tmp_x = mask;
437 #if 0
438                 for(x = 0; x < draw_area_w; x++) {
439 #if LV_COLOR_SCREEN_TRANSP
440                     FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
441 #else
442                     FILL_NORMAL_MASK_PX(x, color)
443 #endif
444                 }
445 #else
446                 for(x = 0; x < draw_area_w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) {
447 #if LV_COLOR_SCREEN_TRANSP
448                     FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
449 #else
450                     FILL_NORMAL_MASK_PX(x, color)
451 #endif
452                 }
453 
454                 uint32_t * mask32 = (uint32_t *) mask_tmp_x;
455                 for(; x <= x_end4; x += 4) {
456                     if(*mask32) {
457                         if((*mask32) == 0xFFFFFFFF) {
458                             disp_buf_first[x] = color;
459                             disp_buf_first[x + 1] = color;
460                             disp_buf_first[x + 2] = color;
461                             disp_buf_first[x + 3] = color;
462                         }
463                         else {
464                             mask_tmp_x = (const lv_opa_t *)mask32;
465 #if LV_COLOR_SCREEN_TRANSP
466                             FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
467                             FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 1, color)
468                             FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 2, color)
469                             FILL_NORMAL_MASK_PX_SCR_TRANSP(x + 3, color)
470 #else
471                             FILL_NORMAL_MASK_PX(x, color)
472                             FILL_NORMAL_MASK_PX(x + 1, color)
473                             FILL_NORMAL_MASK_PX(x + 2, color)
474                             FILL_NORMAL_MASK_PX(x + 3, color)
475 #endif
476                         }
477                     }
478                     mask32++;
479                 }
480 
481                 mask_tmp_x = (const lv_opa_t *)mask32;
482                 for(; x < draw_area_w ; x++) {
483 #if LV_COLOR_SCREEN_TRANSP
484                     FILL_NORMAL_MASK_PX_SCR_TRANSP(x, color)
485 #else
486                     FILL_NORMAL_MASK_PX(x, color)
487 #endif
488                 }
489 #endif
490                 disp_buf_first += disp_w;
491                 mask += draw_area_w;
492             }
493         }
494         /*Handle opa and mask values too*/
495         else {
496             lv_opa_t opa_tmp = LV_OPA_TRANSP;
497             for(y = draw_area->y1; y <= draw_area->y2; y++) {
498                 const lv_opa_t * mask_tmp_x = mask;
499                 for(x = 0; x < draw_area_w; x++) {
500                     if(*mask_tmp_x) {
501                         if(*mask_tmp_x != last_mask) opa_tmp = *mask_tmp_x == LV_OPA_COVER ? opa :
502                                                                    (uint32_t)((uint32_t)(*mask_tmp_x) * opa) >> 8;
503                         if(*mask_tmp_x != last_mask || last_dest_color.full != disp_buf_first[x].full) {
504 #if LV_COLOR_SCREEN_TRANSP
505                             if(disp->driver.screen_transp) {
506                                 lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, color, opa_tmp, &last_res_color,
507                                                         &last_res_color.ch.alpha);
508                             }
509                             else
510 #endif
511                             {
512                                 if(opa_tmp == LV_OPA_COVER) last_res_color = color;
513                                 else last_res_color = lv_color_mix(color, disp_buf_first[x], opa_tmp);
514                             }
515                             last_mask = *mask_tmp_x;
516                             last_dest_color.full = disp_buf_first[x].full;
517                         }
518                         disp_buf_first[x] = last_res_color;
519                     }
520                     mask_tmp_x++;
521                 }
522                 disp_buf_first += disp_w;
523                 mask += draw_area_w;
524             }
525         }
526     }
527 }
528 
529 #if LV_USE_BLEND_MODES
530 /**
531  * Fill an area with a color but apply blending algorithms
532  * @param disp_area the current display area (destination area)
533  * @param disp_buf destination buffer
534  * @param draw_area fill this area (relative to `disp_area`)
535  * @param color fill color
536  * @param opa overall opacity in 0x00..0xff range
537  * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
538  *                It fits into draw_area.
539  * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
540  *                 LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
541  *                 LV_MASK_RES_CHANGED: the mask has mixed values
542  * @param mode blend mode from `lv_blend_mode_t`
543  */
fill_blended(const lv_area_t * disp_area,lv_color_t * disp_buf,const lv_area_t * draw_area,lv_color_t color,lv_opa_t opa,const lv_opa_t * mask,lv_draw_mask_res_t mask_res,lv_blend_mode_t mode)544 static void fill_blended(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
545                          lv_color_t color, lv_opa_t opa,
546                          const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode)
547 {
548     /*Get the width of the `disp_area` it will be used to go to the next line*/
549     int32_t disp_w = lv_area_get_width(disp_area);
550 
551     /*Create a temp. disp_buf which always point to current line to draw*/
552     lv_color_t * disp_buf_tmp = disp_buf + disp_w * draw_area->y1;
553 
554 
555     lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t);
556     switch(mode) {
557         case LV_BLEND_MODE_ADDITIVE:
558             blend_fp = color_blend_true_color_additive;
559             break;
560         case LV_BLEND_MODE_SUBTRACTIVE:
561             blend_fp = color_blend_true_color_subtractive;
562             break;
563         default:
564             LV_LOG_WARN("fill_blended: unsupported blend mode");
565             return;
566     }
567 
568     int32_t x;
569     int32_t y;
570 
571     /*Simple fill (maybe with opacity), no masking*/
572     if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
573         lv_color_t last_dest_color = LV_COLOR_BLACK;
574         lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa);
575         for(y = draw_area->y1; y <= draw_area->y2; y++) {
576             for(x = draw_area->x1; x <= draw_area->x2; x++) {
577                 if(last_dest_color.full != disp_buf_tmp[x].full) {
578                     last_dest_color = disp_buf_tmp[x];
579                     last_res_color = blend_fp(color, disp_buf_tmp[x], opa);
580                 }
581                 disp_buf_tmp[x] = last_res_color;
582             }
583             disp_buf_tmp += disp_w;
584         }
585     }
586     /*Masked*/
587     else {
588         /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
589         int32_t draw_area_w = lv_area_get_width(draw_area);
590 
591         /* The mask is relative to the clipped area.
592          * In the cycles below mask will be indexed from `draw_area.x1`
593          * but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
594         const lv_opa_t * mask_tmp = mask - draw_area->x1;
595 
596         /*Buffer the result color to avoid recalculating the same color*/
597         lv_color_t last_dest_color;
598         lv_color_t last_res_color;
599         lv_opa_t last_mask = LV_OPA_TRANSP;
600         last_dest_color.full = disp_buf_tmp[0].full;
601         last_res_color.full = disp_buf_tmp[0].full;
602 
603         for(y = draw_area->y1; y <= draw_area->y2; y++) {
604             for(x = draw_area->x1; x <= draw_area->x2; x++) {
605                 if(mask_tmp[x] == 0) continue;
606                 if(mask_tmp[x] != last_mask || last_dest_color.full != disp_buf_tmp[x].full) {
607                     lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : (uint32_t)((uint32_t)mask_tmp[x] * opa) >> 8;
608 
609                     last_res_color = blend_fp(color, disp_buf_tmp[x], opa_tmp);
610                     last_mask = mask_tmp[x];
611                     last_dest_color.full = disp_buf_tmp[x].full;
612                 }
613                 disp_buf_tmp[x] = last_res_color;
614             }
615             disp_buf_tmp += disp_w;
616             mask_tmp += draw_area_w;
617         }
618     }
619 }
620 #endif
621 
map_set_px(const lv_area_t * disp_area,lv_color_t * disp_buf,const lv_area_t * draw_area,const lv_area_t * map_area,const lv_color_t * map_buf,lv_opa_t opa,const lv_opa_t * mask,lv_draw_mask_res_t mask_res)622 static void map_set_px(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
623                        const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa,
624                        const lv_opa_t * mask, lv_draw_mask_res_t mask_res)
625 
626 {
627     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
628 
629     /*Get the width of the `disp_area` it will be used to go to the next line*/
630     int32_t disp_w = lv_area_get_width(disp_area);
631 
632     /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
633     int32_t draw_area_w = lv_area_get_width(draw_area);
634 
635     /*Get the width of the `mask_area` it will be used to go to the next line*/
636     int32_t map_w = lv_area_get_width(map_area);
637 
638     /*Create a temp. map_buf which always point to current line to draw*/
639     const lv_color_t * map_buf_tmp = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1));
640 
641     map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1));
642     map_buf_tmp -= draw_area->x1;
643     int32_t x;
644     int32_t y;
645 
646     if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
647         for(y = draw_area->y1; y <= draw_area->y2; y++) {
648             for(x = draw_area->x1; x <= draw_area->x2; x++) {
649                 disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, map_buf_tmp[x], opa);
650             }
651             map_buf_tmp += map_w;
652         }
653     }
654     else {
655         /* The mask is relative to the clipped area.
656          * In the cycles below mask will be indexed from `draw_area.x1`
657          * but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
658         const lv_opa_t * mask_tmp = mask - draw_area->x1;
659 
660         for(y = draw_area->y1; y <= draw_area->y2; y++) {
661             for(x = draw_area->x1; x <= draw_area->x2; x++) {
662                 if(mask_tmp[x]) {
663                     disp->driver.set_px_cb(&disp->driver, (void *)disp_buf, disp_w, x, y, map_buf_tmp[x],
664                                            (uint32_t)((uint32_t)opa * mask_tmp[x]) >> 8);
665                 }
666             }
667             mask_tmp += draw_area_w;
668             map_buf_tmp += map_w;
669         }
670     }
671 }
672 
673 /**
674  * Copy an image to an area
675  * @param disp_area the current display area (destination area)
676  * @param disp_buf destination buffer
677  * @param map_area coordinates of the map (image) to copy. (absolute coordinates)
678  * @param map_buf the pixel of the image
679  * @param opa overall opacity in 0x00..0xff range
680  * @param mask a mask to apply on every pixel (uint8_t array with 0x00..0xff values).
681  *                It fits into draw_area.
682  * @param mask_res LV_MASK_RES_COVER: the mask has only 0xff values (no mask),
683  *                 LV_MASK_RES_TRANSP: the mask has only 0x00 values (full transparent),
684  *                 LV_MASK_RES_CHANGED: the mask has mixed values
685  */
map_normal(const lv_area_t * disp_area,lv_color_t * disp_buf,const lv_area_t * draw_area,const lv_area_t * map_area,const lv_color_t * map_buf,lv_opa_t opa,const lv_opa_t * mask,lv_draw_mask_res_t mask_res)686 LV_ATTRIBUTE_FAST_MEM static void map_normal(const lv_area_t * disp_area, lv_color_t * disp_buf,
687                                              const lv_area_t * draw_area,
688                                              const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa,
689                                              const lv_opa_t * mask, lv_draw_mask_res_t mask_res)
690 {
691 
692     /*Get the width of the `disp_area` it will be used to go to the next line*/
693     int32_t disp_w = lv_area_get_width(disp_area);
694 
695     int32_t draw_area_w = lv_area_get_width(draw_area);
696     int32_t draw_area_h = lv_area_get_height(draw_area);
697 
698     /*Get the width of the `mask_area` it will be used to go to the next line*/
699     int32_t map_w = lv_area_get_width(map_area);
700 
701     /*Create a temp. disp_buf which always point to first pixel to draw*/
702     lv_color_t * disp_buf_first = disp_buf + disp_w * draw_area->y1 + draw_area->x1;
703 
704     /*Create a temp. map_buf which always point to first pixel to draw from the map*/
705     const lv_color_t * map_buf_first = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1));
706     map_buf_first += (draw_area->x1 - (map_area->x1 - disp_area->x1));
707 
708 #if LV_COLOR_SCREEN_TRANSP || LV_USE_GPU
709     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
710 #endif
711 
712     int32_t x;
713     int32_t y;
714 
715     /*Simple fill (maybe with opacity), no masking*/
716     if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
717 #if LV_USE_GPU
718         if(disp->driver.gpu_blend_cb && (lv_area_get_size(draw_area) > GPU_SIZE_LIMIT)) {
719             for(y = draw_area->y1; y <= draw_area->y2; y++) {
720                 disp->driver.gpu_blend_cb(&disp->driver, disp_buf_first, map_buf_first, draw_area_w, opa);
721                 disp_buf_first += disp_w;
722                 map_buf_first += map_w;
723             }
724             return;
725         }
726 #endif
727 
728         if(opa > LV_OPA_MAX) {
729 #if LV_USE_GPU_STM32_DMA2D
730             if(lv_area_get_size(draw_area) >= 240) {
731                 lv_gpu_stm32_dma2d_copy(disp_buf_first, disp_w, map_buf_first, map_w, draw_area_w, draw_area_h);
732                 return;
733             }
734 #endif
735 
736             /*Software rendering*/
737             for(y = 0; y < draw_area_h; y++) {
738                 _lv_memcpy(disp_buf_first, map_buf_first, draw_area_w * sizeof(lv_color_t));
739                 disp_buf_first += disp_w;
740                 map_buf_first += map_w;
741             }
742         }
743         else {
744 #if LV_USE_GPU_STM32_DMA2D
745             if(lv_area_get_size(draw_area) >= 240) {
746                 lv_gpu_stm32_dma2d_blend(disp_buf_first, disp_w, map_buf_first, opa, map_w, draw_area_w, draw_area_h);
747                 return;
748             }
749 #endif
750 
751             /*Software rendering*/
752 
753             for(y = 0; y < draw_area_h; y++) {
754                 for(x = 0; x < draw_area_w; x++) {
755 #if LV_COLOR_SCREEN_TRANSP
756                     if(disp->driver.screen_transp) {
757                         lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, map_buf_first[x], opa, &disp_buf_first[x],
758                                                 &disp_buf_first[x].ch.alpha);
759                     }
760                     else
761 #endif
762                     {
763                         disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], opa);
764                     }
765                 }
766                 disp_buf_first += disp_w;
767                 map_buf_first += map_w;
768             }
769         }
770     }
771     /*Masked*/
772     else {
773         /*Only the mask matters*/
774         if(opa > LV_OPA_MAX) {
775             /*Go to the first pixel of the row */
776 
777             int32_t x_end4 = draw_area_w - 4;
778 
779             for(y = 0; y < draw_area_h; y++) {
780                 const lv_opa_t * mask_tmp_x = mask;
781 #if 0
782                 for(x = 0; x < draw_area_w; x++) {
783                     MAP_NORMAL_MASK_PX(x);
784                 }
785 #else
786                 for(x = 0; x < draw_area_w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) {
787 #if LV_COLOR_SCREEN_TRANSP
788                     MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
789 #else
790                     MAP_NORMAL_MASK_PX(x)
791 #endif
792                 }
793 
794                 uint32_t * mask32 = (uint32_t *) mask_tmp_x;
795                 for(; x < x_end4; x += 4) {
796                     if(*mask32) {
797                         if((*mask32) == 0xFFFFFFFF) {
798                             disp_buf_first[x] = map_buf_first[x];
799                             disp_buf_first[x + 1] = map_buf_first[x + 1];
800                             disp_buf_first[x + 2] = map_buf_first[x + 2];
801                             disp_buf_first[x + 3] = map_buf_first[x + 3];
802                         }
803                         else {
804                             mask_tmp_x = (const lv_opa_t *)mask32;
805 #if LV_COLOR_SCREEN_TRANSP
806                             MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
807                             MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 1)
808                             MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 2)
809                             MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 3)
810 #else
811                             MAP_NORMAL_MASK_PX(x)
812                             MAP_NORMAL_MASK_PX(x + 1)
813                             MAP_NORMAL_MASK_PX(x + 2)
814                             MAP_NORMAL_MASK_PX(x + 3)
815 #endif
816                         }
817                     }
818                     mask32++;
819                 }
820 
821                 mask_tmp_x = (const lv_opa_t *)mask32;
822                 for(; x < draw_area_w ; x++) {
823 #if LV_COLOR_SCREEN_TRANSP
824                     MAP_NORMAL_MASK_PX_SCR_TRANSP(x)
825 #else
826                     MAP_NORMAL_MASK_PX(x)
827 #endif
828                 }
829 #endif
830                 disp_buf_first += disp_w;
831                 mask += draw_area_w;
832                 map_buf_first += map_w;
833             }
834         }
835         /*Handle opa and mask values too*/
836         else {
837             for(y = 0; y < draw_area_h; y++) {
838                 for(x = 0; x < draw_area_w; x++) {
839                     if(mask[x]) {
840                         lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8);
841 #if LV_COLOR_SCREEN_TRANSP
842                         if(disp->driver.screen_transp) {
843                             lv_color_mix_with_alpha(disp_buf_first[x], disp_buf_first[x].ch.alpha, map_buf_first[x], opa_tmp, &disp_buf_first[x],
844                                                     &disp_buf_first[x].ch.alpha);
845                         }
846                         else
847 #endif
848                         {
849                             disp_buf_first[x] = lv_color_mix(map_buf_first[x], disp_buf_first[x], opa_tmp);
850                         }
851                     }
852                 }
853                 disp_buf_first += disp_w;
854                 mask += draw_area_w;
855                 map_buf_first += map_w;
856             }
857         }
858     }
859 }
860 #if LV_USE_BLEND_MODES
map_blended(const lv_area_t * disp_area,lv_color_t * disp_buf,const lv_area_t * draw_area,const lv_area_t * map_area,const lv_color_t * map_buf,lv_opa_t opa,const lv_opa_t * mask,lv_draw_mask_res_t mask_res,lv_blend_mode_t mode)861 static void map_blended(const lv_area_t * disp_area, lv_color_t * disp_buf,  const lv_area_t * draw_area,
862                         const lv_area_t * map_area, const lv_color_t * map_buf, lv_opa_t opa,
863                         const lv_opa_t * mask, lv_draw_mask_res_t mask_res, lv_blend_mode_t mode)
864 {
865 
866     /*Get the width of the `disp_area` it will be used to go to the next line*/
867     int32_t disp_w = lv_area_get_width(disp_area);
868 
869     /*Get the width of the `draw_area` it will be used to go to the next line of the mask*/
870     int32_t draw_area_w = lv_area_get_width(draw_area);
871 
872     /*Get the width of the `mask_area` it will be used to go to the next line*/
873     int32_t map_w = lv_area_get_width(map_area);
874 
875     /*Create a temp. disp_buf which always point to current line to draw*/
876     lv_color_t * disp_buf_tmp = disp_buf + disp_w * draw_area->y1;
877 
878     /*Create a temp. map_buf which always point to current line to draw*/
879     const lv_color_t * map_buf_tmp = map_buf + map_w * (draw_area->y1 - (map_area->y1 - disp_area->y1));
880 
881     lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t);
882     switch(mode) {
883         case LV_BLEND_MODE_ADDITIVE:
884             blend_fp = color_blend_true_color_additive;
885             break;
886         case LV_BLEND_MODE_SUBTRACTIVE:
887             blend_fp = color_blend_true_color_subtractive;
888             break;
889         default:
890             LV_LOG_WARN("fill_blended: unsupported blend mode");
891             return;
892     }
893 
894     int32_t x;
895     int32_t y;
896 
897     /*Simple fill (maybe with opacity), no masking*/
898     if(mask_res == LV_DRAW_MASK_RES_FULL_COVER) {
899         /*Go to the first px of the row*/
900         map_buf_tmp += (draw_area->x1 - (map_area->x1 - disp_area->x1));
901 
902         /*The map will be indexed from `draw_area->x1` so compensate it.*/
903         map_buf_tmp -= draw_area->x1;
904 
905         for(y = draw_area->y1; y <= draw_area->y2; y++) {
906             for(x = draw_area->x1; x <= draw_area->x2; x++) {
907                 disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa);
908             }
909             disp_buf_tmp += disp_w;
910             map_buf_tmp += map_w;
911         }
912     }
913     /*Masked*/
914     else {
915         /* The mask is relative to the clipped area.
916          * In the cycles below mask will be indexed from `draw_area.x1`
917          * but it corresponds to zero index. So prepare `mask_tmp` accordingly. */
918         const lv_opa_t * mask_tmp = mask - draw_area->x1;
919 
920         map_buf_tmp -= draw_area->x1;
921         for(y = draw_area->y1; y <= draw_area->y2; y++) {
922             for(x = draw_area->x1; x <= draw_area->x2; x++) {
923                 if(mask_tmp[x] == 0) continue;
924                 lv_opa_t opa_tmp = mask_tmp[x] >= LV_OPA_MAX ? opa : ((opa * mask_tmp[x]) >> 8);
925                 disp_buf_tmp[x] = blend_fp(map_buf_tmp[x], disp_buf_tmp[x], opa_tmp);
926             }
927             disp_buf_tmp += disp_w;
928             mask_tmp += draw_area_w;
929             map_buf_tmp += map_w;
930         }
931     }
932 }
933 
color_blend_true_color_additive(lv_color_t fg,lv_color_t bg,lv_opa_t opa)934 static inline lv_color_t color_blend_true_color_additive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
935 {
936 
937     if(opa <= LV_OPA_MIN) return bg;
938 
939     uint32_t tmp;
940 #if LV_COLOR_DEPTH == 1
941     tmp = bg.full + fg.full;
942     fg.full = LV_MATH_MIN(tmp, 1);
943 #else
944     tmp = bg.ch.red + fg.ch.red;
945 #if LV_COLOR_DEPTH == 8
946     fg.ch.red = LV_MATH_MIN(tmp, 7);
947 #elif LV_COLOR_DEPTH == 16
948     fg.ch.red = LV_MATH_MIN(tmp, 31);
949 #elif LV_COLOR_DEPTH == 32
950     fg.ch.red = LV_MATH_MIN(tmp, 255);
951 #endif
952 
953 #if LV_COLOR_DEPTH == 8
954     fg.ch.green = LV_MATH_MIN(tmp, 7);
955 #elif LV_COLOR_DEPTH == 16
956 #if LV_COLOR_16_SWAP == 0
957     tmp = bg.ch.green + fg.ch.green;
958     fg.ch.green = LV_MATH_MIN(tmp, 63);
959 #else
960     tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
961     tmp = LV_MATH_MIN(tmp, 63);
962     fg.ch.green_h = tmp >> 3;
963     fg.ch.green_l = tmp & 0x7;
964 #endif
965 
966 #elif LV_COLOR_DEPTH == 32
967     fg.ch.green = LV_MATH_MIN(tmp, 255);
968 #endif
969 
970     tmp = bg.ch.blue + fg.ch.blue;
971 #if LV_COLOR_DEPTH == 8
972     fg.ch.blue = LV_MATH_MIN(tmp, 4);
973 #elif LV_COLOR_DEPTH == 16
974     fg.ch.blue = LV_MATH_MIN(tmp, 31);
975 #elif LV_COLOR_DEPTH == 32
976     fg.ch.blue = LV_MATH_MIN(tmp, 255);
977 #endif
978 #endif
979 
980     if(opa == LV_OPA_COVER) return fg;
981 
982     return lv_color_mix(fg, bg, opa);
983 }
984 
color_blend_true_color_subtractive(lv_color_t fg,lv_color_t bg,lv_opa_t opa)985 static inline lv_color_t color_blend_true_color_subtractive(lv_color_t fg, lv_color_t bg, lv_opa_t opa)
986 {
987 
988     if(opa <= LV_OPA_MIN) return bg;
989 
990     int32_t tmp;
991     tmp = bg.ch.red - fg.ch.red;
992     fg.ch.red = LV_MATH_MAX(tmp, 0);
993 
994 #if LV_COLOR_16_SWAP == 0
995     tmp = bg.ch.green - fg.ch.green;
996     fg.ch.green = LV_MATH_MAX(tmp, 0);
997 #else
998     tmp = (bg.ch.green_h << 3) + bg.ch.green_l + (fg.ch.green_h << 3) + fg.ch.green_l;
999     tmp = LV_MATH_MAX(tmp, 0);
1000     fg.ch.green_h = tmp >> 3;
1001     fg.ch.green_l = tmp & 0x7;
1002 #endif
1003 
1004     tmp = bg.ch.blue - fg.ch.blue;
1005     fg.ch.blue = LV_MATH_MAX(tmp, 0);
1006 
1007     if(opa == LV_OPA_COVER) return fg;
1008 
1009     return lv_color_mix(fg, bg, opa);
1010 }
1011 #endif
1012