From daed553a06716328366f5ea1a2ba09ba4872de1d Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Fri, 22 May 2020 19:52:24 +0100 Subject: javascript: Support Canvas to a basic level Signed-off-by: Daniel Silverstone --- .../duktape/CanvasRenderingContext2D.bnd | 232 +++++++++++++++++++++ .../javascript/duktape/HTMLCanvasElement.bnd | 33 ++- content/handlers/javascript/duktape/ImageData.bnd | 44 ++++ content/handlers/javascript/duktape/dukky.c | 3 + content/handlers/javascript/duktape/netsurf.bnd | 4 + 5 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd create mode 100644 content/handlers/javascript/duktape/ImageData.bnd (limited to 'content') diff --git a/content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd b/content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd new file mode 100644 index 000000000..5acf75000 --- /dev/null +++ b/content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd @@ -0,0 +1,232 @@ +/* HTML canvas element rendering context binding using duktape and libdom + * + * Copyright 2020 Daniel Silverstone + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * Released under the terms of the MIT License, + * http://www.opensource.org/licenses/mit-license + */ + +class CanvasRenderingContext2D { + private struct dom_html_element *canvas; + private struct bitmap *bitmap; + private int width; + private int height; + private size_t stride; + prologue %{ +/* prologue */ +#include "desktop/gui_internal.h" +#include "desktop/gui_table.h" +#include "netsurf/bitmap.h" +#include "utils/corestrings.h" +/* It's a smidge naughty of us to read + * this particular header, but we're needing + * to redraw the node we represent + */ +#include "content/handlers/html/private.h" + +static void redraw_node(dom_node *node) +{ + struct box *box = NULL; + html_content *htmlc = NULL; + dom_exception exc; + dom_document *doc; + + exc = dom_node_get_user_data(node, + corestring_dom___ns_key_box_node_data, + &box); + if (exc != DOM_NO_ERR || box == NULL) { + return; + } + + exc = dom_node_get_owner_document(node, &doc); + if (exc != DOM_NO_ERR || doc == NULL) { + return; + } + + exc = dom_node_get_user_data(doc, + corestring_dom___ns_key_html_content_data, + &htmlc); + if (exc != DOM_NO_ERR || htmlc == NULL) { + dom_node_unref(doc); + return; + } + + html__redraw_a_box(htmlc, box); + + dom_node_unref(doc); +} + +/* prologue ends */ +%}; +}; + +init CanvasRenderingContext2D(struct dom_html_element *canvas) +%{ + struct bitmap *bitmap; + dom_exception exc; + + assert(canvas != NULL); + + priv->canvas = canvas; + dom_node_ref(canvas); + + exc = dom_node_get_user_data(canvas, + corestring_dom___ns_key_canvas_node_data, + &bitmap); + assert(exc == DOM_NO_ERR); + assert(bitmap != NULL); + + priv->bitmap = bitmap; + priv->width = guit->bitmap->get_width(bitmap); + priv->height = guit->bitmap->get_height(bitmap); + priv->stride = guit->bitmap->get_rowstride(bitmap); +%} + +fini CanvasRenderingContext2D() +%{ + dom_node_unref(priv->canvas); +%} + +getter CanvasRenderingContext2D::canvas() +%{ + dukky_push_node(ctx, (dom_node *)priv->canvas); + return 1; +%} + +method CanvasRenderingContext2D::createImageData() +%{ + /* Can be called either with width and height, or with a reference + * imagedata object + */ + image_data_private_t *idpriv; + int width, height; + + if (duk_get_top(ctx) == 2) { + width = duk_to_int(ctx, 0); + height = duk_to_int(ctx, 1); + } else if (dukky_instanceof(ctx, 0, PROTO_NAME(IMAGEDATA))) { + duk_get_prop_string(ctx, 0, dukky_magic_string_private); + idpriv = duk_get_pointer(ctx, -1); + width = idpriv->width; + height = idpriv->height; + duk_pop(ctx); + } else { + duk_push_null(ctx); + return 1; + } + + duk_push_int(ctx, width); + duk_push_int(ctx, height); + if (dukky_create_object(ctx, + PROTO_NAME(IMAGEDATA), + 2) != DUK_EXEC_SUCCESS) { + return duk_error(ctx, + DUK_ERR_ERROR, + "Unable to create ImageData"); + } + return 1; +%} + +method CanvasRenderingContext2D::getImageData() +%{ + /* called with x, y, width, height */ + int x = duk_get_int(ctx, 0); + int y = duk_get_int(ctx, 1); + int width = duk_get_int(ctx, 2); + int height = duk_get_int(ctx, 3); + image_data_private_t *idpriv; + uint8_t *bitmap_base; + + if (width < 1 || height < 1 || + (x + width) > priv->width || (y + height) > priv->height) { + return duk_error(ctx, DUK_ERR_RANGE_ERROR, "invalid (%d,%d) (%dx%d)", x, y, width, height); + } + + duk_push_int(ctx, width); + duk_push_int(ctx, height); + if (dukky_create_object(ctx, + PROTO_NAME(IMAGEDATA), + 2) != DUK_EXEC_SUCCESS) { + return duk_error(ctx, + DUK_ERR_ERROR, + "Unable to create ImageData"); + } + + /* ... imgdata */ + duk_get_prop_string(ctx, -1, dukky_magic_string_private); + idpriv = duk_get_pointer(ctx, -1); + duk_pop(ctx); + + /* We now have access to the imagedata private, so we need to copy + * the pixel range out of ourselves + */ + bitmap_base = guit->bitmap->get_buffer(priv->bitmap); + for (int yy = y; yy < (y+height); ++yy) { + uint8_t *src_base = bitmap_base + (priv->stride * yy); + uint8_t *dst_base = idpriv->data + (width * 4); + memcpy(dst_base + (x * 4), src_base + (x * 4), width * 4); + } + return 1; +%} + +method CanvasRenderingContext2D::putImageData() +%{ + /* imgdata, x, y[, clipx, clipy, clipw, cliph] */ + /* If provided, the clip coordinates are within the input image data */ + /* We pretend the image is placed at x,y within ourselves, and then we + * copy the clip rectangle (defaults to whole image) + */ + image_data_private_t *idpriv; + int x = duk_to_int(ctx, 1); + int y = duk_to_int(ctx, 2); + int clipx = 0; + int clipy = 0; + int clipw = 0; + int cliph = 0; + uint8_t *bitmap_base; + + if (!dukky_instanceof(ctx, 0, PROTO_NAME(IMAGEDATA))) { + return duk_generic_error(ctx, "Expected ImageData as first argument"); + } + + duk_get_prop_string(ctx, 0, dukky_magic_string_private); + idpriv = duk_get_pointer(ctx, -1); + duk_pop(ctx); + + if (duk_get_top(ctx) < 7) { + /* Clipping data not provided */ + clipw = idpriv->width; + cliph = idpriv->height; + } else { + clipx = duk_to_int(ctx, 3); + clipy = duk_to_int(ctx, 4); + clipw = duk_to_int(ctx, 5); + cliph = duk_to_int(ctx, 6); + } + + if (x < 0 || y < 0 || /* Not positioning negative */ + (x + clipx + clipw) > priv->width || /* RHS not beyond bounds */ + (y + clipy + cliph) > priv->height || /* bottom not beyond bounds */ + clipx < 0 || clipy < 0 || /* Input in range */ + (clipx + clipw) > idpriv->width || /* Input in range */ + (clipy + cliph) > idpriv->height) { /* Input in range */ + return duk_error(ctx, DUK_ERR_RANGE_ERROR, "invalid inputs"); + } + + bitmap_base = guit->bitmap->get_buffer(priv->bitmap); + + for (int yy = clipy; yy < (clipy + cliph); yy++) { + uint8_t *dst_row = bitmap_base + ((y + yy) * priv->stride); + uint8_t *src_row = idpriv->data + (yy * idpriv->width * 4); + memcpy(dst_row + ((x + clipx) * 4), + src_row + (clipx * 4), + clipw * 4); + } + guit->bitmap->modified(priv->bitmap); + + redraw_node((dom_node *)(priv->canvas)); + + return 0; +%} diff --git a/content/handlers/javascript/duktape/HTMLCanvasElement.bnd b/content/handlers/javascript/duktape/HTMLCanvasElement.bnd index 189ddb0f4..9fa46ff0b 100644 --- a/content/handlers/javascript/duktape/HTMLCanvasElement.bnd +++ b/content/handlers/javascript/duktape/HTMLCanvasElement.bnd @@ -1,6 +1,7 @@ /* HTML canvas element binding using duktape and libdom * * Copyright 2020 Vincent Sanders + * Copyright 2020 Daniel Silverstone * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -11,8 +12,36 @@ init HTMLCanvasElement(struct dom_html_element *html_canvas_element::html_element); getter HTMLCanvasElement::width(); -setter HTMLCanvasElement::width(); +/* setter HTMLCanvasElement::width(); */ getter HTMLCanvasElement::height(); -setter HTMLCanvasElement::height(); +/* setter HTMLCanvasElement::height(); */ + +method HTMLCanvasElement::getContext() +%{ + /* modetype[, {options}] */ + const char *modetype = duk_to_string(ctx, 0); + if (strcmp(modetype, "2d") != 0) { + duk_push_null(ctx); + return 1; + } + + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, MAGIC(Context2D)); + if (duk_is_undefined(ctx, -1)) { + duk_pop(ctx); + + duk_push_pointer(ctx, ((node_private_t*)priv)->node); + if (dukky_create_object(ctx, + PROTO_NAME(CANVASRENDERINGCONTEXT2D), + 1) != DUK_EXEC_SUCCESS) { + return duk_error(ctx, + DUK_ERR_ERROR, + "Unable to create CanvasRenderingContext2D"); + } + duk_dup(ctx, -1); + duk_put_prop_string(ctx, -3, MAGIC(Context2D)); + } + return 1; +%} diff --git a/content/handlers/javascript/duktape/ImageData.bnd b/content/handlers/javascript/duktape/ImageData.bnd new file mode 100644 index 000000000..17673d92a --- /dev/null +++ b/content/handlers/javascript/duktape/ImageData.bnd @@ -0,0 +1,44 @@ +/* HTML canvas ImageData objects + * + * Copyright 2020 Daniel Silverstone + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * Released under the terms of the MIT License, + * http://www.opensource.org/licenses/mit-license + */ + +class ImageData { + private int width; + private int height; + private uint8_t *data; +}; + +init ImageData(int width, int height) +%{ + priv->width = width; + priv->height = height; + priv->data = duk_push_buffer(ctx, width * height * 4, false); + duk_put_prop_string(ctx, 0, MAGIC(DATA)); + duk_pop(ctx); +%} + +getter ImageData::width() +%{ + duk_push_int(ctx, priv->width); + return 1; +%} + +getter ImageData::height() +%{ + duk_push_int(ctx, priv->height); + return 1; +%} + +getter ImageData::data() +%{ + duk_push_this(ctx); + duk_get_prop_string(ctx, -1, MAGIC(DATA)); + duk_push_buffer_object(ctx, -1, 0, priv->width * priv->height * 4, DUK_BUFOBJ_UINT8CLAMPEDARRAY); + return 1; +%} diff --git a/content/handlers/javascript/duktape/dukky.c b/content/handlers/javascript/duktape/dukky.c index 6d877129b..830f48108 100644 --- a/content/handlers/javascript/duktape/dukky.c +++ b/content/handlers/javascript/duktape/dukky.c @@ -380,6 +380,9 @@ static void dukky_html_element_class_from_tag_type(dom_html_element_type type, case DOM_HTML_ELEMENT_TYPE_ISINDEX: SET_HTML_CLASS(ISINDEX) break; + case DOM_HTML_ELEMENT_TYPE_CANVAS: + SET_HTML_CLASS(CANVAS) + break; case DOM_HTML_ELEMENT_TYPE__COUNT: assert(type != DOM_HTML_ELEMENT_TYPE__COUNT); /* fallthrough */ diff --git a/content/handlers/javascript/duktape/netsurf.bnd b/content/handlers/javascript/duktape/netsurf.bnd index 5fc592771..e47f07d2b 100644 --- a/content/handlers/javascript/duktape/netsurf.bnd +++ b/content/handlers/javascript/duktape/netsurf.bnd @@ -200,4 +200,8 @@ init HTMLFormControlsCollection(struct dom_html_collection *coll); init HTMLOptionsCollection(struct dom_html_collection *coll); init HTMLPropertiesCollection(struct dom_html_collection *coll); +/* Stuff to do with canvasses */ + +#include "CanvasRenderingContext2D.bnd" +#include "ImageData.bnd" -- cgit v1.2.3