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