1 /**
2  * @file lv_draw_sdl_arc.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../lv_conf_internal.h"
10 
11 #if LV_USE_GPU_SDL
12 
13 #include "lv_draw_sdl.h"
14 #include "lv_draw_sdl_utils.h"
15 #include "lv_draw_sdl_texture_cache.h"
16 #include "lv_draw_sdl_composite.h"
17 
18 /*********************
19  *      DEFINES
20  *********************/
21 
22 /**********************
23  *      TYPEDEFS
24  **********************/
25 
26 /**********************
27  *  STATIC PROTOTYPES
28  **********************/
29 
30 /**********************
31  *  STATIC VARIABLES
32  **********************/
33 
34 /**********************
35  *      MACROS
36  **********************/
37 
38 static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
39                        const int16_t * caps);
40 
41 static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
42                          lv_area_t * out);
43 
44 /**********************
45  *   GLOBAL FUNCTIONS
46  **********************/
lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx,const lv_draw_arc_dsc_t * dsc,const lv_point_t * center,uint16_t radius,uint16_t start_angle,uint16_t end_angle)47 void lv_draw_sdl_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
48                           uint16_t radius, uint16_t start_angle, uint16_t end_angle)
49 {
50     lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx;
51 
52     lv_area_t area_out;
53     area_out.x1 = center->x - radius;
54     area_out.y1 = center->y - radius;
55     area_out.x2 = center->x + radius - 1;  /*-1 because the center already belongs to the left/bottom part*/
56     area_out.y2 = center->y + radius - 1;
57 
58     lv_area_t draw_area;
59     if(!_lv_area_intersect(&draw_area, &area_out, draw_ctx->clip_area)) {
60         return;
61     }
62 
63     lv_area_t area_in;
64     lv_area_copy(&area_in, &area_out);
65     area_in.x1 += dsc->width;
66     area_in.y1 += dsc->width;
67     area_in.x2 -= dsc->width;
68     area_in.y2 -= dsc->width;
69 
70     while(start_angle >= 360) start_angle -= 360;
71     while(end_angle >= 360) end_angle -= 360;
72 
73     int16_t mask_ids[3] = {LV_MASK_ID_INV, LV_MASK_ID_INV, LV_MASK_ID_INV}, mask_ids_count = 1;
74     int16_t cap_ids[2] = {LV_MASK_ID_INV, LV_MASK_ID_INV};
75 
76     lv_draw_mask_radius_param_t mask_out_param;
77     lv_draw_mask_radius_init(&mask_out_param, &area_out, LV_RADIUS_CIRCLE, false);
78     mask_ids[0] = lv_draw_mask_add(&mask_out_param, NULL);
79 
80     lv_draw_mask_radius_param_t mask_in_param;
81     if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) {
82         lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true);
83         mask_ids[1] = lv_draw_mask_add(&mask_in_param, NULL);
84         mask_ids_count++;
85     }
86 
87     lv_draw_mask_angle_param_t mask_angle_param;
88     if((start_angle - end_angle) % 360) {
89         lv_draw_mask_angle_init(&mask_angle_param, center->x, center->y, start_angle, end_angle);
90         mask_ids[2] = lv_draw_mask_add(&mask_angle_param, NULL);
91         mask_ids_count++;
92     }
93 
94     lv_draw_mask_radius_param_t cap_start_param, cap_end_param;
95     if(mask_ids_count == 3 && dsc->rounded) {
96         lv_area_t start_area, end_area;
97         get_cap_area((int16_t) start_angle, dsc->width, radius, center, &start_area);
98         get_cap_area((int16_t) end_angle, dsc->width, radius, center, &end_area);
99         lv_draw_mask_radius_init(&cap_start_param, &start_area, dsc->width / 2, false);
100         cap_ids[0] = lv_draw_mask_add(&cap_start_param, NULL);
101         lv_draw_mask_radius_init(&cap_end_param, &end_area, dsc->width / 2, false);
102         cap_ids[1] = lv_draw_mask_add(&cap_end_param, NULL);
103     }
104 
105     lv_coord_t w = lv_area_get_width(&draw_area), h = lv_area_get_height(&draw_area);
106     bool texture_cached = false;
107     SDL_Texture * texture = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, w, h,
108                                                                  &texture_cached);
109     SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
110     dump_masks(texture, &draw_area, mask_ids, mask_ids_count, cap_ids[0] != LV_MASK_ID_INV ? cap_ids : NULL);
111 
112     lv_draw_mask_remove_id(mask_ids[0]);
113     lv_draw_mask_free_param(&mask_out_param);
114 
115     if(mask_ids_count > 1) {
116         lv_draw_mask_remove_id(mask_ids[1]);
117         lv_draw_mask_free_param(&mask_in_param);
118     }
119 
120     if(mask_ids_count > 2) {
121         lv_draw_mask_remove_id(mask_ids[2]);
122         lv_draw_mask_free_param(&mask_angle_param);
123     }
124 
125     if(cap_ids[0] != LV_MASK_ID_INV) {
126         lv_draw_mask_remove_id(cap_ids[0]);
127         lv_draw_mask_remove_id(cap_ids[1]);
128         lv_draw_mask_free_param(&cap_start_param);
129         lv_draw_mask_free_param(&cap_end_param);
130     }
131 
132     SDL_Rect srcrect = {0, 0, w, h}, dstrect;
133     lv_area_to_sdl_rect(&draw_area, &dstrect);
134     SDL_Color color;
135     lv_color_to_sdl_color(&dsc->color, &color);
136     SDL_SetTextureColorMod(texture, color.r, color.g, color.b);
137     SDL_SetTextureAlphaMod(texture, dsc->opa);
138     SDL_RenderCopy(ctx->renderer, texture, &srcrect, &dstrect);
139 
140     if(!texture_cached) {
141         LV_LOG_WARN("Texture is not cached, this will impact performance.");
142         SDL_DestroyTexture(texture);
143     }
144 }
145 
146 /**********************
147  *   STATIC FUNCTIONS
148  **********************/
149 
dump_masks(SDL_Texture * texture,const lv_area_t * coords,const int16_t * ids,int16_t ids_count,const int16_t * caps)150 static void dump_masks(SDL_Texture * texture, const lv_area_t * coords, const int16_t * ids, int16_t ids_count,
151                        const int16_t * caps)
152 {
153     lv_coord_t w = lv_area_get_width(coords), h = lv_area_get_height(coords);
154     SDL_assert(w > 0 && h > 0);
155     SDL_Rect rect = {0, 0, w, h};
156     uint8_t * pixels;
157     int pitch;
158     if(SDL_LockTexture(texture, &rect, (void **) &pixels, &pitch) != 0) return;
159 
160     lv_opa_t * line_buf = lv_mem_buf_get(rect.w);
161     for(lv_coord_t y = 0; y < rect.h; y++) {
162         lv_memset_ff(line_buf, rect.w);
163         lv_coord_t abs_x = (lv_coord_t) coords->x1, abs_y = (lv_coord_t)(y + coords->y1), len = (lv_coord_t) rect.w;
164         lv_draw_mask_res_t res;
165         res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, ids, ids_count);
166         if(res == LV_DRAW_MASK_RES_TRANSP) {
167             lv_memset_00(&pixels[y * pitch], 4 * rect.w);
168         }
169         else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
170             lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
171         }
172         else {
173             for(int x = 0; x < rect.w; x++) {
174                 uint8_t * pixel = &pixels[y * pitch + x * 4];
175                 *pixel = line_buf[x];
176                 pixel[1] = pixel[2] = pixel[3] = 0xFF;
177             }
178         }
179         if(caps) {
180             for(int i = 0; i < 2; i++) {
181                 lv_memset_ff(line_buf, rect.w);
182                 res = lv_draw_mask_apply_ids(line_buf, abs_x, abs_y, len, &caps[i], 1);
183                 if(res == LV_DRAW_MASK_RES_TRANSP) {
184                     /* Ignore */
185                 }
186                 else if(res == LV_DRAW_MASK_RES_FULL_COVER) {
187                     lv_memset_ff(&pixels[y * pitch], 4 * rect.w);
188                 }
189                 else {
190                     for(int x = 0; x < rect.w; x++) {
191                         uint8_t * pixel = &pixels[y * pitch + x * 4];
192                         uint16_t old_opa = line_buf[x] + *pixel;
193                         *pixel = LV_MIN(old_opa, 0xFF);
194                         pixel[1] = pixel[2] = pixel[3] = 0xFF;
195                     }
196                 }
197             }
198         }
199     }
200     lv_mem_buf_release(line_buf);
201     SDL_UnlockTexture(texture);
202 }
203 
get_cap_area(int16_t angle,lv_coord_t thickness,uint16_t radius,const lv_point_t * center,lv_area_t * out)204 static void get_cap_area(int16_t angle, lv_coord_t thickness, uint16_t radius, const lv_point_t * center,
205                          lv_area_t * out)
206 {
207     const uint8_t ps = 8;
208     const uint8_t pa = 127;
209 
210     int32_t thick_half = thickness / 2;
211     uint8_t thick_corr = (thickness & 0x01) ? 0 : 1;
212 
213     int32_t cir_x;
214     int32_t cir_y;
215 
216     cir_x = ((radius - thick_half) * lv_trigo_sin((int16_t)(90 - angle))) >> (LV_TRIGO_SHIFT - ps);
217     cir_y = ((radius - thick_half) * lv_trigo_sin(angle)) >> (LV_TRIGO_SHIFT - ps);
218 
219     /*Actually the center of the pixel need to be calculated so apply 1/2 px offset*/
220     if(cir_x > 0) {
221         cir_x = (cir_x - pa) >> ps;
222         out->x1 = cir_x - thick_half + thick_corr;
223         out->x2 = cir_x + thick_half;
224     }
225     else {
226         cir_x = (cir_x + pa) >> ps;
227         out->x1 = cir_x - thick_half;
228         out->x2 = cir_x + thick_half - thick_corr;
229     }
230 
231     if(cir_y > 0) {
232         cir_y = (cir_y - pa) >> ps;
233         out->y1 = cir_y - thick_half + thick_corr;
234         out->y2 = cir_y + thick_half;
235     }
236     else {
237         cir_y = (cir_y + pa) >> ps;
238         out->y1 = cir_y - thick_half;
239         out->y2 = cir_y + thick_half - thick_corr;
240     }
241     lv_area_move(out, center->x, center->y);
242 }
243 
244 #endif /*LV_USE_GPU_SDL*/
245