~hp/canvas.lv2

add4a62e0ebf97bd32078952b30a29b9da151e1c — Hanspeter Portner 2 years ago 9acca09
outsource idisp and forge functions
6 files changed, 721 insertions(+), 371 deletions(-)

M canvas.lv2/forge.h
M canvas.lv2/idisp.h
M meson.build
A src/forge.c
A src/idisp.c
M test/canvas.c
M canvas.lv2/forge.h => canvas.lv2/forge.h +52 -251
@@ 12,300 12,101 @@
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;
LV2_Atom_Forge_Ref
lv2_canvas_forge_beginPath(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

	ref = lv2_atom_forge_object(forge, &frame, 0, otype);
	if(ref)
		lv2_atom_forge_pop(forge, &frame);
LV2_Atom_Forge_Ref
lv2_canvas_forge_closePath(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

	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_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};
	float x, float y, float r, float a1, float a2);

	return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Arc, 5, vec);
}

static inline LV2_Atom_Forge_Ref
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);
}
	float x1, float y1, float x2, float y2, float x3, float y3);

static inline LV2_Atom_Forge_Ref
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};
	float x, float y);

	return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_LineTo, 2, vec);
}

static inline LV2_Atom_Forge_Ref
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);
}
	float x, float y);

static inline LV2_Atom_Forge_Ref
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};
	float x, float y, float w, float h);

	return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Rectangle, 4, vec);
}

static inline LV2_Atom_Forge_Ref
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);
}
	uint32_t n, const float *vec);

static inline LV2_Atom_Forge_Ref
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);
}
	uint32_t style);

static inline LV2_Atom_Forge_Ref
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);
}
	float line_width);

static inline LV2_Atom_Forge_Ref
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);
}
	float dash_length, float separator_length);

static inline LV2_Atom_Forge_Ref
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);
}
	LV2_URID line_cap);

static inline LV2_Atom_Forge_Ref
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);
}
	LV2_URID line_join);

static inline LV2_Atom_Forge_Ref
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);
}
	float 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);
}
LV2_Atom_Forge_Ref
lv2_canvas_forge_stroke(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

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);
}
LV2_Atom_Forge_Ref
lv2_canvas_forge_fill(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

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);
}
LV2_Atom_Forge_Ref
lv2_canvas_forge_clip(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

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);
}
LV2_Atom_Forge_Ref
lv2_canvas_forge_save(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

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);
}
LV2_Atom_Forge_Ref
lv2_canvas_forge_restore(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

static inline LV2_Atom_Forge_Ref
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};
	float x, float y);

	return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Translate, 2, vec);
}

static inline LV2_Atom_Forge_Ref
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);
}
	float w, float h);

static inline LV2_Atom_Forge_Ref
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);
}
	float a);

static inline LV2_Atom_Forge_Ref
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};
	float xx, float xy, float x0, float yy, float yx, float y0);

	return _lv2_canvas_forge_vec(forge, urid, urid->Canvas_Transform, 6, vec);
}
LV2_Atom_Forge_Ref
lv2_canvas_forge_reset(LV2_Atom_Forge *forge, LV2_Canvas_URID *urid);

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_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);
}
	float size);

static inline LV2_Atom_Forge_Ref
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);
}
	const char *text);

#ifdef __cplusplus
}

M canvas.lv2/idisp.h => canvas.lv2/idisp.h +10 -113
@@ 25,126 25,23 @@ struct _LV2_Canvas_Idisp {
	} 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 = h;
	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_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 == 1.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;
}
	uint32_t w, uint32_t h, float aspect_ratio);

static inline void
int
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;
}
	LV2_URID_Map *map);

static inline void
lv2_canvas_idisp_deinit(LV2_Canvas_Idisp *idisp)
{
	_lv2_canvas_idisp_surf_deinit(idisp);
}
int
lv2_canvas_idisp_deinit(LV2_Canvas_Idisp *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);
	}
}
int
lv2_canvas_idisp_queue_draw(LV2_Canvas_Idisp *idisp);

static inline bool
int
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);
}
	uint32_t size, const LV2_Atom *body);

#ifdef __cplusplus
}

M meson.build => meson.build +14 -0
@@ 15,6 15,7 @@ reuse = find_program('reuse', required : false)
add_project_arguments('-D_GNU_SOURCE', language : 'c')

lv2_dep = dependency('lv2', version : '>=1.14.0')
cairo_dep = dependency('cairo', version : '>=1.16.0')

lib_deps = []
lib_deps += lv2_dep


@@ 24,18 25,31 @@ lib_incs += include_directories('')

lib_srcs = []
lib_srcs += join_paths('src', 'canvas.c')
lib_srcs += join_paths('src', 'forge.c')

lib_cairo_deps = []
lib_cairo_deps += cairo_dep

lib_cairo_incs = []
lib_cairo_src = join_paths('src', 'idisp.c')

canvas_lv2 = declare_dependency(
  include_directories : lib_incs,
  dependencies : lib_deps,
  sources : lib_srcs)

canvas_idisp_lv2 = declare_dependency(
  include_directories : lib_incs,
  dependencies : [lib_deps, lib_cairo_deps],
  sources : [lib_srcs, lib_cairo_src])

test_args = []
test_args += '-fvisibility=hidden'
test_args += '-ffast-math'

test_deps = []
test_deps += canvas_lv2
test_deps += canvas_idisp_lv2

test_srcs = []
test_srcs += join_paths('test', 'canvas.c')

A src/forge.c => src/forge.c +301 -0
@@ 0,0 1,301 @@
/*
 * SPDX-FileCopyrightText: Hanspeter Portner <dev@open-music-kontrollers.ch>
 * SPDX-License-Identifier: Artistic-2.0
 */

#include <canvas.lv2/forge.h>

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;
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

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);
}

A src/idisp.c => src/idisp.c +142 -0
@@ 0,0 1,142 @@
/*
 * SPDX-FileCopyrightText: Hanspeter Portner <dev@open-music-kontrollers.ch>
 * SPDX-License-Identifier: Artistic-2.0
 */

#include <stdlib.h>

#include <canvas.lv2/idisp.h>

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 = h;
	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 int 
_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;
	}

	return 0;
}

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 == 1.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;
}

int
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;

	return 0;
}

int
lv2_canvas_idisp_deinit(LV2_Canvas_Idisp *idisp)
{
	return _lv2_canvas_idisp_surf_deinit(idisp);
}

int
lv2_canvas_idisp_queue_draw(LV2_Canvas_Idisp *idisp)
{
	if(!idisp->queue_draw || !idisp->queue_draw->queue_draw)
	{
		return 0;
		}

	idisp->queue_draw->queue_draw(idisp->queue_draw->handle);

	return 0;
}

int
lv2_canvas_idisp_render_body(LV2_Canvas_Idisp *idisp, uint32_t type,
	uint32_t size, const LV2_Atom *body)
{
	if(!lv2_canvas_render_body(&idisp->canvas, idisp->cairo.ctx,
		type, size, body))
	{
		return 1;
	}

	return 0;
}

M test/canvas.c => test/canvas.c +202 -7
@@ 3,28 3,223 @@
 * SPDX-License-Identifier: Artistic-2.0
 */

#include <stdlib.h>
#include <assert.h>

#include <canvas.lv2/canvas.h>
#include <canvas.lv2/forge.h>
#include <canvas.lv2/idisp.h>

void
test_canvas_urid_init()
#define MAX_URIDS 128

typedef struct _urid_t urid_t;
typedef struct _handle_t handle_t;

struct _urid_t {
	LV2_URID urid;
	char *uri;
};

struct _handle_t {
	LV2_URID_Map map;

	urid_t urids [MAX_URIDS];
	LV2_URID urid;
};

static LV2_URID
_map(LV2_URID_Map_Handle instance, const char *uri)
{
	handle_t *handle = instance;

	urid_t *itm;
	for(itm=handle->urids; itm->urid; itm++)
	{
		if(!strcmp(itm->uri, uri))
		{
			return itm->urid;
		}
	}

	assert(handle->urid + 1 < MAX_URIDS);

	// create new
	itm->urid = ++handle->urid;
	itm->uri = strdup(uri);

	return itm->urid;
}

static void
_map_free(handle_t *handle)
{
	for(unsigned i = 0; i < MAX_URIDS; i++)
	{
		urid_t *itm = &handle->urids[i];

		if(itm->uri)
		{
			free(itm->uri);
		}

		memset(itm, 0x0, sizeof(urid_t));
	}
}

static void
test_canvas_urid_init(handle_t *handle)
{
	static LV2_Canvas_URID urid;
	static LV2_URID_Map map;

	LV2_URID_Map *map = &handle->map;

	assert(lv2_canvas_urid_init(NULL, NULL) == 1);
	assert(lv2_canvas_urid_init(&urid, NULL) == 1);
	assert(lv2_canvas_urid_init(NULL, &map) == 1);
	assert(lv2_canvas_urid_init(&urid, &map) == 1);
	assert(lv2_canvas_urid_init(NULL, map) == 1);

	//FIXME do some real tests
	assert(lv2_canvas_urid_init(&urid, map) == 0);
}

void
test_canvas_forge(handle_t *handle)
{
	static LV2_Canvas_URID urid;

	const float vec [3] = {
		1.f, 2.f, 3.f
	};

	LV2_URID_Map *map = &handle->map;
	uint8_t buf [1024];

	assert(lv2_canvas_urid_init(&urid, map) == 0);
	LV2_Atom_Forge *forge = &(urid.forge);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_beginPath(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_closePath(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_arc(forge, &urid, 1.f, 2.f, 3.f, 4.f, 5.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_curveTo(forge, &urid, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_lineTo(forge, &urid, 1.f, 2.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_moveTo(forge, &urid, 1.f, 2.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_rectangle(forge, &urid, 1.f, 2.f, 3.f, 4.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_polyLine(forge, &urid, 3, vec) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_style(forge, &urid, 0xff0000ff) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_lineWidth(forge, &urid, 1.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_lineDash(forge, &urid, 1.f, 2.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_lineCap(forge, &urid, urid.Canvas_lineCapButt) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_lineJoin(forge, &urid, urid.Canvas_lineJoinMiter) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_miterLimit(forge, &urid, 1.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_stroke(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_fill(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_clip(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_save(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_restore(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_translate(forge, &urid, 1.f, 2.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_scale(forge, &urid, 1.f, 2.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_rotate(forge, &urid, 1.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_transform(forge, &urid, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_reset(forge, &urid) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_fontSize(forge, &urid, 1.f) != 0);

	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_canvas_forge_fillText(forge, &urid, "foo") != 0);
}

static void
test_canvas_idisp(handle_t *handle)
{
	static LV2_Canvas_URID urid;
	static LV2_Canvas_Idisp idisp;
	static LV2_Inline_Display queue_draw;

	LV2_URID_Map *map = &handle->map;
	uint8_t buf [1024];

	assert(lv2_canvas_urid_init(&urid, map) == 0);
	LV2_Atom_Forge *forge = &(urid.forge);

	assert(lv2_canvas_idisp_init(&idisp, &queue_draw, map) == 0);
	
	LV2_Inline_Display_Image_Surface *surf =
		lv2_canvas_idisp_surf_configure(&idisp, 64, 64, 1.f);
	assert(surf);

	assert(lv2_canvas_idisp_queue_draw(&idisp) == 0);

	LV2_Atom_Forge_Frame frame;
	lv2_atom_forge_set_buffer(forge, buf, sizeof(buf));
	assert(lv2_atom_forge_tuple(forge, &frame) != 0);
	lv2_atom_forge_pop(forge, &frame);

	const LV2_Atom *atom = (const LV2_Atom *)buf;
	assert(lv2_canvas_idisp_render_body(&idisp, atom->type, atom->size,
		LV2_ATOM_BODY_CONST(atom)) == 0);

	assert(lv2_canvas_idisp_deinit(&idisp) == 0);
}

int
main(int argc __attribute__((unused)), char **argv __attribute__((unused)))
{
	test_canvas_urid_init();
	static handle_t handle;

	handle.map.handle = &handle;
	handle.map.map = _map;

	test_canvas_urid_init(&handle);
	test_canvas_forge(&handle);
	test_canvas_idisp(&handle);

	_map_free(&handle);

	return 0;
}