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_CAIRO_H
19 #define _LV2_CANVAS_RENDER_CAIRO_H
20 
21 #include <assert.h>
22 
23 #include <canvas.lv2/canvas.h>
24 
25 #include <cairo/cairo.h>
26 
27 #ifdef __cplusplus
28 extern "C" {
29 #endif
30 
31 static inline void
_lv2_canvas_render_beginPath(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)32 _lv2_canvas_render_beginPath(void *data,
33 	LV2_Canvas_URID *urid __attribute__((unused)),
34 	const LV2_Atom *body __attribute__((unused)))
35 {
36 	cairo_t *ctx = data;
37 	cairo_new_sub_path(ctx);
38 }
39 
40 static inline void
_lv2_canvas_render_closePath(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)41 _lv2_canvas_render_closePath(void *data,
42 	LV2_Canvas_URID *urid __attribute__((unused)),
43 	const LV2_Atom *body __attribute__((unused)))
44 {
45 	cairo_t *ctx = data;
46 	cairo_close_path(ctx);
47 }
48 
49 static inline void
_lv2_canvas_render_arc(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)50 _lv2_canvas_render_arc(void *data,
51 	LV2_Canvas_URID *urid, const LV2_Atom *body)
52 {
53 	cairo_t *ctx = data;
54 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 5);
55 
56 	if(v)
57 	{
58 		cairo_arc(ctx, v[0], v[1], v[2], v[3], v[4]);
59 	}
60 }
61 
62 static inline void
_lv2_canvas_render_curveTo(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)63 _lv2_canvas_render_curveTo(void *data,
64 	LV2_Canvas_URID *urid, const LV2_Atom *body)
65 {
66 	cairo_t *ctx = data;
67 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
68 
69 	if(v)
70 	{
71 		cairo_curve_to(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
72 	}
73 }
74 
75 static inline void
_lv2_canvas_render_lineTo(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)76 _lv2_canvas_render_lineTo(void *data,
77 	LV2_Canvas_URID *urid, const LV2_Atom *body)
78 {
79 	cairo_t *ctx = data;
80 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
81 
82 	if(v)
83 	{
84 		cairo_line_to(ctx, v[0], v[1]);
85 	}
86 }
87 
88 static inline void
_lv2_canvas_render_moveTo(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)89 _lv2_canvas_render_moveTo(void *data,
90 	LV2_Canvas_URID *urid, const LV2_Atom *body)
91 {
92 	cairo_t *ctx = data;
93 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
94 
95 	if(v)
96 	{
97 		cairo_move_to(ctx, v[0], v[1]);
98 	}
99 }
100 
101 static inline void
_lv2_canvas_render_rectangle(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)102 _lv2_canvas_render_rectangle(void *data,
103 	LV2_Canvas_URID *urid, const LV2_Atom *body)
104 {
105 	cairo_t *ctx = data;
106 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 4);
107 
108 	if(v)
109 	{
110 		cairo_rectangle(ctx, v[0], v[1], v[2], v[3]);
111 	}
112 }
113 
114 static inline void
_lv2_canvas_render_polyline(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)115 _lv2_canvas_render_polyline(void *data,
116 	LV2_Canvas_URID *urid, const LV2_Atom *body)
117 {
118 	cairo_t *ctx = data;
119 	uint32_t N;
120 	const float *v = _lv2_canvas_render_get_float_vecs(urid, body, &N);
121 
122 	if(v)
123 	{
124 		for(uint32_t i = 0; i < 2; i += 2)
125 		{
126 			cairo_move_to(ctx, v[i], v[i+1]);
127 		}
128 
129 		for(uint32_t i = 2; i < N; i += 2)
130 		{
131 			cairo_line_to(ctx, v[i], v[i+1]);
132 		}
133 	}
134 }
135 
136 static inline void
_lv2_canvas_render_style(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)137 _lv2_canvas_render_style(void *data,
138 	LV2_Canvas_URID *urid, const LV2_Atom *body)
139 {
140 	cairo_t *ctx = data;
141 	const int64_t *v = _lv2_canvas_render_get_type(body, urid->forge.Long);
142 
143 	if(v)
144 	{
145 		const float r = (float)((*v >> 24) & 0xff) / 0xff;
146 		const float g = (float)((*v >> 16) & 0xff) / 0xff;
147 		const float b = (float)((*v >>  8) & 0xff) / 0xff;
148 		const float a = (float)((*v >>  0) & 0xff) / 0xff;
149 
150 		cairo_set_source_rgba(ctx, r, g, b, a);
151 	}
152 }
153 
154 static inline void
_lv2_canvas_render_lineWidth(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)155 _lv2_canvas_render_lineWidth(void *data,
156 	LV2_Canvas_URID *urid, const LV2_Atom *body)
157 {
158 	cairo_t *ctx = data;
159 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
160 
161 	if(v)
162 	{
163 		cairo_set_line_width(ctx, *v);
164 	}
165 }
166 
167 static inline void
_lv2_canvas_render_lineDash(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)168 _lv2_canvas_render_lineDash(void *data,
169 	LV2_Canvas_URID *urid, const LV2_Atom *body)
170 {
171 	cairo_t *ctx = data;
172 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
173 
174 	if(v)
175 	{
176 		const double d[2] = {v[0], v[1]};
177 		cairo_set_dash(ctx, d, 2, 0);
178 	}
179 }
180 
181 static inline void
_lv2_canvas_render_lineCap(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)182 _lv2_canvas_render_lineCap(void *data,
183 	LV2_Canvas_URID *urid, const LV2_Atom *body)
184 {
185 	cairo_t *ctx = data;
186 	const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
187 
188 	if(v)
189 	{
190 		cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
191 
192 		if(*v == urid->Canvas_lineCapButt)
193 			cap = CAIRO_LINE_CAP_BUTT;
194 		else if(*v == urid->Canvas_lineCapRound)
195 			cap = CAIRO_LINE_CAP_ROUND;
196 		else if(*v == urid->Canvas_lineCapSquare)
197 			cap = CAIRO_LINE_CAP_SQUARE;
198 
199 		cairo_set_line_cap(ctx, cap);
200 	}
201 }
202 
203 static inline void
_lv2_canvas_render_lineJoin(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)204 _lv2_canvas_render_lineJoin(void *data,
205 	LV2_Canvas_URID *urid, const LV2_Atom *body)
206 {
207 	cairo_t *ctx = data;
208 	const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
209 
210 	if(v)
211 	{
212 		cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
213 
214 		if(*v == urid->Canvas_lineJoinMiter)
215 			join = CAIRO_LINE_JOIN_MITER;
216 		else if(*v == urid->Canvas_lineJoinRound)
217 			join = CAIRO_LINE_JOIN_ROUND;
218 		else if(*v == urid->Canvas_lineJoinBevel)
219 			join = CAIRO_LINE_JOIN_BEVEL;
220 
221 		cairo_set_line_join(ctx, join);
222 	}
223 }
224 
225 static inline void
_lv2_canvas_render_miterLimit(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)226 _lv2_canvas_render_miterLimit(void *data,
227 	LV2_Canvas_URID *urid, const LV2_Atom *body)
228 {
229 	cairo_t *ctx = data;
230 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
231 
232 	if(v)
233 	{
234 		cairo_set_miter_limit(ctx, *v);
235 	}
236 }
237 
238 static inline void
_lv2_canvas_render_stroke(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)239 _lv2_canvas_render_stroke(void *data,
240 	LV2_Canvas_URID *urid __attribute__((unused)),
241 	const LV2_Atom *body __attribute__((unused)))
242 {
243 	cairo_t *ctx = data;
244 	cairo_stroke(ctx);
245 }
246 
247 static inline void
_lv2_canvas_render_fill(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)248 _lv2_canvas_render_fill(void *data,
249 	LV2_Canvas_URID *urid __attribute__((unused)),
250 	const LV2_Atom *body __attribute__((unused)))
251 {
252 	cairo_t *ctx = data;
253 	cairo_fill(ctx);
254 }
255 
256 static inline void
_lv2_canvas_render_clip(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)257 _lv2_canvas_render_clip(void *data,
258 	LV2_Canvas_URID *urid __attribute__((unused)),
259 	const LV2_Atom *body __attribute__((unused)))
260 {
261 	cairo_t *ctx = data;
262 	cairo_clip(ctx);
263 }
264 
265 static inline void
_lv2_canvas_render_save(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)266 _lv2_canvas_render_save(void *data,
267 	LV2_Canvas_URID *urid __attribute__((unused)),
268 	const LV2_Atom *body __attribute__((unused)))
269 {
270 	cairo_t *ctx = data;
271 	cairo_save(ctx);
272 }
273 
274 static inline void
_lv2_canvas_render_restore(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)275 _lv2_canvas_render_restore(void *data,
276 	LV2_Canvas_URID *urid __attribute__((unused)),
277 	const LV2_Atom *body __attribute__((unused)))
278 {
279 	cairo_t *ctx = data;
280 	cairo_restore(ctx);
281 }
282 
283 static inline void
_lv2_canvas_render_translate(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)284 _lv2_canvas_render_translate(void *data,
285 	LV2_Canvas_URID *urid, const LV2_Atom *body)
286 {
287 	cairo_t *ctx = data;
288 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
289 
290 	if(v)
291 	{
292 		cairo_translate(ctx, v[0], v[1]);
293 	}
294 }
295 
296 static inline void
_lv2_canvas_render_scale(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)297 _lv2_canvas_render_scale(void *data,
298 	LV2_Canvas_URID *urid, const LV2_Atom *body)
299 {
300 	cairo_t *ctx = data;
301 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
302 
303 	if(v)
304 	{
305 		cairo_scale(ctx, v[0], v[1]);
306 	}
307 }
308 
309 static inline void
_lv2_canvas_render_rotate(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)310 _lv2_canvas_render_rotate(void *data,
311 	LV2_Canvas_URID *urid, const LV2_Atom *body)
312 {
313 	cairo_t *ctx = data;
314 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
315 
316 	if(v)
317 	{
318 		cairo_rotate(ctx, *v);
319 	}
320 }
321 
322 static inline void
_lv2_canvas_render_transform(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)323 _lv2_canvas_render_transform(void *data,
324 	LV2_Canvas_URID *urid, const LV2_Atom *body)
325 {
326 	cairo_t *ctx = data;
327 	const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
328 
329 	if(v)
330 	{
331 		const cairo_matrix_t matrix = {
332 			.xx = v[0],
333 			.xy = v[1],
334 			.x0 = v[2],
335 			.yy = v[3],
336 			.yx = v[4],
337 			.y0 = v[5]
338 		};
339 
340 		cairo_transform(ctx, &matrix);
341 	}
342 }
343 
344 static inline void
_lv2_canvas_render_reset(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)345 _lv2_canvas_render_reset(void *data,
346 	LV2_Canvas_URID *urid __attribute__((unused)),
347 	const LV2_Atom *body __attribute__((unused)))
348 {
349 	cairo_t *ctx = data;
350 		cairo_identity_matrix(ctx);
351 }
352 
353 static inline void
_lv2_canvas_render_fontSize(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)354 _lv2_canvas_render_fontSize(void *data,
355 	LV2_Canvas_URID *urid, const LV2_Atom *body)
356 {
357 	cairo_t *ctx = data;
358 	const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
359 
360 	if(v)
361 	{
362 		cairo_set_font_size(ctx, *v);
363 	}
364 }
365 
366 static inline void
_lv2_canvas_render_fillText(void * data,LV2_Canvas_URID * urid,const LV2_Atom * body)367 _lv2_canvas_render_fillText(void *data,
368 	LV2_Canvas_URID *urid, const LV2_Atom *body)
369 {
370 	cairo_t *ctx = data;
371 	const char *v = _lv2_canvas_render_get_type(body, urid->forge.String);
372 
373 	if(v)
374 	{
375 		cairo_text_extents_t extents;
376 		cairo_text_extents(ctx, v, &extents);
377 		const float dx = extents.width/2 + extents.x_bearing;
378 		const float dy = extents.height/2 + extents.y_bearing;
379 		cairo_rel_move_to(ctx, -dx, -dy);
380 		cairo_show_text(ctx, v);
381 	}
382 }
383 
384 static inline void
_lv2_canvas_qsort(LV2_Canvas_Meth * A,int n)385 _lv2_canvas_qsort(LV2_Canvas_Meth *A, int n)
386 {
387 	if(n < 2)
388 		return;
389 
390 	LV2_Canvas_Meth *p = A;
391 
392 	int i = -1;
393 	int j = n;
394 
395 	while(true)
396 	{
397 		do {
398 			i += 1;
399 		} while(A[i].command < p->command);
400 
401 		do {
402 			j -= 1;
403 		} while(A[j].command > p->command);
404 
405 		if(i >= j)
406 			break;
407 
408 		const LV2_Canvas_Meth tmp = A[i];
409 		A[i] = A[j];
410 		A[j] = tmp;
411 	}
412 
413 	_lv2_canvas_qsort(A, j + 1);
414 	_lv2_canvas_qsort(A + j + 1, n - j - 1);
415 }
416 
417 static inline LV2_Canvas_Meth *
_lv2_canvas_bsearch(LV2_URID p,LV2_Canvas_Meth * a,int n)418 _lv2_canvas_bsearch(LV2_URID p, LV2_Canvas_Meth *a, int n)
419 {
420 	LV2_Canvas_Meth *base = a;
421 
422 	for(int N = n, half; N > 1; N -= half)
423 	{
424 		half = N/2;
425 		LV2_Canvas_Meth *dst = &base[half];
426 		base = (dst->command > p) ? base : dst;
427 	}
428 
429 	return (base->command == p) ? base : NULL;
430 }
431 
432 static inline void
lv2_canvas_init(LV2_Canvas * canvas,LV2_URID_Map * map)433 lv2_canvas_init(LV2_Canvas *canvas, LV2_URID_Map *map)
434 {
435 	lv2_canvas_urid_init(&canvas->urid, map);
436 
437 	unsigned ptr = 0;
438 
439 	canvas->methods[ptr].command = canvas->urid.Canvas_BeginPath;
440 	canvas->methods[ptr++].func = _lv2_canvas_render_beginPath;
441 
442 	canvas->methods[ptr].command = canvas->urid.Canvas_ClosePath;
443 	canvas->methods[ptr++].func = _lv2_canvas_render_closePath;
444 
445 	canvas->methods[ptr].command = canvas->urid.Canvas_Arc;
446 	canvas->methods[ptr++].func = _lv2_canvas_render_arc;
447 
448 	canvas->methods[ptr].command = canvas->urid.Canvas_CurveTo;
449 	canvas->methods[ptr++].func = _lv2_canvas_render_curveTo;
450 
451 	canvas->methods[ptr].command = canvas->urid.Canvas_LineTo;
452 	canvas->methods[ptr++].func = _lv2_canvas_render_lineTo;
453 
454 	canvas->methods[ptr].command = canvas->urid.Canvas_MoveTo;
455 	canvas->methods[ptr++].func = _lv2_canvas_render_moveTo;
456 
457 	canvas->methods[ptr].command = canvas->urid.Canvas_Rectangle;
458 	canvas->methods[ptr++].func = _lv2_canvas_render_rectangle;
459 
460 	canvas->methods[ptr].command = canvas->urid.Canvas_PolyLine;
461 	canvas->methods[ptr++].func = _lv2_canvas_render_polyline;
462 
463 	canvas->methods[ptr].command = canvas->urid.Canvas_Style;
464 	canvas->methods[ptr++].func = _lv2_canvas_render_style;
465 
466 	canvas->methods[ptr].command = canvas->urid.Canvas_LineWidth;
467 	canvas->methods[ptr++].func = _lv2_canvas_render_lineWidth;
468 
469 	canvas->methods[ptr].command = canvas->urid.Canvas_LineDash;
470 	canvas->methods[ptr++].func = _lv2_canvas_render_lineDash;
471 
472 	canvas->methods[ptr].command = canvas->urid.Canvas_LineCap;
473 	canvas->methods[ptr++].func = _lv2_canvas_render_lineCap;
474 
475 	canvas->methods[ptr].command = canvas->urid.Canvas_LineJoin;
476 	canvas->methods[ptr++].func = _lv2_canvas_render_lineJoin;
477 
478 	canvas->methods[ptr].command = canvas->urid.Canvas_MiterLimit;
479 	canvas->methods[ptr++].func = _lv2_canvas_render_miterLimit;
480 
481 	canvas->methods[ptr].command = canvas->urid.Canvas_Stroke;
482 	canvas->methods[ptr++].func = _lv2_canvas_render_stroke;
483 
484 	canvas->methods[ptr].command = canvas->urid.Canvas_Fill;
485 	canvas->methods[ptr++].func = _lv2_canvas_render_fill;
486 
487 	canvas->methods[ptr].command = canvas->urid.Canvas_Clip;
488 	canvas->methods[ptr++].func = _lv2_canvas_render_clip;
489 
490 	canvas->methods[ptr].command = canvas->urid.Canvas_Save;
491 	canvas->methods[ptr++].func = _lv2_canvas_render_save;
492 
493 	canvas->methods[ptr].command = canvas->urid.Canvas_Restore;
494 	canvas->methods[ptr++].func = _lv2_canvas_render_restore;
495 
496 	canvas->methods[ptr].command = canvas->urid.Canvas_Translate;
497 	canvas->methods[ptr++].func = _lv2_canvas_render_translate;
498 
499 	canvas->methods[ptr].command = canvas->urid.Canvas_Scale;
500 	canvas->methods[ptr++].func = _lv2_canvas_render_scale;
501 
502 	canvas->methods[ptr].command = canvas->urid.Canvas_Rotate;
503 	canvas->methods[ptr++].func = _lv2_canvas_render_rotate;
504 
505 	canvas->methods[ptr].command = canvas->urid.Canvas_Transform;
506 	canvas->methods[ptr++].func = _lv2_canvas_render_transform;
507 
508 	canvas->methods[ptr].command = canvas->urid.Canvas_Reset;
509 	canvas->methods[ptr++].func = _lv2_canvas_render_reset;
510 
511 	canvas->methods[ptr].command = canvas->urid.Canvas_FontSize;
512 	canvas->methods[ptr++].func = _lv2_canvas_render_fontSize;
513 
514 	canvas->methods[ptr].command = canvas->urid.Canvas_FillText;
515 	canvas->methods[ptr++].func = _lv2_canvas_render_fillText;
516 
517 	assert(ptr == LV2_CANVAS_NUM_METHODS);
518 
519 	_lv2_canvas_qsort(canvas->methods, LV2_CANVAS_NUM_METHODS);
520 }
521 
522 static inline bool
lv2_canvas_render_body(LV2_Canvas * canvas,cairo_t * ctx,uint32_t type,uint32_t size,const LV2_Atom * body)523 lv2_canvas_render_body(LV2_Canvas *canvas, cairo_t *ctx, uint32_t type,
524 	uint32_t size, const LV2_Atom *body)
525 {
526 	LV2_Canvas_URID *urid = &canvas->urid;
527 
528 	if(!body || (type != urid->forge.Tuple) )
529 		return false;
530 
531 	// save state
532 	cairo_save(ctx);
533 
534 	// clear surface
535 	cairo_set_operator(ctx, CAIRO_OPERATOR_CLEAR);
536 	cairo_paint(ctx);
537 
538 	// default attributes
539 	cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
540 	cairo_set_font_size(ctx, 0.1);
541 	cairo_set_line_width(ctx, 0.01);
542 	cairo_set_source_rgba(ctx, 1.0, 1.0, 1.0, 1.0);
543 
544 	LV2_ATOM_TUPLE_BODY_FOREACH(body, size, itm)
545 	{
546 		if(lv2_atom_forge_is_object_type(&urid->forge, itm->type))
547 		{
548 			const LV2_Atom_Object *obj = (const LV2_Atom_Object *)itm;
549 			const LV2_Atom *body = NULL;
550 
551 			lv2_atom_object_get(obj, urid->Canvas_body, &body, 0);
552 
553 			LV2_Canvas_Meth *meth = _lv2_canvas_bsearch(obj->body.otype,
554 				canvas->methods, LV2_CANVAS_NUM_METHODS);
555 
556 			if(meth)
557 			{
558 				meth->func(ctx, urid, body);
559 			}
560 		}
561 	}
562 
563 	// save state
564 	cairo_restore(ctx);
565 
566 	// flush
567 	cairo_surface_t *surface = cairo_get_target(ctx);
568 	cairo_surface_flush(surface);
569 
570 	return true;
571 }
572 
573 static inline bool
lv2_canvas_render(LV2_Canvas * canvas,cairo_t * ctx,const LV2_Atom_Tuple * tup)574 lv2_canvas_render(LV2_Canvas *canvas, cairo_t *ctx, const LV2_Atom_Tuple *tup)
575 {
576 	return lv2_canvas_render_body(canvas, ctx, tup->atom.type, tup->atom.size,
577 		LV2_ATOM_BODY_CONST(&tup->atom));
578 }
579 
580 #ifdef __cplusplus
581 }
582 #endif
583 
584 #endif // LV2_CANVAS_RENDER_CAIRO_H
585