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