1 /**
2  * MIT License
3  *
4  * -----------------------------------------------------------------------------
5  * Copyright (c) 2008-24 Think Silicon Single Member PC
6  * -----------------------------------------------------------------------------
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights to
11  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12  * the Software, and to permit persons to whom the Software is furnished to do so,
13  * subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the next paragraph)
16  * shall be included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
19  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
22  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
23  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  */
26 
27 /**
28  * @file lv_draw_nema_gfx.c
29  *
30  */
31 
32 /*********************
33  *      INCLUDES
34  *********************/
35 #include "../../core/lv_refr.h"
36 
37 #if LV_USE_NEMA_GFX
38 
39 #include "lv_draw_nema_gfx.h"
40 #include "../../font/lv_font.h"
41 #include "../../font/lv_font_fmt_txt.h"
42 
43 /*********************
44  *      DEFINES
45  *********************/
46 
47 #define DRAW_UNIT_ID_NEMA_GFX 7
48 
49 /**********************
50  *      TYPEDEFS
51  **********************/
52 
53 #if LV_USE_OS
54 /**
55  * Structure of pending nema_gfx draw task
56  */
57 typedef struct _nema_gfx_draw_task_t {
58     lv_draw_task_t * task;
59     bool flushed;
60 } nema_gfx_draw_task_t;
61 #endif
62 
63 /**********************
64  *  STATIC PROTOTYPES
65  **********************/
66 
67 #if LV_USE_OS
68     static void nema_gfx_render_thread_cb(void * ptr);
69 #endif
70 
71 static void nema_gfx_execute_drawing(lv_draw_nema_gfx_unit_t * u);
72 
73 static int32_t nema_gfx_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer);
74 
75 static int32_t nema_gfx_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task);
76 
77 static int32_t nema_gfx_delete(lv_draw_unit_t * draw_unit);
78 
79 /**********************
80  *  STATIC VARIABLES
81  **********************/
82 
83 /**********************
84  *      MACROS
85  **********************/
86 
87 /**********************
88  *   GLOBAL FUNCTIONS
89  **********************/
90 
lv_draw_nema_gfx_init(void)91 void lv_draw_nema_gfx_init(void)
92 {
93     lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = lv_draw_create_unit(sizeof(lv_draw_nema_gfx_unit_t));
94     /*Initialize NemaGFX*/
95     nema_init();
96 
97     draw_nema_gfx_unit->base_unit.dispatch_cb = nema_gfx_dispatch;
98     draw_nema_gfx_unit->base_unit.evaluate_cb = nema_gfx_evaluate;
99     draw_nema_gfx_unit->base_unit.delete_cb = nema_gfx_delete;
100     draw_nema_gfx_unit->base_unit.name = "NEMA_GFX";
101 
102 #if LV_USE_NEMA_VG
103     /*Initialize NemaVG */
104     nema_vg_init(LV_NEMA_GFX_MAX_RESX, LV_NEMA_GFX_MAX_RESY);
105     /* Allocate VG Buffers*/
106     draw_nema_gfx_unit->paint = nema_vg_paint_create();
107     draw_nema_gfx_unit->gradient = nema_vg_grad_create();
108     draw_nema_gfx_unit->path = nema_vg_path_create();
109     /*Initialize Freetype Support*/
110     lv_draw_nema_gfx_label_init(&(draw_nema_gfx_unit->base_unit));
111 #endif
112     /*Create GPU Command List*/
113     draw_nema_gfx_unit->cl = nema_cl_create();
114     /*Bind Command List*/
115     nema_cl_bind_circular(&(draw_nema_gfx_unit->cl));
116 
117 
118 #if LV_USE_OS
119     lv_thread_init(&draw_nema_gfx_unit->thread, "nemagfx", LV_THREAD_PRIO_HIGH, nema_gfx_render_thread_cb, 2 * 1024,
120                    draw_nema_gfx_unit);
121 #endif
122 }
123 
lv_draw_nema_gfx_deinit(void)124 void lv_draw_nema_gfx_deinit(void)
125 {
126     return;
127 }
128 
129 /**********************
130  *   STATIC FUNCTIONS
131  **********************/
132 
nema_gfx_evaluate(lv_draw_unit_t * draw_unit,lv_draw_task_t * task)133 static int32_t nema_gfx_evaluate(lv_draw_unit_t * draw_unit, lv_draw_task_t * task)
134 {
135     lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *)draw_unit;
136 
137     switch(task->type) {
138         case LV_DRAW_TASK_TYPE_LAYER: {
139                 if(task->preference_score > 80) {
140                     task->preference_score = 80;
141                     task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
142                 }
143                 return 1;
144             }
145 #if LV_USE_NEMA_VG
146         case LV_DRAW_TASK_TYPE_TRIANGLE:
147         case LV_DRAW_TASK_TYPE_ARC:
148         case LV_DRAW_TASK_TYPE_FILL: {
149                 if(task->preference_score > 80) {
150                     task->preference_score = 80;
151                     task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
152                 }
153                 return 1;
154             }
155 #else
156         case LV_DRAW_TASK_TYPE_FILL: {
157                 lv_draw_fill_dsc_t * draw_fill_dsc = (lv_draw_fill_dsc_t *) task->draw_dsc;
158                 if((draw_fill_dsc->grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE)) {
159                     if(task->preference_score > 80) {
160                         task->preference_score = 80;
161                         task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
162                     }
163                     return 1;
164                 }
165                 break;
166             }
167         case LV_DRAW_TASK_TYPE_TRIANGLE: {
168                 lv_draw_triangle_dsc_t * draw_triangle_dsc = (lv_draw_triangle_dsc_t *) task->draw_dsc;
169                 if((draw_triangle_dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE)) {
170                     if(task->preference_score > 80) {
171                         task->preference_score = 80;
172                         task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
173                     }
174                     return 1;
175                 }
176                 break;
177             }
178 #endif
179         case LV_DRAW_TASK_TYPE_IMAGE: {
180                 lv_draw_image_dsc_t * draw_image_dsc = (lv_draw_image_dsc_t *) task->draw_dsc;
181                 /*Guard for previous NemaGFX Version*/
182 #ifndef NEMA_BLOP_RECOLOR
183                 if(draw_image_dsc->recolor_opa > LV_OPA_MIN)
184                     break;
185 #endif
186                 const lv_image_dsc_t * img_dsc = draw_image_dsc->src;
187                 if(!lv_nemagfx_is_cf_supported(img_dsc->header.cf))
188                     break;
189 
190                 if(draw_image_dsc->blend_mode != LV_BLEND_MODE_SUBTRACTIVE) {
191                     if(task->preference_score > 80) {
192                         task->preference_score = 80;
193                         task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
194                     }
195                     return 1;
196                 }
197                 break;
198             }
199         case LV_DRAW_TASK_TYPE_LABEL: {
200                 lv_draw_label_dsc_t * draw_label_dsc = (lv_draw_label_dsc_t *) task->draw_dsc;
201                 lv_font_fmt_txt_dsc_t * fdsc = (lv_font_fmt_txt_dsc_t *)(draw_label_dsc->font->dsc);
202                 if(fdsc->bitmap_format != LV_FONT_FMT_TXT_COMPRESSED) {
203                     if(task->preference_score > 80) {
204                         task->preference_score = 80;
205                         task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
206                     }
207                     return 1;
208                 }
209                 break;
210             }
211         case LV_DRAW_TASK_TYPE_LINE: {
212                 lv_draw_line_dsc_t * draw_line_dsc = (lv_draw_line_dsc_t *) task->draw_dsc;
213                 bool is_dashed = (draw_line_dsc->dash_width && draw_line_dsc->dash_gap);
214                 if(!is_dashed && !(draw_line_dsc->round_end || draw_line_dsc->round_start)) {
215                     if(task->preference_score > 80) {
216                         task->preference_score = 80;
217                         task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
218                     }
219                     return 1;
220                 }
221                 break;
222             }
223         case LV_DRAW_TASK_TYPE_BORDER: {
224                 const lv_draw_border_dsc_t * draw_dsc = (lv_draw_border_dsc_t *) task->draw_dsc;
225                 if((!(draw_dsc->side != (lv_border_side_t)LV_BORDER_SIDE_FULL && draw_dsc->radius > 0)) &&
226                    (draw_dsc->radius > draw_dsc->width)) {
227                     if(task->preference_score > 80) {
228                         task->preference_score = 80;
229                         task->preferred_draw_unit_id = DRAW_UNIT_ID_NEMA_GFX;
230                     }
231                     return 1;
232                 }
233                 break;
234             }
235         case LV_DRAW_TASK_TYPE_BOX_SHADOW:
236         case LV_DRAW_TASK_TYPE_MASK_RECTANGLE:
237         case LV_DRAW_TASK_TYPE_MASK_BITMAP:
238         case LV_DRAW_TASK_TYPE_VECTOR:
239         default:
240             break;
241     }
242     nema_cl_wait(&(draw_nema_gfx_unit->cl));
243     return 0;
244 }
245 
nema_gfx_dispatch(lv_draw_unit_t * draw_unit,lv_layer_t * layer)246 static int32_t nema_gfx_dispatch(lv_draw_unit_t * draw_unit, lv_layer_t * layer)
247 {
248     lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *) draw_unit;
249 
250     /* Return immediately if it's busy with draw task. */
251     if(draw_nema_gfx_unit->task_act)
252         return 0;
253 
254     /* Try to get an ready to draw. */
255     lv_draw_task_t * t = lv_draw_get_next_available_task(layer, NULL, DRAW_UNIT_ID_NEMA_GFX);
256 
257     /* Return 0 is no selection, some tasks can be supported by other units. */
258     if(t == NULL || t->preferred_draw_unit_id != DRAW_UNIT_ID_NEMA_GFX)
259         return LV_DRAW_UNIT_IDLE;
260 
261     void * buf = lv_draw_layer_alloc_buf(layer);
262     if(buf == NULL)
263         return LV_DRAW_UNIT_IDLE;
264 
265     t->state = LV_DRAW_TASK_STATE_IN_PROGRESS;
266     draw_nema_gfx_unit->base_unit.target_layer = layer;
267     draw_nema_gfx_unit->base_unit.clip_area = &t->clip_area;
268     draw_nema_gfx_unit->task_act = t;
269 
270 #if LV_USE_OS
271     /* Let the render thread work. */
272     if(draw_nema_gfx_unit->inited)
273         lv_thread_sync_signal(&draw_nema_gfx_unit->sync);
274 #else
275     nema_gfx_execute_drawing(draw_nema_gfx_unit);
276 
277     draw_nema_gfx_unit->task_act->state = LV_DRAW_TASK_STATE_READY;
278     draw_nema_gfx_unit->task_act = NULL;
279 
280     /* The draw unit is free now. Request a new dispatching as it can get a new task. */
281     lv_draw_dispatch_request();
282 #endif
283 
284     return 1;
285 }
286 
nema_gfx_execute_drawing(lv_draw_nema_gfx_unit_t * u)287 static void nema_gfx_execute_drawing(lv_draw_nema_gfx_unit_t * u)
288 {
289     lv_draw_task_t * t = u->task_act;
290     lv_draw_unit_t * draw_unit = (lv_draw_unit_t *)u;
291 
292     switch(t->type) {
293         case LV_DRAW_TASK_TYPE_FILL:
294             lv_draw_nema_gfx_fill(draw_unit, t->draw_dsc, &t->area);
295             break;
296         case LV_DRAW_TASK_TYPE_IMAGE:
297             lv_draw_nema_gfx_img(draw_unit, t->draw_dsc, &t->area);
298             break;
299         case LV_DRAW_TASK_TYPE_TRIANGLE:
300             lv_draw_nema_gfx_triangle(draw_unit, t->draw_dsc);
301             break;
302         case LV_DRAW_TASK_TYPE_LABEL:
303             lv_draw_nema_gfx_label(draw_unit, t->draw_dsc, &t->area);
304             break;
305         case LV_DRAW_TASK_TYPE_LAYER:
306             lv_draw_nema_gfx_layer(draw_unit, t->draw_dsc, &t->area);
307             break;
308         case LV_DRAW_TASK_TYPE_LINE:
309             lv_draw_nema_gfx_line(draw_unit, t->draw_dsc);
310             break;
311 #if LV_USE_NEMA_VG
312         case LV_DRAW_TASK_TYPE_ARC:
313             lv_draw_nema_gfx_arc(draw_unit, t->draw_dsc, &t->area);
314             break;
315 #endif
316         case LV_DRAW_TASK_TYPE_BORDER:
317             lv_draw_nema_gfx_border(draw_unit, t->draw_dsc, &t->area);
318             break;
319         default:
320             break;
321     }
322 }
323 
nema_gfx_delete(lv_draw_unit_t * draw_unit)324 static int32_t nema_gfx_delete(lv_draw_unit_t * draw_unit)
325 {
326 #if LV_USE_NEMA_VG
327     /*Free VG Buffers*/
328     lv_draw_nema_gfx_unit_t * draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *) draw_unit;
329     nema_vg_paint_destroy(draw_nema_gfx_unit->paint);
330     nema_vg_path_destroy(draw_nema_gfx_unit->path);
331     nema_vg_grad_destroy(draw_nema_gfx_unit->gradient);
332 #endif
333 
334 #if LV_USE_OS
335     lv_draw_nema_gfx_unit_t * _draw_nema_gfx_unit = (lv_draw_nema_gfx_unit_t *) draw_unit;
336     LV_LOG_INFO("Cancel NemaGFX draw thread.");
337     _draw_nema_gfx_unit->exit_status = true;
338 
339     if(_draw_nema_gfx_unit->inited)
340         lv_thread_sync_signal(&_draw_nema_gfx_unit->sync);
341 
342     lv_result_t res = lv_thread_delete(&_draw_nema_gfx_unit->thread);
343 
344     return res;
345 #endif
346 
347 #if LV_USE_NEMA_VG == 0 && LV_USE_OS == LV_OS_NONE
348     LV_UNUSED(draw_unit);
349 #endif
350     return 0;
351 }
352 
353 #if LV_USE_OS
nema_gfx_render_thread_cb(void * ptr)354 static void nema_gfx_render_thread_cb(void * ptr)
355 {
356     lv_draw_nema_gfx_unit_t * u = ptr;
357 
358     lv_thread_sync_init(&u->sync);
359     u->inited = true;
360 
361     while(1) {
362         /* Wait for sync if there is no task set. */
363         while(u->task_act == NULL) {
364             if(u->exit_status)
365                 break;
366 
367             lv_thread_sync_wait(&u->sync);
368         }
369 
370         if(u->exit_status) {
371             LV_LOG_INFO("Ready to exit NemaGFX draw thread.");
372             break;
373         }
374 
375         if(u->task_act) {
376             nema_gfx_execute_drawing(u);
377         }
378         /* Signal the ready state to dispatcher. */
379         u->task_act->state = LV_DRAW_TASK_STATE_READY;
380         /* Cleanup. */
381         u->task_act = NULL;
382 
383         /* The draw unit is free now. Request a new dispatching as it can get a new task. */
384         lv_draw_dispatch_request();
385     }
386 
387     u->inited = false;
388     lv_thread_sync_delete(&u->sync);
389     LV_LOG_INFO("Exit NemaGFX draw thread.");
390 }
391 #endif
392 
393 #endif
394