diff options
Diffstat (limited to 'content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd')
-rw-r--r-- | content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd | 232 |
1 files changed, 232 insertions, 0 deletions
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 <dsilvers@netsurf-browser.org> + * + * 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; +%} |