1 /**
2  * @file lv_draw_sw_fill.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../misc/lv_area_private.h"
10 #include "lv_draw_sw_mask_private.h"
11 #include "../lv_draw_private.h"
12 #include "lv_draw_sw.h"
13 #if LV_USE_DRAW_SW
14 
15 #include "blend/lv_draw_sw_blend_private.h"
16 #include "lv_draw_sw_gradient_private.h"
17 #include "../../misc/lv_math.h"
18 #include "../../misc/lv_text_ap.h"
19 #include "../../core/lv_refr.h"
20 #include "../../misc/lv_assert.h"
21 #include "../../stdlib/lv_string.h"
22 #include "../lv_draw_mask.h"
23 
24 /*********************
25  *      DEFINES
26  *********************/
27 
28 /**********************
29  *      TYPEDEFS
30  **********************/
31 
32 /**********************
33  *  STATIC PROTOTYPES
34  **********************/
35 
36 /**********************
37  *  STATIC VARIABLES
38  **********************/
39 
40 /**********************
41  *      MACROS
42  **********************/
43 
44 /**********************
45  *   GLOBAL FUNCTIONS
46  **********************/
47 
lv_draw_sw_fill(lv_draw_unit_t * draw_unit,lv_draw_fill_dsc_t * dsc,const lv_area_t * coords)48 void lv_draw_sw_fill(lv_draw_unit_t * draw_unit, lv_draw_fill_dsc_t * dsc, const lv_area_t * coords)
49 {
50     if(dsc->opa <= LV_OPA_MIN) return;
51 
52     lv_area_t bg_coords;
53     lv_area_copy(&bg_coords, coords);
54 
55     lv_area_t clipped_coords;
56     if(!lv_area_intersect(&clipped_coords, &bg_coords, draw_unit->clip_area)) return;
57 
58     lv_grad_dir_t grad_dir = dsc->grad.dir;
59     lv_color_t bg_color    = grad_dir == LV_GRAD_DIR_NONE ? dsc->color : dsc->grad.stops[0].color;
60 
61     lv_draw_sw_blend_dsc_t blend_dsc = {0};
62     blend_dsc.color = bg_color;
63 
64     /*Most simple case: just a plain rectangle*/
65     if(dsc->radius == 0 && (grad_dir == LV_GRAD_DIR_NONE)) {
66         blend_dsc.blend_area = &bg_coords;
67         blend_dsc.opa = dsc->opa;
68         lv_draw_sw_blend(draw_unit, &blend_dsc);
69         return;
70     }
71 
72     /*Complex case: there is gradient, mask, or radius*/
73 #if LV_DRAW_SW_COMPLEX == 0
74     LV_LOG_WARN("Can't draw complex rectangle because LV_DRAW_SW_COMPLEX = 0");
75 #else
76     lv_opa_t opa = dsc->opa >= LV_OPA_MAX ? LV_OPA_COVER : dsc->opa;
77 
78     /*Get the real radius. Can't be larger than the half of the shortest side */
79     int32_t coords_bg_w = lv_area_get_width(&bg_coords);
80     int32_t coords_bg_h = lv_area_get_height(&bg_coords);
81     int32_t short_side = LV_MIN(coords_bg_w, coords_bg_h);
82     int32_t rout = LV_MIN(dsc->radius, short_side >> 1);
83 
84     /*Add a radius mask if there is a radius*/
85     int32_t clipped_w = lv_area_get_width(&clipped_coords);
86     lv_opa_t * mask_buf = NULL;
87     lv_draw_sw_mask_radius_param_t mask_rout_param;
88     void * mask_list[2] = {NULL, NULL};
89     if(rout > 0) {
90         mask_buf = lv_malloc(clipped_w);
91         lv_draw_sw_mask_radius_init(&mask_rout_param, &bg_coords, rout, false);
92         mask_list[0] = &mask_rout_param;
93     }
94 
95     int32_t h;
96 
97     lv_area_t blend_area;
98     blend_area.x1 = clipped_coords.x1;
99     blend_area.x2 = clipped_coords.x2;
100 
101     blend_dsc.mask_buf = mask_buf;
102     blend_dsc.blend_area = &blend_area;
103     blend_dsc.mask_area = &blend_area;
104     blend_dsc.opa = LV_OPA_COVER;
105 
106     /*Get gradient if appropriate*/
107     lv_grad_t * grad = lv_gradient_get(&dsc->grad, coords_bg_w, coords_bg_h);
108     lv_opa_t * grad_opa_map = NULL;
109     bool transp = false;
110     if(grad && grad_dir >= LV_GRAD_DIR_HOR) {
111         blend_dsc.src_area = &blend_area;
112         blend_dsc.src_buf = grad->color_map + clipped_coords.x1 - bg_coords.x1;
113         uint32_t s;
114         for(s = 0; s < dsc->grad.stops_count; s++) {
115             if(dsc->grad.stops[s].opa != LV_OPA_COVER) {
116                 transp = true;
117                 break;
118             }
119         }
120         if(grad_dir == LV_GRAD_DIR_HOR) {
121             if(transp) grad_opa_map = grad->opa_map + clipped_coords.x1 - bg_coords.x1;
122         }
123         blend_dsc.src_color_format = LV_COLOR_FORMAT_RGB888;
124     }
125 
126 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
127 
128     /*Prepare complex gradient*/
129     if(grad_dir >= LV_GRAD_DIR_LINEAR) {
130         LV_ASSERT_NULL(grad);
131         switch(grad_dir) {
132             case LV_GRAD_DIR_LINEAR:
133                 lv_gradient_linear_setup(&dsc->grad, coords);
134                 break;
135             case LV_GRAD_DIR_RADIAL:
136                 lv_gradient_radial_setup(&dsc->grad, coords);
137                 break;
138             case LV_GRAD_DIR_CONICAL:
139                 lv_gradient_conical_setup(&dsc->grad, coords);
140                 break;
141             default:
142                 LV_LOG_WARN("Gradient type is not supported");
143                 return;
144         }
145         blend_dsc.src_area = &blend_area;
146         /* For complex gradients we reuse the color map buffer for the pixel data */
147         blend_dsc.src_buf = grad->color_map;
148         grad_opa_map = grad->opa_map;
149     }
150 #endif
151 
152     /* Draw the top of the rectangle line by line and mirror it to the bottom. */
153     for(h = 0; h < rout; h++) {
154         int32_t top_y = bg_coords.y1 + h;
155         int32_t bottom_y = bg_coords.y2 - h;
156         if(top_y < clipped_coords.y1 && bottom_y > clipped_coords.y2) continue;   /*This line is clipped now*/
157 
158         bool preblend = false;
159 
160         /* Initialize the mask to opa instead of 0xFF and blend with LV_OPA_COVER.
161          * It saves calculating the final opa in lv_draw_sw_blend*/
162         lv_memset(mask_buf, opa, clipped_w);
163         blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, top_y, clipped_w);
164         if(blend_dsc.mask_res == LV_DRAW_SW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
165 
166         bool hor_grad_processed = false;
167         if(top_y >= clipped_coords.y1) {
168             blend_area.y1 = top_y;
169             blend_area.y2 = top_y;
170 
171             switch(grad_dir) {
172                 case LV_GRAD_DIR_VER:
173                     LV_ASSERT_NULL(grad);
174                     blend_dsc.color = grad->color_map[top_y - bg_coords.y1];
175                     blend_dsc.opa = grad->opa_map[top_y - bg_coords.y1];
176                     break;
177                 case LV_GRAD_DIR_HOR:
178                     hor_grad_processed = true;
179                     preblend = grad_opa_map != NULL;
180                     break;
181 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
182                 case LV_GRAD_DIR_LINEAR:
183                     lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad);
184                     preblend = true;
185                     break;
186                 case LV_GRAD_DIR_RADIAL:
187                     lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad);
188                     preblend = true;
189                     break;
190                 case LV_GRAD_DIR_CONICAL:
191                     lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, top_y - bg_coords.y1, coords_bg_w, grad);
192                     preblend = true;
193                     break;
194 #endif
195                 default:
196                     break;
197             }
198             /* pre-blend the mask */
199             if(preblend) {
200                 int32_t i;
201                 for(i = 0; i < clipped_w; i++) {
202                     if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8;
203                 }
204                 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
205             }
206             lv_draw_sw_blend(draw_unit, &blend_dsc);
207         }
208 
209         if(bottom_y <= clipped_coords.y2) {
210             blend_area.y1 = bottom_y;
211             blend_area.y2 = bottom_y;
212 
213             switch(grad_dir) {
214                 case LV_GRAD_DIR_VER:
215                     LV_ASSERT_NULL(grad);
216                     blend_dsc.color = grad->color_map[bottom_y - bg_coords.y1];
217                     blend_dsc.opa = grad->opa_map[bottom_y - bg_coords.y1];
218                     break;
219                 case LV_GRAD_DIR_HOR:
220                     preblend = !hor_grad_processed && (grad_opa_map != NULL);
221                     break;
222 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
223                 case LV_GRAD_DIR_LINEAR:
224                     lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad);
225                     preblend = true;
226                     break;
227                 case LV_GRAD_DIR_RADIAL:
228                     lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad);
229                     preblend = true;
230                     break;
231                 case LV_GRAD_DIR_CONICAL:
232                     lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, bottom_y - bg_coords.y1, coords_bg_w, grad);
233                     preblend = true;
234                     break;
235 #endif
236                 default:
237                     break;
238             }
239             /* pre-blend the mask */
240             if(preblend) {
241                 int32_t i;
242                 if(grad_dir >= LV_GRAD_DIR_LINEAR) {
243                     /*Need to generate the mask again, because we have mixed in the upper part of the gradient*/
244                     lv_memset(mask_buf, opa, clipped_w);
245                     blend_dsc.mask_res = lv_draw_sw_mask_apply(mask_list, mask_buf, blend_area.x1, top_y, clipped_w);
246                     if(blend_dsc.mask_res == LV_DRAW_SW_MASK_RES_FULL_COVER) blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
247                 }
248                 for(i = 0; i < clipped_w; i++) {
249                     if(grad_opa_map[i] < LV_OPA_MAX) mask_buf[i] = (mask_buf[i] * grad_opa_map[i]) >> 8;
250                 }
251                 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
252             }
253             lv_draw_sw_blend(draw_unit, &blend_dsc);
254         }
255     }
256 
257     /* Draw the center of the rectangle.*/
258 
259     /*If no gradient, the center is a simple rectangle*/
260     if(grad_dir == LV_GRAD_DIR_NONE) {
261         blend_area.y1 = bg_coords.y1 + rout;
262         blend_area.y2 = bg_coords.y2 - rout;
263         blend_dsc.opa = opa;
264         blend_dsc.mask_buf = NULL;
265         lv_draw_sw_blend(draw_unit, &blend_dsc);
266     }
267     /*With gradient draw line by line*/
268     else {
269         blend_dsc.opa = opa;
270         switch(grad_dir) {
271             case LV_GRAD_DIR_VER:
272                 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_FULL_COVER;
273                 break;
274             case LV_GRAD_DIR_HOR:
275                 blend_dsc.mask_res = LV_DRAW_SW_MASK_RES_CHANGED;
276                 blend_dsc.mask_buf = grad_opa_map;
277                 break;
278             case LV_GRAD_DIR_LINEAR:
279             case LV_GRAD_DIR_RADIAL:
280             case LV_GRAD_DIR_CONICAL:
281                 blend_dsc.mask_res = transp ? LV_DRAW_SW_MASK_RES_CHANGED : LV_DRAW_SW_MASK_RES_FULL_COVER;
282                 blend_dsc.mask_buf = grad_opa_map;
283                 break;
284             default:
285                 break;
286         }
287 
288         int32_t h_start = LV_MAX(bg_coords.y1 + rout, clipped_coords.y1);
289         int32_t h_end = LV_MIN(bg_coords.y2 - rout, clipped_coords.y2);
290         for(h = h_start; h <= h_end; h++) {
291             blend_area.y1 = h;
292             blend_area.y2 = h;
293 
294             switch(grad_dir) {
295                 case LV_GRAD_DIR_VER:
296                     LV_ASSERT_NULL(grad);
297                     blend_dsc.color = grad->color_map[h - bg_coords.y1];
298                     if(opa >= LV_OPA_MAX) blend_dsc.opa = grad->opa_map[h - bg_coords.y1];
299                     else blend_dsc.opa = LV_OPA_MIX2(grad->opa_map[h - bg_coords.y1], opa);
300                     break;
301 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
302                 case LV_GRAD_DIR_LINEAR:
303                     lv_gradient_linear_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad);
304                     break;
305                 case LV_GRAD_DIR_RADIAL:
306                     lv_gradient_radial_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad);
307                     break;
308                 case LV_GRAD_DIR_CONICAL:
309                     lv_gradient_conical_get_line(&dsc->grad, clipped_coords.x1 - bg_coords.x1, h - bg_coords.y1, coords_bg_w, grad);
310                     break;
311 #endif
312                 default:
313                     break;
314             }
315             lv_draw_sw_blend(draw_unit, &blend_dsc);
316         }
317     }
318 
319     if(mask_buf) {
320         lv_free(mask_buf);
321         lv_draw_sw_mask_free_param(&mask_rout_param);
322     }
323     if(grad) {
324         lv_gradient_cleanup(grad);
325     }
326 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
327     if(grad_dir >= LV_GRAD_DIR_LINEAR) {
328         switch(grad_dir) {
329             case LV_GRAD_DIR_LINEAR:
330                 lv_gradient_linear_cleanup(&dsc->grad);
331                 break;
332             case LV_GRAD_DIR_RADIAL:
333                 lv_gradient_radial_cleanup(&dsc->grad);
334                 break;
335             case LV_GRAD_DIR_CONICAL:
336                 lv_gradient_conical_cleanup(&dsc->grad);
337                 break;
338             default:
339                 break;
340         }
341     }
342 #endif
343 
344 #endif
345 }
346 
347 #endif /*LV_USE_DRAW_SW*/
348