From 63d2ac13cf1e70d8920992fa7f895cd9652cd2db Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Fri, 25 Sep 2026 08:55:55 +0100 Subject: [PATCH] ucode-mod-plutovg: add ucode binding for plutovg Make the plutovg drawing API usable from ucode. Surfaces, canvases, paths, paints and font faces become ucode resource types with method tables, while colours, points, rectangles, matrices and gradient stops cross the boundary as plain ucode objects or arrays, so a whole scene can be built and mutated from script. Enumerations are spelled as strings rather than opaque integers. Each resource holds one plutovg reference and drops it from the type destructor; getters that return a borrowed pointer take a reference before wrapping it, so a canvas keeps its surface alive and nothing is released twice at collection time. The value conversion helpers live in a separate header installed into the staging directory because the plutosvg binding needs the same colour and rectangle marshalling. Signed-off-by: Daniel Golle --- libs/ucode-mod-plutovg/Makefile | 48 + libs/ucode-mod-plutovg/src/LICENSE | 13 + libs/ucode-mod-plutovg/src/plutovg.c | 3311 +++++++++++++++++++++++ libs/ucode-mod-plutovg/src/uc-plutovg.h | 177 ++ 4 files changed, 3549 insertions(+) create mode 100644 libs/ucode-mod-plutovg/Makefile create mode 100644 libs/ucode-mod-plutovg/src/LICENSE create mode 100644 libs/ucode-mod-plutovg/src/plutovg.c create mode 100644 libs/ucode-mod-plutovg/src/uc-plutovg.h diff --git a/libs/ucode-mod-plutovg/Makefile b/libs/ucode-mod-plutovg/Makefile new file mode 100644 index 0000000..536c113 --- /dev/null +++ b/libs/ucode-mod-plutovg/Makefile @@ -0,0 +1,48 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=ucode-mod-plutovg +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Daniel Golle +PKG_LICENSE:=ISC +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +define Package/ucode-mod-plutovg + SECTION:=libs + CATEGORY:=Libraries + SUBMENU:=ucode + TITLE:=ucode binding for the plutovg 2D vector graphics library + DEPENDS:=+libucode +plutovg +endef + +define Package/ucode-mod-plutovg/description + Exposes the plutovg drawing API to ucode scripts: surfaces, canvases, + paths, matrices, solid, gradient and texture paints, stroking with dash + patterns, clipping, compositing and text rendering. Colours, points, + rectangles, matrices and gradient stops are passed as plain ucode + objects or arrays. +endef + +define Build/Configure +endef + +define Build/Compile + $(TARGET_CC) $(TARGET_CFLAGS) $(TARGET_CPPFLAGS) $(TARGET_LDFLAGS) \ + $(FPIC) -Wall -ffunction-sections -Wl,--gc-sections -shared \ + -o $(PKG_BUILD_DIR)/plutovg.so $(PKG_BUILD_DIR)/plutovg.c \ + -lplutovg +endef + +define Build/InstallDev + $(INSTALL_DIR) $(1)/usr/include + $(CP) $(PKG_BUILD_DIR)/uc-plutovg.h $(1)/usr/include/ +endef + +define Package/ucode-mod-plutovg/install + $(INSTALL_DIR) $(1)/usr/lib/ucode + $(CP) $(PKG_BUILD_DIR)/plutovg.so $(1)/usr/lib/ucode/ +endef + +$(eval $(call BuildPackage,ucode-mod-plutovg)) diff --git a/libs/ucode-mod-plutovg/src/LICENSE b/libs/ucode-mod-plutovg/src/LICENSE new file mode 100644 index 0000000..8fbfabd --- /dev/null +++ b/libs/ucode-mod-plutovg/src/LICENSE @@ -0,0 +1,13 @@ +Copyright (C) 2026 Daniel Golle + +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. + +THE 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. diff --git a/libs/ucode-mod-plutovg/src/plutovg.c b/libs/ucode-mod-plutovg/src/plutovg.c new file mode 100644 index 0000000..b28e12e --- /dev/null +++ b/libs/ucode-mod-plutovg/src/plutovg.c @@ -0,0 +1,3311 @@ +/* + * Copyright (C) 2026 Daniel Golle + * + * 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. + * + * THE 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. + */ + +#include +#include + +#include "uc-plutovg.h" + +static const char * const uc_pvg_spread_names[] = { + "pad", "reflect", "repeat", NULL +}; + +static const char * const uc_pvg_texture_names[] = { + "plain", "tiled", NULL +}; + +static const char * const uc_pvg_fill_rule_names[] = { + "nonzero", "evenodd", NULL +}; + +static const char * const uc_pvg_operator_names[] = { + "clear", "src", "dst", "src-over", "dst-over", "src-in", "dst-in", + "src-out", "dst-out", "src-atop", "dst-atop", "xor", NULL +}; + +static const char * const uc_pvg_line_cap_names[] = { + "butt", "round", "square", NULL +}; + +static const char * const uc_pvg_line_join_names[] = { + "miter", "round", "bevel", NULL +}; + +static const char * const uc_pvg_command_names[] = { + "move_to", "line_to", "cubic_to", "close", NULL +}; + +static bool +uc_pvg_args_num(uc_vm_t *vm, size_t nargs, size_t first, size_t count, + float *out) +{ + uc_value_t *arg; + size_t i; + + for (i = 0; i < count; i++) { + arg = _uc_fn_arg(vm, nargs, first + i); + + if (ucv_type(arg) != UC_INTEGER && ucv_type(arg) != UC_DOUBLE) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Argument %zu is not a number", first + i + 1); + + return false; + } + + out[i] = (float)ucv_to_double(arg); + } + + return true; +} + +static bool +uc_pvg_enum_get(uc_vm_t *vm, uc_value_t *uv, const char * const *names, + const char *what, int *out) +{ + const char *str; + int i; + + if (ucv_type(uv) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting %s name string", what); + + return false; + } + + str = ucv_string_get(uv); + + for (i = 0; names[i]; i++) { + if (strcmp(names[i], str)) + continue; + + *out = i; + + return true; + } + + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Unknown %s '%s'", what, str); + + return false; +} + +static bool +uc_pvg_enum_opt(uc_vm_t *vm, uc_value_t *uv, const char * const *names, + const char *what, int *out) +{ + if (uv == NULL || ucv_type(uv) == UC_NULL) + return true; + + return uc_pvg_enum_get(vm, uv, names, what, out); +} + +static bool +uc_pvg_matrix_opt(uc_vm_t *vm, uc_value_t *uv, plutovg_matrix_t *storage, + const plutovg_matrix_t **out) +{ + *out = NULL; + + if (uv == NULL || ucv_type(uv) == UC_NULL) + return true; + + if (!uc_pvg_matrix_get(uv, storage)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Invalid matrix argument"); + + return false; + } + + *out = storage; + + return true; +} + +static bool +uc_pvg_matrix_arg(uc_vm_t *vm, uc_value_t *uv, plutovg_matrix_t *matrix) +{ + if (uc_pvg_matrix_get(uv, matrix)) + return true; + + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid matrix argument"); + + return false; +} + +static plutovg_gradient_stop_t * +uc_pvg_stops_get(uc_vm_t *vm, uc_value_t *uv, int *nstops) +{ + plutovg_gradient_stop_t *stops; + uc_value_t *entry; + size_t count, i; + + *nstops = 0; + + if (ucv_type(uv) != UC_ARRAY) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting array of gradient stops"); + + return NULL; + } + + count = ucv_array_length(uv); + + if (count == 0) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Gradient stop array is empty"); + + return NULL; + } + + stops = calloc(count, sizeof(*stops)); + + if (!stops) + return NULL; + + for (i = 0; i < count; i++) { + entry = ucv_array_get(uv, i); + + if (!uc_pvg_component(entry, "offset", 0, &stops[i].offset) || + !uc_pvg_color_get(ucv_type(entry) == UC_OBJECT + ? ucv_object_get(entry, "color", NULL) + : ucv_array_get(entry, 1), + &stops[i].color)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Invalid gradient stop at index %zu", i); + free(stops); + + return NULL; + } + } + + *nstops = (int)count; + + return stops; +} + +static float * +uc_pvg_dashes_get(uc_vm_t *vm, uc_value_t *uv, int *ndashes) +{ + uc_value_t *entry; + size_t count, i; + float *dashes; + + *ndashes = 0; + + if (uv == NULL || ucv_type(uv) == UC_NULL) + return NULL; + + if (ucv_type(uv) != UC_ARRAY) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting array of dash lengths"); + + return NULL; + } + + count = ucv_array_length(uv); + + if (count == 0) + return NULL; + + dashes = calloc(count, sizeof(*dashes)); + + if (!dashes) + return NULL; + + for (i = 0; i < count; i++) { + entry = ucv_array_get(uv, i); + + if (ucv_type(entry) != UC_INTEGER && + ucv_type(entry) != UC_DOUBLE) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Invalid dash length at index %zu", i); + free(dashes); + + return NULL; + } + + dashes[i] = (float)ucv_to_double(entry); + } + + *ndashes = (int)count; + + return dashes; +} + +static void * +uc_pvg_arg_res(uc_vm_t *vm, uc_value_t *uv, const char *type, const char *what) +{ + void *ptr = ucv_resource_data(uv, type); + + if (!ptr) + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting %s object", what); + + return ptr; +} + +static void +uc_pvg_surface_free(void *ptr) +{ + plutovg_surface_destroy(ptr); +} + +static void +uc_pvg_canvas_free(void *ptr) +{ + plutovg_canvas_destroy(ptr); +} + +static void +uc_pvg_path_free(void *ptr) +{ + plutovg_path_destroy(ptr); +} + +static void +uc_pvg_paint_free(void *ptr) +{ + plutovg_paint_destroy(ptr); +} + +static void +uc_pvg_font_face_free(void *ptr) +{ + plutovg_font_face_destroy(ptr); +} + +static uc_value_t * +uc_pvg_surface_wrap(uc_vm_t *vm, plutovg_surface_t *surface) +{ + uc_value_t *rv; + + if (!surface) + return NULL; + + rv = ucv_resource_create(vm, UC_PVG_SURFACE, surface); + + if (!rv) + plutovg_surface_destroy(surface); + + return rv; +} + +static uc_value_t * +uc_pvg_path_wrap(uc_vm_t *vm, plutovg_path_t *path) +{ + uc_value_t *rv; + + if (!path) + return NULL; + + rv = ucv_resource_create(vm, UC_PVG_PATH, path); + + if (!rv) + plutovg_path_destroy(path); + + return rv; +} + +static uc_value_t * +uc_pvg_paint_wrap(uc_vm_t *vm, plutovg_paint_t *paint) +{ + uc_value_t *rv; + + if (!paint) + return NULL; + + rv = ucv_resource_create(vm, UC_PVG_PAINT, paint); + + if (!rv) + plutovg_paint_destroy(paint); + + return rv; +} + +static uc_value_t * +uc_pvg_font_face_wrap(uc_vm_t *vm, plutovg_font_face_t *face) +{ + uc_value_t *rv; + + if (!face) + return NULL; + + rv = ucv_resource_create(vm, UC_PVG_FONT_FACE, face); + + if (!rv) + plutovg_font_face_destroy(face); + + return rv; +} + +static uc_value_t * +uc_pvg_version(uc_vm_t *vm, size_t nargs) +{ + return ucv_int64_new(plutovg_version()); +} + +static uc_value_t * +uc_pvg_version_string(uc_vm_t *vm, size_t nargs) +{ + return ucv_string_new(plutovg_version_string()); +} + +static uc_value_t * +uc_pvg_deg2rad(uc_vm_t *vm, size_t nargs) +{ + float v[1]; + + if (!uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + return ucv_double_new(PLUTOVG_DEG2RAD(v[0])); +} + +static uc_value_t * +uc_pvg_rad2deg(uc_vm_t *vm, size_t nargs) +{ + float v[1]; + + if (!uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + return ucv_double_new(PLUTOVG_RAD2DEG(v[0])); +} + +static uc_value_t * +uc_pvg_color_parse(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + plutovg_color_t color; + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting color string"); + + return NULL; + } + + if (plutovg_color_parse(&color, ucv_string_get(arg), + ucv_string_length(arg)) == 0) + return NULL; + + return uc_pvg_color_new(vm, &color); +} + +static uc_value_t * +uc_pvg_color_hsl(uc_vm_t *vm, size_t nargs) +{ + plutovg_color_t color; + float v[3]; + + if (!uc_pvg_args_num(vm, nargs, 0, 3, v)) + return NULL; + + plutovg_color_init_hsla(&color, v[0], v[1], v[2], + uc_pvg_number(uc_fn_arg(3), 1.0f)); + + return uc_pvg_color_new(vm, &color); +} + +static uc_value_t * +uc_pvg_color_rgba32(uc_vm_t *vm, size_t nargs) +{ + plutovg_color_t color; + + if (!uc_pvg_color_get(uc_fn_arg(0), &color)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid color value"); + + return NULL; + } + + return ucv_uint64_new(plutovg_color_to_rgba32(&color)); +} + +static uc_value_t * +uc_pvg_color_argb32(uc_vm_t *vm, size_t nargs) +{ + plutovg_color_t color; + + if (!uc_pvg_color_get(uc_fn_arg(0), &color)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid color value"); + + return NULL; + } + + return ucv_uint64_new(plutovg_color_to_argb32(&color)); +} + +static uc_value_t * +uc_pvg_matrix_identity(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + + plutovg_matrix_init_identity(&matrix); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_init_translate(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[2]; + + if (!uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_matrix_init_translate(&matrix, v[0], v[1]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_init_scale(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[2]; + + if (!uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_matrix_init_scale(&matrix, v[0], v[1]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_init_rotate(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[1]; + + if (!uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_matrix_init_rotate(&matrix, v[0]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_init_shear(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[2]; + + if (!uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_matrix_init_shear(&matrix, v[0], v[1]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_translate(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[2]; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!uc_pvg_args_num(vm, nargs, 1, 2, v)) + return NULL; + + plutovg_matrix_translate(&matrix, v[0], v[1]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_scale(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[2]; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!uc_pvg_args_num(vm, nargs, 1, 2, v)) + return NULL; + + plutovg_matrix_scale(&matrix, v[0], v[1]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_rotate(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[1]; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!uc_pvg_args_num(vm, nargs, 1, 1, v)) + return NULL; + + plutovg_matrix_rotate(&matrix, v[0]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_shear(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + float v[2]; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!uc_pvg_args_num(vm, nargs, 1, 2, v)) + return NULL; + + plutovg_matrix_shear(&matrix, v[0], v[1]); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_matrix_multiply(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t left, right, result; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &left)) + return NULL; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(1), &right)) + return NULL; + + plutovg_matrix_multiply(&result, &left, &right); + + return uc_pvg_matrix_new(vm, &result); +} + +static uc_value_t * +uc_pvg_matrix_invert(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix, inverse; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!plutovg_matrix_invert(&matrix, &inverse)) + return NULL; + + return uc_pvg_matrix_new(vm, &inverse); +} + +static uc_value_t * +uc_pvg_matrix_map(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + plutovg_point_t point; + float v[2]; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!uc_pvg_args_num(vm, nargs, 1, 2, v)) + return NULL; + + plutovg_matrix_map(&matrix, v[0], v[1], &point.x, &point.y); + + return uc_pvg_point_new(vm, &point); +} + +static uc_value_t * +uc_pvg_matrix_map_point(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + plutovg_point_t point; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!uc_pvg_point_get(uc_fn_arg(1), &point)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid point value"); + + return NULL; + } + + plutovg_matrix_map_point(&matrix, &point, &point); + + return uc_pvg_point_new(vm, &point); +} + +static uc_value_t * +uc_pvg_matrix_map_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_matrix_t matrix; + plutovg_rect_t rect; + + if (!uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + if (!uc_pvg_rect_get(uc_fn_arg(1), &rect)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Invalid rectangle value"); + + return NULL; + } + + plutovg_matrix_map_rect(&matrix, &rect, &rect); + + return uc_pvg_rect_new(vm, &rect); +} + +static uc_value_t * +uc_pvg_matrix_parse(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + plutovg_matrix_t matrix; + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting transform string"); + + return NULL; + } + + if (!plutovg_matrix_parse(&matrix, ucv_string_get(arg), + ucv_string_length(arg))) + return NULL; + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_surface_create(uc_vm_t *vm, size_t nargs) +{ + float v[2]; + + if (!uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + if (v[0] <= 0 || v[1] <= 0) { + uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, + "Surface dimensions must be positive"); + + return NULL; + } + + return uc_pvg_surface_wrap(vm, plutovg_surface_create((int)v[0], + (int)v[1])); +} + +static uc_value_t * +uc_pvg_surface_from_file(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expecting file name"); + + return NULL; + } + + return uc_pvg_surface_wrap(vm, + plutovg_surface_load_from_image_file(ucv_string_get(arg))); +} + +static uc_value_t * +uc_pvg_surface_from_data(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expecting image data"); + + return NULL; + } + + return uc_pvg_surface_wrap(vm, + plutovg_surface_load_from_image_data(ucv_string_get(arg), + (int)ucv_string_length(arg))); +} + +static uc_value_t * +uc_pvg_surface_from_base64(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting base64 image data"); + + return NULL; + } + + return uc_pvg_surface_wrap(vm, + plutovg_surface_load_from_image_base64(ucv_string_get(arg), + (int)ucv_string_length(arg))); +} + +static uc_value_t * +uc_pvg_surface_width(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + + if (!surface) + return NULL; + + return ucv_int64_new(plutovg_surface_get_width(surface)); +} + +static uc_value_t * +uc_pvg_surface_height(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + + if (!surface) + return NULL; + + return ucv_int64_new(plutovg_surface_get_height(surface)); +} + +static uc_value_t * +uc_pvg_surface_stride(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + + if (!surface) + return NULL; + + return ucv_int64_new(plutovg_surface_get_stride(surface)); +} + +static uc_value_t * +uc_pvg_surface_clear(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + plutovg_color_t color = PLUTOVG_MAKE_COLOR(0, 0, 0, 0); + uc_value_t *arg = uc_fn_arg(0); + + if (!surface) + return NULL; + + if (arg && ucv_type(arg) != UC_NULL && !uc_pvg_color_get(arg, &color)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid color value"); + + return NULL; + } + + plutovg_surface_clear(surface, &color); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_surface_data(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + size_t len; + + if (!surface) + return NULL; + + len = (size_t)plutovg_surface_get_stride(surface) * + (size_t)plutovg_surface_get_height(surface); + + return ucv_string_new_length((char *)plutovg_surface_get_data(surface), + len); +} + +static uc_value_t * +uc_pvg_surface_rgba(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + unsigned char *buf; + uc_value_t *rv; + size_t len; + int stride; + + if (!surface) + return NULL; + + stride = plutovg_surface_get_stride(surface); + len = (size_t)stride * (size_t)plutovg_surface_get_height(surface); + buf = malloc(len); + + if (!buf) + return NULL; + + plutovg_convert_argb_to_rgba(buf, plutovg_surface_get_data(surface), + plutovg_surface_get_width(surface), + plutovg_surface_get_height(surface), stride); + + rv = ucv_string_new_length((char *)buf, len); + free(buf); + + return rv; +} + +static uc_value_t * +uc_pvg_surface_write_png(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + uc_value_t *arg = uc_fn_arg(0); + + if (!surface) + return NULL; + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expecting file name"); + + return NULL; + } + + return ucv_boolean_new(plutovg_surface_write_to_png(surface, + ucv_string_get(arg))); +} + +static uc_value_t * +uc_pvg_surface_write_jpg(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + uc_value_t *arg = uc_fn_arg(0); + + if (!surface) + return NULL; + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expecting file name"); + + return NULL; + } + + return ucv_boolean_new(plutovg_surface_write_to_jpg(surface, + ucv_string_get(arg), + (int)uc_pvg_number(uc_fn_arg(1), 90.0f))); +} + +static void +uc_pvg_stream_write(void *closure, void *data, int size) +{ + ucv_stringbuf_addstr((uc_stringbuf_t *)closure, (const char *)data, + (size_t)size); +} + +static uc_value_t * +uc_pvg_surface_png(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + uc_stringbuf_t *buf; + + if (!surface) + return NULL; + + buf = ucv_stringbuf_new(); + + if (!plutovg_surface_write_to_png_stream(surface, uc_pvg_stream_write, + buf)) { + printbuf_free(buf); + + return NULL; + } + + return ucv_stringbuf_finish(buf); +} + +static uc_value_t * +uc_pvg_surface_jpg(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface = uc_fn_thisval(UC_PVG_SURFACE); + uc_stringbuf_t *buf; + int quality; + + if (!surface) + return NULL; + + quality = (int)uc_pvg_number(uc_fn_arg(0), 90.0f); + buf = ucv_stringbuf_new(); + + if (!plutovg_surface_write_to_jpg_stream(surface, uc_pvg_stream_write, + buf, quality)) { + printbuf_free(buf); + + return NULL; + } + + return ucv_stringbuf_finish(buf); +} + +static uc_value_t * +uc_pvg_path_create(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + plutovg_path_t *path; + + path = plutovg_path_create(); + + if (!path) + return NULL; + + if (ucv_type(arg) == UC_STRING && + !plutovg_path_parse(path, ucv_string_get(arg), + ucv_string_length(arg))) { + plutovg_path_destroy(path); + uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, + "Unable to parse path data"); + + return NULL; + } + + return uc_pvg_path_wrap(vm, path); +} + +static uc_value_t * +uc_pvg_path_move_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[2]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_path_move_to(path, v[0], v[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_line_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[2]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_path_line_to(path, v[0], v[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_quad_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[4]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_path_quad_to(path, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_cubic_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[6]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 6, v)) + return NULL; + + plutovg_path_cubic_to(path, v[0], v[1], v[2], v[3], v[4], v[5]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_arc_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[3], e[2]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 3, v)) + return NULL; + + if (!uc_pvg_args_num(vm, nargs, 5, 2, e)) + return NULL; + + plutovg_path_arc_to(path, v[0], v[1], v[2], + ucv_is_truish(uc_fn_arg(3)), ucv_is_truish(uc_fn_arg(4)), + e[0], e[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_close(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + + if (!path) + return NULL; + + plutovg_path_close(path); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_reset(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + + if (!path) + return NULL; + + plutovg_path_reset(path); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_reserve(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[1]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_path_reserve(path, (int)v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_current_point(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + plutovg_point_t point; + + if (!path) + return NULL; + + plutovg_path_get_current_point(path, &point.x, &point.y); + + return uc_pvg_point_new(vm, &point); +} + +static uc_value_t * +uc_pvg_path_add_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[4]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_path_add_rect(path, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_add_round_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[6]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 6, v)) + return NULL; + + plutovg_path_add_round_rect(path, v[0], v[1], v[2], v[3], v[4], v[5]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_add_ellipse(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[4]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_path_add_ellipse(path, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_add_circle(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[3]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 3, v)) + return NULL; + + plutovg_path_add_circle(path, v[0], v[1], v[2]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_add_arc(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + float v[5]; + + if (!path || !uc_pvg_args_num(vm, nargs, 0, 5, v)) + return NULL; + + plutovg_path_add_arc(path, v[0], v[1], v[2], v[3], v[4], + ucv_is_truish(uc_fn_arg(5))); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_add_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + const plutovg_matrix_t *mptr; + plutovg_matrix_t matrix; + plutovg_path_t *source; + + if (!path) + return NULL; + + source = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_PATH, "path"); + + if (!source) + return NULL; + + if (!uc_pvg_matrix_opt(vm, uc_fn_arg(1), &matrix, &mptr)) + return NULL; + + plutovg_path_add_path(path, source, mptr); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_transform(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + plutovg_matrix_t matrix; + + if (!path || !uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + plutovg_path_transform(path, &matrix); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_path_clone(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + + if (!path) + return NULL; + + return uc_pvg_path_wrap(vm, plutovg_path_clone(path)); +} + +static uc_value_t * +uc_pvg_path_clone_flatten(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + + if (!path) + return NULL; + + return uc_pvg_path_wrap(vm, plutovg_path_clone_flatten(path)); +} + +static uc_value_t * +uc_pvg_path_clone_dashed(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + uc_value_t *rv; + float *dashes; + int ndashes; + + if (!path) + return NULL; + + dashes = uc_pvg_dashes_get(vm, uc_fn_arg(1), &ndashes); + + if (vm->exception.type != EXCEPTION_NONE) + return NULL; + + rv = uc_pvg_path_wrap(vm, plutovg_path_clone_dashed(path, + uc_pvg_number(uc_fn_arg(0), 0.0f), dashes, ndashes)); + free(dashes); + + return rv; +} + +static uc_value_t * +uc_pvg_path_extents(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + plutovg_rect_t rect; + + if (!path) + return NULL; + + plutovg_path_extents(path, &rect, ucv_is_truish(uc_fn_arg(0))); + + return uc_pvg_rect_new(vm, &rect); +} + +static uc_value_t * +uc_pvg_path_length(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + + if (!path) + return NULL; + + return ucv_double_new(plutovg_path_length(path)); +} + +static uc_value_t * +uc_pvg_path_parse(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + uc_value_t *arg = uc_fn_arg(0); + + if (!path) + return NULL; + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expecting path data"); + + return NULL; + } + + return ucv_boolean_new(plutovg_path_parse(path, ucv_string_get(arg), + ucv_string_length(arg))); +} + +static uc_value_t * +uc_pvg_path_elements(uc_vm_t *vm, size_t nargs) +{ + plutovg_path_t *path = uc_fn_thisval(UC_PVG_PATH); + plutovg_path_iterator_t it; + plutovg_point_t points[3]; + uc_value_t *rv, *entry; + int command, npoints; + uc_value_t *coords; + int i; + + if (!path) + return NULL; + + plutovg_path_iterator_init(&it, path); + + rv = ucv_array_new(vm); + + while (plutovg_path_iterator_has_next(&it)) { + command = plutovg_path_iterator_next(&it, points); + npoints = (command == PLUTOVG_PATH_COMMAND_CUBIC_TO) ? 3 : 1; + coords = ucv_array_new(vm); + + for (i = 0; i < npoints; i++) + ucv_array_push(coords, uc_pvg_point_new(vm, &points[i])); + + entry = ucv_object_new(vm); + ucv_object_add(entry, "command", + ucv_string_new(uc_pvg_command_names[command])); + ucv_object_add(entry, "points", coords); + ucv_array_push(rv, entry); + } + + return rv; +} + +static uc_value_t * +uc_pvg_paint_color(uc_vm_t *vm, size_t nargs) +{ + plutovg_color_t color; + + if (!uc_pvg_color_get(uc_fn_arg(0), &color)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid color value"); + + return NULL; + } + + return uc_pvg_paint_wrap(vm, plutovg_paint_create_color(&color)); +} + +static uc_value_t * +uc_pvg_paint_linear_gradient(uc_vm_t *vm, size_t nargs) +{ + plutovg_gradient_stop_t *stops; + const plutovg_matrix_t *mptr; + plutovg_matrix_t matrix; + int spread, nstops; + uc_value_t *rv; + float v[4]; + + spread = PLUTOVG_SPREAD_METHOD_PAD; + + if (!uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + if (!uc_pvg_enum_opt(vm, uc_fn_arg(4), uc_pvg_spread_names, + "spread method", &spread)) + return NULL; + + stops = uc_pvg_stops_get(vm, uc_fn_arg(5), &nstops); + + if (!stops) + return NULL; + + if (!uc_pvg_matrix_opt(vm, uc_fn_arg(6), &matrix, &mptr)) { + free(stops); + + return NULL; + } + + rv = uc_pvg_paint_wrap(vm, plutovg_paint_create_linear_gradient(v[0], + v[1], v[2], v[3], spread, stops, nstops, mptr)); + free(stops); + + return rv; +} + +static uc_value_t * +uc_pvg_paint_radial_gradient(uc_vm_t *vm, size_t nargs) +{ + plutovg_gradient_stop_t *stops; + const plutovg_matrix_t *mptr; + plutovg_matrix_t matrix; + int spread, nstops; + uc_value_t *rv; + float v[6]; + + spread = PLUTOVG_SPREAD_METHOD_PAD; + + if (!uc_pvg_args_num(vm, nargs, 0, 6, v)) + return NULL; + + if (!uc_pvg_enum_opt(vm, uc_fn_arg(6), uc_pvg_spread_names, + "spread method", &spread)) + return NULL; + + stops = uc_pvg_stops_get(vm, uc_fn_arg(7), &nstops); + + if (!stops) + return NULL; + + if (!uc_pvg_matrix_opt(vm, uc_fn_arg(8), &matrix, &mptr)) { + free(stops); + + return NULL; + } + + rv = uc_pvg_paint_wrap(vm, plutovg_paint_create_radial_gradient(v[0], + v[1], v[2], v[3], v[4], v[5], spread, stops, nstops, mptr)); + free(stops); + + return rv; +} + +static uc_value_t * +uc_pvg_paint_texture(uc_vm_t *vm, size_t nargs) +{ + const plutovg_matrix_t *mptr; + plutovg_surface_t *surface; + plutovg_matrix_t matrix; + int type; + + type = PLUTOVG_TEXTURE_TYPE_TILED; + surface = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_SURFACE, "surface"); + + if (!surface) + return NULL; + + if (!uc_pvg_enum_opt(vm, uc_fn_arg(1), uc_pvg_texture_names, + "texture type", &type)) + return NULL; + + if (!uc_pvg_matrix_opt(vm, uc_fn_arg(3), &matrix, &mptr)) + return NULL; + + return uc_pvg_paint_wrap(vm, plutovg_paint_create_texture(surface, type, + uc_pvg_number(uc_fn_arg(2), 1.0f), mptr)); +} + +static uc_value_t * +uc_pvg_font_face_from_file(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expecting file name"); + + return NULL; + } + + return uc_pvg_font_face_wrap(vm, + plutovg_font_face_load_from_file(ucv_string_get(arg), + (int)uc_pvg_number(uc_fn_arg(1), 0.0f))); +} + +static uc_value_t * +uc_pvg_font_face_from_data(uc_vm_t *vm, size_t nargs) +{ + uc_value_t *arg = uc_fn_arg(0); + plutovg_font_face_t *face; + size_t len; + char *copy; + + if (ucv_type(arg) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Expecting font data"); + + return NULL; + } + + len = ucv_string_length(arg); + copy = malloc(len); + + if (!copy) + return NULL; + + memcpy(copy, ucv_string_get(arg), len); + + face = plutovg_font_face_load_from_data(copy, (unsigned int)len, + (int)uc_pvg_number(uc_fn_arg(1), 0.0f), free, copy); + + if (!face) { + free(copy); + + return NULL; + } + + return uc_pvg_font_face_wrap(vm, face); +} + +static uc_value_t * +uc_pvg_font_face_metrics(uc_vm_t *vm, size_t nargs) +{ + plutovg_font_face_t *face = uc_fn_thisval(UC_PVG_FONT_FACE); + float ascent, descent, line_gap; + plutovg_rect_t extents; + uc_value_t *rv; + float v[1]; + + if (!face || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_font_face_get_metrics(face, v[0], &ascent, &descent, &line_gap, + &extents); + + rv = ucv_object_new(vm); + ucv_object_add(rv, "ascent", ucv_double_new(ascent)); + ucv_object_add(rv, "descent", ucv_double_new(descent)); + ucv_object_add(rv, "line_gap", ucv_double_new(line_gap)); + ucv_object_add(rv, "extents", uc_pvg_rect_new(vm, &extents)); + + return rv; +} + +static uc_value_t * +uc_pvg_font_face_glyph_metrics(uc_vm_t *vm, size_t nargs) +{ + plutovg_font_face_t *face = uc_fn_thisval(UC_PVG_FONT_FACE); + float advance_width, left_side_bearing; + plutovg_rect_t extents; + uc_value_t *rv; + float v[2]; + + if (!face || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_font_face_get_glyph_metrics(face, v[0], + (plutovg_codepoint_t)v[1], &advance_width, &left_side_bearing, + &extents); + + rv = ucv_object_new(vm); + ucv_object_add(rv, "advance_width", ucv_double_new(advance_width)); + ucv_object_add(rv, "left_side_bearing", + ucv_double_new(left_side_bearing)); + ucv_object_add(rv, "extents", uc_pvg_rect_new(vm, &extents)); + + return rv; +} + +static uc_value_t * +uc_pvg_font_face_glyph_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_font_face_t *face = uc_fn_thisval(UC_PVG_FONT_FACE); + plutovg_path_t *path; + float v[4]; + + if (!face || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + path = uc_pvg_arg_res(vm, uc_fn_arg(4), UC_PVG_PATH, "path"); + + if (!path) + return NULL; + + return ucv_double_new(plutovg_font_face_get_glyph_path(face, v[0], v[1], + v[2], (plutovg_codepoint_t)v[3], path)); +} + +static uc_value_t * +uc_pvg_font_face_text_extents(uc_vm_t *vm, size_t nargs) +{ + plutovg_font_face_t *face = uc_fn_thisval(UC_PVG_FONT_FACE); + uc_value_t *text = uc_fn_arg(1); + plutovg_rect_t extents; + uc_value_t *rv; + float advance; + float v[1]; + + if (!face || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + if (ucv_type(text) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting text string"); + + return NULL; + } + + advance = plutovg_font_face_text_extents(face, v[0], + ucv_string_get(text), (int)ucv_string_length(text), + PLUTOVG_TEXT_ENCODING_UTF8, &extents); + + rv = ucv_object_new(vm); + ucv_object_add(rv, "advance_width", ucv_double_new(advance)); + ucv_object_add(rv, "extents", uc_pvg_rect_new(vm, &extents)); + + return rv; +} + +static uc_value_t * +uc_pvg_canvas_create(uc_vm_t *vm, size_t nargs) +{ + plutovg_surface_t *surface; + plutovg_canvas_t *canvas; + uc_value_t *rv; + + surface = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_SURFACE, "surface"); + + if (!surface) + return NULL; + + canvas = plutovg_canvas_create(surface); + + if (!canvas) + return NULL; + + rv = ucv_resource_create(vm, UC_PVG_CANVAS, canvas); + + if (!rv) + plutovg_canvas_destroy(canvas); + + return rv; +} + +static uc_value_t * +uc_pvg_canvas_surface(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return uc_pvg_surface_wrap(vm, plutovg_surface_reference( + plutovg_canvas_get_surface(canvas))); +} + +static uc_value_t * +uc_pvg_canvas_save(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_save(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_restore(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_restore(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_color(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_color_t color; + + if (!canvas) + return NULL; + + if (!uc_pvg_color_get(uc_fn_arg(0), &color)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid color value"); + + return NULL; + } + + plutovg_canvas_set_color(canvas, &color); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_color(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_color_t color; + + if (!canvas) + return NULL; + + plutovg_canvas_get_paint(canvas, &color); + + return uc_pvg_color_new(vm, &color); +} + +static uc_value_t * +uc_pvg_canvas_get_paint(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_paint_t *paint; + + if (!canvas) + return NULL; + + paint = plutovg_canvas_get_paint(canvas, NULL); + + if (!paint) + return NULL; + + return uc_pvg_paint_wrap(vm, plutovg_paint_reference(paint)); +} + +static uc_value_t * +uc_pvg_canvas_set_paint(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_paint_t *paint; + + if (!canvas) + return NULL; + + paint = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_PAINT, "paint"); + + if (!paint) + return NULL; + + plutovg_canvas_set_paint(canvas, paint); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_linear_gradient(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_gradient_stop_t *stops; + const plutovg_matrix_t *mptr; + plutovg_matrix_t matrix; + int spread, nstops; + float v[4]; + + spread = PLUTOVG_SPREAD_METHOD_PAD; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + if (!uc_pvg_enum_opt(vm, uc_fn_arg(4), uc_pvg_spread_names, + "spread method", &spread)) + return NULL; + + stops = uc_pvg_stops_get(vm, uc_fn_arg(5), &nstops); + + if (!stops) + return NULL; + + if (!uc_pvg_matrix_opt(vm, uc_fn_arg(6), &matrix, &mptr)) { + free(stops); + + return NULL; + } + + plutovg_canvas_set_linear_gradient(canvas, v[0], v[1], v[2], v[3], + spread, stops, nstops, mptr); + free(stops); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_radial_gradient(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_gradient_stop_t *stops; + const plutovg_matrix_t *mptr; + plutovg_matrix_t matrix; + int spread, nstops; + float v[6]; + + spread = PLUTOVG_SPREAD_METHOD_PAD; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 6, v)) + return NULL; + + if (!uc_pvg_enum_opt(vm, uc_fn_arg(6), uc_pvg_spread_names, + "spread method", &spread)) + return NULL; + + stops = uc_pvg_stops_get(vm, uc_fn_arg(7), &nstops); + + if (!stops) + return NULL; + + if (!uc_pvg_matrix_opt(vm, uc_fn_arg(8), &matrix, &mptr)) { + free(stops); + + return NULL; + } + + plutovg_canvas_set_radial_gradient(canvas, v[0], v[1], v[2], v[3], v[4], + v[5], spread, stops, nstops, mptr); + free(stops); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_texture(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + const plutovg_matrix_t *mptr; + plutovg_surface_t *surface; + plutovg_matrix_t matrix; + int type; + + type = PLUTOVG_TEXTURE_TYPE_TILED; + + if (!canvas) + return NULL; + + surface = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_SURFACE, "surface"); + + if (!surface) + return NULL; + + if (!uc_pvg_enum_opt(vm, uc_fn_arg(1), uc_pvg_texture_names, + "texture type", &type)) + return NULL; + + if (!uc_pvg_matrix_opt(vm, uc_fn_arg(3), &matrix, &mptr)) + return NULL; + + plutovg_canvas_set_texture(canvas, surface, type, + uc_pvg_number(uc_fn_arg(2), 1.0f), mptr); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_font(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_font_face_t *face; + float v[1]; + + if (!canvas) + return NULL; + + face = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_FONT_FACE, "font face"); + + if (!face || !uc_pvg_args_num(vm, nargs, 1, 1, v)) + return NULL; + + plutovg_canvas_set_font(canvas, face, v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_font_face(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_font_face_t *face; + + if (!canvas) + return NULL; + + face = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_FONT_FACE, "font face"); + + if (!face) + return NULL; + + plutovg_canvas_set_font_face(canvas, face); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_font_face(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_font_face_t *face; + + if (!canvas) + return NULL; + + face = plutovg_canvas_get_font_face(canvas); + + if (!face) + return NULL; + + return uc_pvg_font_face_wrap(vm, plutovg_font_face_reference(face)); +} + +static uc_value_t * +uc_pvg_canvas_set_font_size(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_canvas_set_font_size(canvas, v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_font_size(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_double_new(plutovg_canvas_get_font_size(canvas)); +} + +static uc_value_t * +uc_pvg_canvas_add_font_face(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + uc_value_t *family = uc_fn_arg(0); + plutovg_font_face_t *face; + + if (!canvas) + return NULL; + + if (ucv_type(family) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting font family name"); + + return NULL; + } + + face = uc_pvg_arg_res(vm, uc_fn_arg(3), UC_PVG_FONT_FACE, "font face"); + + if (!face) + return NULL; + + plutovg_canvas_add_font_face(canvas, ucv_string_get(family), + ucv_is_truish(uc_fn_arg(1)), ucv_is_truish(uc_fn_arg(2)), face); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_add_font_file(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + uc_value_t *family = uc_fn_arg(0); + uc_value_t *file = uc_fn_arg(3); + + if (!canvas) + return NULL; + + if (ucv_type(family) != UC_STRING || ucv_type(file) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting font family name and file name"); + + return NULL; + } + + return ucv_boolean_new(plutovg_canvas_add_font_file(canvas, + ucv_string_get(family), ucv_is_truish(uc_fn_arg(1)), + ucv_is_truish(uc_fn_arg(2)), ucv_string_get(file), + (int)uc_pvg_number(uc_fn_arg(4), 0.0f))); +} + +static uc_value_t * +uc_pvg_canvas_select_font_face(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + uc_value_t *family = uc_fn_arg(0); + + if (!canvas) + return NULL; + + if (ucv_type(family) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting font family name"); + + return NULL; + } + + return ucv_boolean_new(plutovg_canvas_select_font_face(canvas, + ucv_string_get(family), ucv_is_truish(uc_fn_arg(1)), + ucv_is_truish(uc_fn_arg(2)))); +} + +static uc_value_t * +uc_pvg_canvas_set_fill_rule(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + int rule; + + if (!canvas) + return NULL; + + if (!uc_pvg_enum_get(vm, uc_fn_arg(0), uc_pvg_fill_rule_names, + "fill rule", &rule)) + return NULL; + + plutovg_canvas_set_fill_rule(canvas, rule); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_fill_rule(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_string_new( + uc_pvg_fill_rule_names[plutovg_canvas_get_fill_rule(canvas)]); +} + +static uc_value_t * +uc_pvg_canvas_set_operator(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + int op; + + if (!canvas) + return NULL; + + if (!uc_pvg_enum_get(vm, uc_fn_arg(0), uc_pvg_operator_names, + "operator", &op)) + return NULL; + + plutovg_canvas_set_operator(canvas, op); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_operator(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_string_new( + uc_pvg_operator_names[plutovg_canvas_get_operator(canvas)]); +} + +static uc_value_t * +uc_pvg_canvas_set_opacity(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_canvas_set_opacity(canvas, v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_opacity(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_double_new(plutovg_canvas_get_opacity(canvas)); +} + +static uc_value_t * +uc_pvg_canvas_set_line_width(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_canvas_set_line_width(canvas, v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_line_width(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_double_new(plutovg_canvas_get_line_width(canvas)); +} + +static uc_value_t * +uc_pvg_canvas_set_line_cap(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + int cap; + + if (!canvas) + return NULL; + + if (!uc_pvg_enum_get(vm, uc_fn_arg(0), uc_pvg_line_cap_names, + "line cap", &cap)) + return NULL; + + plutovg_canvas_set_line_cap(canvas, cap); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_line_cap(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_string_new( + uc_pvg_line_cap_names[plutovg_canvas_get_line_cap(canvas)]); +} + +static uc_value_t * +uc_pvg_canvas_set_line_join(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + int join; + + if (!canvas) + return NULL; + + if (!uc_pvg_enum_get(vm, uc_fn_arg(0), uc_pvg_line_join_names, + "line join", &join)) + return NULL; + + plutovg_canvas_set_line_join(canvas, join); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_line_join(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_string_new( + uc_pvg_line_join_names[plutovg_canvas_get_line_join(canvas)]); +} + +static uc_value_t * +uc_pvg_canvas_set_miter_limit(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_canvas_set_miter_limit(canvas, v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_miter_limit(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_double_new(plutovg_canvas_get_miter_limit(canvas)); +} + +static uc_value_t * +uc_pvg_canvas_set_dash(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float *dashes; + int ndashes; + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + dashes = uc_pvg_dashes_get(vm, uc_fn_arg(1), &ndashes); + + if (vm->exception.type != EXCEPTION_NONE) + return NULL; + + plutovg_canvas_set_dash(canvas, v[0], dashes, ndashes); + free(dashes); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_dash_offset(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_canvas_set_dash_offset(canvas, v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_dash_offset(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + return ucv_double_new(plutovg_canvas_get_dash_offset(canvas)); +} + +static uc_value_t * +uc_pvg_canvas_set_dash_array(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float *dashes; + int ndashes; + + if (!canvas) + return NULL; + + dashes = uc_pvg_dashes_get(vm, uc_fn_arg(0), &ndashes); + + if (vm->exception.type != EXCEPTION_NONE) + return NULL; + + plutovg_canvas_set_dash_array(canvas, dashes, ndashes); + free(dashes); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_dash_array(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + const float *dashes; + uc_value_t *rv; + int count, i; + + if (!canvas) + return NULL; + + count = plutovg_canvas_get_dash_array(canvas, &dashes); + rv = ucv_array_new(vm); + + for (i = 0; i < count; i++) + ucv_array_push(rv, ucv_double_new(dashes[i])); + + return rv; +} + +static uc_value_t * +uc_pvg_canvas_translate(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_canvas_translate(canvas, v[0], v[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_scale(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_canvas_scale(canvas, v[0], v[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_shear(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_canvas_shear(canvas, v[0], v[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_rotate(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_canvas_rotate(canvas, v[0]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_transform(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_matrix_t matrix; + + if (!canvas || !uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + plutovg_canvas_transform(canvas, &matrix); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_reset_matrix(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_reset_matrix(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_set_matrix(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_matrix_t matrix; + + if (!canvas || !uc_pvg_matrix_arg(vm, uc_fn_arg(0), &matrix)) + return NULL; + + plutovg_canvas_set_matrix(canvas, &matrix); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_matrix(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_matrix_t matrix; + + if (!canvas) + return NULL; + + plutovg_canvas_get_matrix(canvas, &matrix); + + return uc_pvg_matrix_new(vm, &matrix); +} + +static uc_value_t * +uc_pvg_canvas_map(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_point_t point; + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_canvas_map(canvas, v[0], v[1], &point.x, &point.y); + + return uc_pvg_point_new(vm, &point); +} + +static uc_value_t * +uc_pvg_canvas_map_point(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_point_t point; + + if (!canvas) + return NULL; + + if (!uc_pvg_point_get(uc_fn_arg(0), &point)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid point value"); + + return NULL; + } + + plutovg_canvas_map_point(canvas, &point, &point); + + return uc_pvg_point_new(vm, &point); +} + +static uc_value_t * +uc_pvg_canvas_map_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_rect_t rect; + + if (!canvas) + return NULL; + + if (!uc_pvg_rect_get(uc_fn_arg(0), &rect)) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Invalid rectangle value"); + + return NULL; + } + + plutovg_canvas_map_rect(canvas, &rect, &rect); + + return uc_pvg_rect_new(vm, &rect); +} + +static uc_value_t * +uc_pvg_canvas_move_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_canvas_move_to(canvas, v[0], v[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_line_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + plutovg_canvas_line_to(canvas, v[0], v[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_quad_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[4]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_canvas_quad_to(canvas, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_cubic_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[6]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 6, v)) + return NULL; + + plutovg_canvas_cubic_to(canvas, v[0], v[1], v[2], v[3], v[4], v[5]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_arc_to(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[3], e[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 3, v)) + return NULL; + + if (!uc_pvg_args_num(vm, nargs, 5, 2, e)) + return NULL; + + plutovg_canvas_arc_to(canvas, v[0], v[1], v[2], + ucv_is_truish(uc_fn_arg(3)), ucv_is_truish(uc_fn_arg(4)), + e[0], e[1]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[4]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_canvas_rect(canvas, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_round_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[6]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 6, v)) + return NULL; + + plutovg_canvas_round_rect(canvas, v[0], v[1], v[2], v[3], v[4], v[5]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_ellipse(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[4]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_canvas_ellipse(canvas, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_circle(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[3]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 3, v)) + return NULL; + + plutovg_canvas_circle(canvas, v[0], v[1], v[2]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_arc(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[5]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 5, v)) + return NULL; + + plutovg_canvas_arc(canvas, v[0], v[1], v[2], v[3], v[4], + ucv_is_truish(uc_fn_arg(5))); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_add_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_path_t *path; + + if (!canvas) + return NULL; + + path = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_PATH, "path"); + + if (!path) + return NULL; + + plutovg_canvas_add_path(canvas, path); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_new_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_new_path(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_close_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_close_path(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_current_point(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_point_t point; + + if (!canvas) + return NULL; + + plutovg_canvas_get_current_point(canvas, &point.x, &point.y); + + return uc_pvg_point_new(vm, &point); +} + +static uc_value_t * +uc_pvg_canvas_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_path_t *path; + + if (!canvas) + return NULL; + + path = plutovg_canvas_get_path(canvas); + + if (!path) + return NULL; + + return uc_pvg_path_wrap(vm, plutovg_path_reference(path)); +} + +static uc_value_t * +uc_pvg_canvas_fill_contains(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + return ucv_boolean_new(plutovg_canvas_fill_contains(canvas, v[0], v[1])); +} + +static uc_value_t * +uc_pvg_canvas_stroke_contains(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + return ucv_boolean_new(plutovg_canvas_stroke_contains(canvas, v[0], + v[1])); +} + +static uc_value_t * +uc_pvg_canvas_clip_contains(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[2]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 2, v)) + return NULL; + + return ucv_boolean_new(plutovg_canvas_clip_contains(canvas, v[0], v[1])); +} + +static uc_value_t * +uc_pvg_canvas_fill_extents(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_rect_t rect; + + if (!canvas) + return NULL; + + plutovg_canvas_fill_extents(canvas, &rect); + + return uc_pvg_rect_new(vm, &rect); +} + +static uc_value_t * +uc_pvg_canvas_stroke_extents(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_rect_t rect; + + if (!canvas) + return NULL; + + plutovg_canvas_stroke_extents(canvas, &rect); + + return uc_pvg_rect_new(vm, &rect); +} + +static uc_value_t * +uc_pvg_canvas_clip_extents(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_rect_t rect; + + if (!canvas) + return NULL; + + plutovg_canvas_clip_extents(canvas, &rect); + + return uc_pvg_rect_new(vm, &rect); +} + +static uc_value_t * +uc_pvg_canvas_fill(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_fill(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_stroke(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_stroke(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_clip(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_clip(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_paint(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_paint(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_fill_preserve(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_fill_preserve(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_stroke_preserve(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_stroke_preserve(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_clip_preserve(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + + if (!canvas) + return NULL; + + plutovg_canvas_clip_preserve(canvas); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_fill_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[4]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_canvas_fill_rect(canvas, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_stroke_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[4]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_canvas_stroke_rect(canvas, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_clip_rect(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[4]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 4, v)) + return NULL; + + plutovg_canvas_clip_rect(canvas, v[0], v[1], v[2], v[3]); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_fill_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_path_t *path; + + if (!canvas) + return NULL; + + path = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_PATH, "path"); + + if (!path) + return NULL; + + plutovg_canvas_fill_path(canvas, path); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_stroke_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_path_t *path; + + if (!canvas) + return NULL; + + path = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_PATH, "path"); + + if (!path) + return NULL; + + plutovg_canvas_stroke_path(canvas, path); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_clip_path(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + plutovg_path_t *path; + + if (!canvas) + return NULL; + + path = uc_pvg_arg_res(vm, uc_fn_arg(0), UC_PVG_PATH, "path"); + + if (!path) + return NULL; + + plutovg_canvas_clip_path(canvas, path); + + return ucv_boolean_new(true); +} + +static uc_value_t * +uc_pvg_canvas_add_glyph(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float v[3]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 3, v)) + return NULL; + + return ucv_double_new(plutovg_canvas_add_glyph(canvas, + (plutovg_codepoint_t)v[0], v[1], v[2])); +} + +static float +uc_pvg_canvas_text_op(uc_vm_t *vm, size_t nargs, plutovg_canvas_t *canvas, + int op, bool *ok) +{ + uc_value_t *text = _uc_fn_arg(vm, nargs, 0); + const char *str; + float v[2]; + int len; + + *ok = false; + + if (ucv_type(text) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting text string"); + + return 0; + } + + if (!uc_pvg_args_num(vm, nargs, 1, 2, v)) + return 0; + + str = ucv_string_get(text); + len = (int)ucv_string_length(text); + *ok = true; + + if (op == 0) + return plutovg_canvas_add_text(canvas, str, len, + PLUTOVG_TEXT_ENCODING_UTF8, v[0], v[1]); + + if (op == 1) + return plutovg_canvas_fill_text(canvas, str, len, + PLUTOVG_TEXT_ENCODING_UTF8, v[0], v[1]); + + if (op == 2) + return plutovg_canvas_stroke_text(canvas, str, len, + PLUTOVG_TEXT_ENCODING_UTF8, v[0], v[1]); + + return plutovg_canvas_clip_text(canvas, str, len, + PLUTOVG_TEXT_ENCODING_UTF8, v[0], v[1]); +} + +static uc_value_t * +uc_pvg_canvas_add_text(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float advance; + bool ok; + + if (!canvas) + return NULL; + + advance = uc_pvg_canvas_text_op(vm, nargs, canvas, 0, &ok); + + return ok ? ucv_double_new(advance) : NULL; +} + +static uc_value_t * +uc_pvg_canvas_fill_text(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float advance; + bool ok; + + if (!canvas) + return NULL; + + advance = uc_pvg_canvas_text_op(vm, nargs, canvas, 1, &ok); + + return ok ? ucv_double_new(advance) : NULL; +} + +static uc_value_t * +uc_pvg_canvas_stroke_text(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float advance; + bool ok; + + if (!canvas) + return NULL; + + advance = uc_pvg_canvas_text_op(vm, nargs, canvas, 2, &ok); + + return ok ? ucv_double_new(advance) : NULL; +} + +static uc_value_t * +uc_pvg_canvas_clip_text(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float advance; + bool ok; + + if (!canvas) + return NULL; + + advance = uc_pvg_canvas_text_op(vm, nargs, canvas, 3, &ok); + + return ok ? ucv_double_new(advance) : NULL; +} + +static uc_value_t * +uc_pvg_canvas_font_metrics(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float ascent, descent, line_gap; + plutovg_rect_t extents; + uc_value_t *rv; + + if (!canvas) + return NULL; + + plutovg_canvas_font_metrics(canvas, &ascent, &descent, &line_gap, + &extents); + + rv = ucv_object_new(vm); + ucv_object_add(rv, "ascent", ucv_double_new(ascent)); + ucv_object_add(rv, "descent", ucv_double_new(descent)); + ucv_object_add(rv, "line_gap", ucv_double_new(line_gap)); + ucv_object_add(rv, "extents", uc_pvg_rect_new(vm, &extents)); + + return rv; +} + +static uc_value_t * +uc_pvg_canvas_glyph_metrics(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + float advance_width, left_side_bearing; + plutovg_rect_t extents; + uc_value_t *rv; + float v[1]; + + if (!canvas || !uc_pvg_args_num(vm, nargs, 0, 1, v)) + return NULL; + + plutovg_canvas_glyph_metrics(canvas, (plutovg_codepoint_t)v[0], + &advance_width, &left_side_bearing, &extents); + + rv = ucv_object_new(vm); + ucv_object_add(rv, "advance_width", ucv_double_new(advance_width)); + ucv_object_add(rv, "left_side_bearing", + ucv_double_new(left_side_bearing)); + ucv_object_add(rv, "extents", uc_pvg_rect_new(vm, &extents)); + + return rv; +} + +static uc_value_t * +uc_pvg_canvas_text_extents(uc_vm_t *vm, size_t nargs) +{ + plutovg_canvas_t *canvas = uc_fn_thisval(UC_PVG_CANVAS); + uc_value_t *text = uc_fn_arg(0); + plutovg_rect_t extents; + uc_value_t *rv; + float advance; + + if (!canvas) + return NULL; + + if (ucv_type(text) != UC_STRING) { + uc_vm_raise_exception(vm, EXCEPTION_TYPE, + "Expecting text string"); + + return NULL; + } + + advance = plutovg_canvas_text_extents(canvas, ucv_string_get(text), + (int)ucv_string_length(text), PLUTOVG_TEXT_ENCODING_UTF8, + &extents); + + rv = ucv_object_new(vm); + ucv_object_add(rv, "advance_width", ucv_double_new(advance)); + ucv_object_add(rv, "extents", uc_pvg_rect_new(vm, &extents)); + + return rv; +} + +static const uc_function_list_t surface_fns[] = { + { "width", uc_pvg_surface_width }, + { "height", uc_pvg_surface_height }, + { "stride", uc_pvg_surface_stride }, + { "clear", uc_pvg_surface_clear }, + { "data", uc_pvg_surface_data }, + { "rgba", uc_pvg_surface_rgba }, + { "write_png", uc_pvg_surface_write_png }, + { "write_jpg", uc_pvg_surface_write_jpg }, + { "png", uc_pvg_surface_png }, + { "jpg", uc_pvg_surface_jpg }, +}; + +static const uc_function_list_t path_fns[] = { + { "move_to", uc_pvg_path_move_to }, + { "line_to", uc_pvg_path_line_to }, + { "quad_to", uc_pvg_path_quad_to }, + { "cubic_to", uc_pvg_path_cubic_to }, + { "arc_to", uc_pvg_path_arc_to }, + { "close", uc_pvg_path_close }, + { "reset", uc_pvg_path_reset }, + { "reserve", uc_pvg_path_reserve }, + { "current_point", uc_pvg_path_current_point }, + { "add_rect", uc_pvg_path_add_rect }, + { "add_round_rect", uc_pvg_path_add_round_rect }, + { "add_ellipse", uc_pvg_path_add_ellipse }, + { "add_circle", uc_pvg_path_add_circle }, + { "add_arc", uc_pvg_path_add_arc }, + { "add_path", uc_pvg_path_add_path }, + { "transform", uc_pvg_path_transform }, + { "clone", uc_pvg_path_clone }, + { "clone_flatten", uc_pvg_path_clone_flatten }, + { "clone_dashed", uc_pvg_path_clone_dashed }, + { "extents", uc_pvg_path_extents }, + { "length", uc_pvg_path_length }, + { "parse", uc_pvg_path_parse }, + { "elements", uc_pvg_path_elements }, +}; + +static const uc_function_list_t font_face_fns[] = { + { "metrics", uc_pvg_font_face_metrics }, + { "glyph_metrics", uc_pvg_font_face_glyph_metrics }, + { "glyph_path", uc_pvg_font_face_glyph_path }, + { "text_extents", uc_pvg_font_face_text_extents }, +}; + +static const uc_function_list_t canvas_fns[] = { + { "surface", uc_pvg_canvas_surface }, + { "save", uc_pvg_canvas_save }, + { "restore", uc_pvg_canvas_restore }, + { "set_color", uc_pvg_canvas_set_color }, + { "color", uc_pvg_canvas_color }, + { "set_paint", uc_pvg_canvas_set_paint }, + { "get_paint", uc_pvg_canvas_get_paint }, + { "set_linear_gradient", uc_pvg_canvas_set_linear_gradient }, + { "set_radial_gradient", uc_pvg_canvas_set_radial_gradient }, + { "set_texture", uc_pvg_canvas_set_texture }, + { "set_font", uc_pvg_canvas_set_font }, + { "set_font_face", uc_pvg_canvas_set_font_face }, + { "font_face", uc_pvg_canvas_font_face }, + { "set_font_size", uc_pvg_canvas_set_font_size }, + { "font_size", uc_pvg_canvas_font_size }, + { "add_font_face", uc_pvg_canvas_add_font_face }, + { "add_font_file", uc_pvg_canvas_add_font_file }, + { "select_font_face", uc_pvg_canvas_select_font_face }, + { "set_fill_rule", uc_pvg_canvas_set_fill_rule }, + { "fill_rule", uc_pvg_canvas_fill_rule }, + { "set_operator", uc_pvg_canvas_set_operator }, + { "operator", uc_pvg_canvas_operator }, + { "set_opacity", uc_pvg_canvas_set_opacity }, + { "opacity", uc_pvg_canvas_opacity }, + { "set_line_width", uc_pvg_canvas_set_line_width }, + { "line_width", uc_pvg_canvas_line_width }, + { "set_line_cap", uc_pvg_canvas_set_line_cap }, + { "line_cap", uc_pvg_canvas_line_cap }, + { "set_line_join", uc_pvg_canvas_set_line_join }, + { "line_join", uc_pvg_canvas_line_join }, + { "set_miter_limit", uc_pvg_canvas_set_miter_limit }, + { "miter_limit", uc_pvg_canvas_miter_limit }, + { "set_dash", uc_pvg_canvas_set_dash }, + { "set_dash_offset", uc_pvg_canvas_set_dash_offset }, + { "dash_offset", uc_pvg_canvas_dash_offset }, + { "set_dash_array", uc_pvg_canvas_set_dash_array }, + { "dash_array", uc_pvg_canvas_dash_array }, + { "translate", uc_pvg_canvas_translate }, + { "scale", uc_pvg_canvas_scale }, + { "shear", uc_pvg_canvas_shear }, + { "rotate", uc_pvg_canvas_rotate }, + { "transform", uc_pvg_canvas_transform }, + { "reset_matrix", uc_pvg_canvas_reset_matrix }, + { "set_matrix", uc_pvg_canvas_set_matrix }, + { "matrix", uc_pvg_canvas_matrix }, + { "map", uc_pvg_canvas_map }, + { "map_point", uc_pvg_canvas_map_point }, + { "map_rect", uc_pvg_canvas_map_rect }, + { "move_to", uc_pvg_canvas_move_to }, + { "line_to", uc_pvg_canvas_line_to }, + { "quad_to", uc_pvg_canvas_quad_to }, + { "cubic_to", uc_pvg_canvas_cubic_to }, + { "arc_to", uc_pvg_canvas_arc_to }, + { "rect", uc_pvg_canvas_rect }, + { "round_rect", uc_pvg_canvas_round_rect }, + { "ellipse", uc_pvg_canvas_ellipse }, + { "circle", uc_pvg_canvas_circle }, + { "arc", uc_pvg_canvas_arc }, + { "add_path", uc_pvg_canvas_add_path }, + { "new_path", uc_pvg_canvas_new_path }, + { "close_path", uc_pvg_canvas_close_path }, + { "current_point", uc_pvg_canvas_current_point }, + { "path", uc_pvg_canvas_path }, + { "fill_contains", uc_pvg_canvas_fill_contains }, + { "stroke_contains", uc_pvg_canvas_stroke_contains }, + { "clip_contains", uc_pvg_canvas_clip_contains }, + { "fill_extents", uc_pvg_canvas_fill_extents }, + { "stroke_extents", uc_pvg_canvas_stroke_extents }, + { "clip_extents", uc_pvg_canvas_clip_extents }, + { "fill", uc_pvg_canvas_fill }, + { "stroke", uc_pvg_canvas_stroke }, + { "clip", uc_pvg_canvas_clip }, + { "paint", uc_pvg_canvas_paint }, + { "fill_preserve", uc_pvg_canvas_fill_preserve }, + { "stroke_preserve", uc_pvg_canvas_stroke_preserve }, + { "clip_preserve", uc_pvg_canvas_clip_preserve }, + { "fill_rect", uc_pvg_canvas_fill_rect }, + { "stroke_rect", uc_pvg_canvas_stroke_rect }, + { "clip_rect", uc_pvg_canvas_clip_rect }, + { "fill_path", uc_pvg_canvas_fill_path }, + { "stroke_path", uc_pvg_canvas_stroke_path }, + { "clip_path", uc_pvg_canvas_clip_path }, + { "add_glyph", uc_pvg_canvas_add_glyph }, + { "add_text", uc_pvg_canvas_add_text }, + { "fill_text", uc_pvg_canvas_fill_text }, + { "stroke_text", uc_pvg_canvas_stroke_text }, + { "clip_text", uc_pvg_canvas_clip_text }, + { "font_metrics", uc_pvg_canvas_font_metrics }, + { "glyph_metrics", uc_pvg_canvas_glyph_metrics }, + { "text_extents", uc_pvg_canvas_text_extents }, +}; + +static const uc_function_list_t module_fns[] = { + { "version", uc_pvg_version }, + { "version_string", uc_pvg_version_string }, + { "deg2rad", uc_pvg_deg2rad }, + { "rad2deg", uc_pvg_rad2deg }, + { "color_parse", uc_pvg_color_parse }, + { "color_hsl", uc_pvg_color_hsl }, + { "color_rgba32", uc_pvg_color_rgba32 }, + { "color_argb32", uc_pvg_color_argb32 }, + { "matrix_identity", uc_pvg_matrix_identity }, + { "matrix_init_translate", uc_pvg_matrix_init_translate }, + { "matrix_init_scale", uc_pvg_matrix_init_scale }, + { "matrix_init_rotate", uc_pvg_matrix_init_rotate }, + { "matrix_init_shear", uc_pvg_matrix_init_shear }, + { "matrix_translate", uc_pvg_matrix_translate }, + { "matrix_scale", uc_pvg_matrix_scale }, + { "matrix_rotate", uc_pvg_matrix_rotate }, + { "matrix_shear", uc_pvg_matrix_shear }, + { "matrix_multiply", uc_pvg_matrix_multiply }, + { "matrix_invert", uc_pvg_matrix_invert }, + { "matrix_map", uc_pvg_matrix_map }, + { "matrix_map_point", uc_pvg_matrix_map_point }, + { "matrix_map_rect", uc_pvg_matrix_map_rect }, + { "matrix_parse", uc_pvg_matrix_parse }, + { "surface", uc_pvg_surface_create }, + { "surface_from_file", uc_pvg_surface_from_file }, + { "surface_from_data", uc_pvg_surface_from_data }, + { "surface_from_base64", uc_pvg_surface_from_base64 }, + { "canvas", uc_pvg_canvas_create }, + { "path", uc_pvg_path_create }, + { "paint_color", uc_pvg_paint_color }, + { "paint_linear_gradient", uc_pvg_paint_linear_gradient }, + { "paint_radial_gradient", uc_pvg_paint_radial_gradient }, + { "paint_texture", uc_pvg_paint_texture }, + { "font_face_from_file", uc_pvg_font_face_from_file }, + { "font_face_from_data", uc_pvg_font_face_from_data }, +}; + +void +uc_module_init(uc_vm_t *vm, uc_value_t *scope) +{ + uc_function_list_register(scope, module_fns); + + uc_type_declare(vm, UC_PVG_SURFACE, surface_fns, uc_pvg_surface_free); + uc_type_declare(vm, UC_PVG_CANVAS, canvas_fns, uc_pvg_canvas_free); + uc_type_declare(vm, UC_PVG_PATH, path_fns, uc_pvg_path_free); + uc_type_declare(vm, UC_PVG_FONT_FACE, font_face_fns, + uc_pvg_font_face_free); + + ucv_resource_type_add(vm, UC_PVG_PAINT, ucv_object_new(NULL), + uc_pvg_paint_free); +} diff --git a/libs/ucode-mod-plutovg/src/uc-plutovg.h b/libs/ucode-mod-plutovg/src/uc-plutovg.h new file mode 100644 index 0000000..d4efc2d --- /dev/null +++ b/libs/ucode-mod-plutovg/src/uc-plutovg.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2026 Daniel Golle + * + * 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. + * + * THE 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 UC_PLUTOVG_H +#define UC_PLUTOVG_H + +#include +#include + +#define UC_PVG_SURFACE "plutovg.surface" +#define UC_PVG_CANVAS "plutovg.canvas" +#define UC_PVG_PATH "plutovg.path" +#define UC_PVG_PAINT "plutovg.paint" +#define UC_PVG_FONT_FACE "plutovg.font_face" + +static inline float +uc_pvg_number(uc_value_t *uv, float fallback) +{ + if (ucv_type(uv) != UC_INTEGER && ucv_type(uv) != UC_DOUBLE) + return fallback; + + return (float)ucv_to_double(uv); +} + +static inline bool +uc_pvg_component(uc_value_t *uv, const char *key, size_t idx, float *out) +{ + uc_value_t *val; + + if (ucv_type(uv) == UC_OBJECT) + val = ucv_object_get(uv, key, NULL); + else + val = ucv_array_get(uv, idx); + + if (ucv_type(val) != UC_INTEGER && ucv_type(val) != UC_DOUBLE) + return false; + + *out = (float)ucv_to_double(val); + + return true; +} + +static inline bool +uc_pvg_color_get(uc_value_t *uv, plutovg_color_t *color) +{ + uc_value_t *alpha; + + if (ucv_type(uv) == UC_STRING) + return plutovg_color_parse(color, ucv_string_get(uv), + ucv_string_length(uv)) > 0; + + if (ucv_type(uv) != UC_OBJECT && ucv_type(uv) != UC_ARRAY) + return false; + + if (!uc_pvg_component(uv, "r", 0, &color->r)) + return false; + if (!uc_pvg_component(uv, "g", 1, &color->g)) + return false; + if (!uc_pvg_component(uv, "b", 2, &color->b)) + return false; + + if (ucv_type(uv) == UC_OBJECT) + alpha = ucv_object_get(uv, "a", NULL); + else + alpha = ucv_array_get(uv, 3); + + color->a = uc_pvg_number(alpha, 1.0f); + + return true; +} + +static inline uc_value_t * +uc_pvg_color_new(uc_vm_t *vm, const plutovg_color_t *color) +{ + uc_value_t *rv = ucv_object_new(vm); + + ucv_object_add(rv, "r", ucv_double_new(color->r)); + ucv_object_add(rv, "g", ucv_double_new(color->g)); + ucv_object_add(rv, "b", ucv_double_new(color->b)); + ucv_object_add(rv, "a", ucv_double_new(color->a)); + + return rv; +} + +static inline bool +uc_pvg_rect_get(uc_value_t *uv, plutovg_rect_t *rect) +{ + if (ucv_type(uv) != UC_OBJECT && ucv_type(uv) != UC_ARRAY) + return false; + + return uc_pvg_component(uv, "x", 0, &rect->x) && + uc_pvg_component(uv, "y", 1, &rect->y) && + uc_pvg_component(uv, "w", 2, &rect->w) && + uc_pvg_component(uv, "h", 3, &rect->h); +} + +static inline uc_value_t * +uc_pvg_rect_new(uc_vm_t *vm, const plutovg_rect_t *rect) +{ + uc_value_t *rv = ucv_object_new(vm); + + ucv_object_add(rv, "x", ucv_double_new(rect->x)); + ucv_object_add(rv, "y", ucv_double_new(rect->y)); + ucv_object_add(rv, "w", ucv_double_new(rect->w)); + ucv_object_add(rv, "h", ucv_double_new(rect->h)); + + return rv; +} + +static inline bool +uc_pvg_point_get(uc_value_t *uv, plutovg_point_t *point) +{ + if (ucv_type(uv) != UC_OBJECT && ucv_type(uv) != UC_ARRAY) + return false; + + return uc_pvg_component(uv, "x", 0, &point->x) && + uc_pvg_component(uv, "y", 1, &point->y); +} + +static inline uc_value_t * +uc_pvg_point_new(uc_vm_t *vm, const plutovg_point_t *point) +{ + uc_value_t *rv = ucv_object_new(vm); + + ucv_object_add(rv, "x", ucv_double_new(point->x)); + ucv_object_add(rv, "y", ucv_double_new(point->y)); + + return rv; +} + +static inline bool +uc_pvg_matrix_get(uc_value_t *uv, plutovg_matrix_t *matrix) +{ + if (ucv_type(uv) == UC_STRING) + return plutovg_matrix_parse(matrix, ucv_string_get(uv), + ucv_string_length(uv)); + + if (ucv_type(uv) != UC_OBJECT && ucv_type(uv) != UC_ARRAY) + return false; + + return uc_pvg_component(uv, "a", 0, &matrix->a) && + uc_pvg_component(uv, "b", 1, &matrix->b) && + uc_pvg_component(uv, "c", 2, &matrix->c) && + uc_pvg_component(uv, "d", 3, &matrix->d) && + uc_pvg_component(uv, "e", 4, &matrix->e) && + uc_pvg_component(uv, "f", 5, &matrix->f); +} + +static inline uc_value_t * +uc_pvg_matrix_new(uc_vm_t *vm, const plutovg_matrix_t *matrix) +{ + uc_value_t *rv = ucv_object_new(vm); + + ucv_object_add(rv, "a", ucv_double_new(matrix->a)); + ucv_object_add(rv, "b", ucv_double_new(matrix->b)); + ucv_object_add(rv, "c", ucv_double_new(matrix->c)); + ucv_object_add(rv, "d", ucv_double_new(matrix->d)); + ucv_object_add(rv, "e", ucv_double_new(matrix->e)); + ucv_object_add(rv, "f", ucv_double_new(matrix->f)); + + return rv; +} + +#endif