mirror of
https://github.com/netsurf-browser/netsurf
synced 2024-11-25 07:49:38 +03:00
javascript: Support Canvas to a basic level
Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
This commit is contained in:
parent
244c49df26
commit
daed553a06
232
content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd
Normal file
232
content/handlers/javascript/duktape/CanvasRenderingContext2D.bnd
Normal file
@ -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;
|
||||
%}
|
@ -1,6 +1,7 @@
|
||||
/* HTML canvas element binding using duktape and libdom
|
||||
*
|
||||
* Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
|
||||
* Copyright 2020 Daniel Silverstone <dsilvers@netsurf-browser.org>
|
||||
*
|
||||
* 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;
|
||||
%}
|
||||
|
||||
|
44
content/handlers/javascript/duktape/ImageData.bnd
Normal file
44
content/handlers/javascript/duktape/ImageData.bnd
Normal file
@ -0,0 +1,44 @@
|
||||
/* HTML canvas ImageData objects
|
||||
*
|
||||
* 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 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;
|
||||
%}
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user