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