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