1 /*
2  * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
3  *
4  * This is free software: you can redistribute it and/or modify
5  * it under the terms of the Artistic License 2.0 as published by
6  * The Perl Foundation.
7  *
8  * This source is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * Artistic License 2.0 for more details.
12  *
13  * You should have received a copy of the Artistic License 2.0
14  * along the source as a COPYING file. If not, obtain it from
15  * http://www.perlfoundation.org/artistic_license_2_0.
16  */
17 
18 #ifndef _LV2_CANVAS_RENDER_NANOVG_H
19 #define _LV2_CANVAS_RENDER_NANOVG_H
20 
21 #include <assert.h>
22 
23 #include <nanovg.h>
24 
25 #if defined(__APPLE__)
26 #	include <OpenGL/gl.h>
27 #	include <OpenGL/glext.h>
28 #else
29 #	include <GL/glew.h>
30 #endif
31 
32 #define NANOVG_GL2_IMPLEMENTATION
33 #include <nanovg_gl.h>
34 
35 #if defined(NANOVG_GL2_IMPLEMENTATION)
36 #	define nvgCreate nvgCreateGL2
37 #	define nvgDelete nvgDeleteGL2
38 #elif defined(NANOVG_GL3_IMPLEMENTATION)
39 #	define nvgCreate nvgCreateGL3
40 #	define nvgDelete nvgDeleteGL3
41 #elif defined(NANOVG_GLES2_IMPLEMENTATION)
42 #	define nvgCreate nvgCreateGLES2
43 #	define nvgDelete nvgDeleteGLES2
44 #elif defined(NANOVG_GLES3_IMPLEMENTATION)
45 #	define nvgCreate nvgCreateGLES3
46 #	define nvgDelete nvgDeleteGLES3
47 #endif
48 
49 #ifdef __cplusplus
50 extern "C" {
51 #endif
52 
53 static inline void
_lv2_canvas_render_beginPath(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)54 _lv2_canvas_render_beginPath(void *data,
55 	LV2_Canvas_URID *urid, const LV2_Atom *body)
56 {
57 	NVGcontext *ctx = data;
58 	nvgBeginPath(ctx);
59 }
60 
61 static inline void
_lv2_canvas_render_closePath(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)62 _lv2_canvas_render_closePath(void *data,
63 	LV2_Canvas_URID *urid, const LV2_Atom *body)
64 {
65 	NVGcontext *ctx = data;
66 	nvgClosePath(ctx);
67 }
68 
69 static inline void
_lv2_canvas_render_arc(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)70 _lv2_canvas_render_arc(void *data,
71 	LV2_Canvas_URID *urid, const LV2_Atom *body)
72 {
73 	NVGcontext *ctx = data;
74 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 5);
75 
76 	if(v)
77 	{
78 		nvgArc(ctx, v[0], v[1], v[2], v[3], v[4], NVG_CCW);
79 	}
80 }
81 
82 static inline void
_lv2_canvas_render_curveTo(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)83 _lv2_canvas_render_curveTo(void *data,
84 	LV2_Canvas_URID *urid, const LV2_Atom *body)
85 {
86 	NVGcontext *ctx = data;
87 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
88 
89 	if(v)
90 	{
91 		nvgBezierTo(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
92 	}
93 }
94 
95 static inline void
_lv2_canvas_render_lineTo(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)96 _lv2_canvas_render_lineTo(void *data,
97 	LV2_Canvas_URID *urid, const LV2_Atom *body)
98 {
99 	NVGcontext *ctx = data;
100 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
101 
102 	if(v)
103 	{
104 		nvgLineTo(ctx, v[0], v[1]);
105 	}
106 }
107 
108 static inline void
_lv2_canvas_render_moveTo(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)109 _lv2_canvas_render_moveTo(void *data,
110 	LV2_Canvas_URID *urid, const LV2_Atom *body)
111 {
112 	NVGcontext *ctx = data;
113 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
114 
115 	if(v)
116 	{
117 		nvgMoveTo(ctx, v[0], v[1]);
118 	}
119 }
120 
121 static inline void
_lv2_canvas_render_rectangle(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)122 _lv2_canvas_render_rectangle(void *data,
123 	LV2_Canvas_URID *urid, const LV2_Atom *body)
124 {
125 	NVGcontext *ctx = data;
126 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 4);
127 
128 	if(v)
129 	{
130 		nvgRect(ctx, v[0], v[1], v[2], v[3]);
131 	}
132 }
133 
134 static inline void
_lv2_canvas_render_polyline(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)135 _lv2_canvas_render_polyline(void *data,
136 	LV2_Canvas_URID *urid, const LV2_Atom *body)
137 {
138 	NVGcontext *ctx = data;
139 	uint32_t N;
140 	const float *v = _lv2_canvas_render_get_float_vecs(urid, body, &N);
141 
142 	if(v)
143 	{
144 		for(uint32_t i = 0; i < 2; i += 2)
145 		{
146 			nvgMoveTo(ctx, v[i], v[i+1]);
147 		}
148 
149 		for(uint32_t i = 2; i < N; i += 2)
150 		{
151 			nvgLineTo(ctx, v[i], v[i+1]);
152 		}
153 	}
154 }
155 
156 static inline void
_lv2_canvas_render_style(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)157 _lv2_canvas_render_style(void *data,
158 	LV2_Canvas_URID *urid, const LV2_Atom *body)
159 {
160 	NVGcontext *ctx = data;
161 	const int64_t *v = _lv2_canvas_render_get_type(body, urid->forge.Long);
162 
163 	if(v)
164 	{
165 		const uint8_t r = (*v >> 24) & 0xff;
166 		const uint8_t g = (*v >> 16) & 0xff;
167 		const uint8_t b = (*v >>  8) & 0xff;
168 		const uint8_t a = (*v >>  0) & 0xff;
169 
170 		nvgStrokeColor(ctx, nvgRGBA(r, g, b, a));
171 		nvgFillColor(ctx, nvgRGBA(r, g, b, a));
172 	}
173 }
174 
175 static inline void
_lv2_canvas_render_lineWidth(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)176 _lv2_canvas_render_lineWidth(void *data,
177 	LV2_Canvas_URID *urid, const LV2_Atom *body)
178 {
179 	NVGcontext *ctx = data;
180 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
181 
182 	if(v)
183 	{
184 		nvgStrokeWidth(ctx, *v);
185 	}
186 }
187 
188 static inline void
_lv2_canvas_render_lineDash(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)189 _lv2_canvas_render_lineDash(void *data,
190 	LV2_Canvas_URID *urid, const LV2_Atom *body)
191 {
192 	NVGcontext *ctx = data;
193 	(void)ctx; //FIXME
194 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
195 
196 	if(v)
197 	{
198 		//const double d[2] = {v[0], v[1]};
199 		//FIXME cairo_set_dash(ctx, d, 2, 0);
200 	}
201 }
202 
203 static inline void
_lv2_canvas_render_lineCap(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)204 _lv2_canvas_render_lineCap(void *data,
205 	LV2_Canvas_URID *urid, const LV2_Atom *body)
206 {
207 	NVGcontext *ctx = data;
208 	const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
209 
210 	if(v)
211 	{
212 		int cap = NVG_BUTT;
213 
214 		if(*v == urid->Canvas_lineCapButt)
215 			cap = NVG_BUTT;
216 		else if(*v == urid->Canvas_lineCapRound)
217 			cap = NVG_ROUND;
218 		else if(*v == urid->Canvas_lineCapSquare)
219 			cap = NVG_SQUARE;
220 
221 		nvgLineCap(ctx, cap);
222 	}
223 }
224 
225 static inline void
_lv2_canvas_render_lineJoin(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)226 _lv2_canvas_render_lineJoin(void *data,
227 	LV2_Canvas_URID *urid, const LV2_Atom *body)
228 {
229 	NVGcontext *ctx = data;
230 	const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
231 
232 	if(v)
233 	{
234 		int join = NVG_MITER;
235 
236 		if(*v == urid->Canvas_lineJoinMiter)
237 			join = NVG_MITER;
238 		else if(*v == urid->Canvas_lineJoinRound)
239 			join = NVG_ROUND;
240 		else if(*v == urid->Canvas_lineJoinBevel)
241 			join = NVG_BEVEL;
242 
243 		nvgLineJoin(ctx, join);
244 	}
245 }
246 
247 static inline void
_lv2_canvas_render_miterLimit(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)248 _lv2_canvas_render_miterLimit(void *data,
249 	LV2_Canvas_URID *urid, const LV2_Atom *body)
250 {
251 	NVGcontext *ctx = data;
252 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
253 
254 	if(v)
255 	{
256 		nvgMiterLimit(ctx, *v);
257 	}
258 }
259 
260 static inline void
_lv2_canvas_render_stroke(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)261 _lv2_canvas_render_stroke(void *data,
262 	LV2_Canvas_URID *urid, const LV2_Atom *body)
263 {
264 	NVGcontext *ctx = data;
265 	nvgStroke(ctx);
266 }
267 
268 static inline void
_lv2_canvas_render_fill(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)269 _lv2_canvas_render_fill(void *data,
270 	LV2_Canvas_URID *urid, const LV2_Atom *body)
271 {
272 	NVGcontext *ctx = data;
273 	nvgFill(ctx);
274 }
275 
276 static inline void
_lv2_canvas_render_clip(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)277 _lv2_canvas_render_clip(void *data,
278 	LV2_Canvas_URID *urid, const LV2_Atom *body)
279 {
280 	NVGcontext *ctx = data;
281 	(void)ctx; //FIXME
282 	//FIXME cairo_clip(ctx);
283 }
284 
285 static inline void
_lv2_canvas_render_save(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)286 _lv2_canvas_render_save(void *data,
287 	LV2_Canvas_URID *urid, const LV2_Atom *body)
288 {
289 	NVGcontext *ctx = data;
290 	nvgSave(ctx);
291 }
292 
293 static inline void
_lv2_canvas_render_restore(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)294 _lv2_canvas_render_restore(void *data,
295 	LV2_Canvas_URID *urid, const LV2_Atom *body)
296 {
297 	NVGcontext *ctx = data;
298 	nvgRestore(ctx);
299 }
300 
301 static inline void
_lv2_canvas_render_translate(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)302 _lv2_canvas_render_translate(void *data,
303 	LV2_Canvas_URID *urid, const LV2_Atom *body)
304 {
305 	NVGcontext *ctx = data;
306 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
307 
308 	if(v)
309 	{
310 		nvgTranslate(ctx, v[0], v[1]);
311 	}
312 }
313 
314 static inline void
_lv2_canvas_render_scale(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)315 _lv2_canvas_render_scale(void *data,
316 	LV2_Canvas_URID *urid, const LV2_Atom *body)
317 {
318 	NVGcontext *ctx = data;
319 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
320 
321 	if(v)
322 	{
323 		nvgScale(ctx, v[0], v[1]);
324 	}
325 }
326 
327 static inline void
_lv2_canvas_render_rotate(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)328 _lv2_canvas_render_rotate(void *data,
329 	LV2_Canvas_URID *urid, const LV2_Atom *body)
330 {
331 	NVGcontext *ctx = data;
332 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
333 
334 	if(v)
335 	{
336 		nvgRotate(ctx, *v);
337 	}
338 }
339 
340 static inline void
_lv2_canvas_render_transform(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)341 _lv2_canvas_render_transform(void *data,
342 	LV2_Canvas_URID *urid, const LV2_Atom *body)
343 {
344 	NVGcontext *ctx = data;
345 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
346 
347 	if(v)
348 	{
349 		nvgTransform(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
350 	}
351 }
352 
353 static inline void
_lv2_canvas_render_reset(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)354 _lv2_canvas_render_reset(void *data,
355 	LV2_Canvas_URID *urid, const LV2_Atom *body)
356 {
357 	NVGcontext *ctx = data;
358 	nvgReset(ctx);
359 }
360 
361 static inline void
_lv2_canvas_render_fontSize(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)362 _lv2_canvas_render_fontSize(void *data,
363 	LV2_Canvas_URID *urid, const LV2_Atom *body)
364 {
365 	NVGcontext *ctx = data;
366 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
367 
368 	if(v)
369 	{
370 		nvgFontSize(ctx, *v);
371 	}
372 }
373 
374 static inline void
_lv2_canvas_render_fillText(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)375 _lv2_canvas_render_fillText(void *data,
376 	LV2_Canvas_URID *urid, const LV2_Atom *body)
377 {
378 	NVGcontext *ctx = data;
379 	(void)ctx; //FIXME
380 	const char *v = _lv2_canvas_render_get_type(body, urid->forge.String);
381 
382 	if(v)
383 	{
384 		/* FIXME
385 		NVGcontextext_extents_t extents;
386 		NVGcontextext_extents(ctx, v, &extents);
387 		const float dx = extents.width/2 + extents.x_bearing;
388 		const float dy = extents.height/2 + extents.y_bearing;
389 		cairo_rel_move_to(ctx, -dx, -dy);
390 		cairo_show_text(ctx, v);
391 		*/
392 		/*
393 		float bounds [4];
394 		const float adv_x = nvgTextBounds(ctx, 0.f, 0.f, v, NULL, bounds);
395 		nvgText(ctx, float x, float y, const char* string, const char* end);
396 		*/
397 	}
398 }
399 
400 static inline void
_lv2_canvas_qsort(LV2_Canvas_Meth * A,int n)401 _lv2_canvas_qsort(LV2_Canvas_Meth *A, int n)
402 {
403 	if(n < 2)
404 		return;
405 
406 	LV2_Canvas_Meth *p = A;
407 
408 	int i = -1;
409 	int j = n;
410 
411 	while(true)
412 	{
413 		do {
414 			i += 1;
415 		} while(A[i].command < p->command);
416 
417 		do {
418 			j -= 1;
419 		} while(A[j].command > p->command);
420 
421 		if(i >= j)
422 			break;
423 
424 		const LV2_Canvas_Meth tmp = A[i];
425 		A[i] = A[j];
426 		A[j] = tmp;
427 	}
428 
429 	_lv2_canvas_qsort(A, j + 1);
430 	_lv2_canvas_qsort(A + j + 1, n - j - 1);
431 }
432 
433 static inline LV2_Canvas_Meth *
_lv2_canvas_bsearch(LV2_URID p,LV2_Canvas_Meth * a,int n)434 _lv2_canvas_bsearch(LV2_URID p, LV2_Canvas_Meth *a, int n)
435 {
436 	LV2_Canvas_Meth *base = a;
437 
438 	for(int N = n, half; N > 1; N -= half)
439 	{
440 		half = N/2;
441 		LV2_Canvas_Meth *dst = &base[half];
442 		base = (dst->command > p) ? base : dst;
443 	}
444 
445 	return (base->command == p) ? base : NULL;
446 }
447 
448 static inline void
lv2_canvas_init(LV2_Canvas * canvas,LV2_URID_Map * map)449 lv2_canvas_init(LV2_Canvas *canvas, LV2_URID_Map *map)
450 {
451 	lv2_canvas_urid_init(&canvas->urid, map);
452 
453 	unsigned ptr = 0;
454 
455 	canvas->methods[ptr].command = canvas->urid.Canvas_BeginPath;
456 	canvas->methods[ptr++].func = _lv2_canvas_render_beginPath;
457 
458 	canvas->methods[ptr].command = canvas->urid.Canvas_ClosePath;
459 	canvas->methods[ptr++].func = _lv2_canvas_render_closePath;
460 
461 	canvas->methods[ptr].command = canvas->urid.Canvas_Arc;
462 	canvas->methods[ptr++].func = _lv2_canvas_render_arc;
463 
464 	canvas->methods[ptr].command = canvas->urid.Canvas_CurveTo;
465 	canvas->methods[ptr++].func = _lv2_canvas_render_curveTo;
466 
467 	canvas->methods[ptr].command = canvas->urid.Canvas_LineTo;
468 	canvas->methods[ptr++].func = _lv2_canvas_render_lineTo;
469 
470 	canvas->methods[ptr].command = canvas->urid.Canvas_MoveTo;
471 	canvas->methods[ptr++].func = _lv2_canvas_render_moveTo;
472 
473 	canvas->methods[ptr].command = canvas->urid.Canvas_Rectangle;
474 	canvas->methods[ptr++].func = _lv2_canvas_render_rectangle;
475 
476 	canvas->methods[ptr].command = canvas->urid.Canvas_PolyLine;
477 	canvas->methods[ptr++].func = _lv2_canvas_render_polyline;
478 
479 	canvas->methods[ptr].command = canvas->urid.Canvas_Style;
480 	canvas->methods[ptr++].func = _lv2_canvas_render_style;
481 
482 	canvas->methods[ptr].command = canvas->urid.Canvas_LineWidth;
483 	canvas->methods[ptr++].func = _lv2_canvas_render_lineWidth;
484 
485 	canvas->methods[ptr].command = canvas->urid.Canvas_LineDash;
486 	canvas->methods[ptr++].func = _lv2_canvas_render_lineDash;
487 
488 	canvas->methods[ptr].command = canvas->urid.Canvas_LineCap;
489 	canvas->methods[ptr++].func = _lv2_canvas_render_lineCap;
490 
491 	canvas->methods[ptr].command = canvas->urid.Canvas_LineJoin;
492 	canvas->methods[ptr++].func = _lv2_canvas_render_lineJoin;
493 
494 	canvas->methods[ptr].command = canvas->urid.Canvas_MiterLimit;
495 	canvas->methods[ptr++].func = _lv2_canvas_render_miterLimit;
496 
497 	canvas->methods[ptr].command = canvas->urid.Canvas_Stroke;
498 	canvas->methods[ptr++].func = _lv2_canvas_render_stroke;
499 
500 	canvas->methods[ptr].command = canvas->urid.Canvas_Fill;
501 	canvas->methods[ptr++].func = _lv2_canvas_render_fill;
502 
503 	canvas->methods[ptr].command = canvas->urid.Canvas_Clip;
504 	canvas->methods[ptr++].func = _lv2_canvas_render_clip;
505 
506 	canvas->methods[ptr].command = canvas->urid.Canvas_Save;
507 	canvas->methods[ptr++].func = _lv2_canvas_render_save;
508 
509 	canvas->methods[ptr].command = canvas->urid.Canvas_Restore;
510 	canvas->methods[ptr++].func = _lv2_canvas_render_restore;
511 
512 	canvas->methods[ptr].command = canvas->urid.Canvas_Translate;
513 	canvas->methods[ptr++].func = _lv2_canvas_render_translate;
514 
515 	canvas->methods[ptr].command = canvas->urid.Canvas_Scale;
516 	canvas->methods[ptr++].func = _lv2_canvas_render_scale;
517 
518 	canvas->methods[ptr].command = canvas->urid.Canvas_Rotate;
519 	canvas->methods[ptr++].func = _lv2_canvas_render_rotate;
520 
521 	canvas->methods[ptr].command = canvas->urid.Canvas_Transform;
522 	canvas->methods[ptr++].func = _lv2_canvas_render_transform;
523 
524 	canvas->methods[ptr].command = canvas->urid.Canvas_Reset;
525 	canvas->methods[ptr++].func = _lv2_canvas_render_reset;
526 
527 	canvas->methods[ptr].command = canvas->urid.Canvas_FontSize;
528 	canvas->methods[ptr++].func = _lv2_canvas_render_fontSize;
529 
530 	canvas->methods[ptr].command = canvas->urid.Canvas_FillText;
531 	canvas->methods[ptr++].func = _lv2_canvas_render_fillText;
532 
533 	assert(ptr == LV2_CANVAS_NUM_METHODS);
534 
535 	_lv2_canvas_qsort(canvas->methods, LV2_CANVAS_NUM_METHODS);
536 }
537 
538 static inline bool
lv2_canvas_render_body(LV2_Canvas * canvas,NVGcontext * ctx,uint32_t type,uint32_t size,const LV2_Atom * body)539 lv2_canvas_render_body(LV2_Canvas *canvas, NVGcontext *ctx, uint32_t type,
540 	uint32_t size, const LV2_Atom *body)
541 {
542 	LV2_Canvas_URID *urid = &canvas->urid;
543 
544 	if(!body || (type != urid->forge.Tuple) )
545 		return false;
546 
547 	// save state
548 	nvgSave(ctx);
549 
550 	// clear surface
551 	nvgBeginPath(ctx);
552 	nvgRect(ctx, 0, 0, 1.f, 1.f);
553 	nvgFillColor(ctx, nvgRGBA(0x1e, 0x1e, 0x1e, 0xff));
554 	nvgFill(ctx);
555 
556 	nvgFontSize(ctx, 0.1);
557 	nvgStrokeWidth(ctx, 0.01);
558 	nvgStrokeColor(ctx, nvgRGBA(0xff, 0xff, 0xff, 0xff));
559 	nvgFillColor(ctx, nvgRGBA(0xff, 0xff, 0xff, 0xff));
560 
561 	LV2_ATOM_TUPLE_BODY_FOREACH(body, size, itm)
562 	{
563 		if(lv2_atom_forge_is_object_type(&urid->forge, itm->type))
564 		{
565 			const LV2_Atom_Object *obj = (const LV2_Atom_Object *)itm;
566 			const LV2_Atom *body = NULL;
567 
568 			lv2_atom_object_get(obj, urid->Canvas_body, &body, 0);
569 
570 			LV2_Canvas_Meth *meth = _lv2_canvas_bsearch(obj->body.otype,
571 				canvas->methods, LV2_CANVAS_NUM_METHODS);
572 
573 			if(meth)
574 			{
575 				meth->func(ctx, urid, body);
576 			}
577 		}
578 	}
579 
580 	// save state
581 	nvgRestore(ctx);
582 
583 	return true;
584 }
585 
586 static inline bool
lv2_canvas_render(LV2_Canvas * canvas,NVGcontext * ctx,const LV2_Atom_Tuple * tup)587 lv2_canvas_render(LV2_Canvas *canvas, NVGcontext *ctx, const LV2_Atom_Tuple *tup)
588 {
589 	return lv2_canvas_render_body(canvas, ctx, tup->atom.type, tup->atom.size,
590 		LV2_ATOM_BODY_CONST(&tup->atom));
591 }
592 
593 #ifdef __cplusplus
594 }
595 #endif
596 
597 #endif // LV2_CANVAS_RENDER_NANOVG_H
598