aboutsummaryrefslogtreecommitdiff
path: root/canvas.lv2/canvas.lv2
diff options
context:
space:
mode:
Diffstat (limited to 'canvas.lv2/canvas.lv2')
-rw-r--r--canvas.lv2/canvas.lv2/canvas.h195
-rw-r--r--canvas.lv2/canvas.lv2/forge.h326
-rw-r--r--canvas.lv2/canvas.lv2/idisp.h166
-rw-r--r--canvas.lv2/canvas.lv2/lv2_extensions.h174
-rw-r--r--canvas.lv2/canvas.lv2/render.h87
-rw-r--r--canvas.lv2/canvas.lv2/render_cairo.h584
-rw-r--r--canvas.lv2/canvas.lv2/render_nanovg.h597
7 files changed, 2129 insertions, 0 deletions
diff --git a/canvas.lv2/canvas.lv2/canvas.h b/canvas.lv2/canvas.lv2/canvas.h
new file mode 100644
index 0000000..7cca0fa
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/canvas.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_H
+#define _LV2_CANVAS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <lv2/lv2plug.in/ns/lv2core/lv2.h>
+#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
+#include <lv2/lv2plug.in/ns/ext/atom/forge.h>
+
+#define CANVAS_URI "http://open-music-kontrollers.ch/lv2/canvas"
+#define CANVAS_PREFIX CANVAS_URI"#"
+
+#define CANVAS__graph CANVAS_PREFIX"graph"
+#define CANVAS__body CANVAS_PREFIX"body"
+#define CANVAS__aspectRatio CANVAS_PREFIX"aspectRatio"
+
+// Graph properties and attributes
+#define CANVAS__BeginPath CANVAS_PREFIX"BeginPath"
+#define CANVAS__ClosePath CANVAS_PREFIX"ClosePath"
+#define CANVAS__Arc CANVAS_PREFIX"Arc"
+#define CANVAS__CurveTo CANVAS_PREFIX"CurveTo"
+#define CANVAS__LineTo CANVAS_PREFIX"LineTo"
+#define CANVAS__MoveTo CANVAS_PREFIX"MoveTo"
+#define CANVAS__Rectangle CANVAS_PREFIX"Rectangle"
+#define CANVAS__PolyLine CANVAS_PREFIX"PolyLine"
+#define CANVAS__Style CANVAS_PREFIX"Style"
+#define CANVAS__LineWidth CANVAS_PREFIX"LineWidth"
+#define CANVAS__LineDash CANVAS_PREFIX"LineDash"
+#define CANVAS__LineCap CANVAS_PREFIX"LineCap"
+#define CANVAS__LineJoin CANVAS_PREFIX"LineJoin"
+#define CANVAS__MiterLimit CANVAS_PREFIX"MiterLimig"
+#define CANVAS__Stroke CANVAS_PREFIX"Stroke"
+#define CANVAS__Fill CANVAS_PREFIX"Fill"
+#define CANVAS__Clip CANVAS_PREFIX"Clip"
+#define CANVAS__Save CANVAS_PREFIX"Save"
+#define CANVAS__Restore CANVAS_PREFIX"Restore"
+#define CANVAS__Translate CANVAS_PREFIX"Translate"
+#define CANVAS__Scale CANVAS_PREFIX"Scale"
+#define CANVAS__Rotate CANVAS_PREFIX"Rotate"
+#define CANVAS__Transform CANVAS_PREFIX"Transform"
+#define CANVAS__Reset CANVAS_PREFIX"Reset"
+#define CANVAS__FontSize CANVAS_PREFIX"FontSize"
+#define CANVAS__FillText CANVAS_PREFIX"FillText"
+
+#define CANVAS__lineCapButt CANVAS_PREFIX"lineCapButt"
+#define CANVAS__lineCapRound CANVAS_PREFIX"lineCapRound"
+#define CANVAS__lineCapSquare CANVAS_PREFIX"lineCapSquare"
+
+#define CANVAS__lineJoinMiter CANVAS_PREFIX"lineJoinMiter"
+#define CANVAS__lineJoinRound CANVAS_PREFIX"lineJoinRound"
+#define CANVAS__lineJoinBevel CANVAS_PREFIX"lineJoinBevel"
+
+// Input properties and attributes
+
+#define CANVAS__mouseButtonLeft CANVAS_PREFIX"mouseButtonLeft"
+#define CANVAS__mouseButtonMiddle CANVAS_PREFIX"mouseButtonMiddle"
+#define CANVAS__mouseButtonRight CANVAS_PREFIX"mouseButtonRight"
+#define CANVAS__mouseWheelX CANVAS_PREFIX"mouseWheelX"
+#define CANVAS__mouseWheelY CANVAS_PREFIX"mouseWheelY"
+#define CANVAS__mousePositionX CANVAS_PREFIX"mousePositionX"
+#define CANVAS__mousePositionY CANVAS_PREFIX"mousePositionY"
+#define CANVAS__mouseFocus CANVAS_PREFIX"mouseFocus"
+
+typedef struct _LV2_Canvas_URID LV2_Canvas_URID;
+
+struct _LV2_Canvas_URID {
+ LV2_URID Canvas_graph;
+ LV2_URID Canvas_body;
+ LV2_URID Canvas_aspectRatio;
+
+ LV2_URID Canvas_BeginPath;
+ LV2_URID Canvas_ClosePath;
+ LV2_URID Canvas_Arc;
+ LV2_URID Canvas_CurveTo;
+ LV2_URID Canvas_LineTo;
+ LV2_URID Canvas_MoveTo;
+ LV2_URID Canvas_Rectangle;
+ LV2_URID Canvas_PolyLine;
+ LV2_URID Canvas_Style;
+ LV2_URID Canvas_LineWidth;
+ LV2_URID Canvas_LineDash;
+ LV2_URID Canvas_LineCap;
+ LV2_URID Canvas_LineJoin;
+ LV2_URID Canvas_MiterLimit;
+ LV2_URID Canvas_Stroke;
+ LV2_URID Canvas_Fill;
+ LV2_URID Canvas_Clip;
+ LV2_URID Canvas_Save;
+ LV2_URID Canvas_Restore;
+ LV2_URID Canvas_Translate;
+ LV2_URID Canvas_Scale;
+ LV2_URID Canvas_Rotate;
+ LV2_URID Canvas_Transform;
+ LV2_URID Canvas_Reset;
+ LV2_URID Canvas_FontSize;
+ LV2_URID Canvas_FillText;
+
+ LV2_URID Canvas_lineCapButt;
+ LV2_URID Canvas_lineCapRound;
+ LV2_URID Canvas_lineCapSquare;
+
+ LV2_URID Canvas_lineJoinMiter;
+ LV2_URID Canvas_lineJoinRound;
+ LV2_URID Canvas_lineJoinBevel;
+
+ LV2_URID Canvas_mouseButtonLeft;
+ LV2_URID Canvas_mouseButtonMiddle;
+ LV2_URID Canvas_mouseButtonRight;
+ LV2_URID Canvas_mouseWheelX;
+ LV2_URID Canvas_mouseWheelY;
+ LV2_URID Canvas_mousePositionX;
+ LV2_URID Canvas_mousePositionY;
+ LV2_URID Canvas_mouseFocus;
+
+ LV2_Atom_Forge forge;
+};
+
+static inline void
+lv2_canvas_urid_init(LV2_Canvas_URID *urid, LV2_URID_Map *map)
+{
+ urid->Canvas_graph = map->map(map->handle, CANVAS__graph);
+ urid->Canvas_body = map->map(map->handle, CANVAS__body);
+ urid->Canvas_aspectRatio = map->map(map->handle, CANVAS__aspectRatio);
+
+ urid->Canvas_BeginPath = map->map(map->handle, CANVAS__BeginPath);
+ urid->Canvas_ClosePath = map->map(map->handle, CANVAS__ClosePath);
+ urid->Canvas_Arc = map->map(map->handle, CANVAS__Arc);
+ urid->Canvas_CurveTo = map->map(map->handle, CANVAS__CurveTo);
+ urid->Canvas_LineTo = map->map(map->handle, CANVAS__LineTo);
+ urid->Canvas_MoveTo = map->map(map->handle, CANVAS__MoveTo);
+ urid->Canvas_Rectangle = map->map(map->handle, CANVAS__Rectangle);
+ urid->Canvas_PolyLine = map->map(map->handle, CANVAS__PolyLine);
+ urid->Canvas_Style = map->map(map->handle, CANVAS__Style);
+ urid->Canvas_LineWidth = map->map(map->handle, CANVAS__LineWidth);
+ urid->Canvas_LineDash = map->map(map->handle, CANVAS__LineDash);
+ urid->Canvas_LineCap = map->map(map->handle, CANVAS__LineCap);
+ urid->Canvas_LineJoin = map->map(map->handle, CANVAS__LineJoin);
+ urid->Canvas_MiterLimit = map->map(map->handle, CANVAS__MiterLimit);
+ urid->Canvas_Stroke = map->map(map->handle, CANVAS__Stroke);
+ urid->Canvas_Fill = map->map(map->handle, CANVAS__Fill);
+ urid->Canvas_Clip = map->map(map->handle, CANVAS__Clip);
+ urid->Canvas_Save = map->map(map->handle, CANVAS__Save);
+ urid->Canvas_Restore = map->map(map->handle, CANVAS__Restore);
+ urid->Canvas_Translate = map->map(map->handle, CANVAS__Translate);
+ urid->Canvas_Scale = map->map(map->handle, CANVAS__Scale);
+ urid->Canvas_Rotate = map->map(map->handle, CANVAS__Rotate);
+ urid->Canvas_Transform = map->map(map->handle, CANVAS__Transform);
+ urid->Canvas_Reset = map->map(map->handle, CANVAS__Reset);
+ urid->Canvas_FontSize = map->map(map->handle, CANVAS__FontSize);
+ urid->Canvas_FillText = map->map(map->handle, CANVAS__FillText);
+
+ urid->Canvas_lineCapButt = map->map(map->handle, CANVAS__lineCapButt);
+ urid->Canvas_lineCapRound = map->map(map->handle, CANVAS__lineCapRound);
+ urid->Canvas_lineCapSquare = map->map(map->handle, CANVAS__lineCapSquare);
+
+ urid->Canvas_lineJoinMiter = map->map(map->handle, CANVAS__lineJoinMiter);
+ urid->Canvas_lineJoinRound = map->map(map->handle, CANVAS__lineJoinRound);
+ urid->Canvas_lineJoinBevel = map->map(map->handle, CANVAS__lineJoinBevel);
+
+ urid->Canvas_mouseButtonLeft = map->map(map->handle, CANVAS__mouseButtonLeft);
+ urid->Canvas_mouseButtonMiddle = map->map(map->handle, CANVAS__mouseButtonMiddle);
+ urid->Canvas_mouseButtonRight = map->map(map->handle, CANVAS__mouseButtonRight);
+ urid->Canvas_mouseWheelX = map->map(map->handle, CANVAS__mouseWheelX);
+ urid->Canvas_mouseWheelY = map->map(map->handle, CANVAS__mouseWheelY);
+ urid->Canvas_mousePositionX = map->map(map->handle, CANVAS__mousePositionX);
+ urid->Canvas_mousePositionY = map->map(map->handle, CANVAS__mousePositionY);
+ urid->Canvas_mouseFocus = map->map(map->handle, CANVAS__mouseFocus);
+
+ lv2_atom_forge_init(&urid->forge, map);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_CANVAS_H
diff --git a/canvas.lv2/canvas.lv2/forge.h b/canvas.lv2/canvas.lv2/forge.h
new file mode 100644
index 0000000..c5ce5fe
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/forge.h
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_FORGE_H
+#define _LV2_CANVAS_FORGE_H
+
+#include <canvas.lv2/canvas.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_simple(LV2_Atom_Forge *forge, LV2_URID otype)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_vec(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, uint32_t n, const float *vec)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_vector(forge, sizeof(float), forge->Float, n, vec);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_flt(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, float flt)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_float(forge, flt);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_lng(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, int64_t lng)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_long(forge, lng);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_prp(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, LV2_URID prop)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_urid(forge, prop);
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+_lv2_canvas_forge_str(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID otype, const char *text)
+{
+ LV2_Atom_Forge_Ref ref;
+ LV2_Atom_Forge_Frame frame;
+
+ ref = lv2_atom_forge_object(forge, &frame, 0, otype);
+ if(ref)
+ ref = lv2_atom_forge_key(forge, urid->Canvas_body);
+ if(ref)
+ ref = lv2_atom_forge_string(forge, text, strlen(text));
+ if(ref)
+ lv2_atom_forge_pop(forge, &frame);
+
+ return ref;
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_beginPath(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_BeginPath);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_closePath(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_ClosePath);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_arc(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y, float r, float a1, float a2)
+{
+ const float vec [5] = {x, y, r, a1, a2};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Arc, 5, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_curveTo(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x1, float y1, float x2, float y2, float x3, float y3)
+{
+ const float vec [6] = {x1, y1, x2, y2, x3, y3};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_CurveTo, 6, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineTo(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y)
+{
+ const float vec [2] = {x, y};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_LineTo, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_moveTo(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y)
+{
+ const float vec [2] = {x, y};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_MoveTo, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_rectangle(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y, float w, float h)
+{
+ const float vec [4] = {x, y, w, h};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Rectangle, 4, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_polyLine(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ uint32_t n, const float *vec)
+{
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_PolyLine, n, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_style(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ uint32_t style)
+{
+ return _lv2_canvas_forge_lng(forge, urid, urid->Canvas_Style, style);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineWidth(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float line_width)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_LineWidth, line_width);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineDash(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float dash_length, float separator_length)
+{
+ const float vec [2] = {dash_length, separator_length};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_LineDash, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineCap(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID line_cap)
+{
+ return _lv2_canvas_forge_prp(forge, urid, urid->Canvas_LineCap, line_cap);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_lineJoin(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ LV2_URID line_join)
+{
+ return _lv2_canvas_forge_prp(forge, urid, urid->Canvas_LineJoin, line_join);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_miterLimit(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float miter_limit)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_MiterLimit, miter_limit);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_stroke(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Stroke);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_fill(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Fill);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_clip(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Clip);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_save(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Save);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_restore(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Restore);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_translate(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float x, float y)
+{
+ const float vec [2] = {x, y};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Translate, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_scale(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float w, float h)
+{
+ const float vec [2] = {w, h};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Scale, 2, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_rotate(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float a)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_Rotate, a);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_transform(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float xx, float xy, float x0, float yy, float yx, float y0)
+{
+ const float vec [6] = {xx, xy, x0, yy, yx, y0};
+
+ return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Transform, 6, vec);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_reset(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid)
+{
+ return _lv2_canvas_forge_simple(forge, urid->Canvas_Reset);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_fontSize(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ float size)
+{
+ return _lv2_canvas_forge_flt(forge, urid, urid->Canvas_FontSize, size);
+}
+
+static inline LV2_Atom_Forge_Ref
+lv2_canvas_forge_fillText(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid,
+ const char *text)
+{
+ return _lv2_canvas_forge_str(forge, urid, urid->Canvas_FillText, text);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_CANVAS_FORGE_H
diff --git a/canvas.lv2/canvas.lv2/idisp.h b/canvas.lv2/canvas.lv2/idisp.h
new file mode 100644
index 0000000..afe7371
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/idisp.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_IDISP_H
+#define _LV2_CANVAS_IDISP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LV2_CANVAS_RENDER_CAIRO
+#include <canvas.lv2/render.h>
+#include <canvas.lv2/lv2_extensions.h>
+
+typedef struct _LV2_Canvas_Idisp LV2_Canvas_Idisp;
+
+struct _LV2_Canvas_Idisp {
+ LV2_Inline_Display *queue_draw;
+ LV2_Canvas canvas;
+ LV2_Inline_Display_Image_Surface image_surface;
+ struct {
+ cairo_surface_t *surface;
+ cairo_t *ctx;
+ } cairo;
+};
+
+static inline LV2_Inline_Display_Image_Surface *
+_lv2_canvas_idisp_surf_init(LV2_Canvas_Idisp *idisp, int w, int h)
+{
+ LV2_Inline_Display_Image_Surface *surf = &idisp->image_surface;
+
+ surf->width = w;
+ surf->height = w > h ? h : w; // try to use 1:1 ratio
+ surf->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, surf->width);
+ surf->data = realloc(surf->data, surf->stride * surf->height);
+ if(!surf->data)
+ {
+ return NULL;
+ }
+
+ idisp->cairo.surface = cairo_image_surface_create_for_data(
+ surf->data, CAIRO_FORMAT_ARGB32, surf->width, surf->height, surf->stride);
+
+ if(idisp->cairo.surface)
+ {
+ cairo_surface_set_device_scale(idisp->cairo.surface, surf->width, surf->height);
+
+ idisp->cairo.ctx = cairo_create(idisp->cairo.surface);
+ if(idisp->cairo.ctx)
+ {
+ cairo_select_font_face(idisp->cairo.ctx, "cairo:monospace",
+ CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
+ }
+ }
+
+ return surf;
+}
+
+static inline void
+_lv2_canvas_idisp_surf_deinit(LV2_Canvas_Idisp *idisp)
+{
+ LV2_Inline_Display_Image_Surface *surf = &idisp->image_surface;
+
+ if(idisp->cairo.ctx)
+ {
+ cairo_destroy(idisp->cairo.ctx);
+ idisp->cairo.ctx = NULL;
+ }
+
+ if(idisp->cairo.surface)
+ {
+ cairo_surface_finish(idisp->cairo.surface);
+ cairo_surface_destroy(idisp->cairo.surface);
+ idisp->cairo.surface = NULL;
+ }
+
+ if(surf->data)
+ {
+ free(surf->data);
+ surf->data = NULL;
+ }
+}
+
+static inline LV2_Inline_Display_Image_Surface *
+lv2_canvas_idisp_surf_configure(LV2_Canvas_Idisp *idisp,
+ uint32_t w, uint32_t h, float aspect_ratio)
+{
+ LV2_Inline_Display_Image_Surface *surf = &idisp->image_surface;
+ int W;
+ int H;
+
+ if(aspect_ratio < 1.f)
+ {
+ W = h * aspect_ratio;
+ H = h;
+ }
+ else if(aspect_ratio > 1.f)
+ {
+ W = w;
+ H = w / aspect_ratio;
+ }
+ else // aspect_ratio == 0.f
+ {
+ W = w;
+ H = h;
+ }
+
+ if( (surf->width != W) || (surf->height != H) || !surf->data)
+ {
+ _lv2_canvas_idisp_surf_deinit(idisp);
+ surf = _lv2_canvas_idisp_surf_init(idisp, W, H);
+ }
+
+ return surf;
+}
+
+static inline void
+lv2_canvas_idisp_init(LV2_Canvas_Idisp *idisp, LV2_Inline_Display *queue_draw,
+ LV2_URID_Map *map)
+{
+ lv2_canvas_init(&idisp->canvas, map);
+ idisp->queue_draw = queue_draw;
+}
+
+static inline void
+lv2_canvas_idisp_deinit(LV2_Canvas_Idisp *idisp)
+{
+ _lv2_canvas_idisp_surf_deinit(idisp);
+}
+
+static inline void
+lv2_canvas_idisp_queue_draw(LV2_Canvas_Idisp *idisp)
+{
+ if(idisp->queue_draw)
+ {
+ idisp->queue_draw->queue_draw(idisp->queue_draw->handle);
+ }
+}
+
+static inline bool
+lv2_canvas_idisp_render_body(LV2_Canvas_Idisp *idisp, uint32_t type,
+ uint32_t size, const LV2_Atom *body)
+{
+ return lv2_canvas_render_body(&idisp->canvas, idisp->cairo.ctx,
+ type, size, body);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _LV2_CANVAS_IDISP_H
diff --git a/canvas.lv2/canvas.lv2/lv2_extensions.h b/canvas.lv2/canvas.lv2/lv2_extensions.h
new file mode 100644
index 0000000..64fc3bc
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/lv2_extensions.h
@@ -0,0 +1,174 @@
+/*
+ Copyright 2016 Robin Gareus <robin@gareus.org>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef _ardour_lv2_extensions_h_
+#define _ardour_lv2_extensions_h_
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+/**
+ @defgroup inlinedisplay Inline-Display
+
+ Support for displaying a miniaturized generic view
+ directly in the host's Mixer Window.
+
+ @{
+*/
+
+#define LV2_INLINEDISPLAY_URI "http://harrisonconsoles.com/lv2/inlinedisplay"
+#define LV2_INLINEDISPLAY_PREFIX LV2_INLINEDISPLAY_URI "#"
+#define LV2_INLINEDISPLAY__interface LV2_INLINEDISPLAY_PREFIX "interface"
+#define LV2_INLINEDISPLAY__queue_draw LV2_INLINEDISPLAY_PREFIX "queue_draw"
+
+/** Opaque handle for LV2_Inline_Display::queue_draw() */
+typedef void* LV2_Inline_Display_Handle;
+
+/** raw image pixmap format is ARGB32,
+ * the data pointer is owned by the plugin and must be valid
+ * from the first call to render until cleanup.
+ */
+typedef struct {
+ unsigned char *data;
+ int width;
+ int height;
+ int stride;
+} LV2_Inline_Display_Image_Surface;
+
+/** a LV2 Feature provided by the Host to the plugin */
+typedef struct {
+ /** Opaque host data */
+ LV2_Inline_Display_Handle handle;
+ /** Request from run() that the host should call render() at a later time
+ * to update the inline display */
+ void (*queue_draw)(LV2_Inline_Display_Handle handle);
+} LV2_Inline_Display;
+
+/**
+ * Plugin Inline-Display Interface.
+ */
+typedef struct {
+ /**
+ * The render method. This is called by the host in a non-realtime context,
+ * usually the main GUI thread.
+ * The data pointer is owned by the plugin and must be valid
+ * from the first call to render until cleanup.
+ *
+ * @param instance The LV2 instance
+ * @param w the max available width
+ * @param h the max available height
+ * @return pointer to a LV2_Inline_Display_Image_Surface or NULL
+ */
+ LV2_Inline_Display_Image_Surface* (*render)(LV2_Handle instance, uint32_t w, uint32_t h);
+} LV2_Inline_Display_Interface;
+
+/**
+ @}
+*/
+
+/**
+ @defgroup automate Self-Automation
+
+ Support for plugins to write automation data via Atom Events
+
+ @{
+*/
+
+#define LV2_AUTOMATE_URI "http://ardour.org/lv2/automate"
+#define LV2_AUTOMATE_URI_PREFIX LV2_AUTOMATE_URI "#"
+/** an lv2:optionalFeature */
+#define LV2_AUTOMATE_URI__can_write LV2_AUTOMATE_URI_PREFIX "canWriteAutomatation"
+/** atom:supports */
+#define LV2_AUTOMATE_URI__control LV2_AUTOMATE_URI_PREFIX "automationControl"
+/** lv2:portProperty */
+#define LV2_AUTOMATE_URI__controlled LV2_AUTOMATE_URI_PREFIX "automationControlled"
+#define LV2_AUTOMATE_URI__controller LV2_AUTOMATE_URI_PREFIX "automationController"
+
+/** atom messages */
+#define LV2_AUTOMATE_URI__event LV2_AUTOMATE_URI_PREFIX "event"
+#define LV2_AUTOMATE_URI__setup LV2_AUTOMATE_URI_PREFIX "setup"
+#define LV2_AUTOMATE_URI__finalize LV2_AUTOMATE_URI_PREFIX "finalize"
+#define LV2_AUTOMATE_URI__start LV2_AUTOMATE_URI_PREFIX "start"
+#define LV2_AUTOMATE_URI__end LV2_AUTOMATE_URI_PREFIX "end"
+#define LV2_AUTOMATE_URI__parameter LV2_AUTOMATE_URI_PREFIX "parameter"
+#define LV2_AUTOMATE_URI__value LV2_AUTOMATE_URI_PREFIX "value"
+
+/**
+ @}
+*/
+
+/**
+ @defgroup license License-Report
+
+ Allow for commercial LV2 to report their
+ licensing status.
+
+ @{
+*/
+
+#define LV2_PLUGINLICENSE_URI "http://harrisonconsoles.com/lv2/license"
+#define LV2_PLUGINLICENSE_PREFIX LV2_PLUGINLICENSE_URI "#"
+#define LV2_PLUGINLICENSE__interface LV2_PLUGINLICENSE_PREFIX "interface"
+
+typedef struct _LV2_License_Interface {
+ /* @return -1 if no license is needed; 0 if unlicensed, 1 if licensed */
+ int (*is_licensed)(LV2_Handle instance);
+ /* @return a string copy of the licensee name if licensed, or NULL, the caller needs to free this */
+ char* (*licensee)(LV2_Handle instance);
+ /* @return a URI identifying the plugin-bundle or plugin for which a given license is valid */
+ const char* (*product_uri)(LV2_Handle instance);
+ /* @return human readable product name for the URI */
+ const char* (*product_name)(LV2_Handle instance);
+ /* @return link to website or webstore */
+ const char* (*store_url)(LV2_Handle instance);
+} LV2_License_Interface;
+
+/**
+ @}
+*/
+
+/**
+ @defgroup plugin provided bypass
+
+ A port with the designation "processing#enable" must
+ control a plugin's internal bypass mode.
+
+ If the port value is larger than zero the plugin processes
+ normally.
+
+ If the port value is zero, the plugin is expected to bypass
+ all signals unmodified.
+
+ The plugin is responsible for providing a click-free transition
+ between the states.
+
+ (values less than zero are reserved for future use:
+ e.g click-free insert/removal of latent plugins.
+ Generally values <= 0 are to be treated as bypassed.)
+
+ lv2:designation <http://ardour.org/lv2/processing#enable> ;
+
+ @{
+*/
+
+#define LV2_PROCESSING_URI "http://ardour.org/lv2/processing"
+#define LV2_PROCESSING_URI_PREFIX LV2_PROCESSING_URI "#"
+#define LV2_PROCESSING_URI__enable LV2_PROCESSING_URI_PREFIX "enable"
+
+/**
+ @}
+*/
+
+#endif
diff --git a/canvas.lv2/canvas.lv2/render.h b/canvas.lv2/canvas.lv2/render.h
new file mode 100644
index 0000000..b809804
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/render.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_RENDER_H
+#define _LV2_CANVAS_RENDER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <canvas.lv2/canvas.h>
+
+#define LV2_CANVAS_NUM_METHODS 26
+
+typedef struct _LV2_Canvas_Meth LV2_Canvas_Meth;
+typedef struct _LV2_Canvas LV2_Canvas;
+typedef void (*LV2_Canvas_Func)(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body);
+
+struct _LV2_Canvas_Meth {
+ LV2_URID command;
+ LV2_Canvas_Func func;
+};
+
+struct _LV2_Canvas {
+ LV2_Canvas_URID urid;
+ LV2_Canvas_Meth methods [LV2_CANVAS_NUM_METHODS];
+};
+
+static inline const float *
+_lv2_canvas_render_get_float_vecs(LV2_Canvas_URID *urid, const LV2_Atom *body,
+ uint32_t *n)
+{
+ const LV2_Atom_Vector *vec = (const LV2_Atom_Vector *)body;
+ const float *flt = LV2_ATOM_CONTENTS_CONST(LV2_Atom_Vector, vec);
+ *n = (vec->atom.type == urid->forge.Vector)
+ && (vec->body.child_type == urid->forge.Float)
+ && (vec->body.child_size == sizeof(float))
+ ? (vec->atom.size - sizeof(LV2_Atom_Vector_Body)) / vec->body.child_size
+ : 0;
+
+ return flt;
+}
+
+static inline const float *
+_lv2_canvas_render_get_float_vec(LV2_Canvas_URID *urid, const LV2_Atom *body,
+ uint32_t n)
+{
+ uint32_t N;
+ const float *flt = _lv2_canvas_render_get_float_vecs(urid, body, &N);
+
+ return n == N ? flt : NULL;
+}
+
+static inline const void *
+_lv2_canvas_render_get_type(const LV2_Atom *body, LV2_URID type)
+{
+ return body->type == type
+ ? LV2_ATOM_BODY_CONST(body)
+ : NULL;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(LV2_CANVAS_RENDER_NANOVG)
+# include <canvas.lv2/render_nanovg.h>
+#else
+# include <canvas.lv2/render_cairo.h>
+#endif
+
+#endif // _LV2_CANVAS_RENDER_H
diff --git a/canvas.lv2/canvas.lv2/render_cairo.h b/canvas.lv2/canvas.lv2/render_cairo.h
new file mode 100644
index 0000000..a41c72c
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/render_cairo.h
@@ -0,0 +1,584 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_RENDER_CAIRO_H
+#define _LV2_CANVAS_RENDER_CAIRO_H
+
+#include <assert.h>
+
+#include <canvas.lv2/canvas.h>
+
+#include <cairo/cairo.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void
+_lv2_canvas_render_beginPath(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_new_sub_path(ctx);
+}
+
+static inline void
+_lv2_canvas_render_closePath(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_close_path(ctx);
+}
+
+static inline void
+_lv2_canvas_render_arc(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 5);
+
+ if(v)
+ {
+ cairo_arc(ctx, v[0], v[1], v[2], v[3], v[4]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_curveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ cairo_curve_to(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_line_to(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_moveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_move_to(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rectangle(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 4);
+
+ if(v)
+ {
+ cairo_rectangle(ctx, v[0], v[1], v[2], v[3]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_polyline(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ uint32_t N;
+ const float *v = _lv2_canvas_render_get_float_vecs(urid, body, &N);
+
+ if(v)
+ {
+ for(uint32_t i = 0; i < 2; i += 2)
+ {
+ cairo_move_to(ctx, v[i], v[i+1]);
+ }
+
+ for(uint32_t i = 2; i < N; i += 2)
+ {
+ cairo_line_to(ctx, v[i], v[i+1]);
+ }
+ }
+}
+
+static inline void
+_lv2_canvas_render_style(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const int64_t *v = _lv2_canvas_render_get_type(body, urid->forge.Long);
+
+ if(v)
+ {
+ const float r = (float)((*v >> 24) & 0xff) / 0xff;
+ const float g = (float)((*v >> 16) & 0xff) / 0xff;
+ const float b = (float)((*v >> 8) & 0xff) / 0xff;
+ const float a = (float)((*v >> 0) & 0xff) / 0xff;
+
+ cairo_set_source_rgba(ctx, r, g, b, a);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineWidth(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_set_line_width(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineDash(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ const double d[2] = {v[0], v[1]};
+ cairo_set_dash(ctx, d, 2, 0);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineCap(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ cairo_line_cap_t cap = CAIRO_LINE_CAP_BUTT;
+
+ if(*v == urid->Canvas_lineCapButt)
+ cap = CAIRO_LINE_CAP_BUTT;
+ else if(*v == urid->Canvas_lineCapRound)
+ cap = CAIRO_LINE_CAP_ROUND;
+ else if(*v == urid->Canvas_lineCapSquare)
+ cap = CAIRO_LINE_CAP_SQUARE;
+
+ cairo_set_line_cap(ctx, cap);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineJoin(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ cairo_line_join_t join = CAIRO_LINE_JOIN_MITER;
+
+ if(*v == urid->Canvas_lineJoinMiter)
+ join = CAIRO_LINE_JOIN_MITER;
+ else if(*v == urid->Canvas_lineJoinRound)
+ join = CAIRO_LINE_JOIN_ROUND;
+ else if(*v == urid->Canvas_lineJoinBevel)
+ join = CAIRO_LINE_JOIN_BEVEL;
+
+ cairo_set_line_join(ctx, join);
+ }
+}
+
+static inline void
+_lv2_canvas_render_miterLimit(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_set_miter_limit(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_stroke(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_stroke(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fill(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_fill(ctx);
+}
+
+static inline void
+_lv2_canvas_render_clip(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_clip(ctx);
+}
+
+static inline void
+_lv2_canvas_render_save(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_save(ctx);
+}
+
+static inline void
+_lv2_canvas_render_restore(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_restore(ctx);
+}
+
+static inline void
+_lv2_canvas_render_translate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_translate(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_scale(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ cairo_scale(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rotate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_rotate(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_transform(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ const cairo_matrix_t matrix = {
+ .xx = v[0],
+ .xy = v[1],
+ .x0 = v[2],
+ .yy = v[3],
+ .yx = v[4],
+ .y0 = v[5]
+ };
+
+ cairo_transform(ctx, &matrix);
+ }
+}
+
+static inline void
+_lv2_canvas_render_reset(void *data,
+ LV2_Canvas_URID *urid __attribute__((unused)),
+ const LV2_Atom *body __attribute__((unused)))
+{
+ cairo_t *ctx = data;
+ cairo_identity_matrix(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fontSize(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ cairo_set_font_size(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_fillText(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ cairo_t *ctx = data;
+ const char *v = _lv2_canvas_render_get_type(body, urid->forge.String);
+
+ if(v)
+ {
+ cairo_text_extents_t extents;
+ cairo_text_extents(ctx, v, &extents);
+ const float dx = extents.width/2 + extents.x_bearing;
+ const float dy = extents.height/2 + extents.y_bearing;
+ cairo_rel_move_to(ctx, -dx, -dy);
+ cairo_show_text(ctx, v);
+ }
+}
+
+static inline void
+_lv2_canvas_qsort(LV2_Canvas_Meth *A, int n)
+{
+ if(n < 2)
+ return;
+
+ LV2_Canvas_Meth *p = A;
+
+ int i = -1;
+ int j = n;
+
+ while(true)
+ {
+ do {
+ i += 1;
+ } while(A[i].command < p->command);
+
+ do {
+ j -= 1;
+ } while(A[j].command > p->command);
+
+ if(i >= j)
+ break;
+
+ const LV2_Canvas_Meth tmp = A[i];
+ A[i] = A[j];
+ A[j] = tmp;
+ }
+
+ _lv2_canvas_qsort(A, j + 1);
+ _lv2_canvas_qsort(A + j + 1, n - j - 1);
+}
+
+static inline LV2_Canvas_Meth *
+_lv2_canvas_bsearch(LV2_URID p, LV2_Canvas_Meth *a, int n)
+{
+ LV2_Canvas_Meth *base = a;
+
+ for(int N = n, half; N > 1; N -= half)
+ {
+ half = N/2;
+ LV2_Canvas_Meth *dst = &base[half];
+ base = (dst->command > p) ? base : dst;
+ }
+
+ return (base->command == p) ? base : NULL;
+}
+
+static inline void
+lv2_canvas_init(LV2_Canvas *canvas, LV2_URID_Map *map)
+{
+ lv2_canvas_urid_init(&canvas->urid, map);
+
+ unsigned ptr = 0;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_BeginPath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_beginPath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_ClosePath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_closePath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Arc;
+ canvas->methods[ptr++].func = _lv2_canvas_render_arc;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_CurveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_curveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MoveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_moveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rectangle;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rectangle;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_PolyLine;
+ canvas->methods[ptr++].func = _lv2_canvas_render_polyline;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Style;
+ canvas->methods[ptr++].func = _lv2_canvas_render_style;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineWidth;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineWidth;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineDash;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineDash;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineCap;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineCap;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineJoin;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineJoin;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MiterLimit;
+ canvas->methods[ptr++].func = _lv2_canvas_render_miterLimit;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Stroke;
+ canvas->methods[ptr++].func = _lv2_canvas_render_stroke;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Fill;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fill;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Clip;
+ canvas->methods[ptr++].func = _lv2_canvas_render_clip;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Save;
+ canvas->methods[ptr++].func = _lv2_canvas_render_save;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Restore;
+ canvas->methods[ptr++].func = _lv2_canvas_render_restore;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Translate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_translate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Scale;
+ canvas->methods[ptr++].func = _lv2_canvas_render_scale;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rotate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rotate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Transform;
+ canvas->methods[ptr++].func = _lv2_canvas_render_transform;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Reset;
+ canvas->methods[ptr++].func = _lv2_canvas_render_reset;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FontSize;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fontSize;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FillText;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fillText;
+
+ assert(ptr == LV2_CANVAS_NUM_METHODS);
+
+ _lv2_canvas_qsort(canvas->methods, LV2_CANVAS_NUM_METHODS);
+}
+
+static inline bool
+lv2_canvas_render_body(LV2_Canvas *canvas, cairo_t *ctx, uint32_t type,
+ uint32_t size, const LV2_Atom *body)
+{
+ LV2_Canvas_URID *urid = &canvas->urid;
+
+ if(!body || (type != urid->forge.Tuple) )
+ return false;
+
+ // save state
+ cairo_save(ctx);
+
+ // clear surface
+ cairo_set_operator(ctx, CAIRO_OPERATOR_CLEAR);
+ cairo_paint(ctx);
+
+ // default attributes
+ cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
+ cairo_set_font_size(ctx, 0.1);
+ cairo_set_line_width(ctx, 0.01);
+ cairo_set_source_rgba(ctx, 1.0, 1.0, 1.0, 1.0);
+
+ LV2_ATOM_TUPLE_BODY_FOREACH(body, size, itm)
+ {
+ if(lv2_atom_forge_is_object_type(&urid->forge, itm->type))
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)itm;
+ const LV2_Atom *body = NULL;
+
+ lv2_atom_object_get(obj, urid->Canvas_body, &body, 0);
+
+ LV2_Canvas_Meth *meth = _lv2_canvas_bsearch(obj->body.otype,
+ canvas->methods, LV2_CANVAS_NUM_METHODS);
+
+ if(meth)
+ {
+ meth->func(ctx, urid, body);
+ }
+ }
+ }
+
+ // save state
+ cairo_restore(ctx);
+
+ // flush
+ cairo_surface_t *surface = cairo_get_target(ctx);
+ cairo_surface_flush(surface);
+
+ return true;
+}
+
+static inline bool
+lv2_canvas_render(LV2_Canvas *canvas, cairo_t *ctx, const LV2_Atom_Tuple *tup)
+{
+ return lv2_canvas_render_body(canvas, ctx, tup->atom.type, tup->atom.size,
+ LV2_ATOM_BODY_CONST(&tup->atom));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // LV2_CANVAS_RENDER_CAIRO_H
diff --git a/canvas.lv2/canvas.lv2/render_nanovg.h b/canvas.lv2/canvas.lv2/render_nanovg.h
new file mode 100644
index 0000000..a98bc95
--- /dev/null
+++ b/canvas.lv2/canvas.lv2/render_nanovg.h
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2016 Hanspeter Portner (dev@open-music-kontrollers.ch)
+ *
+ * This is free software: you can redistribute it and/or modify
+ * it under the terms of the Artistic License 2.0 as published by
+ * The Perl Foundation.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Artistic License 2.0 for more details.
+ *
+ * You should have received a copy of the Artistic License 2.0
+ * along the source as a COPYING file. If not, obtain it from
+ * http://www.perlfoundation.org/artistic_license_2_0.
+ */
+
+#ifndef _LV2_CANVAS_RENDER_NANOVG_H
+#define _LV2_CANVAS_RENDER_NANOVG_H
+
+#include <assert.h>
+
+#include <nanovg.h>
+
+#if defined(__APPLE__)
+# include <OpenGL/gl.h>
+# include <OpenGL/glext.h>
+#else
+# include <GL/glew.h>
+#endif
+
+#define NANOVG_GL2_IMPLEMENTATION
+#include <nanovg_gl.h>
+
+#if defined(NANOVG_GL2_IMPLEMENTATION)
+# define nvgCreate nvgCreateGL2
+# define nvgDelete nvgDeleteGL2
+#elif defined(NANOVG_GL3_IMPLEMENTATION)
+# define nvgCreate nvgCreateGL3
+# define nvgDelete nvgDeleteGL3
+#elif defined(NANOVG_GLES2_IMPLEMENTATION)
+# define nvgCreate nvgCreateGLES2
+# define nvgDelete nvgDeleteGLES2
+#elif defined(NANOVG_GLES3_IMPLEMENTATION)
+# define nvgCreate nvgCreateGLES3
+# define nvgDelete nvgDeleteGLES3
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static inline void
+_lv2_canvas_render_beginPath(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgBeginPath(ctx);
+}
+
+static inline void
+_lv2_canvas_render_closePath(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgClosePath(ctx);
+}
+
+static inline void
+_lv2_canvas_render_arc(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 5);
+
+ if(v)
+ {
+ nvgArc(ctx, v[0], v[1], v[2], v[3], v[4], NVG_CCW);
+ }
+}
+
+static inline void
+_lv2_canvas_render_curveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ nvgBezierTo(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgLineTo(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_moveTo(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgMoveTo(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rectangle(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 4);
+
+ if(v)
+ {
+ nvgRect(ctx, v[0], v[1], v[2], v[3]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_polyline(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ uint32_t N;
+ const float *v = _lv2_canvas_render_get_float_vecs(urid, body, &N);
+
+ if(v)
+ {
+ for(uint32_t i = 0; i < 2; i += 2)
+ {
+ nvgMoveTo(ctx, v[i], v[i+1]);
+ }
+
+ for(uint32_t i = 2; i < N; i += 2)
+ {
+ nvgLineTo(ctx, v[i], v[i+1]);
+ }
+ }
+}
+
+static inline void
+_lv2_canvas_render_style(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const int64_t *v = _lv2_canvas_render_get_type(body, urid->forge.Long);
+
+ if(v)
+ {
+ const uint8_t r = (*v >> 24) & 0xff;
+ const uint8_t g = (*v >> 16) & 0xff;
+ const uint8_t b = (*v >> 8) & 0xff;
+ const uint8_t a = (*v >> 0) & 0xff;
+
+ nvgStrokeColor(ctx, nvgRGBA(r, g, b, a));
+ nvgFillColor(ctx, nvgRGBA(r, g, b, a));
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineWidth(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgStrokeWidth(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineDash(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ (void)ctx; //FIXME
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ //const double d[2] = {v[0], v[1]};
+ //FIXME cairo_set_dash(ctx, d, 2, 0);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineCap(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ int cap = NVG_BUTT;
+
+ if(*v == urid->Canvas_lineCapButt)
+ cap = NVG_BUTT;
+ else if(*v == urid->Canvas_lineCapRound)
+ cap = NVG_ROUND;
+ else if(*v == urid->Canvas_lineCapSquare)
+ cap = NVG_SQUARE;
+
+ nvgLineCap(ctx, cap);
+ }
+}
+
+static inline void
+_lv2_canvas_render_lineJoin(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const LV2_URID *v = _lv2_canvas_render_get_type(body, urid->forge.URID);
+
+ if(v)
+ {
+ int join = NVG_MITER;
+
+ if(*v == urid->Canvas_lineJoinMiter)
+ join = NVG_MITER;
+ else if(*v == urid->Canvas_lineJoinRound)
+ join = NVG_ROUND;
+ else if(*v == urid->Canvas_lineJoinBevel)
+ join = NVG_BEVEL;
+
+ nvgLineJoin(ctx, join);
+ }
+}
+
+static inline void
+_lv2_canvas_render_miterLimit(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgMiterLimit(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_stroke(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgStroke(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fill(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgFill(ctx);
+}
+
+static inline void
+_lv2_canvas_render_clip(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ (void)ctx; //FIXME
+ //FIXME cairo_clip(ctx);
+}
+
+static inline void
+_lv2_canvas_render_save(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgSave(ctx);
+}
+
+static inline void
+_lv2_canvas_render_restore(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgRestore(ctx);
+}
+
+static inline void
+_lv2_canvas_render_translate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgTranslate(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_scale(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 2);
+
+ if(v)
+ {
+ nvgScale(ctx, v[0], v[1]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_rotate(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgRotate(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_transform(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_float_vec(urid, body, 6);
+
+ if(v)
+ {
+ nvgTransform(ctx, v[0], v[1], v[2], v[3], v[4], v[5]);
+ }
+}
+
+static inline void
+_lv2_canvas_render_reset(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ nvgReset(ctx);
+}
+
+static inline void
+_lv2_canvas_render_fontSize(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ const float *v = _lv2_canvas_render_get_type(body, urid->forge.Float);
+
+ if(v)
+ {
+ nvgFontSize(ctx, *v);
+ }
+}
+
+static inline void
+_lv2_canvas_render_fillText(void *data,
+ LV2_Canvas_URID *urid, const LV2_Atom *body)
+{
+ NVGcontext *ctx = data;
+ (void)ctx; //FIXME
+ const char *v = _lv2_canvas_render_get_type(body, urid->forge.String);
+
+ if(v)
+ {
+ /* FIXME
+ NVGcontextext_extents_t extents;
+ NVGcontextext_extents(ctx, v, &extents);
+ const float dx = extents.width/2 + extents.x_bearing;
+ const float dy = extents.height/2 + extents.y_bearing;
+ cairo_rel_move_to(ctx, -dx, -dy);
+ cairo_show_text(ctx, v);
+ */
+ /*
+ float bounds [4];
+ const float adv_x = nvgTextBounds(ctx, 0.f, 0.f, v, NULL, bounds);
+ nvgText(ctx, float x, float y, const char* string, const char* end);
+ */
+ }
+}
+
+static inline void
+_lv2_canvas_qsort(LV2_Canvas_Meth *A, int n)
+{
+ if(n < 2)
+ return;
+
+ LV2_Canvas_Meth *p = A;
+
+ int i = -1;
+ int j = n;
+
+ while(true)
+ {
+ do {
+ i += 1;
+ } while(A[i].command < p->command);
+
+ do {
+ j -= 1;
+ } while(A[j].command > p->command);
+
+ if(i >= j)
+ break;
+
+ const LV2_Canvas_Meth tmp = A[i];
+ A[i] = A[j];
+ A[j] = tmp;
+ }
+
+ _lv2_canvas_qsort(A, j + 1);
+ _lv2_canvas_qsort(A + j + 1, n - j - 1);
+}
+
+static inline LV2_Canvas_Meth *
+_lv2_canvas_bsearch(LV2_URID p, LV2_Canvas_Meth *a, int n)
+{
+ LV2_Canvas_Meth *base = a;
+
+ for(int N = n, half; N > 1; N -= half)
+ {
+ half = N/2;
+ LV2_Canvas_Meth *dst = &base[half];
+ base = (dst->command > p) ? base : dst;
+ }
+
+ return (base->command == p) ? base : NULL;
+}
+
+static inline void
+lv2_canvas_init(LV2_Canvas *canvas, LV2_URID_Map *map)
+{
+ lv2_canvas_urid_init(&canvas->urid, map);
+
+ unsigned ptr = 0;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_BeginPath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_beginPath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_ClosePath;
+ canvas->methods[ptr++].func = _lv2_canvas_render_closePath;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Arc;
+ canvas->methods[ptr++].func = _lv2_canvas_render_arc;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_CurveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_curveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MoveTo;
+ canvas->methods[ptr++].func = _lv2_canvas_render_moveTo;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rectangle;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rectangle;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_PolyLine;
+ canvas->methods[ptr++].func = _lv2_canvas_render_polyline;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Style;
+ canvas->methods[ptr++].func = _lv2_canvas_render_style;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineWidth;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineWidth;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineDash;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineDash;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineCap;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineCap;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_LineJoin;
+ canvas->methods[ptr++].func = _lv2_canvas_render_lineJoin;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_MiterLimit;
+ canvas->methods[ptr++].func = _lv2_canvas_render_miterLimit;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Stroke;
+ canvas->methods[ptr++].func = _lv2_canvas_render_stroke;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Fill;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fill;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Clip;
+ canvas->methods[ptr++].func = _lv2_canvas_render_clip;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Save;
+ canvas->methods[ptr++].func = _lv2_canvas_render_save;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Restore;
+ canvas->methods[ptr++].func = _lv2_canvas_render_restore;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Translate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_translate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Scale;
+ canvas->methods[ptr++].func = _lv2_canvas_render_scale;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Rotate;
+ canvas->methods[ptr++].func = _lv2_canvas_render_rotate;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Transform;
+ canvas->methods[ptr++].func = _lv2_canvas_render_transform;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_Reset;
+ canvas->methods[ptr++].func = _lv2_canvas_render_reset;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FontSize;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fontSize;
+
+ canvas->methods[ptr].command = canvas->urid.Canvas_FillText;
+ canvas->methods[ptr++].func = _lv2_canvas_render_fillText;
+
+ assert(ptr == LV2_CANVAS_NUM_METHODS);
+
+ _lv2_canvas_qsort(canvas->methods, LV2_CANVAS_NUM_METHODS);
+}
+
+static inline bool
+lv2_canvas_render_body(LV2_Canvas *canvas, NVGcontext *ctx, uint32_t type,
+ uint32_t size, const LV2_Atom *body)
+{
+ LV2_Canvas_URID *urid = &canvas->urid;
+
+ if(!body || (type != urid->forge.Tuple) )
+ return false;
+
+ // save state
+ nvgSave(ctx);
+
+ // clear surface
+ nvgBeginPath(ctx);
+ nvgRect(ctx, 0, 0, 1.f, 1.f);
+ nvgFillColor(ctx, nvgRGBA(0x1e, 0x1e, 0x1e, 0xff));
+ nvgFill(ctx);
+
+ nvgFontSize(ctx, 0.1);
+ nvgStrokeWidth(ctx, 0.01);
+ nvgStrokeColor(ctx, nvgRGBA(0xff, 0xff, 0xff, 0xff));
+ nvgFillColor(ctx, nvgRGBA(0xff, 0xff, 0xff, 0xff));
+
+ LV2_ATOM_TUPLE_BODY_FOREACH(body, size, itm)
+ {
+ if(lv2_atom_forge_is_object_type(&urid->forge, itm->type))
+ {
+ const LV2_Atom_Object *obj = (const LV2_Atom_Object *)itm;
+ const LV2_Atom *body = NULL;
+
+ lv2_atom_object_get(obj, urid->Canvas_body, &body, 0);
+
+ LV2_Canvas_Meth *meth = _lv2_canvas_bsearch(obj->body.otype,
+ canvas->methods, LV2_CANVAS_NUM_METHODS);
+
+ if(meth)
+ {
+ meth->func(ctx, urid, body);
+ }
+ }
+ }
+
+ // save state
+ nvgRestore(ctx);
+
+ return true;
+}
+
+static inline bool
+lv2_canvas_render(LV2_Canvas *canvas, NVGcontext *ctx, const LV2_Atom_Tuple *tup)
+{
+ return lv2_canvas_render_body(canvas, ctx, tup->atom.type, tup->atom.size,
+ LV2_ATOM_BODY_CONST(&tup->atom));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // LV2_CANVAS_RENDER_NANOVG_H