1 /**
2 * @file lv_draw_sw_gradient.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_draw_sw_gradient_private.h"
10 #if LV_USE_DRAW_SW
11
12 #include "../../misc/lv_types.h"
13 #include "../../osal/lv_os.h"
14 #include "../../misc/lv_math.h"
15
16 /*********************
17 * DEFINES
18 *********************/
19 #define GRAD_CM(r,g,b) lv_color_make(r,g,b)
20 #define GRAD_CONV(t, x) t = x
21
22 #undef ALIGN
23 #if defined(LV_ARCH_64)
24 #define ALIGN(X) (((X) + 7) & ~7)
25 #else
26 #define ALIGN(X) (((X) + 3) & ~3)
27 #endif
28
29 /**********************
30 * TYPEDEFS
31 **********************/
32
33 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
34
35 typedef struct {
36 /* w = (-b(xp, yp) + sqrt(sqr(b(xp, yp)) - 4 * a * c(xp, yp))) / (2 * a) */
37 int32_t x0; /* center of the start circle */
38 int32_t y0; /* center of the start circle */
39 int32_t r0; /* radius of the start circle */
40 int32_t inv_dr; /* 1 / (r1 - r0) */
41 int32_t a4; /* 4 * a */
42 int32_t inv_a4; /* 1 / (4 * a) */
43 int32_t dx;
44 /* b(xp, yp) = xp * bpx + yp * bpy + bc */
45 int32_t bpx;
46 int32_t bpy;
47 int32_t bc;
48 lv_area_t clip_area;
49 lv_grad_t * cgrad; /*256 element cache buffer containing the gradient color map*/
50 } lv_grad_radial_state_t;
51
52 typedef struct {
53 /* w = a * xp + b * yp + c */
54 int32_t a;
55 int32_t b;
56 int32_t c;
57 lv_grad_t * cgrad; /*256 element cache buffer containing the gradient color map*/
58 } lv_grad_linear_state_t;
59
60 typedef struct {
61 /* w = a * xp + b * yp + c */
62 int32_t x0;
63 int32_t y0;
64 int32_t a;
65 int32_t da;
66 int32_t inv_da;
67 lv_grad_t * cgrad; /*256 element cache buffer containing the gradient color map*/
68 } lv_grad_conical_state_t;
69
70 #endif
71
72 /**********************
73 * STATIC PROTOTYPES
74 **********************/
75 typedef lv_result_t (*op_cache_t)(lv_grad_t * c, void * ctx);
76 static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h);
77
78 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
79
80 static inline int32_t extend_w(int32_t w, lv_grad_extend_t extend);
81
82 #endif
83
84 /**********************
85 * STATIC VARIABLE
86 **********************/
87
88 /**********************
89 * STATIC FUNCTIONS
90 **********************/
91
allocate_item(const lv_grad_dsc_t * g,int32_t w,int32_t h)92 static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h)
93 {
94 int32_t size;
95 switch(g->dir) {
96 case LV_GRAD_DIR_HOR:
97 case LV_GRAD_DIR_LINEAR:
98 case LV_GRAD_DIR_RADIAL:
99 case LV_GRAD_DIR_CONICAL:
100 size = w;
101 break;
102 case LV_GRAD_DIR_VER:
103 size = h;
104 break;
105 default:
106 size = 64;
107 }
108
109 size_t req_size = ALIGN(sizeof(lv_grad_t)) + ALIGN(size * sizeof(lv_color_t)) + ALIGN(size * sizeof(lv_opa_t));
110 lv_grad_t * item = lv_malloc(req_size);
111 LV_ASSERT_MALLOC(item);
112 if(item == NULL) return NULL;
113
114 uint8_t * p = (uint8_t *)item;
115 item->color_map = (lv_color_t *)(p + ALIGN(sizeof(*item)));
116 item->opa_map = (lv_opa_t *)(p + ALIGN(sizeof(*item)) + ALIGN(size * sizeof(lv_color_t)));
117 item->size = size;
118 return item;
119 }
120
121 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
122
extend_w(int32_t w,lv_grad_extend_t extend)123 static inline int32_t extend_w(int32_t w, lv_grad_extend_t extend)
124 {
125 if(extend == LV_GRAD_EXTEND_PAD) { /**< Repeat the same color*/
126 return w < 0 ? 0 : LV_MIN(w, 255);
127 }
128 if(extend == LV_GRAD_EXTEND_REPEAT) { /**< Repeat the pattern*/
129 return w & 255;
130 }
131 /*LV_GRAD_EXTEND_REFLECT*/
132 w &= 511;
133 if(w > 255)
134 w ^= 511; /* 511 - w */
135 return w;
136 }
137
138 #endif
139
140 /**********************
141 * FUNCTIONS
142 **********************/
143
lv_gradient_get(const lv_grad_dsc_t * g,int32_t w,int32_t h)144 lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * g, int32_t w, int32_t h)
145 {
146 /* No gradient, no cache */
147 if(g->dir == LV_GRAD_DIR_NONE) return NULL;
148
149 /* Step 1: Search cache for the given key */
150 lv_grad_t * item = allocate_item(g, w, h);
151 if(item == NULL) {
152 LV_LOG_WARN("Failed to allocate item for the gradient");
153 return item;
154 }
155
156 /* Step 3: Fill it with the gradient, as expected */
157 uint32_t i;
158 for(i = 0; i < item->size; i++) {
159 lv_gradient_color_calculate(g, item->size, i, &item->color_map[i], &item->opa_map[i]);
160 }
161 return item;
162 }
163
lv_gradient_color_calculate(const lv_grad_dsc_t * dsc,int32_t range,int32_t frac,lv_grad_color_t * color_out,lv_opa_t * opa_out)164 void LV_ATTRIBUTE_FAST_MEM lv_gradient_color_calculate(const lv_grad_dsc_t * dsc, int32_t range,
165 int32_t frac, lv_grad_color_t * color_out, lv_opa_t * opa_out)
166 {
167 lv_grad_color_t tmp;
168 /*Clip out-of-bounds first*/
169 int32_t min = (dsc->stops[0].frac * range) >> 8;
170 if(frac <= min) {
171 GRAD_CONV(tmp, dsc->stops[0].color);
172 *color_out = tmp;
173 *opa_out = dsc->stops[0].opa;
174 return;
175 }
176
177 int32_t max = (dsc->stops[dsc->stops_count - 1].frac * range) >> 8;
178 if(frac >= max) {
179 GRAD_CONV(tmp, dsc->stops[dsc->stops_count - 1].color);
180 *color_out = tmp;
181 *opa_out = dsc->stops[dsc->stops_count - 1].opa;
182 return;
183 }
184
185 /*Find the 2 closest stop now*/
186 int32_t d = 0;
187 int32_t found_i = 0;
188 for(uint8_t i = 1; i < dsc->stops_count; i++) {
189 int32_t cur = (dsc->stops[i].frac * range) >> 8;
190 if(frac <= cur) {
191 found_i = i;
192 break;
193 }
194 }
195
196 LV_ASSERT(found_i != 0);
197
198 lv_color_t one, two;
199 one = dsc->stops[found_i - 1].color;
200 two = dsc->stops[found_i].color;
201 min = (dsc->stops[found_i - 1].frac * range) >> 8;
202 max = (dsc->stops[found_i].frac * range) >> 8;
203 d = max - min;
204
205 /*Then interpolate*/
206 frac -= min;
207 lv_opa_t mix = (frac * 255) / d;
208 lv_opa_t imix = 255 - mix;
209
210 *color_out = GRAD_CM(LV_UDIV255(two.red * mix + one.red * imix),
211 LV_UDIV255(two.green * mix + one.green * imix),
212 LV_UDIV255(two.blue * mix + one.blue * imix));
213
214 *opa_out = LV_UDIV255(dsc->stops[found_i].opa * mix + dsc->stops[found_i - 1].opa * imix);
215 }
216
lv_gradient_cleanup(lv_grad_t * grad)217 void lv_gradient_cleanup(lv_grad_t * grad)
218 {
219 lv_free(grad);
220 }
221
lv_gradient_init_stops(lv_grad_dsc_t * grad,const lv_color_t colors[],const lv_opa_t opa[],const uint8_t fracs[],int num_stops)222 void lv_gradient_init_stops(lv_grad_dsc_t * grad, const lv_color_t colors[], const lv_opa_t opa[],
223 const uint8_t fracs[], int num_stops)
224 {
225 LV_ASSERT(num_stops <= LV_GRADIENT_MAX_STOPS);
226 grad->stops_count = num_stops;
227 for(int i = 0; i < num_stops; i++) {
228 grad->stops[i].color = colors[i];
229 grad->stops[i].opa = opa != NULL ? opa[i] : LV_OPA_COVER;
230 grad->stops[i].frac = fracs != NULL ? fracs[i] : 255 * i / (num_stops - 1);
231 }
232 }
233
234 #if LV_USE_DRAW_SW_COMPLEX_GRADIENTS
235
236 /*
237 Calculate radial gradient based on the following equation:
238
239 | P - (C1 - C0)w - C0 | = (r1 - r0)w + r0, where
240
241 P: {xp, yp} is the point of interest
242 C0: {x0, y0} is the center of the start circle
243 C1: {x1, y1} is the center of the end circle
244 r0 is the radius of the start circle
245 r1 is the radius of the end circle
246 w is the unknown variable
247 || is the length of the vector
248
249 The above equation can be rewritten as:
250
251 ((r1-r0)^2 - (x1-x0)^2 - (y1-y0)^2) * w^2 + 2*((xp-x0)*(x1-x0) + (yp-y0)*(y1-y0)) * w + (-(xp-x0)^2 - (yp-y0)^) = 0
252
253 The roots of the quadratical equation can be obtained using the well-known formula (-b +- sqrt(b^2 - 4ac)) / 2a
254 We only need the more positive root.
255
256 Let's denote
257 dx = x1 - x0
258 dy = y1 - y0
259 dr = r1 - r0
260
261 Thus:
262
263 w = (-b(xp, yp) + sqrt(sqr(b(xp, yp)) - 4 * a * c(xp, yp))) / (2 * a), where
264
265 b(xp, yp) = 2dx * xp + 2dy * yp + 2(r0 * dr - x0 * dx - y0 * dy)
266 c(xp, yp) = r0^2 - (xp - x0)^2 - (yp - y0)^2
267
268 Rewrite b(xp, yp) as:
269
270 b(xp, yp) = xp * bpx + yp * bpy + bc, where
271
272 bpx = 2dx
273 bpy = 2dy
274 bc = 2(r0 * dr - x0 * dx - y0 * dy)
275
276 We can pre-calculate the constants, because they do not depend on the pixel coordinates.
277
278 */
279
lv_gradient_radial_setup(lv_grad_dsc_t * dsc,const lv_area_t * coords)280 void lv_gradient_radial_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords)
281 {
282 lv_point_t start = dsc->params.radial.focal;
283 lv_point_t end = dsc->params.radial.end;
284 lv_point_t start_extent = dsc->params.radial.focal_extent;
285 lv_point_t end_extent = dsc->params.radial.end_extent;
286 lv_grad_radial_state_t * state = lv_malloc(sizeof(lv_grad_radial_state_t));
287 dsc->state = state;
288
289 /* Convert from percentage coordinates */
290 int32_t wdt = lv_area_get_width(coords);
291 int32_t hgt = lv_area_get_height(coords);
292
293 start.x = lv_pct_to_px(start.x, wdt);
294 end.x = lv_pct_to_px(end.x, wdt);
295 start_extent.x = lv_pct_to_px(start_extent.x, wdt);
296 end_extent.x = lv_pct_to_px(end_extent.x, wdt);
297 start.y = lv_pct_to_px(start.y, hgt);
298 end.y = lv_pct_to_px(end.y, hgt);
299 start_extent.y = lv_pct_to_px(start_extent.y, hgt);
300 end_extent.y = lv_pct_to_px(end_extent.y, hgt);
301
302 /* Calculate radii */
303 int16_t r_start = lv_sqrt32(lv_sqr(start_extent.x - start.x) + lv_sqr(start_extent.y - start.y));
304 int16_t r_end = lv_sqrt32(lv_sqr(end_extent.x - end.x) + lv_sqr(end_extent.y - end.y));
305 LV_ASSERT(r_end != 0);
306
307 /* Create gradient color map */
308 state->cgrad = lv_gradient_get(dsc, 256, 0);
309
310 state->x0 = start.x;
311 state->y0 = start.y;
312 state->r0 = r_start;
313 int32_t dr = r_end - r_start;
314 if(end.x == start.x && end.y == start.y) {
315 LV_ASSERT(dr != 0);
316 state->a4 = lv_sqr(dr) << 2;
317 state->bpx = 0;
318 state->bpy = 0;
319 state->bc = (state->r0 * dr) << 1;
320 state->dx = 0;
321 state->inv_dr = (1 << (8 + 16)) / dr;
322 }
323 else {
324 int32_t dx = end.x - start.x;
325 int32_t dy = end.y - start.y;
326 state->dx = dx; /* needed for incremental calculation */
327 state->a4 = (lv_sqr(dr) - lv_sqr(dx) - lv_sqr(dy)) << 2;
328 /* b(xp, yp) = xp * bpx + yp * bpy + bc */
329 state->bpx = dx << 1;
330 state->bpy = dy << 1;
331 state->bc = (state->r0 * dr - state->x0 * dx - state->y0 * dy) << 1;
332 }
333 state->inv_a4 = state->a4 != 0 ? (1 << (13 + 16)) / state->a4 : 0;
334 /* check for possible clipping */
335 if(dsc->extend == LV_GRAD_EXTEND_PAD &&
336 /* if extend mode is 'pad', then we can clip to the end circle's bounding box, if the start circle is entirely within the end circle */
337 (lv_sqr(start.x - end.x) + lv_sqr(start.y - end.y) < lv_sqr(r_end - r_start))) {
338 if(r_end > r_start) {
339 lv_area_set(&state->clip_area, end.x - r_end, end.y - r_end, end.x + r_end, end.y + r_end);
340 }
341 else {
342 lv_area_set(&state->clip_area, start.x - r_start, start.y - r_start, start.x + r_start, start.y + r_start);
343 }
344 }
345 else {
346 state->clip_area.x1 = -0x7fffffff;
347 }
348 }
349
lv_gradient_radial_cleanup(lv_grad_dsc_t * dsc)350 void lv_gradient_radial_cleanup(lv_grad_dsc_t * dsc)
351 {
352 lv_grad_radial_state_t * state = dsc->state;
353 if(state == NULL)
354 return;
355 if(state->cgrad)
356 lv_gradient_cleanup(state->cgrad);
357 lv_free(state);
358 }
359
lv_gradient_radial_get_line(lv_grad_dsc_t * dsc,int32_t xp,int32_t yp,int32_t width,lv_grad_t * result)360 void LV_ATTRIBUTE_FAST_MEM lv_gradient_radial_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp,
361 int32_t width, lv_grad_t * result)
362 {
363 lv_grad_radial_state_t * state = (lv_grad_radial_state_t *)dsc->state;
364 lv_color_t * buf = result->color_map;
365 lv_opa_t * opa = result->opa_map;
366 lv_grad_t * grad = state->cgrad;
367
368 int32_t w; /* the result: this is an offset into the 256 element gradient color table */
369 int32_t b, db, c, dc;
370
371 /* check for possible clipping */
372 if(state->clip_area.x1 != -0x7fffffff) {
373 /* fill line with end color for pixels outside the clipped region */
374 lv_color_t * _buf = buf;
375 lv_opa_t * _opa = opa;
376 lv_color_t _c = grad->color_map[255];
377 lv_opa_t _o = grad->opa_map[255];
378 int32_t _w = width;
379 for(; _w > 0; _w--) {
380 *_buf++ = _c;
381 *_opa++ = _o;
382 }
383 /* is this line fully outside the clip area? */
384 if(yp < state->clip_area.y1 ||
385 yp >= state->clip_area.y2 ||
386 xp >= state->clip_area.x2 ||
387 xp + width < state->clip_area.x1) {
388 return;
389 }
390 else { /* not fully outside: clip line to the bounding box */
391 int32_t _x1 = LV_MAX(xp, state->clip_area.x1);
392 int32_t _x2 = LV_MIN(xp + width, state->clip_area.x2);
393 buf += _x1 - xp;
394 opa += _x1 - xp;
395 xp = _x1;
396 width = _x2 - _x1;
397 }
398 }
399
400 b = xp * state->bpx + yp * state->bpy + state->bc;
401 c = lv_sqr(state->r0) - lv_sqr(xp - state->x0) - lv_sqr(yp - state->y0);
402 /* We can save some calculations by using the previous values of b and c */
403 db = state->dx << 1;
404 dc = ((xp - state->x0) << 1) + 1;
405
406 if(state->a4 == 0) { /* not a quadratic equation: solve linear equation: w = -c/b */
407 for(; width > 0; width--) {
408 w = extend_w(b == 0 ? 0 : -(c << 8) / b, dsc->extend);
409 *buf++ = grad->color_map[w];
410 *opa++ = grad->opa_map[w];
411 b += db;
412 c -= dc;
413 dc += 2;
414 }
415 }
416 else { /* solve quadratical equation */
417 if(state->bpx ||
418 state->bpy) { /* general case (circles are not concentric): w = (-b + sqrt(b^2 - 4ac))/2a (we only need the more positive root)*/
419 int32_t a4 = state->a4 >> 4;
420 for(; width > 0; width--) {
421 int32_t det = lv_sqr(b >> 4) - (a4 * (c >> 4)); /* b^2 shifted down by 2*4=8, 4ac shifted down by 8 */
422 /* check determinant: if negative, then there is no solution: use starting color */
423 w = det < 0 ? 0 : extend_w(((lv_sqrt32(det) - (b >> 4)) * state->inv_a4) >> 16,
424 dsc->extend); /* square root shifted down by 4 (includes *256 to set output range) */
425 *buf++ = grad->color_map[w];
426 *opa++ = grad->opa_map[w];
427 b += db;
428 c -= dc;
429 dc += 2;
430 }
431 }
432 else { /* special case: concentric circles: w = (sqrt((xp-x0)^2 + (yx-y0)^2)-r0)/(r1-r0) */
433 c = lv_sqr(xp - state->x0) + lv_sqr(yp - state->y0);
434 for(; width > 0; width--) {
435 w = extend_w((((lv_sqrt32(c) - state->r0)) * state->inv_dr) >> 16, dsc->extend);
436 *buf++ = grad->color_map[w];
437 *opa++ = grad->opa_map[w];
438 c += dc;
439 dc += 2;
440 }
441 }
442 }
443 }
444
445 /*
446 Calculate linear gradient based on the following equation:
447
448 w = ((P - C0) x (C1 - C0)) / | C1 - C0 |^2, where
449
450 P: {xp, yp} is the point of interest
451 C0: {x0, y0} is the start point of the gradient vector
452 C1: {x1, y1} is the end point of the gradient vector
453 w is the unknown variable
454
455 || is the length of the vector
456 x is a dot product
457
458 The above equation can be rewritten as:
459
460 w = xp * (dx / (dx^2 + dy^2)) + yp * (dy / (dx^2 + dy^2)) - (x0 * dx + y0 * dy) / (dx^2 + dy^2), where
461
462 dx = x1 - x0
463 dy = y1 - y0
464
465 We can pre-calculate the constants, because they do not depend on the pixel coordinates.
466
467 */
468
lv_gradient_linear_setup(lv_grad_dsc_t * dsc,const lv_area_t * coords)469 void lv_gradient_linear_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords)
470 {
471 lv_point_t start = dsc->params.linear.start;
472 lv_point_t end = dsc->params.linear.end;
473 lv_grad_linear_state_t * state = lv_malloc(sizeof(lv_grad_linear_state_t));
474 dsc->state = state;
475
476 /* Create gradient color map */
477 state->cgrad = lv_gradient_get(dsc, 256, 0);
478
479 /* Convert from percentage coordinates */
480 int32_t wdt = lv_area_get_width(coords);
481 int32_t hgt = lv_area_get_height(coords);
482
483 start.x = lv_pct_to_px(start.x, wdt);
484 end.x = lv_pct_to_px(end.x, wdt);
485 start.y = lv_pct_to_px(start.y, hgt);
486 end.y = lv_pct_to_px(end.y, hgt);
487
488 /* Precalculate constants */
489 int32_t dx = end.x - start.x;
490 int32_t dy = end.y - start.y;
491
492 int32_t l2 = lv_sqr(dx) + lv_sqr(dy);
493 state->a = (dx << 16) / l2;
494 state->b = (dy << 16) / l2;
495 state->c = ((start.x * dx + start.y * dy) << 16) / l2;
496 }
497
lv_gradient_linear_cleanup(lv_grad_dsc_t * dsc)498 void lv_gradient_linear_cleanup(lv_grad_dsc_t * dsc)
499 {
500 lv_grad_linear_state_t * state = dsc->state;
501 if(state == NULL)
502 return;
503 if(state->cgrad)
504 lv_free(state->cgrad);
505 lv_free(state);
506 }
507
lv_gradient_linear_get_line(lv_grad_dsc_t * dsc,int32_t xp,int32_t yp,int32_t width,lv_grad_t * result)508 void LV_ATTRIBUTE_FAST_MEM lv_gradient_linear_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp,
509 int32_t width, lv_grad_t * result)
510 {
511 lv_grad_linear_state_t * state = (lv_grad_linear_state_t *)dsc->state;
512 lv_color_t * buf = result->color_map;
513 lv_opa_t * opa = result->opa_map;
514 lv_grad_t * grad = state->cgrad;
515
516 int32_t w; /* the result: this is an offset into the 256 element gradient color table */
517 int32_t x, d;
518
519 x = xp * state->a + yp * state->b - state->c;
520 d = state->a;
521
522 for(; width > 0; width--) {
523 w = extend_w(x >> 8, dsc->extend);
524 *buf++ = grad->color_map[w];
525 *opa++ = grad->opa_map[w];
526 x += d;
527 }
528 }
529
530 /*
531 Calculate conical gradient based on the following equation:
532
533 w = (atan((yp - y0)/(xp - x0)) - alpha) / (beta - alpha), where
534
535 P: {xp, yp} is the point of interest
536 C0: {x0, y0} is the center of the gradient
537 alpha is the start angle
538 beta is the end angle
539 w is the unknown variable
540 */
541
lv_gradient_conical_setup(lv_grad_dsc_t * dsc,const lv_area_t * coords)542 void lv_gradient_conical_setup(lv_grad_dsc_t * dsc, const lv_area_t * coords)
543 {
544 lv_point_t c0 = dsc->params.conical.center;
545 int32_t alpha = dsc->params.conical.start_angle % 360;
546 int32_t beta = dsc->params.conical.end_angle % 360;
547 lv_grad_conical_state_t * state = lv_malloc(sizeof(lv_grad_conical_state_t));
548 dsc->state = state;
549
550 /* Create gradient color map */
551 state->cgrad = lv_gradient_get(dsc, 256, 0);
552
553 /* Convert from percentage coordinates */
554 int32_t wdt = lv_area_get_width(coords);
555 int32_t hgt = lv_area_get_height(coords);
556
557 c0.x = lv_pct_to_px(c0.x, wdt);
558 c0.y = lv_pct_to_px(c0.y, hgt);
559
560 /* Precalculate constants */
561 if(beta <= alpha)
562 beta += 360;
563 state->x0 = c0.x;
564 state->y0 = c0.y;
565 state->a = alpha;
566 state->da = beta - alpha;
567 state->inv_da = (1 << 16) / (beta - alpha);
568 }
569
lv_gradient_conical_cleanup(lv_grad_dsc_t * dsc)570 void lv_gradient_conical_cleanup(lv_grad_dsc_t * dsc)
571 {
572 lv_grad_conical_state_t * state = dsc->state;
573 if(state == NULL)
574 return;
575 if(state->cgrad)
576 lv_free(state->cgrad);
577 lv_free(state);
578 }
579
lv_gradient_conical_get_line(lv_grad_dsc_t * dsc,int32_t xp,int32_t yp,int32_t width,lv_grad_t * result)580 void LV_ATTRIBUTE_FAST_MEM lv_gradient_conical_get_line(lv_grad_dsc_t * dsc, int32_t xp, int32_t yp,
581 int32_t width, lv_grad_t * result)
582 {
583 lv_grad_conical_state_t * state = (lv_grad_conical_state_t *)dsc->state;
584 lv_color_t * buf = result->color_map;
585 lv_opa_t * opa = result->opa_map;
586 lv_grad_t * grad = state->cgrad;
587
588 int32_t w; /* the result: this is an offset into the 256 element gradient color table */
589 int32_t dx = xp - state->x0;
590 int32_t dy = yp - state->y0;
591
592 if(dy == 0) { /* we will eventually go through the center of the conical: need an extra test in the loop to avoid both dx and dy being zero in atan2 */
593 for(; width > 0; width--) {
594 if(dx == 0) {
595 w = 0;
596 }
597 else {
598 int32_t d = lv_atan2(dy, dx) - state->a;
599 if(d < 0)
600 d += 360;
601 w = extend_w((d * state->inv_da) >> 8, dsc->extend);
602 }
603 *buf++ = grad->color_map[w];
604 *opa++ = grad->opa_map[w];
605 dx++;
606 }
607 }
608 else {
609 for(; width > 0; width--) {
610 int32_t d = lv_atan2(dy, dx) - state->a;
611 if(d < 0)
612 d += 360;
613 w = extend_w((d * state->inv_da) >> 8, dsc->extend);
614 *buf++ = grad->color_map[w];
615 *opa++ = grad->opa_map[w];
616 dx++;
617 }
618 }
619 }
620
lv_grad_linear_init(lv_grad_dsc_t * dsc,int32_t from_x,int32_t from_y,int32_t to_x,int32_t to_y,lv_grad_extend_t extend)621 void lv_grad_linear_init(lv_grad_dsc_t * dsc, int32_t from_x, int32_t from_y, int32_t to_x, int32_t to_y,
622 lv_grad_extend_t extend)
623 {
624 dsc->dir = LV_GRAD_DIR_LINEAR;
625 dsc->params.linear.start.x = from_x;
626 dsc->params.linear.start.y = from_y;
627 dsc->params.linear.end.x = to_x;
628 dsc->params.linear.end.y = to_y;
629 dsc->extend = extend;
630 }
631
lv_grad_radial_init(lv_grad_dsc_t * dsc,int32_t center_x,int32_t center_y,int32_t to_x,int32_t to_y,lv_grad_extend_t extend)632 void lv_grad_radial_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t to_x, int32_t to_y,
633 lv_grad_extend_t extend)
634 {
635 dsc->dir = LV_GRAD_DIR_RADIAL;
636 dsc->params.radial.focal.x = center_x;
637 dsc->params.radial.focal.y = center_y;
638 dsc->params.radial.focal_extent.x = center_x;
639 dsc->params.radial.focal_extent.y = center_y;
640 dsc->params.radial.end.x = center_x;
641 dsc->params.radial.end.y = center_y;
642 dsc->params.radial.end_extent.x = to_x;
643 dsc->params.radial.end_extent.y = to_y;
644 dsc->extend = extend;
645 }
646
lv_grad_conical_init(lv_grad_dsc_t * dsc,int32_t center_x,int32_t center_y,int32_t start_angle,int32_t end_angle,lv_grad_extend_t extend)647 void lv_grad_conical_init(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t start_angle,
648 int32_t end_angle, lv_grad_extend_t extend)
649 {
650 dsc->dir = LV_GRAD_DIR_CONICAL;
651 dsc->params.conical.center.x = center_x;
652 dsc->params.conical.center.y = center_y;
653 dsc->params.conical.start_angle = start_angle;
654 dsc->params.conical.end_angle = end_angle;
655 dsc->extend = extend;
656 }
657
lv_grad_radial_set_focal(lv_grad_dsc_t * dsc,int32_t center_x,int32_t center_y,int32_t radius)658 void lv_grad_radial_set_focal(lv_grad_dsc_t * dsc, int32_t center_x, int32_t center_y, int32_t radius)
659 {
660 dsc->params.radial.focal.x = center_x;
661 dsc->params.radial.focal.y = center_y;
662 dsc->params.radial.focal_extent.x = center_x + radius;
663 dsc->params.radial.focal_extent.y = center_y;
664 }
665
666 #endif /* LV_USE_DRAW_SW_COMPLEX_GRADIENTS */
667
668 #endif /*LV_USE_DRAW_SW*/
669