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