blob: 8926278d9f6663855a4c0b9a73e698045de4b77a [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2019-2026
* All rights reserved
*
* This file is part of GPAC / JavaScript vector graphics bindings
*
* GPAC is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* GPAC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/*
ANY CHANGE TO THE API MUST BE REFLECTED IN THE DOCUMENTATION IN gpac/share/doc/idl/evg.idl
(no way to define inline JS doc with doxygen)
*/
#include <gpac/setup.h>
#if defined(GPAC_HAS_QJS) && !defined(GPAC_DISABLE_EVG)
/*base SVG type*/
#include <gpac/nodes_svg.h>
#include <gpac/nodes_mpeg4.h>
#include <gpac/nodes_x3d.h>
/*dom events*/
#include <gpac/evg.h>
#include "../scenegraph/qjs_common.h"
#include <gpac/internal/compositor_dev.h>
/*for texture from png/rgb*/
#include <gpac/bitstream.h>
#include <gpac/avparse.h>
#include <gpac/network.h>
#ifdef GPAC_HAS_FFMPEG
#include "../filters/ff_common.h"
#endif
#ifndef GPAC_DISABLE_3D
#include <gpac/internal/mesh.h>
#endif
#define EVG_GET_FLOAT(_name, _jsval) { Double _v; if (JS_ToFloat64(ctx, &_v, _jsval)) return js_throw_err(ctx, GF_BAD_PARAM); _name = (Float) _v; }
#define CLAMPCOLF(_name) if (_name<0) _name=0; else if (_name>1.0) _name=1.0;
uint8_t *evg_get_array(JSContext *ctx, JSValueConst obj, u32 *size);
//test code, unused
//#define BUILTIN_SHADERS
//not used, wau too slow - we kept the code for future testing
//#define EVG_USE_JS_SHADER
typedef struct
{
u32 width, height, pf, stride, stride_uv, nb_comp;
char *data;
u32 data_size;
u8 owns_data;
u8 wide;
u32 flags;
GF_EVGStencil *stencil;
JSValue param_fun, obj, par_obj;
JSContext *ctx;
#ifndef GPAC_DISABLE_3D
char *named_tx;
void *gl_named_tx;
u8 force_resetup;
#endif //GPAC_DISABLE_3D
#ifdef GPAC_HAS_FFMPEG
struct SwsContext *swscaler;
#endif
} GF_JSTexture;
#define MAX_ATTR_DIM 4
typedef struct
{
u32 dim;
u8 comp_type;
} EVG_VAIRes;
enum
{
GF_EVG_VAI_VERTEX_INDEX=0,
GF_EVG_VAI_VERTEX,
GF_EVG_VAI_PRIMITIVE,
};
typedef struct
{
u32 prim_idx;
u32 nb_comp;
Float *values;
u32 nb_values;
JSValue ab;
#ifdef EVG_USE_JS_SHADER
JSValue res;
#endif
u32 interp_type;
Float anchors[3][MAX_ATTR_DIM];
EVG_VAIRes result;
Bool normalize;
} EVG_VAI;
typedef struct
{
u32 nb_comp, att_type;
Float *values;
u32 nb_values;
JSValue ab;
JSValue res;
u32 interp_type;
Bool normalize;
} EVG_VA;
enum
{
COMP_X = 1,//x or r or s
COMP_Y = 1<<1,//y or g or t
COMP_Z = 1<<2, //z or b
COMP_Q = 1<<3, //w/q or a
COMP_V2_XY = COMP_X|COMP_Y,
COMP_V2_XZ = COMP_X|COMP_Z,
COMP_V2_YZ = COMP_Y|COMP_Z,
COMP_V3 = COMP_X|COMP_Y|COMP_Z,
COMP_V4 = COMP_X|COMP_Y|COMP_Z|COMP_Q,
COMP_BOOL,
COMP_INT,
COMP_FLOAT,
//for builtin shaders only
COMP_TX,
COMP_FLAG_INT = 1<<6
};
typedef struct
{
s32 x;
s32 y;
s32 z;
s32 q;
} GF_IVec4;
typedef struct
{
u8 op_type;
u8 cond_type;
u8 left_value_type;
u8 right_value_type;
s32 left_value, right_value, right_value_second;
char *uni_name;
JSValue tx_ref;
GF_JSTexture *tx;
//we separate VAI/MX from base value in order to be able to assign a VAI value
union {
struct {
EVG_VAI *vai;
JSValue ref;
} vai;
struct {
EVG_VA *va;
JSValue ref;
} va;
struct {
GF_Matrix *mx;
JSValue ref;
} mx;
};
union {
Float vec[4];
s32 veci[4];
s32 ival;
Bool bval;
};
} ShaderOp;
enum
{
GF_EVG_SHADER_FRAGMENT=1,
GF_EVG_SHADER_VERTEX,
};
typedef struct
{
char *name;
union {
GF_Vec4 vecval;
s32 ival;
Bool bval;
void *ptr;
};
u8 value_type;
} ShaderVar;
typedef struct __evg_shader
{
u32 mode;
u32 nb_ops, alloc_ops;
ShaderOp *ops;
u32 nb_vars, alloc_vars;
ShaderVar *vars;
Bool invalid, disable_early_z;
Bool has_branches;
GF_List *vars_stack;
#ifdef BUILTIN_SHADERS
//native shaders
Bool (*frag_shader)(void *udta, GF_EVGFragmentParam *frag);
Bool (*frag_shader_init)(void *udta, GF_EVGFragmentParam *frag, u32 th_id, Bool is_cleanup);
#endif
} EVGShader;
typedef struct
{
u32 width, height, pf, stride, stride_uv, mem_size;
char *data;
Bool owns_data;
Bool center_coords;
GF_EVGSurface *surface;
u32 composite_op;
JSValue alpha_cbk;
JSValue frag_shader;
Bool frag_is_cbk;
JSValue vert_shader;
Bool vert_is_cbk;
JSValue depth_buffer;
EVGShader *frag, *vert;
JSContext *ctx;
JSValue obj;
#ifdef EVG_USE_JS_SHADER
JSValue frag_obj;
JSValue vert_obj;
#endif
} GF_JSCanvas;
typedef struct
{
GF_FontManager *fm;
GF_Path *path;
char **fontnames;
u32 nb_fonts;
Double font_size;
u32 align;
u32 baseline;
u32 styles;
GF_List *spans;
Bool horizontal;
Bool flip;
Double maxWidth;
Double lineSpacing;
Fixed min_x, min_y, max_x, max_y, max_w, max_h;
GF_Font *font;
Bool path_for_centered;
Bool right_to_left;
} GF_JSText;
JSClassID canvas_class_id;
JSClassID path_class_id;
JSClassID mx2d_class_id;
JSClassID colmx_class_id;
JSClassID stencil_class_id;
JSClassID texture_class_id;
JSClassID text_class_id;
JSClassID matrix_class_id;
JSClassID mesh_class_id;
#ifdef EVG_USE_JS_SHADER
JSClassID fragment_class_id;
JSClassID vertex_class_id;
JSClassID vaires_class_id;
#endif
JSClassID vai_class_id;
JSClassID va_class_id;
JSClassID shader_class_id;
#ifndef GPAC_DISABLE_FONTS
static void text_set_path(GF_JSCanvas *canvas, GF_JSText *text);
#endif
Bool get_color_from_args(JSContext *c, int argc, JSValueConst *argv, u32 idx, Double *a, Double *r, Double *g, Double *b);
static Bool get_color(JSContext *c, JSValueConst obj, Double *a, Double *r, Double *g, Double *b);
static void canvas_finalize(JSRuntime *rt, JSValue obj)
{
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return;
JS_FreeValueRT(rt, canvas->alpha_cbk);
JS_FreeValueRT(rt, canvas->frag_shader);
JS_FreeValueRT(rt, canvas->vert_shader);
#ifdef EVG_USE_JS_SHADER
JS_FreeValueRT(rt, canvas->frag_obj);
JS_FreeValueRT(rt, canvas->vert_obj);
#endif
JS_FreeValueRT(rt, canvas->depth_buffer);
if (canvas->owns_data)
gf_free(canvas->data);
if (canvas->surface)
gf_evg_surface_delete(canvas->surface);
gf_free(canvas);
}
static void canvas_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
{
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return;
JS_MarkValue(rt, canvas->alpha_cbk, mark_func);
JS_MarkValue(rt, canvas->frag_shader, mark_func);
JS_MarkValue(rt, canvas->vert_shader, mark_func);
#ifdef EVG_USE_JS_SHADER
JS_MarkValue(rt, canvas->frag_obj, mark_func);
JS_MarkValue(rt, canvas->vert_obj, mark_func);
#endif
JS_MarkValue(rt, canvas->depth_buffer, mark_func);
}
JSClassDef canvas_class = {
"Canvas",
.finalizer = canvas_finalize,
.gc_mark = canvas_gc_mark
};
enum
{
GF_EVG_CENTERED = 0,
GF_EVG_PATH,
GF_EVG_MATRIX,
GF_EVG_MATRIX_3D,
GF_EVG_CLIPPER,
GF_EVG_COMPOSITE_OP,
GF_EVG_ALPHA_FUN,
GF_EVG_IS_YUV,
GF_EVG_BIT_DEPTH,
GF_EVG_MASK_MODE,
GF_EVG_FRAG_SHADER,
GF_EVG_VERT_SHADER,
GF_EVG_CCW,
GF_EVG_BACKCULL,
GF_EVG_MINDEPTH,
GF_EVG_MAXDEPTH,
GF_EVG_DEPTHTEST,
GF_EVG_ANTIALIAS,
GF_EVG_POINTSIZE,
GF_EVG_POINTSMOOTH,
GF_EVG_LINESIZE,
GF_EVG_CLIP_ZERO,
GF_EVG_DEPTH_BUFFER,
GF_EVG_DEPTH_TEST,
GF_EVG_WRITE_DEPTH,
GF_EVG_RASTER_LEVEL,
};
u8 evg_get_alpha(void *cbk, u8 src_alpha, s32 x, s32 y)
{
u32 res_a=0;
GF_JSCanvas *canvas = cbk;
JSValue ret, args[3];
args[0] = JS_NewInt32(canvas->ctx, src_alpha);
args[1] = JS_NewInt32(canvas->ctx, x);
args[2] = JS_NewInt32(canvas->ctx, y);
ret = JS_Call(canvas->ctx, canvas->alpha_cbk, canvas->obj, 3, args);
JS_ToInt32(canvas->ctx, &res_a, ret);
return res_a;
}
static JSValue canvas_constructor_internal(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv, GF_JSCanvas *canvas_reconfig)
{
u32 width, height, pf=0, osize;
size_t data_size=0;
u8 *data=NULL;
u32 stride = 0;
u32 stride_uv = 0;
GF_JSCanvas *the_canvas = NULL;
GF_JSCanvas *canvas = NULL;
GF_Err e;
if (argc<3)
return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &width, argv[0]))
return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &height, argv[1]))
return GF_JS_EXCEPTION(c);
if (JS_IsString(argv[2])) {
const char *str = JS_ToCString(c, argv[2]);
pf = gf_pixel_fmt_parse(str);
JS_FreeCString(c, str);
} else if (JS_ToInt32(c, &pf, argv[2])) {
return GF_JS_EXCEPTION(c);
}
if (!width || !height || !pf)
return GF_JS_EXCEPTION(c);
if (argc>3) {
s32 idx=0;
if (JS_IsObject(argv[3])) {
data = JS_GetArrayBuffer(c, &data_size, argv[3]);
if (!data) return GF_JS_EXCEPTION(c);
idx=1;
} else if (!JS_IsInteger(argv[3]))
return GF_JS_EXCEPTION(c);
if (argc>3+idx) {
if (JS_ToInt32(c, &stride, argv[3+idx]))
return GF_JS_EXCEPTION(c);
if (argc>4+idx) {
if (JS_ToInt32(c, &stride_uv, argv[4+idx]))
return GF_JS_EXCEPTION(c);
}
}
}
if (!canvas_reconfig) {
GF_SAFEALLOC(the_canvas, GF_JSCanvas);
if (!the_canvas)
return GF_JS_EXCEPTION(c);
}
if (!gf_pixel_get_size_info(pf, width, height, &osize, &stride, &stride_uv, NULL, NULL)) {
if (canvas)
gf_free(canvas);
return GF_JS_EXCEPTION(c);
}
if (data && (data_size<osize)) {
if (the_canvas)
gf_free(the_canvas);
return GF_JS_EXCEPTION(c);
}
canvas = canvas_reconfig ? canvas_reconfig : the_canvas;
canvas->mem_size = osize;
canvas->width = width;
canvas->height = height;
canvas->pf = pf;
canvas->stride = stride;
if (the_canvas) {
canvas->alpha_cbk = JS_UNDEFINED;
canvas->frag_shader = JS_UNDEFINED;
canvas->vert_shader = JS_UNDEFINED;
#ifdef EVG_USE_JS_SHADER
canvas->frag_obj = JS_UNDEFINED;
canvas->vert_obj = JS_UNDEFINED;
#endif
canvas->depth_buffer = JS_UNDEFINED;
}
canvas->ctx = c;
if (data) {
canvas->data = data;
canvas->owns_data = GF_FALSE;
} else {
canvas->data = gf_malloc(sizeof(u8)*osize);
canvas->owns_data = GF_TRUE;
}
e = GF_OK;
if (the_canvas) {
canvas->surface = gf_evg_surface_new(GF_TRUE);
canvas->center_coords = GF_TRUE;
}
if (!canvas->surface) {
e = GF_BAD_PARAM;
} else if (!e) {
e = gf_evg_surface_attach_to_buffer(canvas->surface, canvas->data, canvas->width, canvas->height, 0, canvas->stride, canvas->pf);
}
if (e) {
if (canvas_reconfig)
return GF_JS_EXCEPTION(c);
if (canvas->owns_data) gf_free(canvas->data);
gf_evg_surface_delete(canvas->surface);
gf_free(canvas);
return GF_JS_EXCEPTION(c);
}
if (canvas_reconfig)
return JS_UNDEFINED;
canvas->obj = JS_NewObjectClass(c, canvas_class_id);
if (JS_IsException(canvas->obj)) return canvas->obj;
JS_SetOpaque(canvas->obj, canvas);
return canvas->obj;
}
static JSValue canvas_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return canvas_constructor_internal(c, new_target, argc, argv, NULL);
}
static JSValue canvas_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(c);
switch (magic) {
case GF_EVG_CENTERED: return JS_NewBool(c, canvas->center_coords);
case GF_EVG_COMPOSITE_OP: return JS_NewInt32(c, canvas->composite_op);
case GF_EVG_ALPHA_FUN: return JS_DupValue(c, canvas->alpha_cbk);
case GF_EVG_FRAG_SHADER: return JS_DupValue(c, canvas->frag_shader);
case GF_EVG_VERT_SHADER: return JS_DupValue(c, canvas->vert_shader);
case GF_EVG_DEPTH_BUFFER: return JS_DupValue(c, canvas->depth_buffer);
case GF_EVG_RASTER_LEVEL: return JS_NewInt32(c, gf_evg_surface_get_raster_level(canvas->surface));
case GF_EVG_IS_YUV:
if (gf_pixel_fmt_is_yuv(canvas->pf)) return JS_TRUE;
return JS_FALSE;
case GF_EVG_BIT_DEPTH:
return JS_NewInt32(c, gf_pixel_is_wide_depth(canvas->pf));
case GF_EVG_MASK_MODE:
return JS_NewInt32(c, gf_evg_surface_get_mask_mode(canvas->surface) );
case GF_EVG_CLIPPER:
return gf_evg_surface_use_clipper(canvas->surface) ? JS_TRUE : JS_FALSE;
}
return JS_UNDEFINED;
}
Bool canvas_get_irect(JSContext *c, JSValueConst obj, GF_IRect *rc, Bool reset)
{
JSValue v;
Double fval;
int res;
if (reset)
memset(rc, 0, sizeof(GF_IRect));
#define GET_PROP( _n, _f, _mandat)\
v = JS_GetPropertyStr(c, obj, _n);\
if (JS_IsUndefined(v)) {\
if (_mandat) return GF_FALSE;\
} else {\
if (JS_IsInteger(v)) \
res = JS_ToInt32(c, &(rc->_f), v);\
else {\
res = JS_ToFloat64(c, &fval, v);\
rc->_f = (s32) fval;\
}\
JS_FreeValue(c, v);\
if (res) return GF_FALSE;\
}\
GET_PROP("x", x, 0)
GET_PROP("y", y, 0)
GET_PROP("w", width, 1)
GET_PROP("h", height, 1)
#undef GET_PROP
return GF_TRUE;
}
static Bool evg_frag_shader_ops(void *udta, GF_EVGFragmentParam *frag);
static Bool evg_frag_shader_ops_init(void *udta, GF_EVGFragmentParam *frag, u32 th_id, Bool is_cleanup);
static Bool evg_vert_shader_ops(void *udta, GF_EVGVertexParam *frag);
static JSValue canvas_setProperty(JSContext *ctx, JSValueConst obj, JSValueConst value, int magic)
{
Float f;
s32 ival;
GF_Err e = GF_OK;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(ctx);
switch (magic) {
case GF_EVG_CENTERED:
canvas->center_coords = JS_ToBool(ctx, value) ? GF_TRUE : GF_FALSE;
gf_evg_surface_set_center_coords(canvas->surface, canvas->center_coords);
return JS_UNDEFINED;
case GF_EVG_PATH:
if (JS_IsNull(value)) {
gf_evg_surface_set_path(canvas->surface, NULL);
} else {
GF_Path *gp = JS_GetOpaque(value, path_class_id);
if (gp)
gf_evg_surface_set_path(canvas->surface, gp);
else {
GF_JSText *text = JS_GetOpaque(value, text_class_id);
if (text) {
#ifndef GPAC_DISABLE_FONTS
text_set_path(canvas, text);
#else
return GF_JS_EXCEPTION(ctx);
#endif
}
}
}
return JS_UNDEFINED;
case GF_EVG_MATRIX:
if (JS_IsNull(value)) {
gf_evg_surface_set_matrix(canvas->surface, NULL);
} else {
GF_Matrix2D *mx = JS_GetOpaque(value, mx2d_class_id);
gf_evg_surface_set_matrix(canvas->surface, mx);
}
return JS_UNDEFINED;
case GF_EVG_MATRIX_3D:
if (JS_IsNull(value)) {
gf_evg_surface_set_matrix(canvas->surface, NULL);
} else {
GF_Matrix *mx = JS_GetOpaque(value, matrix_class_id);
gf_evg_surface_set_matrix_3d(canvas->surface, mx);
}
return JS_UNDEFINED;
case GF_EVG_RASTER_LEVEL:
JS_ToInt32(ctx, &ival, value);
gf_evg_surface_set_raster_level(canvas->surface, ival);
return JS_UNDEFINED;
case GF_EVG_CLIPPER:
if (JS_IsNull(value)) {
gf_evg_surface_set_clipper(canvas->surface, NULL);
} else {
GF_IRect rc;
canvas_get_irect(ctx, value, &rc, GF_TRUE);
gf_evg_surface_set_clipper(canvas->surface, &rc);
}
return JS_UNDEFINED;
case GF_EVG_COMPOSITE_OP:
if (JS_ToInt32(ctx, &canvas->composite_op, value)) return GF_JS_EXCEPTION(ctx);
gf_evg_surface_set_composite_mode(canvas->surface, canvas->composite_op);
break;
case GF_EVG_ALPHA_FUN:
JS_FreeValue(ctx, canvas->alpha_cbk);
if (JS_IsNull(value) || !JS_IsFunction(ctx, value)) {
canvas->alpha_cbk = JS_UNDEFINED;
gf_evg_surface_set_alpha_callback(canvas->surface, NULL, NULL);
} else {
canvas->alpha_cbk = JS_DupValue(ctx, value);
gf_evg_surface_set_alpha_callback(canvas->surface, evg_get_alpha, canvas);
}
return JS_UNDEFINED;
//3D extensions
case GF_EVG_FRAG_SHADER:
JS_FreeValue(ctx, canvas->frag_shader);
canvas->frag_shader = JS_UNDEFINED;
canvas->frag = NULL;
if (JS_IsNull(value)) {
canvas->frag_shader = JS_UNDEFINED;
e = gf_evg_surface_set_fragment_shader(canvas->surface, NULL, NULL, NULL);
#ifdef EVG_USE_JS_SHADER
} else if (JS_IsFunction(ctx, value)) {
canvas->frag_shader = JS_DupValue(ctx, value);
e = gf_evg_surface_set_fragment_shader(canvas->surface, evg_frag_shader_fun, NULL, canvas);
#endif
} else if (JS_IsObject(value)) {
canvas->frag = JS_GetOpaque(value, shader_class_id);
if (!canvas->frag || (canvas->frag->mode != GF_EVG_SHADER_FRAGMENT))
return js_throw_err_msg(ctx, GF_BAD_PARAM, "Invalid fragment shader object");
canvas->frag_shader = JS_DupValue(ctx, value);
#ifdef BUILTIN_SHADERS
if (canvas->frag->frag_shader) {
e = gf_evg_surface_set_fragment_shader(canvas->surface, canvas->frag->frag_shader, canvas->frag->frag_shader_init, canvas->frag);
} else
#endif
{
e = gf_evg_surface_set_fragment_shader(canvas->surface, evg_frag_shader_ops, evg_frag_shader_ops_init, canvas);
}
if (!e) e = gf_evg_surface_disable_early_depth(canvas->surface, canvas->frag->disable_early_z);
} else {
canvas->frag_shader = JS_UNDEFINED;
e = gf_evg_surface_set_fragment_shader(canvas->surface, NULL, NULL, NULL);
}
break;
case GF_EVG_VERT_SHADER:
JS_FreeValue(ctx, canvas->vert_shader);
canvas->vert_shader = JS_UNDEFINED;
canvas->vert = NULL;
if (JS_IsNull(value)) {
canvas->vert_shader = JS_UNDEFINED;
e = gf_evg_surface_set_vertex_shader(canvas->surface, NULL, NULL);
#ifdef EVG_USE_JS_SHADER
} else if (JS_IsFunction(ctx, value)) {
canvas->vert_shader = JS_DupValue(ctx, value);
e = gf_evg_surface_set_vertex_shader(canvas->surface, evg_vert_shader_fun, canvas);
#endif
} else if (JS_IsObject(value)) {
canvas->vert = JS_GetOpaque(value, shader_class_id);
if (!canvas->vert || (canvas->vert->mode != GF_EVG_SHADER_VERTEX))
return js_throw_err_msg(ctx, GF_BAD_PARAM, "Invalid fragment shader object");
canvas->vert_shader = JS_DupValue(ctx, value);
e = gf_evg_surface_set_vertex_shader(canvas->surface, evg_vert_shader_ops, canvas);
} else {
canvas->frag_shader = JS_UNDEFINED;
e = gf_evg_surface_set_fragment_shader(canvas->surface, NULL, NULL, NULL);
}
break;
case GF_EVG_CCW:
e = gf_evg_surface_set_ccw(canvas->surface, JS_ToBool(ctx, value) );
break;
case GF_EVG_BACKCULL:
e = gf_evg_surface_set_backcull(canvas->surface, JS_ToBool(ctx, value) );
break;
case GF_EVG_ANTIALIAS:
e = gf_evg_surface_set_antialias(canvas->surface, JS_ToBool(ctx, value) );
break;
case GF_EVG_DEPTH_BUFFER:
{
Float *depthb;
u32 dsize;
JS_FreeValue(ctx, canvas->depth_buffer);
canvas->depth_buffer = JS_UNDEFINED;
depthb = (Float *) evg_get_array(ctx, value, &dsize);
if (!depthb) {
canvas->depth_buffer = JS_NULL;
e = gf_evg_surface_set_depth_buffer(canvas->surface, NULL);
} else {
u32 req_size = canvas->width*canvas->height*sizeof(Float);
if (req_size>dsize) {
e = GF_BAD_PARAM;
gf_evg_surface_set_depth_buffer(canvas->surface, NULL);
} else {
canvas->depth_buffer = JS_DupValue(ctx, value);
e = gf_evg_surface_set_depth_buffer(canvas->surface, depthb);
}
}
break;
}
case GF_EVG_MINDEPTH:
EVG_GET_FLOAT(f, value);
e = gf_evg_surface_set_min_depth(canvas->surface, f);
break;
case GF_EVG_MAXDEPTH:
EVG_GET_FLOAT(f, value);
e = gf_evg_surface_set_max_depth(canvas->surface, f);
break;
case GF_EVG_DEPTHTEST:
if (JS_ToInt32(ctx, &ival, value))
return js_throw_err(ctx, GF_BAD_PARAM);
e = gf_evg_set_depth_test(canvas->surface, ival);
break;
case GF_EVG_POINTSIZE:
EVG_GET_FLOAT(f, value);
e = gf_evg_surface_set_point_size(canvas->surface, f);
break;
case GF_EVG_POINTSMOOTH:
e = gf_evg_surface_set_point_smooth(canvas->surface, JS_ToBool(ctx, value) );
break;
case GF_EVG_LINESIZE:
EVG_GET_FLOAT(f, value);
e = gf_evg_surface_set_line_size(canvas->surface, f);
break;
case GF_EVG_CLIP_ZERO:
e = gf_evg_surface_set_clip_zero(canvas->surface, JS_ToBool(ctx, value) ? GF_TRUE : GF_FALSE);
break;
case GF_EVG_DEPTH_TEST:
if (JS_ToInt32(ctx, &ival, value)) return GF_JS_EXCEPTION(ctx);
e = gf_evg_set_depth_test(canvas->surface, ival);
break;
case GF_EVG_WRITE_DEPTH:
e = gf_evg_surface_write_depth(canvas->surface, JS_ToBool(ctx, value) ? GF_TRUE : GF_FALSE);
break;
case GF_EVG_MASK_MODE:
if (JS_ToInt32(ctx, &ival, value))
return js_throw_err(ctx, GF_BAD_PARAM);
e = gf_evg_surface_set_mask_mode(canvas->surface, ival);
break;
}
if (e)
return js_throw_err(ctx, e);
return JS_UNDEFINED;
}
static JSValue canvas_clear_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_float)
{
s32 i;
s32 idx=0;
GF_Err e;
GF_IRect rc, *irc;
u32 r=0, g=0, b=0, a=255;
GF_Color col;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas)
return GF_JS_EXCEPTION(c);
irc = NULL;
if (argc && JS_IsObject(argv[0])) {
irc = &rc;
idx=1;
if (!canvas_get_irect(c, argv[0], &rc, GF_TRUE))
return GF_JS_EXCEPTION(c);
}
if ((argc>idx) && JS_IsString(argv[idx])) {
const char *str = JS_ToCString(c, argv[idx]);
col = gf_color_parse(str);
JS_FreeCString(c, str);
} else {
if (argc>4+idx) argc = 4+idx;
for (i=idx; i<argc; i++) {
s32 v;
if (use_float) {
Double d;
if (JS_ToFloat64(c, &d, argv[i]))
return GF_JS_EXCEPTION(c);
v = (s32) (d*255);
} else if (JS_ToInt32(c, &v, argv[i])) {
return GF_JS_EXCEPTION(c);
}
if (v<0) v = 0;
else if (v>255) v = 255;
if (i==idx) r=v;
else if (i==idx+1) g=v;
else if (i==idx+2) b=v;
else a=v;
}
col = GF_COL_ARGB(a, r, g, b) ;
}
e = gf_evg_surface_clear(canvas->surface, irc, col);
if (e)
return GF_JS_EXCEPTION(c);
return JS_UNDEFINED;
}
static JSValue canvas_clear(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return canvas_clear_ex(c, obj, argc, argv, GF_FALSE);
}
static JSValue canvas_clearf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return canvas_clear_ex(c, obj, argc, argv, GF_TRUE);
}
static JSValue canvas_rgb_yuv(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool to_rgb)
{
GF_Err e;
Double _r=0, _g=0, _b=0, _a=1.0;
Float r=0, g=0, b=0, a=1.0;
Bool as_array = GF_FALSE;
u32 arg_idx=0;
Float y, u, v;
JSValue ret;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas || !argc)
return GF_JS_EXCEPTION(c);
if (JS_IsBool(argv[0])) {
as_array = JS_ToBool(c, argv[0]);
arg_idx=1;
}
if (!get_color_from_args(c, argc, argv, arg_idx, &_a, &_r, &_g, &_b))
return GF_JS_EXCEPTION(c);
r = (Float) _r;
g = (Float) _g;
b = (Float) _b;
a = (Float) _a;
if (to_rgb) {
e = gf_evg_yuv_to_rgb_f(canvas->surface, r, g, b, &y, &u, &v);
} else {
e = gf_gf_evg_rgb_to_yuv_f(canvas->surface, r, g, b, &y, &u, &v);
}
if (e)
return GF_JS_EXCEPTION(c);
if (as_array) {
ret = JS_NewArray(c);
JS_SetPropertyStr(c, ret, "length", JS_NewInt32(c, 4) );
JS_SetPropertyUint32(c, ret, 0, JS_NewFloat64(c, y) );
JS_SetPropertyUint32(c, ret, 1, JS_NewFloat64(c, u) );
JS_SetPropertyUint32(c, ret, 2, JS_NewFloat64(c, v) );
JS_SetPropertyUint32(c, ret, 3, JS_NewFloat64(c, a) );
} else {
ret = JS_NewObject(c);
JS_SetPropertyStr(c, ret, "r", JS_NewFloat64(c, y) );
JS_SetPropertyStr(c, ret, "g", JS_NewFloat64(c, u) );
JS_SetPropertyStr(c, ret, "b", JS_NewFloat64(c, v) );
JS_SetPropertyStr(c, ret, "a", JS_NewFloat64(c, a) );
}
return ret;
}
static JSValue canvas_reassign(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
u8 *data;
size_t data_size=0;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas || !argc) return GF_JS_EXCEPTION(c);
if (canvas->owns_data) {
gf_free(canvas->data);
canvas->owns_data = GF_FALSE;
}
canvas->data = NULL;
if (argc==1) {
if (!JS_IsObject(argv[0])) return GF_JS_EXCEPTION(c);
} else {
return canvas_constructor_internal(c, obj, argc, argv, canvas);
}
data = JS_GetArrayBuffer(c, &data_size, argv[0]);
if (!data || (data_size<canvas->mem_size)) {
e = GF_BAD_PARAM;
} else {
canvas->data = data;
e = gf_evg_surface_attach_to_buffer(canvas->surface, canvas->data, canvas->width, canvas->height, 0, canvas->stride, canvas->pf);
}
if (e) return GF_JS_EXCEPTION(c);
return JS_UNDEFINED;
}
static JSValue canvas_toYUV(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return canvas_rgb_yuv(c, obj, argc, argv, GF_FALSE);
}
static JSValue canvas_toRGB(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return canvas_rgb_yuv(c, obj, argc, argv, GF_TRUE);
}
static GF_EVGStencil *get_stencil(JSContext *c, JSValue v)
{
GF_JSTexture *tx;
GF_EVGStencil *stencil = JS_GetOpaque(v, stencil_class_id);
if (stencil) return stencil;
tx = JS_GetOpaque(v, texture_class_id);
if (tx) return tx->stencil;
return NULL;
}
static JSValue canvas_fill(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_EVGStencil *sten1 = NULL;
GF_EVGStencil *sten2 = NULL;
GF_EVGStencil *sten3 = NULL;
u32 sten_idx=1;
u32 operand = 0;
GF_Err e;
Float op_params[4] = {0};
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(c);
if (!argc) {
if (!JS_IsUndefined(canvas->frag_shader)) {
gf_evg_surface_fill(canvas->surface, NULL);
return JS_UNDEFINED;
}
return GF_JS_EXCEPTION(c);
}
if (JS_IsObject(argv[0])) {
sten1 = get_stencil(c, argv[0]);
if (!sten1) return GF_JS_EXCEPTION(c);
e = gf_evg_surface_fill(canvas->surface, sten1);
return e ? GF_JS_EXCEPTION(c) : JS_UNDEFINED;
}
if (JS_ToInt32(c, &operand, argv[0]))
return GF_JS_EXCEPTION(c);
if (JS_IsArray(c, argv[1])) {
JSValue v;
u32 i, nb_items;
v = JS_GetPropertyStr(c, argv[1], "length");
JS_ToInt32(c, &nb_items, v);
JS_FreeValue(c, v);
if (nb_items>4) nb_items=3;
for (i=0; i<nb_items; i++) {
Double d;
v = JS_GetPropertyUint32(c, argv[1], i);
JS_ToFloat64(c, &d, v);
JS_FreeValue(c, v);
op_params[i] = (Float) d;
}
sten_idx = 2;
}
else if (JS_IsNumber(argv[1])) {
Double d;
JS_ToFloat64(c, &d, argv[1]);
op_params[0] = (Float) d;
sten_idx = 2;
}
sten1 = get_stencil(c, argv[sten_idx]);
if (!sten1) return GF_JS_EXCEPTION(c);
if ((u32) argc>sten_idx+1)
sten2 = get_stencil(c, argv[sten_idx+1]);
if ((u32) argc>sten_idx+2)
sten3 = get_stencil(c, argv[sten_idx+2]);
e = gf_evg_surface_multi_fill(canvas->surface, operand, sten1, sten2, sten3, op_params);
return e ? GF_JS_EXCEPTION(c) : JS_UNDEFINED;
}
static JSValue canvas_blit(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
#ifdef GPAC_HAS_FFMPEG
GF_JSTexture *tx;
GF_IRect dst_rc, src_rc;
enum AVPixelFormat pf_src, pf_dst;
double par_p[2];
u8 *src_data[5];
u8 *dst_data[5];
u32 src_stride[5];
u32 dst_stride[5];
u32 swsmode = 0;
u32 bpp, arg_idx=0;
GF_Err gf_evg_stencil_get_texture_planes(GF_EVGStencil *stencil, u8 **pY_or_RGB, u8 **pU, u8 **pV, u8 **pA, u32 *stride, u32 *stride_uv);
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas || !argc) return GF_JS_EXCEPTION(c);
if (!JS_IsObject(argv[0]))
return GF_JS_EXCEPTION(c);
tx = JS_GetOpaque(argv[0], texture_class_id);
if (!tx) return GF_JS_EXCEPTION(c);
pf_src = ffmpeg_pixfmt_from_gpac(tx->pf, GF_FALSE);
pf_dst = ffmpeg_pixfmt_from_gpac(canvas->pf, GF_FALSE);
if ((pf_src==AV_PIX_FMT_NONE) || (pf_dst==AV_PIX_FMT_NONE))
return js_throw_err(c, GF_NOT_SUPPORTED);
dst_rc.x = dst_rc.y = 0;
dst_rc.width = canvas->width;
dst_rc.height = canvas->height;
src_rc.x = src_rc.y = 0;
src_rc.width = tx->width;
src_rc.height = tx->height;
//get dst and src rectangles
if ((1+arg_idx < (u32) argc) && (JS_IsNull(argv[1+arg_idx]) || canvas_get_irect(c, argv[1+arg_idx], &dst_rc, GF_FALSE))) {
arg_idx++;
if ((1+arg_idx < (u32) argc) && (JS_IsNull(argv[1+arg_idx]) || canvas_get_irect(c, argv[1+arg_idx], &src_rc, GF_FALSE))) {
arg_idx++;
}
}
if (!dst_rc.width || !dst_rc.height) return JS_UNDEFINED;
if (!src_rc.width || !src_rc.height) return JS_UNDEFINED;
if ((1+arg_idx < (u32) argc) && JS_IsObject(argv[1+arg_idx])) {
u32 nb_params=0;
JSValue v = JS_GetPropertyStr(c, argv[1+arg_idx], "mode");
if (JS_IsString(v)) {
const char *smode = JS_ToCString(c, v);
if (!strcmp(smode, "fastbilinear")) swsmode = SWS_FAST_BILINEAR;
else if (!strcmp(smode, "bilinear")) swsmode = SWS_BILINEAR;
else if (!strcmp(smode, "bicubic")) { swsmode = SWS_BICUBIC; nb_params=2; }
else if (!strcmp(smode, "X")) swsmode = SWS_X;
else if (!strcmp(smode, "point")) swsmode = SWS_POINT;
else if (!strcmp(smode, "area")) swsmode = SWS_AREA;
else if (!strcmp(smode, "bicublin")) swsmode = SWS_BICUBLIN;
else if (!strcmp(smode, "gauss")) { swsmode = SWS_GAUSS; nb_params=1; }
else if (!strcmp(smode, "sinc")) swsmode = SWS_SINC;
else if (!strcmp(smode, "lanzcos")) { swsmode = SWS_LANCZOS; nb_params=1; }
else if (!strcmp(smode, "spline")) swsmode = SWS_SPLINE;
JS_FreeCString(c, smode);
}
JS_FreeValue(c, v);
if (nb_params) {
v = JS_GetPropertyStr(c, argv[1+arg_idx], "p1");
JS_ToFloat64(c, &par_p[0], v);
JS_FreeValue(c, v);
if (nb_params>1) {
v = JS_GetPropertyStr(c, argv[1+arg_idx], "p2");
JS_ToFloat64(c, &par_p[0], v);
JS_FreeValue(c, v);
}
}
}
if ((dst_rc.x<0) || (dst_rc.x+dst_rc.width > (s32) canvas->width)
|| (dst_rc.y<0) || (dst_rc.y+dst_rc.height > (s32) canvas->height)
) {
return js_throw_err(c, GF_BAD_PARAM);
}
if ((src_rc.x<0) || (src_rc.x+src_rc.width > (s32) tx->width)
|| (src_rc.y<0) || (src_rc.y+src_rc.height > (s32) tx->height)
) {
return js_throw_err(c, GF_BAD_PARAM);
}
par_p[0] = par_p[1] = 0;
tx->swscaler = sws_getCachedContext(tx->swscaler, src_rc.width, src_rc.height, pf_src, dst_rc.width, dst_rc.height, pf_dst, swsmode, NULL, NULL, par_p);
if (!tx->swscaler) {
return js_throw_err(c, GF_BAD_PARAM);
}
memset(src_data, 0, sizeof(u8*) * 5);
memset(dst_data, 0, sizeof(u8*) * 5);
memset(src_stride, 0, sizeof(u32) * 5);
memset(dst_stride, 0, sizeof(u32) * 5);
bpp = gf_pixel_get_bytes_per_pixel(canvas->pf);
dst_data[0] = canvas->data + dst_rc.x*bpp + dst_rc.y * canvas->stride;
dst_stride[0] = canvas->stride;
if (gf_pixel_fmt_is_yuv(canvas->pf)) {
u32 nb_planes, uv_height, off_x, off_y;
gf_pixel_get_size_info(canvas->pf, canvas->width, canvas->height, NULL, &dst_stride[0], &dst_stride[1], &nb_planes, &uv_height);
off_x = dst_rc.x * dst_stride[1] / dst_stride[0];
off_y = dst_rc.y * uv_height / canvas->height;
if (nb_planes==1) {
} else if (nb_planes==2) {
dst_data[1] = canvas->data + dst_stride[0] * canvas->height;
dst_data[1] += off_x * bpp + off_y * dst_stride[1];
} else if (nb_planes==3) {
dst_stride[2] = dst_stride[1];
dst_data[1] = canvas->data + dst_stride[0] * canvas->height;
dst_data[2] = dst_data[1] + dst_stride[1] * uv_height;
dst_data[1] += off_x * bpp + off_y * dst_stride[1];
dst_data[2] += off_x * bpp + off_y * dst_stride[2];
} else if (nb_planes==4) {
dst_stride[2] = dst_stride[1];
dst_stride[3] = dst_stride[0];
dst_data[1] = canvas->data + dst_stride[0] * canvas->height;
dst_data[2] = dst_data[1] + dst_stride[1] * uv_height;
dst_data[3] = dst_data[2] + dst_stride[2] * uv_height;
dst_data[1] += off_x * bpp + off_y * dst_stride[1];
dst_data[2] += off_x * bpp + off_y * dst_stride[2];
dst_data[3] += dst_rc.x * bpp + dst_rc.y * dst_stride[3];
}
}
gf_evg_stencil_get_texture_planes((GF_EVGStencil *) tx->stencil, &src_data[0], &src_data[1], &src_data[2], &src_data[3], &src_stride[0], &src_stride[1]);
if (src_data[3])
src_stride[3] = src_stride[0];
if (src_data[2])
src_stride[2] = src_stride[1];
bpp = gf_pixel_get_bytes_per_pixel(tx->pf);
src_data[0] += src_rc.x*bpp + src_rc.y * tx->stride;
if (gf_pixel_fmt_is_yuv(tx->pf)) {
u32 nb_planes, uv_height, off_x, off_y;
gf_pixel_get_size_info(tx->pf, tx->width, tx->height, NULL, &src_stride[0], &src_stride[1], &nb_planes, &uv_height);
off_x = src_rc.x * src_stride[1] / src_stride[0];
off_y = src_rc.y * uv_height / tx->height;
if (off_y != src_rc.y) {
while (off_y % 2)
off_y--;
}
if (nb_planes==1) {
} else if (nb_planes==2) {
src_data[1] += (off_x/2) * 2*bpp + off_y * src_stride[1];
src_data[2] = NULL;
src_stride[2] = 0;
} else if (nb_planes==3) {
src_data[1] += off_x * bpp + off_y * src_stride[1];
src_data[2] += off_x * bpp + off_y * src_stride[2];
} else if (nb_planes==4) {
src_data[1] += off_x * bpp + off_y * src_stride[1];
src_data[2] += off_x * bpp + off_y * src_stride[2];
src_data[3] += src_rc.x * bpp + src_rc.y * src_stride[3];
}
}
int res = sws_scale(tx->swscaler, (const u8**) src_data, src_stride, 0, src_rc.height, dst_data, dst_stride);
if (res != dst_rc.height)
return js_throw_err(c, GF_BAD_PARAM);
return JS_UNDEFINED;
#else
return js_throw_err(c, GF_NOT_SUPPORTED);
#endif
}
static JSValue canvas_enable_threading(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
s32 nb_threads = -1;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(c);
if (argc) {
if (JS_ToInt32(c, &nb_threads, argv[0]))
return GF_JS_EXCEPTION(c);
}
e = gf_evg_enable_threading(canvas->surface, nb_threads);
if (e) return js_throw_err(c, e);
return JS_UNDEFINED;
}
static JSValue canvas_enable_3d(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(c);
e = gf_evg_surface_enable_3d(canvas->surface);
if (e) return js_throw_err(c, e);
#ifdef EVG_USE_JS_SHADER
JS_FreeValue(c, the_canvas->frag_obj);
the_canvas->frag_obj = JS_NewObjectClass(c, fragment_class_id);
JS_SetOpaque(the_canvas->frag_obj, NULL);
JS_FreeValue(c, the_canvas->vert_obj);
the_canvas->vert_obj = JS_NewObjectClass(c, vertex_class_id);
JS_SetOpaque(the_canvas->vert_obj, NULL);
#endif
return JS_UNDEFINED;
}
static Bool vai_call_lerp(EVG_VAI *vai, GF_EVGFragmentParam *frag, Float *values);
static Bool vai_call_lerp_init(EVG_VAI *vai, GF_EVGFragmentParam *frag);
#ifdef EVG_USE_JS_SHADER
static Bool evg_frag_shader_fun(void *udta, GF_EVGFragmentParam *frag)
{
Bool frag_valid;
JSValue res;
GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
if (!canvas) return GF_FALSE;
JS_SetOpaque(canvas->frag_obj, frag);
res = JS_Call(canvas->ctx, canvas->frag_shader, canvas->obj, 1, &canvas->frag_obj);
frag_valid = frag->frag_valid ? 1 : 0;
if (JS_IsException(res)) frag_valid = GF_FALSE;
else if (!JS_IsUndefined(res)) frag_valid = JS_ToBool(canvas->ctx, res) ? GF_TRUE : GF_FALSE;
JS_FreeValue(canvas->ctx, res);
return frag_valid;
}
static Bool evg_vert_shader_fun(void *udta, GF_EVGVertexParam *vertex)
{
Bool vert_valid=GF_TRUE;
JSValue res;
GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
if (!canvas) return GF_FALSE;
JS_SetOpaque(canvas->vert_obj, vertex);
res = JS_Call(canvas->ctx, canvas->frag_shader, canvas->obj, 1, &canvas->vert_obj);
if (JS_IsException(res)) vert_valid = GF_FALSE;
else if (!JS_IsUndefined(res)) vert_valid = JS_ToBool(canvas->ctx, res) ? GF_TRUE : GF_FALSE;
JS_FreeValue(canvas->ctx, res);
return vert_valid;
}
#endif
static JSValue canvas_set_matrix_3d(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_proj)
{
GF_Err e;
GF_Matrix mx;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(c);
gf_mx_init(mx);
if (argc) {
if (JS_IsArray(c, argv[0])) {
u32 i, len=0;
JSValue v = JS_GetPropertyStr(c, argv[0], "length");
JS_ToInt32(c, &len, v);
JS_FreeValue(c, v);
if (len < 16) return GF_JS_EXCEPTION(c);
for (i=0; i<16; i++) {
Double val=0;
v = JS_GetPropertyUint32(c, argv[0], i);
s32 res = JS_ToFloat64(c, &val, v);
JS_FreeValue(c, v);
if (res) return GF_JS_EXCEPTION(c);
mx.m[i] = FLT2FIX(val);
}
} else {
return js_throw_err_msg(c, GF_NOT_SUPPORTED, "only float array currently supported for matrices");
}
}
if (is_proj)
e = gf_evg_surface_set_projection(canvas->surface, &mx);
else
e = gf_evg_surface_set_modelview(canvas->surface, &mx);
return e ? GF_JS_EXCEPTION(c) : JS_UNDEFINED;
}
static JSValue canvas_projection(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return canvas_set_matrix_3d(c, obj, argc, argv, GF_TRUE);
}
static JSValue canvas_modelview(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return canvas_set_matrix_3d(c, obj, argc, argv, GF_FALSE);
}
uint8_t *evg_get_array(JSContext *ctx, JSValueConst obj, u32 *size)
{
JSValue v;
/*ArrayBuffer*/
size_t psize;
uint8_t *res = JS_GetArrayBuffer(ctx, &psize, obj);
if (res) {
*size = (u32) psize;
return res;
}
/*ArrayView*/
v = JS_GetPropertyStr(ctx, obj, "buffer");
if (JS_IsUndefined(v)) return NULL;
res = JS_GetArrayBuffer(ctx, &psize, v);
JS_FreeValue(ctx, v);
*size = (u32) psize;
return res;
}
static JSValue canvas_draw_array(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
uint8_t *indices=NULL;
uint8_t *vertices=NULL;
u32 idx_size=0, vx_size, nb_comp=3;
GF_Err e;
GF_EVGPrimitiveType prim_type=GF_EVG_TRIANGLES;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas || argc<2) return GF_JS_EXCEPTION(c);
indices = evg_get_array(c, argv[0], &idx_size);
vertices = evg_get_array(c, argv[1], &vx_size);
if (!indices || ! vertices) return GF_JS_EXCEPTION(c);
if (argc>2) {
JS_ToInt32(c, (s32 *)&prim_type, argv[2]);
if (!prim_type) return GF_JS_EXCEPTION(c);
if (argc>3) {
JS_ToInt32(c, &nb_comp, argv[3]);
if ((nb_comp<2) || (nb_comp>4)) return GF_JS_EXCEPTION(c);
}
}
if (vx_size % nb_comp)
return GF_JS_EXCEPTION(c);
idx_size /= sizeof(s32);
vx_size /= sizeof(Float);
e = gf_evg_surface_draw_array(canvas->surface, (u32 *)indices, idx_size, (Float *)vertices, vx_size, nb_comp, prim_type);
if (e) return GF_JS_EXCEPTION(c);
return JS_UNDEFINED;
}
#ifndef GPAC_DISABLE_FONTS
static void text_update_path(GF_JSText *txt, Bool for_centered);
#endif
static JSValue canvas_draw_path(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e = GF_OK;
Float z = 0;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas || argc<1) return GF_JS_EXCEPTION(ctx);
if (argc>1) {
EVG_GET_FLOAT(z, argv[1]);
}
GF_Path *gp = JS_GetOpaque(argv[0], path_class_id);
if (gp) {
e = gf_evg_surface_draw_path(canvas->surface, gp, z);
} else {
GF_JSText *text = JS_GetOpaque(argv[0], text_class_id);
if (text) {
#ifndef GPAC_DISABLE_FONTS
text_update_path(text, GF_TRUE);
e = gf_evg_surface_draw_path(canvas->surface, text->path, z);
#else
e = GF_NOT_SUPPORTED;
#endif
}
}
if (e) return js_throw_err(ctx, e);
return JS_UNDEFINED;
}
static JSValue canvas_clear_depth(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
Float depth = 1.0;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(ctx);
if (argc)
EVG_GET_FLOAT(depth, argv[0]);
e = gf_evg_surface_clear_depth(canvas->surface, depth);
if (e) return GF_JS_EXCEPTION(ctx);
return JS_UNDEFINED;
}
static JSValue canvas_viewport(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
{
s32 x, y, w, h;
GF_Err e;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(ctx);
if (argc) {
if (argc<4) return js_throw_err(ctx, GF_BAD_PARAM);
if (JS_ToInt32(ctx, &x, argv[0])) return js_throw_err(ctx, GF_BAD_PARAM);
if (JS_ToInt32(ctx, &y, argv[1])) return js_throw_err(ctx, GF_BAD_PARAM);
if (JS_ToInt32(ctx, &w, argv[2])) return js_throw_err(ctx, GF_BAD_PARAM);
if (JS_ToInt32(ctx, &h, argv[3])) return js_throw_err(ctx, GF_BAD_PARAM);
} else {
x = y = 0;
w = canvas->width;
h = canvas->height;
}
e = gf_evg_surface_viewport(canvas->surface, x, y, w, h);
if (e) return GF_JS_EXCEPTION(ctx);
return JS_UNDEFINED;
}
enum
{
//0 reserved for last op
EVG_OP_IF=1,
EVG_OP_ELSE,
EVG_OP_ELSEIF,
EVG_OP_END,
EVG_OP_GOTO,
EVG_OP_ASSIGN,
EVG_OP_DISCARD,
EVG_OP_ADD,
EVG_OP_SUB,
EVG_OP_MUL,
EVG_OP_DIV,
EVG_OP_NEG,
EVG_OP_LESS,
EVG_OP_LESS_EQUAL,
EVG_OP_GREATER,
EVG_OP_GREATER_EQUAL,
EVG_OP_EQUAL,
EVG_OP_NOT_EQUAL,
EVG_OP_SAMPLER,
EVG_OP_SAMPLER_YUV,
EVG_OP_PRINT,
EVG_OP_NORMALIZE,
EVG_OP_LENGTH,
EVG_OP_DISTANCE,
EVG_OP_DOT,
EVG_OP_CROSS,
EVG_OP_POW,
EVG_OP_SIN,
EVG_OP_ASIN,
EVG_OP_COS,
EVG_OP_ACOS,
EVG_OP_TAN,
EVG_OP_ATAN,
EVG_OP_LOG,
EVG_OP_EXP,
EVG_OP_LOG2,
EVG_OP_EXP2,
EVG_OP_SINH,
EVG_OP_COSH,
EVG_OP_SQRT,
EVG_OP_INVERSE_SQRT,
EVG_OP_ABS,
EVG_OP_SIGN,
EVG_OP_FLOOR,
EVG_OP_CEIL,
EVG_OP_FRACT,
EVG_OP_MOD,
EVG_OP_MIN,
EVG_OP_MAX,
EVG_OP_CLAMP,
EVG_OP_RGB2YUV,
EVG_OP_YUV2RGB,
EVG_FIRST_VAR_ID
};
enum
{
VAR_FRAG_ARGB=1,
VAR_FRAG_YUV,
VAR_FRAG_X,
VAR_FRAG_Y,
VAR_FRAG_DEPTH,
VAR_FRAG_W,
VAR_FRAG_TX_COORD,
VAR_FRAG_TX_COORDI,
VAR_FRAG_ODD,
VAR_UNIFORM,
VAR_VERTEX_IN,
VAR_VERTEX_OUT,
VAR_VAI,
VAR_VA,
VAR_MATRIX,
};
static GFINLINE Float isqrtf(Float v)
{
v = sqrtf(v);
if (v) v = 1 / v;
return v;
}
static GFINLINE Float signf(Float v)
{
if (v==0) return 0;
if (v>0) return 1.0;
return -1.0;
}
static GFINLINE Float fractf(Float v)
{
return v - floorf(v);
}
static GFINLINE Float _modf(Float x, Float y)
{
if (!y) return 0.0;
return x - y * floorf(x/y);
}
#if defined(WIN32) && !defined(__GNUC__)
# include <intrin.h>
# define GPAC_HAS_SSE2
#else
# ifdef __SSE2__
# include <emmintrin.h>
# define GPAC_HAS_SSE2
# endif
#endif
#ifdef GPAC_HAS_SSE2
static Float evg_float_clamp(Float val, Float minval, Float maxval)
{
_mm_store_ss( &val, _mm_min_ss( _mm_max_ss(_mm_set_ss(val),_mm_set_ss(minval)), _mm_set_ss(maxval) ) );
return val;
}
#else
#define evg_float_clamp(_val, _minval, _maxval)\
(_val<_minval) ? _minval : (_val>_maxval) ? _maxval : _val;
#endif
/*
*/
static Bool evg_shader_ops(GF_JSCanvas *canvas, EVGShader *shader, GF_EVGFragmentParam *frag, GF_EVGVertexParam *vert, ShaderVar *vars)
{
register u32 dim;
Bool frag_valid = GF_FALSE;
GF_Vec4 tmpl, tmpr;
GF_Vec4 *left_val, *right_val, *right2_val;
GF_IVec4 *right_vali;
register u32 if_level=0;
register u32 nif_level=0;
register Bool cond_res=GF_FALSE;
register ShaderOp *op = &shader->ops[0];
//assign to dummy values, this will prevent any badly formatted shader to assign a value to a NULL left-val or read a null right-val
// tmpl.x = tmpl.y = tmpl.z = tmpl.q = 0;
// tmpr.x = tmpr.y = tmpr.z = tmpr.q = 0;
// memset(&tmpl, 0, sizeof(tmpl));
// tmpr = tmpl;
left_val = &tmpl;
right_val = &tmpr;
right_vali = (GF_IVec4 *) &tmpr;
while ((++op)->op_type) {
u32 next_idx, idx, var_idx;
Bool has_next, norm_result=GF_FALSE;
u8 right_val_type, left_val_type=0;
u8 *left_val_type_ptr=NULL;
if (shader->has_branches) {
if (op->op_type == EVG_OP_GOTO) {
u32 stack_idx = op->left_value;
if (op->uni_name) stack_idx = op->ival;
if (!stack_idx || (stack_idx > shader->nb_ops)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Shader] Invalid goto operation, stack index %d not in stack indices [1, %d]\n", op->left_value, shader->nb_ops));
shader->invalid = GF_TRUE;
return GF_FALSE;
}
//op index in goto are 1-based, and our first op is a dummy one
op = &shader->ops[stack_idx];
} else if (op->op_type == EVG_OP_ELSE) {
if (nif_level) {
if (nif_level==1) {
nif_level=0;
if_level++;
}
} else if (if_level) {
if_level--;
nif_level++;
}
continue;
} else if (op->op_type == EVG_OP_END) {
gf_assert(nif_level || if_level);
if (nif_level) nif_level--;
else if (if_level) if_level--;
continue;
}
if (nif_level) {
if (op->op_type==EVG_OP_IF) {
nif_level++;
}
continue;
}
}
dim=4;
switch (op->left_value) {
case VAR_FRAG_ARGB:
left_val = &frag->color;
left_val_type = COMP_V4;
frag->frag_valid = GF_EVG_FRAG_RGB;
frag_valid=GF_TRUE;
break;
case VAR_FRAG_YUV:
left_val = &frag->color;
left_val_type = COMP_V4;
frag->frag_valid = GF_EVG_FRAG_YUV;
frag_valid=GF_TRUE;
break;
case VAR_FRAG_X:
left_val = &tmpl;
tmpl.x = frag->screen_x;
left_val_type = COMP_FLOAT;
break;
case VAR_FRAG_Y:
left_val = &tmpl;
tmpl.x = frag->screen_y;
left_val_type = COMP_FLOAT;
break;
case VAR_FRAG_W:
left_val = &tmpl;
tmpl.x = frag->persp_denum;
left_val_type = COMP_FLOAT;
break;
case VAR_FRAG_DEPTH:
left_val = (GF_Vec4 *) &frag->depth;
left_val_type = COMP_FLOAT;
break;
case VAR_FRAG_ODD:
left_val = &tmpl;
tmpl.x = frag->odd_flag;
left_val_type = COMP_BOOL;
break;
case VAR_VERTEX_IN:
left_val = (GF_Vec4 *) &vert->in_vertex;
left_val_type = COMP_V4;
break;
case VAR_VERTEX_OUT:
left_val = (GF_Vec4 *) &vert->out_vertex;
left_val_type = COMP_V4;
break;
case VAR_VAI:
left_val = (GF_Vec4 *) &op->vai.vai->anchors[vert->vertex_idx_in_prim];
left_val_type = COMP_V4;
norm_result = op->vai.vai->normalize;
break;
case 0:
break;
default:
{
u32 l_var_idx = op->left_value - EVG_FIRST_VAR_ID-1;
left_val = &vars[l_var_idx].vecval;
left_val_type = vars[l_var_idx].value_type;
left_val_type_ptr = & vars[l_var_idx].value_type;
}
break;
}
if (op->right_value>EVG_FIRST_VAR_ID) {
var_idx = op->right_value - EVG_FIRST_VAR_ID-1;
right_val = &vars[var_idx].vecval;
right_val_type = vars[var_idx].value_type;
} else {
right_val_type = 0;
switch (op->right_value) {
case VAR_VAI:
if (op->vai.vai) {
vai_call_lerp(op->vai.vai, frag, (Float *) &tmpr);
// dim = MIN(4, op->vai.vai->result.dim);
dim = op->vai.vai->result.dim;
right_val = &tmpr;
right_val_type = op->vai.vai->result.comp_type;
}
break;
case VAR_VA:
if (op->va.va) {
u32 va_idx, j, nb_v_per_prim=3;
EVG_VA *va = op->va.va;
if (vert->ptype == GF_EVG_LINES)
nb_v_per_prim=2;
else if (vert->ptype == GF_EVG_POINTS)
nb_v_per_prim=1;
if (va->interp_type==GF_EVG_VAI_PRIMITIVE) {
va_idx = vert->prim_index * va->nb_comp;
}
else if (va->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
va_idx = vert->vertex_idx * va->nb_comp;
} else {
va_idx = vert->prim_index * nb_v_per_prim * va->nb_comp;
}
if (va_idx+va->nb_comp > va->nb_values)
return GF_FALSE;
right_val = &tmpr;
right_val->x = right_val->y = right_val->z = right_val->q = 0;
gf_assert(va->nb_comp<=4);
for (j=0; j<va->nb_comp; j++) {
((Float *)right_val)[j] = va->values[va_idx+j];
}
if (va->normalize) {
if (va->nb_comp==2) {
Float len;
if (!right_val->x) len = ABS(right_val->y);
else if (!right_val->y) len = ABS(right_val->x);
else len = sqrtf(right_val->x*right_val->x + right_val->y*right_val->y);
if (len) {
right_val->x/=len;
right_val->y/=len;
}
} else {
gf_vec_norm((GF_Vec *) right_val);
}
}
right_val_type = va->att_type;
}
break;
case VAR_MATRIX:
if (op->mx.mx) {
if (op->op_type==EVG_OP_MUL) {
gf_mx_apply_vec_4x4(op->mx.mx, left_val);
continue;
}
GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Shader] Invalid operation for right value matrix\n"));
shader->invalid = GF_TRUE;
return GF_FALSE;
}
break;
case VAR_FRAG_ARGB:
right_val = &frag->color;
right_val_type = COMP_V4;
break;
case VAR_FRAG_YUV:
right_val = &frag->color;
right_val_type = COMP_V4;
break;
case VAR_FRAG_X:
right_val = &tmpr;
tmpr.x = frag->screen_x;
right_val_type = COMP_FLOAT;
break;
case VAR_FRAG_Y:
right_val = &tmpr;
tmpr.x = frag->screen_y;
right_val_type = COMP_FLOAT;
break;
case VAR_FRAG_W:
right_val = &tmpr;
tmpr.x = frag->persp_denum;
right_val_type = COMP_FLOAT;
break;
case VAR_FRAG_DEPTH:
right_val = &tmpr;
tmpr.x = frag->depth;
right_val_type = COMP_FLOAT;
break;
case VAR_VERTEX_IN:
right_val = &vert->in_vertex;
right_val_type = COMP_V4;
break;
case VAR_VERTEX_OUT:
right_val = &vert->out_vertex;
right_val_type = COMP_V4;
break;
case VAR_FRAG_TX_COORD:
right_val = &tmpr;
right_val->x = ((Float)frag->tx_x) / frag->tx_width;
right_val->y = ((Float)frag->tx_y) / frag->tx_height;
right_val_type = COMP_V2_XY;
break;
case VAR_FRAG_TX_COORDI:
right_val = &tmpr;
right_vali->x = frag->tx_x;
right_vali->y = frag->tx_y;
right_val_type = COMP_V2_XY | COMP_FLAG_INT;
break;
case VAR_FRAG_ODD:
right_val = &tmpr;
right_val->x = frag->odd_flag;
right_val_type = COMP_BOOL;
break;
default:
if (!op->right_value || (op->right_value==VAR_UNIFORM) ) {
right_val = (GF_Vec4 *) &op->vec[0];
right_val_type = op->right_value_type;
}
break;
}
}
if (!right_val_type) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Shader] Invalid right-value type in operation (stack index %d)\n", (u32) ((op - shader->ops) / sizeof(ShaderOp)) ));
shader->invalid = GF_TRUE;
return GF_FALSE;
}
#define GET_FIRST_COMP\
idx=0;\
while (1) {\
if (op->right_value_type & (1<<idx))\
break;\
if (idx==3)\
break;\
idx++;\
}\
#define GET_NEXT_COMP\
has_next = GF_FALSE;\
next_idx = idx;\
while (1) {\
if (next_idx==3)\
break;\
if (op->right_value_type & (1<< (next_idx+1)) ) {\
has_next = GF_TRUE;\
next_idx = idx+1;\
break;\
}\
next_idx++;\
}\
if (has_next) idx = next_idx;\
switch (op->op_type) {
case EVG_OP_ASSIGN:
//full assignment
if (op->left_value_type==COMP_V4) {
if (left_val_type_ptr) {
left_val_type = *left_val_type_ptr = right_val_type;
}
if (op->right_value_type==COMP_V4) {
*left_val = *right_val;
if (dim<4) left_val->q = 1.0;
} else {
switch (right_val_type) {
case COMP_BOOL:
if (left_val_type_ptr) {
*((Bool *) left_val) = *((Bool *) right_val) ? GF_TRUE : GF_FALSE;
} else {
left_val->x = (Float) ( *(Bool *) right_val ? 1.0 : 0.0 );
left_val->y = left_val->z = left_val->q = left_val->x;
}
break;
case COMP_INT:
if (left_val_type_ptr || (left_val_type==COMP_INT)) {
*((s32 *) left_val) = *(s32 *) right_val;
} else {
left_val->x = (Float) *(s32 *) right_val;
left_val->y = left_val->z = left_val->q = left_val->x;
}
break;
case COMP_FLOAT:
if (left_val_type_ptr) {
*((Float *) left_val) = *(Float *) right_val;
} else {
left_val->x = *(Float *) right_val;
left_val->y = left_val->z = left_val->q = left_val->x;
}
break;
case COMP_V4:
*left_val = *right_val;
if (dim<4) left_val->q = 1.0;
break;
default:
{
Float *srcs = (Float *) &right_val->x;
GET_FIRST_COMP
if (left_val_type & COMP_X) { left_val->x = srcs[idx]; GET_NEXT_COMP }
if (left_val_type & COMP_Y) { left_val->y = srcs[idx]; GET_NEXT_COMP }
if (left_val_type & COMP_Z) { left_val->z = srcs[idx]; GET_NEXT_COMP }
if (dim<4) left_val->q = 1.0;
else if (left_val_type & COMP_Q) { left_val->q = srcs[idx]; }
}
break;
}
}
}
//partial assignment, only valid for float sources
else {
Bool use_const;
Float cval;
if (right_val_type==COMP_FLOAT) {
use_const = GF_TRUE;
cval = right_val->x;
} else if (right_val_type==COMP_INT) {
use_const = GF_TRUE;
cval = (Float) ( *(s32*)right_val);
} else {
use_const = GF_FALSE;
}
if (use_const) {
if (op->left_value_type & COMP_X) left_val->x = cval;
if (op->left_value_type & COMP_Y) left_val->y = cval;
if (op->left_value_type & COMP_Z) left_val->z = cval;
if (op->left_value_type & COMP_Q) left_val->q = cval;
} else {
Float *srcs = (Float *) &right_val->x;
GET_FIRST_COMP
if (op->left_value_type & COMP_X) { left_val->x = srcs[idx]; GET_NEXT_COMP }
if (op->left_value_type & COMP_Y) { left_val->y = srcs[idx]; GET_NEXT_COMP }
if (op->left_value_type & COMP_Z) { left_val->z = srcs[idx]; GET_NEXT_COMP }
if (op->left_value_type & COMP_Q) { left_val->q = srcs[idx]; }
}
}
if (norm_result)
gf_vec_norm((GF_Vec *)left_val);
break;
#define BASE_OP(_opv, _opv2)\
if (op->left_value_type==COMP_V4) {\
switch (right_val_type) {\
case COMP_INT: \
if (left_val_type == COMP_INT) {\
*((s32 *) left_val) _opv *(s32 *) right_val;\
} else if (left_val_type == COMP_FLOAT) {\
left_val->x _opv *(s32 *) right_val;\
} else {\
left_val->x _opv *(s32 *) right_val;\
left_val->y _opv *(s32 *) right_val;\
left_val->z _opv *(s32 *) right_val;\
left_val->q _opv *(s32 *) right_val;\
}\
break;\
case COMP_FLOAT:\
if (left_val_type == COMP_INT) {\
left_val->x = *((s32 *) left_val) _opv2 right_val->x;\
*left_val_type_ptr = COMP_FLOAT;\
} else if (left_val_type == COMP_FLOAT) {\
left_val->x _opv right_val->x;\
} else {\
left_val->x _opv right_val->x;\
left_val->y _opv right_val->x;\
left_val->z _opv right_val->x;\
left_val->q _opv right_val->x;\
}\
break;\
case COMP_BOOL:\
break;\
default:\
{\
Float *srcs = (Float *) &right_val->x;\
GET_FIRST_COMP\
if (left_val_type & COMP_X) { left_val->x _opv srcs[idx]; GET_NEXT_COMP } \
if (left_val_type & COMP_Y) { left_val->y _opv srcs[idx]; GET_NEXT_COMP } \
if (left_val_type & COMP_Z) { left_val->z _opv srcs[idx]; GET_NEXT_COMP } \
if (left_val_type & COMP_Q) { left_val->q _opv srcs[idx]; }\
}\
break;\
}\
}\
else {\
Bool use_const = GF_FALSE;\
Float cval;\
if (right_val_type==COMP_FLOAT) {\
use_const = GF_TRUE;\
cval = right_val->x;\
} else if (right_val_type==COMP_INT) {\
use_const = GF_TRUE;\
cval = (Float) ( *(s32*)right_val);\
}\
if (use_const) {\
if (op->left_value_type & COMP_X) left_val->x _opv cval;\
if (op->left_value_type & COMP_Y) left_val->y _opv cval;\
if (op->left_value_type & COMP_Z) left_val->z _opv cval;\
if (op->left_value_type & COMP_Q) left_val->q _opv cval;\
} else {\
Float *srcs = (Float *) &right_val->x;\
GET_FIRST_COMP\
if (op->left_value_type & COMP_X) { left_val->x _opv srcs[idx]; GET_NEXT_COMP }\
if (op->left_value_type & COMP_Y) { left_val->y _opv srcs[idx]; GET_NEXT_COMP }\
if (op->left_value_type & COMP_Z) { left_val->z _opv srcs[idx]; GET_NEXT_COMP }\
if (op->left_value_type & COMP_Q) left_val->q _opv srcs[idx];\
}\
}\
case EVG_OP_MUL:
BASE_OP(*=, *)
break;
case EVG_OP_DIV:
BASE_OP(/=, /)
break;
case EVG_OP_ADD:
BASE_OP(+=, +)
break;
case EVG_OP_SUB:
BASE_OP(-=, -)
break;
case EVG_OP_NEG:
BASE_OP(= !, *)
break;
case EVG_OP_IF:
#define BASE_COND(_opv)\
cond_res=GF_FALSE;\
if (op->left_value_type==COMP_V4) {\
switch (right_val_type) {\
case COMP_INT:\
case COMP_BOOL:\
if ((left_val_type == COMP_INT) || (left_val_type == COMP_BOOL)) {\
cond_res = ( *((s32 *) left_val) _opv *(s32 *) right_val) ? GF_TRUE : GF_FALSE;\
} else if (left_val_type == COMP_FLOAT) {\
cond_res = (left_val->x _opv *(s32 *) right_val) ? GF_TRUE : GF_FALSE;\
} else {\
cond_res = ( (left_val->x _opv *(s32 *) right_val) && \
(left_val->y _opv *(s32 *) right_val) && \
(left_val->z _opv *(s32 *) right_val) && \
(left_val->q _opv *(s32 *) right_val) ) ? GF_TRUE : GF_FALSE;\
}\
break;\
case COMP_FLOAT:\
if (left_val_type == COMP_INT) {\
cond_res = ( *((s32 *) left_val) _opv right_val->x) ? GF_TRUE : GF_FALSE;\
} else if (left_val_type == COMP_FLOAT) {\
cond_res = (left_val->x _opv right_val->x) ? GF_TRUE : GF_FALSE;\
} else {\
cond_res = ( (left_val->x _opv right_val->x) &&\
(left_val->y _opv right_val->x) &&\
(left_val->z _opv right_val->x) && \
(left_val->q _opv right_val->x) ) ? GF_TRUE : GF_FALSE;\
}\
break;\
default:\
{\
cond_res=GF_TRUE;\
Float *srcs = (Float *) &right_val->x;\
GET_FIRST_COMP\
if (left_val_type & COMP_X) { if (! (left_val->x _opv srcs[idx]) ) cond_res = GF_FALSE; GET_NEXT_COMP } \
if (left_val_type & COMP_Y) { if (! (left_val->y _opv srcs[idx]) ) cond_res = GF_FALSE; GET_NEXT_COMP } \
if (left_val_type & COMP_Z) { if (! (left_val->z _opv srcs[idx]) ) cond_res = GF_FALSE; GET_NEXT_COMP } \
if (left_val_type & COMP_Q) { if (! (left_val->q _opv srcs[idx]) ) cond_res = GF_FALSE; }\
}\
break;\
}\
}\
else {\
Bool use_const = GF_FALSE;\
Float cval;\
if (right_val_type==COMP_FLOAT) {\
use_const = GF_TRUE;\
cval = right_val->x;\
} else if (right_val_type==COMP_INT) {\
use_const = GF_TRUE;\
cval = (Float) ( *(s32*)right_val);\
}\
if (use_const) {\
cond_res=GF_TRUE;\
if (op->left_value_type & COMP_X) if (! (left_val->x _opv cval) ) cond_res=GF_FALSE;\
if (op->left_value_type & COMP_Y) if (! (left_val->y _opv cval) ) cond_res=GF_FALSE;\
if (op->left_value_type & COMP_Z) if (! (left_val->z _opv cval) ) cond_res=GF_FALSE;\
if (op->left_value_type & COMP_Q) if (! (left_val->q _opv cval) ) cond_res=GF_FALSE;\
} else {\
Float *srcs = (Float *) &right_val->x;\
GET_FIRST_COMP\
if (op->left_value_type & COMP_X) { if (! (left_val->x _opv srcs[idx]) ) cond_res=GF_FALSE; GET_NEXT_COMP }\
if (op->left_value_type & COMP_Y) { if (! (left_val->y _opv srcs[idx]) ) cond_res=GF_FALSE; GET_NEXT_COMP }\
if (op->left_value_type & COMP_Z) { if (! (left_val->z _opv srcs[idx]) ) cond_res=GF_FALSE; GET_NEXT_COMP }\
if (op->left_value_type & COMP_Q) { if (! (left_val->q _opv srcs[idx]) ) cond_res=GF_FALSE; }\
}\
}
switch (op->cond_type) {
case EVG_OP_LESS:
{ BASE_COND(<) }
break;
case EVG_OP_LESS_EQUAL:
{ BASE_COND(<=) }
break;
case EVG_OP_GREATER:
{ BASE_COND(>) }
break;
case EVG_OP_GREATER_EQUAL:
{ BASE_COND(>=) }
break;
case EVG_OP_EQUAL:
{ BASE_COND(==) }
break;
case EVG_OP_NOT_EQUAL:
{ BASE_COND(!=) }
break;
}
if (cond_res) if_level++;
else nif_level++;
break;
case EVG_OP_SAMPLER:
if (left_val_type_ptr) {
*left_val_type_ptr = COMP_V4;
}
if ((right_val_type==(COMP_V2_XY|COMP_FLAG_INT)) && (op->left_value == VAR_FRAG_ARGB)) {
u32 tx = ((GF_IVec4 *)right_val)->x * op->tx->width / frag->tx_width;
u32 ty = ((GF_IVec4 *)right_val)->y * op->tx->height / frag->tx_height;
if (op->tx->wide) {
frag->color_pack_wide = gf_evg_stencil_get_pixel_wide(op->tx->stencil, tx, ty);
} else {
frag->color_pack = gf_evg_stencil_get_pixel(op->tx->stencil, tx, ty);
}
frag->frag_valid = GF_EVG_FRAG_RGB_PACK;
frag_valid=GF_TRUE;
}
else if (op->left_value_type==COMP_V4) {
*left_val = gf_evg_stencil_get_pixel_f(op->tx->stencil, right_val->x, right_val->y);
} else {
tmpr = gf_evg_stencil_get_pixel_f(op->tx->stencil, right_val->x, right_val->y);
if (op->left_value_type & COMP_X) left_val->x = tmpr.x;
if (op->left_value_type & COMP_Y) left_val->y = tmpr.y;
if (op->left_value_type & COMP_Z) left_val->z = tmpr.z;
if (op->left_value_type & COMP_Q) left_val->q = tmpr.q;
}
break;
case EVG_OP_SAMPLER_YUV:
if (left_val_type_ptr) {
*left_val_type_ptr = COMP_V4;
}
if ((right_val_type==(COMP_V2_XY|COMP_FLAG_INT)) && (op->left_value == VAR_FRAG_YUV)) {
u32 tx = ((GF_IVec4 *)right_val)->x * op->tx->width / frag->tx_width;
u32 ty = ((GF_IVec4 *)right_val)->y * op->tx->height / frag->tx_height;
if (op->tx->wide) {
frag->color_pack_wide = gf_evg_stencil_get_pixel_yuv_wide(op->tx->stencil, tx, ty);
} else {
frag->color_pack = gf_evg_stencil_get_pixel_yuv(op->tx->stencil, tx, ty);
}
frag->frag_valid = GF_EVG_FRAG_YUV_PACK;
frag_valid=GF_TRUE;
}
else if (op->left_value_type==COMP_V4) {
*left_val = gf_evg_stencil_get_pixel_yuv_f(op->tx->stencil, right_val->x, right_val->y);
} else {
tmpr = gf_evg_stencil_get_pixel_yuv_f(op->tx->stencil, right_val->x, right_val->y);
if (op->left_value_type & COMP_X) left_val->x = tmpr.x;
if (op->left_value_type & COMP_Y) left_val->y = tmpr.y;
if (op->left_value_type & COMP_Z) left_val->z = tmpr.z;
if (op->left_value_type & COMP_Q) left_val->q = tmpr.q;
}
break;
case EVG_OP_DISCARD:
frag->frag_valid = 0;
return GF_FALSE;
case EVG_OP_NORMALIZE:
if (left_val_type_ptr) {
*left_val_type_ptr = COMP_V4;
}
*left_val = *right_val;
gf_vec_norm((GF_Vec *)left_val);
break;
case EVG_OP_LENGTH:
if (left_val_type_ptr) {
*left_val_type_ptr = COMP_FLOAT;
}
left_val->x = gf_vec_len_p( (GF_Vec *) right_val);
break;
case EVG_OP_DISTANCE:
if (left_val_type_ptr) {
*left_val_type_ptr = COMP_FLOAT;
}
right2_val = &vars[op->right_value_second - EVG_FIRST_VAR_ID-1].vecval;
//right2_val_type = vars[op->right_value_second - EVG_FIRST_VAR_ID-1].value_type;
gf_vec_diff(tmpr, *right_val, *right2_val);
left_val->x = gf_vec_len_p( (GF_Vec *) &tmpr);
break;
case EVG_OP_DOT:
if (left_val_type_ptr) {
*left_val_type_ptr = COMP_FLOAT;
}
right2_val = &vars[op->right_value_second - EVG_FIRST_VAR_ID-1].vecval;
//right2_val_type = vars[op->right_value_second - EVG_FIRST_VAR_ID-1].value_type;
left_val->x = gf_vec_dot_p( (GF_Vec *) right_val, (GF_Vec *) right2_val);
break;
case EVG_OP_CROSS:
if (left_val_type_ptr) {
*left_val_type_ptr = COMP_V4;
}
right2_val = &vars[op->right_value_second - EVG_FIRST_VAR_ID-1].vecval;
//right2_val_type = vars[op->right_value_second - EVG_FIRST_VAR_ID-1].value_type;
* (GF_Vec *) left_val = gf_vec_cross_p( (GF_Vec *) right_val, (GF_Vec *) right2_val);
break;
#define BASE_FUN2(__fun) \
if (left_val_type_ptr) {\
*left_val_type_ptr = right_val_type;\
}\
var_idx = op->right_value_second - EVG_FIRST_VAR_ID-1;\
right2_val = &vars[var_idx].vecval;\
if (right_val_type==COMP_FLOAT) {\
left_val->x = __fun(right_val->x, right2_val->x);\
} else {\
if (right_val_type&COMP_X) {\
left_val->x = __fun(right_val->x, right2_val->x);\
}\
if (right_val_type&COMP_Y) {\
left_val->y = __fun(right_val->y, right2_val->y);\
}\
if (right_val_type&COMP_Z) {\
left_val->z = __fun(right_val->z, right2_val->z);\
}\
if (right_val_type&COMP_Q) {\
left_val->q = __fun(right_val->z, right2_val->q);\
}\
}\
#define BASE_FUN(__fun) \
if (left_val_type_ptr) {\
*left_val_type_ptr = right_val_type;\
}\
if (right_val_type==COMP_FLOAT) {\
left_val->x = (Float) __fun(right_val->x);\
} else {\
if (right_val_type&COMP_X) {\
left_val->x = (Float) __fun(right_val->x);\
}\
if (right_val_type&COMP_Y) {\
left_val->y = (Float) __fun(right_val->y);\
}\
if (right_val_type&COMP_Z) {\
left_val->z = (Float) __fun(right_val->z);\
}\
if (right_val_type&COMP_Q) {\
left_val->q = (Float) __fun(right_val->z);\
}\
}\
case EVG_OP_POW:
BASE_FUN2(powf)
break;
case EVG_OP_SIN:
BASE_FUN(sinf)
break;
case EVG_OP_ASIN:
BASE_FUN(asinf)
break;
case EVG_OP_COS:
BASE_FUN(cosf)
break;
case EVG_OP_ACOS:
BASE_FUN(acosf)
break;
case EVG_OP_TAN:
BASE_FUN(tanf)
break;
case EVG_OP_ATAN:
BASE_FUN2(atan2f)
break;
case EVG_OP_LOG:
BASE_FUN(logf)
break;
case EVG_OP_EXP:
BASE_FUN(expf)
break;
case EVG_OP_LOG2:
BASE_FUN(log2f)
break;
case EVG_OP_EXP2:
BASE_FUN(exp2f)
break;
case EVG_OP_SINH:
BASE_FUN(sinhf)
break;
case EVG_OP_COSH:
BASE_FUN(coshf)
break;
case EVG_OP_SQRT:
BASE_FUN(sqrtf)
break;
case EVG_OP_INVERSE_SQRT:
BASE_FUN(isqrtf)
break;
case EVG_OP_ABS:
BASE_FUN(ABS)
break;
case EVG_OP_SIGN:
BASE_FUN(signf)
break;
case EVG_OP_FLOOR:
BASE_FUN(floorf)
break;
case EVG_OP_CEIL:
BASE_FUN(ceilf)
break;
case EVG_OP_FRACT:
BASE_FUN(fractf)
break;
case EVG_OP_MOD:
BASE_FUN2(_modf)
break;
case EVG_OP_MIN:
BASE_FUN2(MIN)
break;
case EVG_OP_MAX:
BASE_FUN2(MAX)
break;
case EVG_OP_CLAMP:
if (left_val_type_ptr) {
*left_val_type_ptr = right_val_type;
}
var_idx = op->right_value_second - EVG_FIRST_VAR_ID-1;
right2_val = &vars[var_idx].vecval;
//right2_val_type = vars[var_idx].value_type;
if (right_val_type==COMP_FLOAT) {
left_val->x = evg_float_clamp(left_val->x, right_val->x, right2_val->x);
} else {
if (right_val_type&COMP_X) {
left_val->x = evg_float_clamp(left_val->x, right_val->x, right2_val->x);
}
if (right_val_type&COMP_Y) {
left_val->y = evg_float_clamp(left_val->y, right_val->y, right2_val->x);
}
if (right_val_type&COMP_Z) {
left_val->z = evg_float_clamp(left_val->z, right_val->z, right2_val->x);
}
if (right_val_type&COMP_Q) {
left_val->q = evg_float_clamp(left_val->q, right_val->z, right2_val->x);
}
}
break;
case EVG_OP_RGB2YUV:
if (left_val_type_ptr) {
*left_val_type_ptr = right_val_type;
}
left_val->q = right_val->q;
gf_gf_evg_rgb_to_yuv_f(canvas->surface, right_val->x, right_val->y, right_val->z, &left_val->x, &left_val->y, &left_val->z);
break;
case EVG_OP_YUV2RGB:
if (left_val_type_ptr) {
*left_val_type_ptr = right_val_type;
}
left_val->q = right_val->q;
gf_evg_yuv_to_rgb_f(canvas->surface, right_val->x, right_val->y, right_val->z, &left_val->x, &left_val->y, &left_val->z);
break;
case EVG_OP_PRINT:
if (op->right_value>EVG_FIRST_VAR_ID) {
fprintf(stderr, "%s: ", vars[op->right_value - EVG_FIRST_VAR_ID - 1].name);
}
if (right_val_type==COMP_FLOAT) {
fprintf(stderr, "%g\n", right_val->x);
} else if (right_val_type==COMP_INT) {
fprintf(stderr, "%d\n", * (s32 *) &right_val);
} else {
if (right_val_type&COMP_X) fprintf(stderr, "x=%g ", right_val->x);
if (right_val_type&COMP_Y) fprintf(stderr, "y=%g ", right_val->y);
if (right_val_type&COMP_Z) fprintf(stderr, "z=%g ", right_val->z);
if (right_val_type&COMP_Q) fprintf(stderr, "q=%g ", right_val->q);
fprintf(stderr, "\n");
}
break;
}
}
return frag_valid;
}
static GF_JSTexture *tx = NULL;
static Bool evg_frag_shader_ops(void *udta, GF_EVGFragmentParam *frag)
{
GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
return evg_shader_ops(canvas, canvas->frag, frag, NULL, frag->shader_udta);
}
static Bool evg_frag_shader_ops_init(void *udta, GF_EVGFragmentParam *frag, u32 th_id, Bool is_cleanup)
{
u32 i;
GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
if (!canvas->frag || canvas->frag->invalid) return GF_FALSE;
if (!th_id) {
for (i=0; i<canvas->frag->nb_ops; i++) {
if (canvas->frag->ops[i].right_value==VAR_VAI) {
if (!vai_call_lerp_init(canvas->frag->ops[i].vai.vai, frag))
return GF_FALSE;
}
}
frag->shader_udta = canvas->frag->vars;
tx = canvas->frag->ops[1].tx;
return GF_TRUE;
}
if (is_cleanup) {
if (frag->shader_udta) {
gf_list_add(canvas->frag->vars_stack, frag->shader_udta);
frag->shader_udta = NULL;
}
} else {
ShaderVar *vars = gf_list_pop_back(canvas->frag->vars_stack);
if (!vars) {
vars = gf_malloc(sizeof(ShaderVar) * canvas->frag->nb_vars);
if (!vars) return GF_FALSE;
memcpy(vars, canvas->frag->vars, sizeof(ShaderVar) * canvas->frag->nb_vars);
}
frag->shader_udta = vars;
}
return GF_TRUE;
}
static Bool evg_vert_shader_ops(void *udta, GF_EVGVertexParam *vert)
{
GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
if (!canvas->vert || canvas->vert->invalid) return GF_FALSE;
return evg_shader_ops(canvas, canvas->vert, NULL, vert, canvas->vert->vars);
}
static void shader_reset(JSRuntime *rt, EVGShader *shader)
{
u32 i;
for (i=0; i<shader->nb_ops; i++) {
if (shader->ops[i].right_value==VAR_VAI) {
JS_FreeValueRT(rt, shader->ops[i].vai.ref);
}
else if (shader->ops[i].right_value==VAR_MATRIX) {
JS_FreeValueRT(rt, shader->ops[i].mx.ref);
}
else if (shader->ops[i].right_value==VAR_VA) {
JS_FreeValueRT(rt, shader->ops[i].va.ref);
}
else if (shader->ops[i].left_value==VAR_VAI) {
JS_FreeValueRT(rt, shader->ops[i].vai.ref);
}
else if (shader->ops[i].left_value==VAR_MATRIX) {
JS_FreeValueRT(rt, shader->ops[i].mx.ref);
}
if (shader->ops[i].uni_name) {
gf_free(shader->ops[i].uni_name);
shader->ops[i].uni_name = NULL;
}
if ((shader->ops[i].op_type==EVG_OP_SAMPLER) || (shader->ops[i].op_type==EVG_OP_SAMPLER_YUV)) {
JS_FreeValueRT(rt, shader->ops[i].tx_ref);
shader->ops[i].tx_ref = JS_UNDEFINED;
}
shader->ops[i].right_value = 0;
}
shader->nb_ops = 0;
for (i=0; i<shader->nb_vars; i++) {
if (
#ifdef BUILTIN_SHADERS
!shader->frag_shader &&
#endif
shader->vars[i].name
) {
gf_free(shader->vars[i].name);
}
shader->vars[i].name = NULL;
}
shader->nb_vars = 0;
shader->invalid = GF_FALSE;
shader->disable_early_z = GF_FALSE;
shader->has_branches = GF_FALSE;
}
static void shader_reset_vars_stack(EVGShader *shader)
{
while (gf_list_count(shader->vars_stack)) {
ShaderVar *vars = gf_list_pop_back(shader->vars_stack);
gf_free(vars);
}
}
static void shader_finalize(JSRuntime *rt, JSValue obj)
{
EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
if (!shader) return;
shader_reset(rt, shader);
gf_free(shader->ops);
gf_free(shader->vars);
shader_reset_vars_stack(shader);
gf_list_del(shader->vars_stack);
gf_free(shader);
}
static void shader_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
{
u32 i;
EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
if (!shader) return;
for (i=0; i<shader->nb_ops; i++) {
if (shader->ops[i].tx)
JS_MarkValue(rt, shader->ops[i].tx_ref, mark_func);
if (shader->ops[i].right_value==VAR_VAI) {
JS_MarkValue(rt, shader->ops[i].vai.ref, mark_func);
}
else if (shader->ops[i].right_value==VAR_MATRIX) {
JS_MarkValue(rt, shader->ops[i].mx.ref, mark_func);
}
else if (shader->ops[i].right_value==VAR_VA) {
JS_MarkValue(rt, shader->ops[i].va.ref, mark_func);
}
else if (shader->ops[i].left_value==VAR_VAI) {
JS_MarkValue(rt, shader->ops[i].vai.ref, mark_func);
}
else if (shader->ops[i].left_value==VAR_MATRIX) {
JS_MarkValue(rt, shader->ops[i].mx.ref, mark_func);
}
}
}
JSClassDef shader_class = {
.class_name = "Shader",
.finalizer = shader_finalize,
.gc_mark = shader_gc_mark
};
static u32 get_builtin_var_name(EVGShader *shader, const char *val_name)
{
u32 i;
if (shader->mode==GF_EVG_SHADER_FRAGMENT) {
if (!strcmp(val_name, "fragColor") || !strcmp(val_name, "fragRGBA")) return VAR_FRAG_ARGB;
if (!strcmp(val_name, "fragYUVA")) return VAR_FRAG_YUV;
if (!strcmp(val_name, "fragDepth") || !strcmp(val_name, "fragZ")) return VAR_FRAG_DEPTH;
if (!strcmp(val_name, "fragX")) return VAR_FRAG_X;
if (!strcmp(val_name, "fragY")) return VAR_FRAG_Y;
if (!strcmp(val_name, "fragW")) return VAR_FRAG_W;
if (!strcmp(val_name, "txCoord")) return VAR_FRAG_TX_COORD;
if (!strcmp(val_name, "txCoordi")) return VAR_FRAG_TX_COORDI;
if (!strcmp(val_name, "fragOdd")) return VAR_FRAG_ODD;
}
if (shader->mode==GF_EVG_SHADER_VERTEX) {
if (!strcmp(val_name, "vertex")) return VAR_VERTEX_IN;
if (!strcmp(val_name, "vertexOut")) return VAR_VERTEX_OUT;
}
if (val_name[0] == '.') return VAR_UNIFORM;
for (i=0; i<shader->nb_vars; i++) {
if (!strcmp(shader->vars[i].name, val_name)) {
return EVG_FIRST_VAR_ID+i+1;
}
}
return 0;
}
static u8 get_value_type(const char *comp)
{
if (!strcmp(comp, "x") || !strcmp(comp, "r") || !strcmp(comp, "s")) return COMP_X;
if (!strcmp(comp, "y") || !strcmp(comp, "g") || !strcmp(comp, "t")) return COMP_Y;
if (!strcmp(comp, "z") || !strcmp(comp, "b")) return COMP_Z;
if (!strcmp(comp, "q") || !strcmp(comp, "a")) return COMP_Q;
if (!strcmp(comp, "xyz") || !strcmp(comp, "rgb")) return COMP_V3;
if (!strcmp(comp, "xy") || !strcmp(comp, "rg") || !strcmp(comp, "st")) return COMP_V2_XY;
if (!strcmp(comp, "xz") || !strcmp(comp, "rb")) return COMP_V2_XZ;
if (!strcmp(comp, "yz") || !strcmp(comp, "gb")) return COMP_V2_YZ;
return COMP_V4;
}
#ifdef BUILTIN_SHADERS
static JSValue shader_push_builtin(JSContext *ctx, EVGShader *shader, int argc, JSValueConst *argv)
{
u32 i;
Double dval;
const char *pname;
if (argc<2) return JS_UNDEFINED;
if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(ctx);
pname = JS_ToCString(ctx, argv[0]);
for (i=0; i<shader->nb_vars; i++) {
ShaderVar *var = &shader->vars[i];
if (!var->name || strcmp(var->name, pname)) continue;
switch (var->value_type) {
case COMP_INT:
if (JS_ToInt32(ctx, &var->ival, argv[1])) return js_throw_err_msg(ctx, GF_BAD_PARAM, "Bad value specified for %s (int type)", var->name);
break;
case COMP_BOOL:
var->bval = JS_ToBool(ctx, argv[1]);
break;
case COMP_FLOAT:
if (JS_ToFloat64(ctx, &dval, argv[1])) return js_throw_err_msg(ctx, GF_BAD_PARAM, "Bad value specified for %s (float type)", var->name);
var->vecval.x = (Float) dval;
break;
case COMP_TX:
var->ptr = JS_GetOpaque(argv[1], texture_class_id);
if (!var->ptr) return js_throw_err_msg(ctx, GF_BAD_PARAM, "Bad value specified for %s (texture type)", var->name);
break;
}
break;
}
return JS_UNDEFINED;
}
#endif
static JSValue shader_push(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
{
const char *val_name=NULL, *arg_str;
const char *op_name;
char *uni_name=NULL;
Bool dual_right_val = GF_FALSE;
u32 var_idx=0;
u32 left_op_idx=0, right_op_idx=0, right_op2_idx=0;
u8 left_value_type=COMP_V4, right_value_type=COMP_V4;
u8 op_type=0;
u8 cond_type=0;
ShaderOp new_op;
EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
if (!shader) return GF_JS_EXCEPTION(ctx);
#ifdef BUILTIN_SHADERS
if (shader->frag_shader)
return shader_push_builtin(ctx, shader, argc, argv);
#endif
shader->invalid = GF_FALSE;
if (!argc) {
shader_reset(JS_GetRuntime(ctx), shader);
return JS_UNDEFINED;
}
if (!shader->nb_ops) {
if (shader->alloc_ops <= 2) {
shader->alloc_ops = 2;
shader->ops = gf_realloc(shader->ops, sizeof(ShaderOp)*shader->alloc_ops);
shader->ops[shader->nb_ops].uni_name = NULL;
}
shader->ops[0].op_type = 0;
shader->ops[1].op_type = 0;
shader->nb_ops = 1;
}
memset(&new_op, 0, sizeof(ShaderOp));
new_op.op_type = 0;
new_op.tx_ref = JS_UNDEFINED;
if (JS_IsObject(argv[0])) {
new_op.vai.vai = JS_GetOpaque(argv[0], vai_class_id);
if (!new_op.vai.vai) {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid left-operand value, must be a VAI");
}
new_op.vai.ref = JS_DupValue(ctx, argv[0]);
left_op_idx = VAR_VAI;
} else {
arg_str = JS_ToCString(ctx, argv[0]);
if (!arg_str) {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid left-operand value, must be a string");
}
val_name = arg_str;
while ((val_name[0]==' ') || (val_name[0]=='\t'))
val_name++;
if (!strcmp(val_name, "if")) {
op_type = EVG_OP_IF;
JS_FreeCString(ctx, arg_str);
arg_str = JS_ToCString(ctx, argv[1]);
if (!arg_str) {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid if first var, must be a string");
}
var_idx=1;
val_name = arg_str;
shader->has_branches = GF_TRUE;
}
else if (!strcmp(val_name, "else")) {
op_type = EVG_OP_ELSE;
shader->has_branches = GF_TRUE;
JS_FreeCString(ctx, arg_str);
goto op_parsed;
}
else if (!strcmp(val_name, "discard")) {
if (shader->mode==GF_EVG_SHADER_VERTEX) {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "discard is invalid in vertex shader");
}
op_type = EVG_OP_DISCARD;
JS_FreeCString(ctx, arg_str);
goto op_parsed;
}
else if (!strcmp(val_name, "elseif")) {
op_type = EVG_OP_ELSEIF;
shader->has_branches = GF_TRUE;
var_idx=1;
}
else if (!strcmp(val_name, "goto")) {
op_type = EVG_OP_GOTO;
shader->has_branches = GF_TRUE;
JS_FreeCString(ctx, arg_str);
if (JS_IsString(argv[1])) {
arg_str = JS_ToCString(ctx, argv[1]);
if (arg_str[0] != '.') {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid goto var, must be a uniform string");
}
right_op_idx = VAR_UNIFORM;
uni_name = gf_strdup(arg_str+1);
JS_FreeCString(ctx, arg_str);
} else if (JS_ToInt32(ctx, &left_op_idx, argv[1]) ) {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid goto var, must be a number greater than 1, 1 being the first instruction in the stack");
}
goto op_parsed;
}
else if (!strcmp(val_name, "end")) {
shader->has_branches = GF_TRUE;
op_type = EVG_OP_END;
JS_FreeCString(ctx, arg_str);
goto op_parsed;
}
else if (!strcmp(val_name, "print")) {
op_type = EVG_OP_PRINT;
JS_FreeCString(ctx, arg_str);
var_idx=-1;
goto parse_right_val;
}
else if (!strcmp(val_name, "toRGB")) {
op_type = EVG_OP_YUV2RGB;
JS_FreeCString(ctx, arg_str);
var_idx=-1;
goto parse_right_val;
}
else if (!strcmp(val_name, "toYUV")) {
op_type = EVG_OP_RGB2YUV;
JS_FreeCString(ctx, arg_str);
var_idx=-1;
goto parse_right_val;
}
char *sep = strchr(val_name, '.');
if (sep) sep[0] = 0;
left_op_idx = get_builtin_var_name(shader, val_name);
if (!left_op_idx) {
if (shader->alloc_vars <= shader->nb_vars) {
shader->alloc_vars = shader->nb_vars+1;
shader->vars = gf_realloc(shader->vars, sizeof(ShaderVar)*shader->alloc_vars);
shader_reset_vars_stack(shader);
}
shader->vars[shader->nb_vars].name = gf_strdup(val_name);
shader->nb_vars++;
left_op_idx = EVG_FIRST_VAR_ID + shader->nb_vars;
}
if (sep) {
left_value_type = get_value_type(sep+1);
sep[0] = '.';
}
JS_FreeCString(ctx, arg_str);
}
op_name = JS_ToCString(ctx, argv[var_idx+1]);
if (!op_name) {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid operand type, must be a string");
}
if (!strcmp(op_name, "=")) {
op_type = EVG_OP_ASSIGN;
if (left_op_idx==VAR_FRAG_DEPTH)
shader->disable_early_z = GF_TRUE;
} else if (!strcmp(op_name, "+=")) {
op_type = EVG_OP_ADD;
if (left_op_idx==VAR_FRAG_DEPTH)
shader->disable_early_z = GF_TRUE;
} else if (!strcmp(op_name, "-=")) {
op_type = EVG_OP_SUB;
if (left_op_idx==VAR_FRAG_DEPTH)
shader->disable_early_z = GF_TRUE;
} else if (!strcmp(op_name, "*=")) {
op_type = EVG_OP_MUL;
if (left_op_idx==VAR_FRAG_DEPTH)
shader->disable_early_z = GF_TRUE;
} else if (!strcmp(op_name, "/=")) {
op_type = EVG_OP_DIV;
if (left_op_idx==VAR_FRAG_DEPTH)
shader->disable_early_z = GF_TRUE;
} else if (!strcmp(op_name, "=!")) {
op_type = EVG_OP_NEG;
if (left_op_idx==VAR_FRAG_DEPTH)
shader->disable_early_z = GF_TRUE;
} else if (!strcmp(op_name, "<")) {
cond_type = EVG_OP_LESS;
} else if (!strcmp(op_name, "<=")) {
cond_type = EVG_OP_LESS_EQUAL;
} else if (!strcmp(op_name, ">")) {
cond_type = EVG_OP_GREATER;
} else if (!strcmp(op_name, ">=")) {
cond_type = EVG_OP_GREATER_EQUAL;
} else if (!strcmp(op_name, "==")) {
cond_type = EVG_OP_EQUAL;
} else if (!strcmp(op_name, "!=")) {
cond_type = EVG_OP_NOT_EQUAL;
} else if (!strcmp(op_name, "sampler") || !strcmp(op_name, "samplerYUV") ) {
new_op.tx = JS_GetOpaque(argv[var_idx+2], texture_class_id);
if (!strcmp(op_name, "samplerYUV")) op_type = EVG_OP_SAMPLER_YUV;
else op_type = EVG_OP_SAMPLER;
if (!new_op.tx) {
JS_FreeCString(ctx, val_name);
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid texture object for 2D sampler");
}
new_op.tx_ref = argv[var_idx+2];
var_idx++;
} else if (!strcmp(op_name, "normalize")) {
op_type = EVG_OP_NORMALIZE;
} else if (!strcmp(op_name, "length")) {
op_type = EVG_OP_LENGTH;
} else if (!strcmp(op_name, "distance")) {
op_type = EVG_OP_DISTANCE;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "dot")) {
op_type = EVG_OP_DOT;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "cross")) {
op_type = EVG_OP_CROSS;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "pow")) {
op_type = EVG_OP_POW;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "sin")) {
op_type = EVG_OP_SIN;
} else if (!strcmp(op_name, "asin")) {
op_type = EVG_OP_ASIN;
} else if (!strcmp(op_name, "cos")) {
op_type = EVG_OP_COS;
} else if (!strcmp(op_name, "acos")) {
op_type = EVG_OP_ACOS;
} else if (!strcmp(op_name, "tan")) {
op_type = EVG_OP_TAN;
} else if (!strcmp(op_name, "atan")) {
op_type = EVG_OP_ATAN;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "log")) {
op_type = EVG_OP_LOG;
} else if (!strcmp(op_name, "exp")) {
op_type = EVG_OP_EXP;
} else if (!strcmp(op_name, "log2")) {
op_type = EVG_OP_LOG2;
} else if (!strcmp(op_name, "exp2")) {
op_type = EVG_OP_EXP2;
} else if (!strcmp(op_name, "sinh")) {
op_type = EVG_OP_SINH;
} else if (!strcmp(op_name, "cosh")) {
op_type = EVG_OP_COSH;
} else if (!strcmp(op_name, "sqrt")) {
op_type = EVG_OP_SQRT;
} else if (!strcmp(op_name, "inversesqrt")) {
op_type = EVG_OP_INVERSE_SQRT;
} else if (!strcmp(op_name, "abs")) {
op_type = EVG_OP_ABS;
} else if (!strcmp(op_name, "sign")) {
op_type = EVG_OP_SIGN;
} else if (!strcmp(op_name, "floor")) {
op_type = EVG_OP_FLOOR;
} else if (!strcmp(op_name, "ceil")) {
op_type = EVG_OP_CEIL;
} else if (!strcmp(op_name, "fract")) {
op_type = EVG_OP_FRACT;
} else if (!strcmp(op_name, "mod")) {
op_type = EVG_OP_MOD;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "min")) {
op_type = EVG_OP_MIN;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "max")) {
op_type = EVG_OP_MAX;
dual_right_val = GF_TRUE;
} else if (!strcmp(op_name, "clamp")) {
op_type = EVG_OP_CLAMP;
dual_right_val = GF_TRUE;
} else {
JS_FreeCString(ctx, op_name);
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid operand type, must be a string");
}
JS_FreeCString(ctx, op_name);
parse_right_val:
if (JS_IsString(argv[var_idx+2])) {
val_name = JS_ToCString(ctx, argv[var_idx+2]);
if (!val_name) {
shader->invalid = GF_TRUE;
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid right-operand value, must be a string");
}
char *sep = strchr(val_name+1, '.');
if (sep) sep[0] = 0;
right_op_idx = get_builtin_var_name(shader, val_name);
if (right_op_idx==VAR_UNIFORM) {
uni_name = gf_strdup(val_name+1);
}
if (sep) sep[0] = '.';
if (!right_op_idx) {
JSValue ret = js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid right-operand value, undefined variable %s", val_name);
JS_FreeCString(ctx, val_name);
shader->invalid = GF_TRUE;
if (uni_name) gf_free(uni_name);
return ret;
}
if (sep) {
right_value_type = get_value_type(sep+1);
}
JS_FreeCString(ctx, val_name);
}
else if (JS_IsArray(ctx, argv[var_idx+2])) {
u32 idx, length;
JSValue comp = JS_GetPropertyStr(ctx, argv[var_idx+2], "length");
if (JS_ToInt32(ctx, &length, comp)) return GF_JS_EXCEPTION(ctx);
JS_FreeValue(ctx, comp);
if (length>4) length=4;
right_value_type = 0;
for (idx=0;idx<length; idx++) {
comp = JS_GetPropertyUint32(ctx, argv[var_idx+2], idx);
EVG_GET_FLOAT(new_op.vec[idx], comp);
right_value_type |= 1<<idx;
JS_FreeValue(ctx, comp);
}
}
else if (JS_IsObject(argv[var_idx+2])) {
EVG_VAI *vai = JS_GetOpaque(argv[var_idx+2], vai_class_id);
GF_Matrix *mx = JS_GetOpaque(argv[var_idx+2], matrix_class_id);
EVG_VA *va = (shader->mode==GF_EVG_SHADER_VERTEX) ? JS_GetOpaque(argv[var_idx+2], va_class_id) : NULL;
if (vai) {
new_op.vai.vai = vai;
new_op.vai.ref = JS_DupValue(ctx, argv[var_idx+2]);
right_op_idx = VAR_VAI;
} else if (mx) {
new_op.mx.mx = mx;
new_op.mx.ref = JS_DupValue(ctx, argv[var_idx+2]);
right_op_idx = VAR_MATRIX;
} else if (va) {
new_op.va.va = va;
new_op.va.ref = JS_DupValue(ctx, argv[var_idx+2]);
right_op_idx = VAR_VA;
} else {
shader->invalid = GF_TRUE;
if (uni_name) gf_free(uni_name);
return js_throw_err_msg(ctx, GF_BAD_PARAM, "unknown object type for right operand");
}
}
else if (JS_IsBool(argv[var_idx+2])) {
new_op.bval = JS_ToBool(ctx, argv[var_idx+2]) ? GF_TRUE : GF_FALSE;
right_value_type = COMP_BOOL;
}
else if (JS_IsNumber(argv[var_idx+2])) {
if (JS_IsInteger(argv[var_idx+2])) {
JS_ToInt32(ctx, &new_op.ival, argv[var_idx+2]);
right_value_type = COMP_INT;
} else {
Double v;
JS_ToFloat64(ctx, &v, argv[var_idx+2]);
new_op.vec[0] = (Float) v;
right_value_type = COMP_FLOAT;
}
}
if (dual_right_val) {
Bool is_ok=GF_FALSE;
if ((u32) argc<=var_idx+3) {}
else {
val_name = JS_ToCString(ctx, argv[var_idx+3]);
if (val_name) {
right_op2_idx = get_builtin_var_name(shader, val_name);
if (right_op2_idx >= EVG_FIRST_VAR_ID) {
is_ok=GF_TRUE;
}
}
JS_FreeCString(ctx, val_name);
}
if (!is_ok) {
JSValue ret = js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid second right-operand value, only local variable allowed");
JS_FreeCString(ctx, val_name);
shader->invalid = GF_TRUE;
if (uni_name) gf_free(uni_name);
return ret;
}
}
op_parsed:
if (dual_right_val && !right_op2_idx) {
shader->invalid = GF_TRUE;
if (uni_name) gf_free(uni_name);
return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid second right-operand value, only local variable allowed");
}
new_op.op_type = op_type;
if (!new_op.op_type) {
shader->invalid = GF_TRUE;
if (uni_name) gf_free(uni_name);
return js_throw_err_msg(ctx, GF_BAD_PARAM, "unknown operation type");
}
new_op.cond_type = cond_type;
new_op.left_value = left_op_idx;
new_op.right_value = right_op_idx;
new_op.left_value_type = left_value_type;
new_op.right_value_type = right_value_type;
new_op.right_value_second = right_op2_idx;
if (new_op.tx) {
new_op.tx_ref = JS_DupValue(ctx, new_op.tx_ref);
}
if (shader->alloc_ops <= shader->nb_ops+1) {
shader->alloc_ops = shader->nb_ops+2;
shader->ops = gf_realloc(shader->ops, sizeof(ShaderOp)*shader->alloc_ops);
shader->ops[shader->nb_ops].uni_name = NULL;
}
if (shader->ops[shader->nb_ops].uni_name) {
gf_free(shader->ops[shader->nb_ops].uni_name);
shader->ops[shader->nb_ops].uni_name = NULL;
}
shader->ops[shader->nb_ops] = new_op;
shader->ops[shader->nb_ops].uni_name = uni_name;
shader->nb_ops++;
shader->ops[shader->nb_ops].op_type = 0;
//return 1-based index of op, excluding first dummy op (we hide it)
return JS_NewInt32(ctx, shader->nb_ops-1);
}
static JSValue shader_update(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 i;
EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
if (!shader) return GF_JS_EXCEPTION(ctx);
if (shader->invalid) return GF_JS_EXCEPTION(ctx);
for (i=0; i<shader->nb_ops; i++) {
JSValue v;
ShaderOp *op = &shader->ops[i];
if (!op->uni_name) continue;
v = JS_GetPropertyStr(ctx, obj, op->uni_name);
if (JS_IsUndefined(v)) return js_throw_err_msg(ctx, GF_BAD_PARAM, "uniform %s cannot be found in shader", op->uni_name);
if (JS_IsBool(v)) {
op->right_value_type = COMP_BOOL;
op->bval = JS_ToBool(ctx, v) ? GF_TRUE : GF_FALSE;
}
else if (JS_IsNumber(v)) {
if (JS_IsInteger(v)) {
op->right_value_type = COMP_INT;
if (JS_ToInt32(ctx, &op->ival, v)) return GF_JS_EXCEPTION(ctx);
} else {
op->right_value_type = COMP_FLOAT;
EVG_GET_FLOAT(op->vec[0], v)
}
}
else if (JS_IsArray(ctx, v)) {
u32 idx, length;
JSValue comp = JS_GetPropertyStr(ctx, v, "length");
if (JS_ToInt32(ctx, &length, comp)) return GF_JS_EXCEPTION(ctx);
JS_FreeValue(ctx, comp);
if (length>4) length=4;
op->right_value_type = 0;
for (idx=0;idx<length; idx++) {
comp = JS_GetPropertyUint32(ctx, v, idx);
EVG_GET_FLOAT(op->vec[idx], comp);
op->right_value_type |= 1<<idx;
JS_FreeValue(ctx, comp);
}
}
else if (JS_IsObject(v)) {
u32 data_size;
Float *vals;
u32 idx;
u8 *data = evg_get_array(ctx, v, &data_size);
if (data) {
if (data_size%4) return GF_JS_EXCEPTION(ctx);
vals = (Float*)data;
data_size/= sizeof(Float);
if (data_size>4) data_size=4;
op->right_value_type = 0;
for (idx=0;idx<data_size; idx++) {
op->vec[idx] = vals[idx];
op->right_value_type |= 1<<idx;
}
} else {
JSValue comp;
op->right_value_type = 0;
#define GET_COMP(_name, _idx, _mask)\
comp = JS_GetPropertyStr(ctx, v, _name);\
if (!JS_IsUndefined(comp)) { EVG_GET_FLOAT(op->vec[_idx], comp); op->right_value_type |= _mask; }\
JS_FreeValue(ctx, comp);\
GET_COMP("x", 0, COMP_X);
GET_COMP("r", 0, COMP_X);
GET_COMP("s", 0, COMP_X);
GET_COMP("y", 1, COMP_Y);
GET_COMP("g", 1, COMP_Y);
GET_COMP("t", 1, COMP_Y);
GET_COMP("z", 2, COMP_Z);
GET_COMP("b", 2, COMP_Z);
GET_COMP("q", 3, COMP_Q);
GET_COMP("w", 3, COMP_Q);
GET_COMP("a", 3, COMP_Q);
}
}
JS_FreeValue(ctx, v);
}
return JS_UNDEFINED;
}
static const JSCFunctionListEntry shader_funcs[] =
{
JS_CFUNC_DEF("push", 0, shader_push),
JS_CFUNC_DEF("update", 0, shader_update),
};
//test code
#ifdef BUILTIN_SHADERS
Bool rvideo_shader(void *udta, GF_EVGFragmentParam *frag)
{
EVGShader *shader = udta;
GF_JSTexture *tx = shader->vars[0].ptr;
u32 col = gf_evg_stencil_get_pixel_yuv(tx->stencil, frag->tx_x * tx->width / frag->tx_width, frag->tx_y * tx->height / frag->tx_height);
if (shader->vars[1].ival) {
if (frag->odd_flag) {
u8 a, r, g, b;
a = GF_COL_A(col);
r = 0xFF - GF_COL_R(col);
g = 0xFF - GF_COL_G(col);
b = 0xFF - GF_COL_B(col);
col = GF_COL_ARGB(a, r, g, b);
}
}
frag->color_pack = col;
frag->frag_valid = GF_EVG_FRAG_YUV_PACK;
return GF_TRUE;
}
Bool builtin_shader_check(void *udta, GF_EVGFragmentParam *frag, u32 th_id, Bool is_cleanup)
{
u32 i;
EVGShader *shader;
if (is_cleanup) return GF_TRUE;
shader = udta;
for (i=0; i<shader->nb_vars; i++) {
if ((shader->vars[i].value_type == COMP_TX) && !shader->vars[i].ptr)
return GF_FALSE;
}
return GF_TRUE;
}
EVGShader *load_builtin_shader(const char *sname, GF_Err *e)
{
EVGShader *shader;
if (!strcmp(sname, "rvideo")) {
GF_SAFEALLOC(shader, EVGShader);
if (!shader) { *e = GF_OUT_OF_MEM; return NULL;}
shader->nb_vars = 4;
shader->vars = gf_malloc(sizeof(ShaderVar)*shader->nb_vars);
if (!shader->vars) { *e = GF_OUT_OF_MEM; gf_free(shader); return NULL;}
shader->vars[0].name = "tx";
shader->vars[0].value_type = COMP_TX;
shader->vars[1].name = "odd";
shader->vars[1].value_type = COMP_INT;
shader->frag_shader = rvideo_shader;
shader->frag_shader_init = builtin_shader_check;
return shader;
}
return NULL;
}
#endif
static JSValue canvas_new_shader(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
{
EVGShader *shader;
u32 mode;
JSValue res;
GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
if (!canvas) return GF_JS_EXCEPTION(ctx);
if (!argc) return GF_JS_EXCEPTION(ctx);
#ifdef BUILTIN_SHADERS
if (JS_IsString(argv[0])) {
GF_Err e = GF_OK;
const char *sname = JS_ToCString(ctx, argv[0]);
shader = load_builtin_shader(sname, &e);
if (!shader) {
res = js_throw_err_msg(ctx, GF_BAD_PARAM, "Error loading native shader %s: %s", sname, gf_error_to_string(e) );
JS_FreeCString(ctx, sname);
return res;
}
JS_FreeCString(ctx, sname);
shader->mode = GF_EVG_SHADER_FRAGMENT;
res = JS_NewObjectClass(ctx, shader_class_id);
JS_SetOpaque(res, shader);
return res;
}
#endif
if (JS_ToInt32(ctx, &mode, argv[0]))
return GF_JS_EXCEPTION(ctx);
if ((mode != GF_EVG_SHADER_FRAGMENT) && (mode != GF_EVG_SHADER_VERTEX))
return GF_JS_EXCEPTION(ctx);
GF_SAFEALLOC(shader, EVGShader);
if (!shader) {
return js_throw_err(ctx, GF_OUT_OF_MEM);
}
shader->mode = mode;
shader->vars_stack = gf_list_new();
res = JS_NewObjectClass(ctx, shader_class_id);
JS_SetOpaque(res, shader);
return res;
}
static const JSCFunctionListEntry canvas_funcs[] =
{
JS_CGETSET_MAGIC_DEF("centered", canvas_getProperty, canvas_setProperty, GF_EVG_CENTERED),
JS_CGETSET_MAGIC_DEF("path", NULL, canvas_setProperty, GF_EVG_PATH),
JS_CGETSET_MAGIC_DEF("clipper", NULL, canvas_setProperty, GF_EVG_CLIPPER),
JS_CGETSET_MAGIC_DEF("has_clipper", canvas_getProperty, NULL, GF_EVG_CLIPPER),
JS_CGETSET_MAGIC_DEF("matrix", NULL, canvas_setProperty, GF_EVG_MATRIX),
JS_CGETSET_MAGIC_DEF("matrix3d", NULL, canvas_setProperty, GF_EVG_MATRIX_3D),
JS_CGETSET_MAGIC_DEF("compositeOperation", canvas_getProperty, canvas_setProperty, GF_EVG_COMPOSITE_OP),
JS_CGETSET_MAGIC_DEF("level", canvas_getProperty, canvas_setProperty, GF_EVG_RASTER_LEVEL),
JS_CGETSET_MAGIC_DEF("on_alpha", canvas_getProperty, canvas_setProperty, GF_EVG_ALPHA_FUN),
JS_CGETSET_MAGIC_DEF("is_yuv", canvas_getProperty, NULL, GF_EVG_IS_YUV),
JS_CGETSET_MAGIC_DEF("depth", canvas_getProperty, NULL, GF_EVG_BIT_DEPTH),
JS_CGETSET_MAGIC_DEF("mask_mode", canvas_getProperty, canvas_setProperty, GF_EVG_MASK_MODE),
JS_CFUNC_DEF("enable_threading", 0, canvas_enable_threading),
JS_CFUNC_DEF("enable_3d", 0, canvas_enable_3d),
JS_CFUNC_DEF("clear", 0, canvas_clear),
JS_CFUNC_DEF("clearf", 0, canvas_clearf),
JS_CFUNC_DEF("fill", 0, canvas_fill),
JS_CFUNC_DEF("reassign", 0, canvas_reassign),
JS_CFUNC_DEF("toYUV", 0, canvas_toYUV),
JS_CFUNC_DEF("toRGB", 0, canvas_toRGB),
JS_CFUNC_DEF("blit", 0, canvas_blit),
//3D extensions
JS_CGETSET_MAGIC_DEF("fragment", canvas_getProperty, canvas_setProperty, GF_EVG_FRAG_SHADER),
JS_CGETSET_MAGIC_DEF("vertex", canvas_getProperty, canvas_setProperty, GF_EVG_VERT_SHADER),
JS_CGETSET_MAGIC_DEF("ccw", NULL, canvas_setProperty, GF_EVG_CCW),
JS_CGETSET_MAGIC_DEF("backcull", NULL, canvas_setProperty, GF_EVG_BACKCULL),
JS_CGETSET_MAGIC_DEF("antialias", NULL, canvas_setProperty, GF_EVG_ANTIALIAS),
JS_CGETSET_MAGIC_DEF("min_depth", NULL, canvas_setProperty, GF_EVG_MINDEPTH),
JS_CGETSET_MAGIC_DEF("max_depth", NULL, canvas_setProperty, GF_EVG_MAXDEPTH),
JS_CGETSET_MAGIC_DEF("point_size", NULL, canvas_setProperty, GF_EVG_POINTSIZE),
JS_CGETSET_MAGIC_DEF("point_smooth", NULL, canvas_setProperty, GF_EVG_POINTSMOOTH),
JS_CGETSET_MAGIC_DEF("line_size", NULL, canvas_setProperty, GF_EVG_LINESIZE),
JS_CGETSET_MAGIC_DEF("clip_zero", NULL, canvas_setProperty, GF_EVG_CLIP_ZERO),
JS_CGETSET_MAGIC_DEF("depth_test", NULL, canvas_setProperty, GF_EVG_DEPTH_TEST),
JS_CGETSET_MAGIC_DEF("write_depth", NULL, canvas_setProperty, GF_EVG_WRITE_DEPTH),
JS_CGETSET_MAGIC_DEF("depth_buffer", canvas_getProperty, canvas_setProperty, GF_EVG_DEPTH_BUFFER),
JS_CFUNC_DEF("projection", 0, canvas_projection),
JS_CFUNC_DEF("modelview", 0, canvas_modelview),
JS_CFUNC_DEF("draw_array", 0, canvas_draw_array),
JS_CFUNC_DEF("draw_path", 0, canvas_draw_path),
JS_CFUNC_DEF("clear_depth", 0, canvas_clear_depth),
JS_CFUNC_DEF("viewport", 0, canvas_viewport),
JS_CFUNC_DEF("new_shader", 0, canvas_new_shader),
};
#ifdef EVG_USE_JS_SHADER
JSClassDef fragment_class = {
.class_name = "Fragment",
};
enum {
EVG_FRAG_SCREENX,
EVG_FRAG_SCREENY,
EVG_FRAG_DEPTH,
EVG_FRAG_R,
EVG_FRAG_G,
EVG_FRAG_B,
EVG_FRAG_A,
EVG_FRAG_Y,
EVG_FRAG_U,
EVG_FRAG_V,
EVG_FRAG_RGBA,
EVG_FRAG_RGB,
EVG_FRAG_YUV,
EVG_FRAG_YUVA,
};
static JSValue fragment_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_EVGFragmentParam *frag = JS_GetOpaque(obj, fragment_class_id);
if (!frag) return GF_JS_EXCEPTION(c);
switch (magic) {
case EVG_FRAG_SCREENX: return JS_NewFloat64(c, frag->screen_x);
case EVG_FRAG_SCREENY: return JS_NewFloat64(c, frag->screen_y);
case EVG_FRAG_DEPTH: return JS_NewFloat64(c, frag->depth);
}
return JS_UNDEFINED;
}
static JSValue fragment_setProperty(JSContext *ctx, JSValueConst obj, JSValueConst value, int magic)
{
EVG_VAIRes *vr;
GF_EVGFragmentType frag_type = GF_EVG_FRAG_RGB;
GF_EVGFragmentParam *frag = JS_GetOpaque(obj, fragment_class_id);
if (!frag) return GF_JS_EXCEPTION(c);
switch (magic) {
case EVG_FRAG_DEPTH:
EVG_GET_FLOAT(frag->depth, value)
break;
case EVG_FRAG_Y:
frag_type = GF_EVG_FRAG_YUV;
case EVG_FRAG_R:
EVG_GET_FLOAT(frag->color.x, value)
CLAMPCOLF(frag->color.x)
frag->frag_valid = frag_type;
break;
case EVG_FRAG_U:
frag_type = GF_EVG_FRAG_YUV;
case EVG_FRAG_G:
EVG_GET_FLOAT(frag->color.y, value)
CLAMPCOLF(frag->color.y)
frag->frag_valid = frag_type;
break;
case EVG_FRAG_V:
frag_type = GF_EVG_FRAG_YUV;
case EVG_FRAG_B:
EVG_GET_FLOAT(frag->color.z, value)
CLAMPCOLF(frag->color.z)
frag->frag_valid = frag_type;
break;
case EVG_FRAG_A:
EVG_GET_FLOAT(frag->color.q, value)
CLAMPCOLF(frag->color.q)
if (!frag->frag_valid)
frag->frag_valid = GF_EVG_FRAG_RGB;
break;
case EVG_FRAG_YUVA:
frag_type = GF_EVG_FRAG_YUV;
case EVG_FRAG_RGBA:
vr = JS_GetOpaque(value, vaires_class_id);
if (vr) {
frag->color.x = vr->values[0];
frag->color.y = vr->values[1];
frag->color.z = vr->values[2];
frag->color.q = vr->values[3];
frag->frag_valid = frag_type;
} else {
Double a, r, g, b;
a=1.0;
if (!get_color_from_args(ctx, 1, &value, 0, &a, &r, &g, &b))
return GF_JS_EXCEPTION(c);
frag->color.q = (Float) a;
frag->color.x = (Float) r;
frag->color.y = (Float) g;
frag->color.z = (Float) b;
frag->frag_valid = GF_EVG_FRAG_RGB;
}
break;
case EVG_FRAG_YUV:
frag_type = GF_EVG_FRAG_YUV;
case EVG_FRAG_RGB:
vr = JS_GetOpaque(value, vaires_class_id);
if (vr) {
frag->color.x = vr->values[0];
frag->color.y = vr->values[1];
frag->color.z = vr->values[2];
frag->frag_valid = frag_type;
} else
return GF_JS_EXCEPTION(c);
default:
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
static const JSCFunctionListEntry fragment_funcs[] =
{
JS_CGETSET_MAGIC_DEF("x", fragment_getProperty, NULL, EVG_FRAG_SCREENX),
JS_CGETSET_MAGIC_DEF("y", fragment_getProperty, NULL, EVG_FRAG_SCREENY),
JS_CGETSET_MAGIC_DEF("z", fragment_getProperty, fragment_setProperty, EVG_FRAG_DEPTH),
JS_ALIAS_DEF("depth", "z"),
JS_CGETSET_MAGIC_DEF("r", NULL, fragment_setProperty, EVG_FRAG_R),
JS_CGETSET_MAGIC_DEF("g", NULL, fragment_setProperty, EVG_FRAG_G),
JS_CGETSET_MAGIC_DEF("b", NULL, fragment_setProperty, EVG_FRAG_B),
JS_CGETSET_MAGIC_DEF("a", NULL, fragment_setProperty, EVG_FRAG_A),
JS_ALIAS_DEF("Y", "r"),
JS_ALIAS_DEF("U", "g"),
JS_ALIAS_DEF("V", "b"),
JS_CGETSET_MAGIC_DEF("rgba", NULL, fragment_setProperty, EVG_FRAG_RGBA),
JS_CGETSET_MAGIC_DEF("rgb", NULL, fragment_setProperty, EVG_FRAG_RGB),
JS_CGETSET_MAGIC_DEF("yuv", NULL, fragment_setProperty, EVG_FRAG_YUV),
JS_CGETSET_MAGIC_DEF("yuva", NULL, fragment_setProperty, EVG_FRAG_YUVA),
};
JSClassDef vertex_class = {
.class_name = "Vertex",
};
enum {
EVG_VERTEX_IN,
EVG_VERTEX_OUT,
};
static JSValue vertex_getProperty(JSContext *c, JSValueConst obj, int magic)
{
JSValue res;
GF_EVGVertexParam *vert= JS_GetOpaque(obj, vertex_class_id);
if (!vert) return GF_JS_EXCEPTION(c);
switch (magic) {
case EVG_VERTEX_IN:
res = JS_NewObject(c);
JS_SetPropertyStr(c, res, "x", JS_NewFloat64(c, vert->in_vertex.x));
JS_SetPropertyStr(c, res, "y", JS_NewFloat64(c, vert->in_vertex.y));
JS_SetPropertyStr(c, res, "z", JS_NewFloat64(c, vert->in_vertex.z));
JS_SetPropertyStr(c, res, "q", JS_NewFloat64(c, vert->in_vertex.q));
return res;
}
return JS_UNDEFINED;
}
static JSValue vertex_setProperty(JSContext *ctx, JSValueConst obj, JSValueConst value, int magic)
{
Double _f;
JSValue v;
GF_EVGVertexParam *vert= JS_GetOpaque(obj, vertex_class_id);
if (!vert) return GF_JS_EXCEPTION(c);
switch (magic) {
case EVG_VERTEX_OUT:
v = JS_GetPropertyStr(ctx, value, "x");
EVG_GET_FLOAT(_f, v);
vert->out_vertex.x = FLT2FIX(_f);
v = JS_GetPropertyStr(ctx, value, "y");
EVG_GET_FLOAT(_f, v);
vert->out_vertex.y = FLT2FIX(_f);
v = JS_GetPropertyStr(ctx, value, "z");
EVG_GET_FLOAT(_f, v);
vert->out_vertex.z = FLT2FIX(_f);
v = JS_GetPropertyStr(ctx, value, "q");
EVG_GET_FLOAT(_f, v);
vert->out_vertex.q = FLT2FIX(_f);
break;
default:
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
static const JSCFunctionListEntry vertex_funcs[] =
{
JS_CGETSET_MAGIC_DEF("vertex", vertex_getProperty, NULL, EVG_VERTEX_IN),
JS_CGETSET_MAGIC_DEF("vertexOut", NULL, vertex_setProperty, EVG_VERTEX_OUT),
};
#endif // EVG_USE_JS_SHADER
static Bool vai_call_lerp_init(EVG_VAI *vai, GF_EVGFragmentParam *frag)
{
u32 i;
//different primitive, setup inperpolation points
if (frag->prim_index != vai->prim_idx) {
u32 idx;
vai->prim_idx = frag->prim_index;
//no values, this is a VAI filled by the vertex shader
if (!vai->values) {
} else if (vai->interp_type==GF_EVG_VAI_PRIMITIVE) {
} else if (frag->ptype!=GF_EVG_POINTS) {
u32 nb_v_per_prim = 3;
if (frag->ptype==GF_EVG_LINES)
nb_v_per_prim=2;
if (vai->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
idx = frag->idx1 * vai->nb_comp;
} else {
idx = frag->prim_index * nb_v_per_prim * vai->nb_comp;
}
if (idx+vai->nb_comp > vai->nb_values)
return GF_FALSE;
for (i=0; i<vai->nb_comp; i++) {
vai->anchors[0][i] = vai->values[idx+i];
}
if (vai->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
idx = frag->idx2 * vai->nb_comp;
} else {
idx = (frag->prim_index * nb_v_per_prim + 1) * vai->nb_comp;
}
if (idx+vai->nb_comp > vai->nb_values)
return GF_FALSE;
for (i=0; i<vai->nb_comp; i++) {
vai->anchors[1][i] = vai->values[idx+i];
}
if (frag->ptype!=GF_EVG_LINES) {
if (vai->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
idx = frag->idx3 * vai->nb_comp;
} else {
idx = (frag->prim_index * nb_v_per_prim + 2) * vai->nb_comp;
}
if (idx+vai->nb_comp > vai->nb_values)
return GF_FALSE;
for (i=0; i<vai->nb_comp; i++) {
vai->anchors[2][i] = vai->values[idx+i];
}
}
}
}
return GF_TRUE;
}
static Bool vai_call_lerp(EVG_VAI *vai, GF_EVGFragmentParam *frag, Float *values)
{
u32 i;
if (vai->interp_type==GF_EVG_VAI_PRIMITIVE) {
u32 idx;
idx = vai->prim_idx * vai->nb_comp;
if (idx+vai->nb_comp > vai->nb_values)
return GF_FALSE;
for (i=0; i<vai->nb_comp; i++) {
values[i] = vai->values[idx+i];
}
return GF_TRUE;
}
if (frag->ptype==GF_EVG_LINES) {
for (i=0; i<vai->nb_comp; i++) {
Float v = frag->pbc1 * vai->anchors[0][i] + frag->pbc2 * vai->anchors[1][i];
values[i] = v / frag->persp_denum;
}
} else {
for (i=0; i<vai->nb_comp; i++) {
Float v = (Float) ( frag->pbc1 * vai->anchors[0][i] + frag->pbc2 * vai->anchors[1][i] + frag->pbc3 * vai->anchors[2][i] );
values[i] = v / frag->persp_denum;
}
}
if (vai->normalize) {
if (vai->nb_comp==2) {
Float len;
if (!values[0]) len = ABS(values[1]);
else if (!values[1]) len = ABS(values[0]);
else len = sqrtf(values[0]*values[0] + values[1]*values[1]);
if (len) {
values[0]/=len;
values[1]/=len;
}
} else {
gf_vec_norm((GF_Vec *) &values[0]);
}
}
return GF_TRUE;
}
#ifdef EVG_USE_JS_SHADER
static JSValue vai_lerp(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
if (!vai || !argc) return GF_JS_EXCEPTION(c);
GF_EVGFragmentParam *frag = JS_GetOpaque(argv[0], fragment_class_id);
if (!frag) return GF_JS_EXCEPTION(c);
if (!vai_call_lerp(vai, frag))
return GF_JS_EXCEPTION(c);
return JS_DupValue(c, vai->res);
}
#endif
static JSValue vai_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
if (!vai) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
vai->normalize = JS_ToBool(c, value) ? GF_TRUE : GF_FALSE;
break;
}
return JS_UNDEFINED;
}
static JSValue vai_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
EVG_VAI *vai;
JSValue obj;
u8 *data=NULL;
u32 data_size=0;
u32 nb_comp;
s32 interp_type = GF_EVG_VAI_VERTEX_INDEX;
if (argc<1)
return js_throw_err_msg(c, GF_BAD_PARAM, "Missing parameter for VertexAttribInterpolator");
if (argc>=2) {
data = evg_get_array(c, argv[0], &data_size);
if (!data) return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &nb_comp, argv[1])) return GF_JS_EXCEPTION(c);
if (nb_comp>MAX_ATTR_DIM)
return js_throw_err_msg(c, GF_BAD_PARAM, "Dimension too big, max is %d", MAX_ATTR_DIM);
if (data_size % sizeof(Float)) return GF_JS_EXCEPTION(c);
data_size /= sizeof(Float);
if (data_size % nb_comp) return GF_JS_EXCEPTION(c);
if (argc>2) {
if (JS_ToInt32(c, &interp_type, argv[2])) return GF_JS_EXCEPTION(c);
}
} else {
if (JS_ToInt32(c, &nb_comp, argv[0])) return GF_JS_EXCEPTION(c);
}
GF_SAFEALLOC(vai, EVG_VAI);
if (!vai)
return js_throw_err(c, GF_OUT_OF_MEM);
vai->nb_comp = nb_comp;
vai->values = (Float *)data;
vai->nb_values = data_size;
if (data)
vai->ab = JS_DupValue(c, argv[0]);
vai->prim_idx = (u32) -1;
vai->interp_type = interp_type;
#ifdef EVG_USE_JS_SHADER
vai->res = JS_NewObjectClass(c, vaires_class_id);
JS_SetOpaque(vai->res, &vai->result);
JS_SetPropertyStr(c, vai->res, "length", JS_NewInt32(c, nb_comp));
#endif
vai->result.dim = nb_comp;
if (nb_comp==1) vai->result.comp_type = COMP_X;
else if (nb_comp==2) vai->result.comp_type = COMP_V2_XY;
else if (nb_comp==3) vai->result.comp_type = COMP_V3;
else vai->result.comp_type = COMP_V4;
obj = JS_NewObjectClass(c, vai_class_id);
JS_SetOpaque(obj, vai);
return obj;
}
static void vai_finalize(JSRuntime *rt, JSValue obj)
{
EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
if (!vai) return;
JS_FreeValueRT(rt, vai->ab);
#ifdef EVG_USE_JS_SHADER
JS_FreeValueRT(rt, vai->res);
#endif
gf_free(vai);
}
static void vai_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
{
EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
if (!vai) return;
JS_MarkValue(rt, vai->ab, mark_func);
#ifdef EVG_USE_JS_SHADER
JS_MarkValue(rt, vai->res, mark_func);
#endif
}
JSClassDef vai_class = {
.class_name = "VertexAttribInterpolator",
.finalizer = vai_finalize,
.gc_mark = vai_gc_mark
};
static const JSCFunctionListEntry vai_funcs[] =
{
JS_CGETSET_MAGIC_DEF("normalize", NULL, vai_setProperty, 0),
#ifdef EVG_USE_JS_SHADER
JS_CFUNC_DEF("lerp", 0, vai_lerp),
#endif
};
static JSValue va_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
EVG_VA *va;
JSValue obj;
u8 *data=NULL;
u32 data_size;
u32 nb_comp;
s32 interp_type = GF_EVG_VAI_VERTEX_INDEX;
if (argc<2)
return js_throw_err_msg(c, GF_BAD_PARAM, "Missing parameter / data for VertexAttrib");
data = evg_get_array(c, argv[0], &data_size);
if (!data) return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &nb_comp, argv[1])) return GF_JS_EXCEPTION(c);
if (nb_comp>MAX_ATTR_DIM)
return js_throw_err_msg(c, GF_BAD_PARAM, "Dimension too big, max is %d", MAX_ATTR_DIM);
if (data_size % sizeof(Float)) return GF_JS_EXCEPTION(c);
data_size /= sizeof(Float);
if (data_size % nb_comp) return GF_JS_EXCEPTION(c);
if (argc>2) {
if (JS_ToInt32(c, &interp_type, argv[2])) return GF_JS_EXCEPTION(c);
}
GF_SAFEALLOC(va, EVG_VA);
if (!va)
return js_throw_err(c, GF_OUT_OF_MEM);
va->nb_comp = nb_comp;
va->values = (Float *)data;
va->nb_values = data_size;
va->ab = JS_DupValue(c, argv[0]);
va->interp_type = interp_type;
if (va->nb_comp==1) va->att_type = COMP_FLOAT;
else if (va->nb_comp==2) va->att_type = COMP_V2_XY;
else if (va->nb_comp==3) va->att_type = COMP_V3;
else va->att_type = COMP_V4;
obj = JS_NewObjectClass(c, va_class_id);
JS_SetOpaque(obj, va);
return obj;
}
static JSValue va_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
EVG_VA *va = JS_GetOpaque(obj, va_class_id);
if (!va) return GF_JS_EXCEPTION(c);
switch (magic) {
case 0:
va->normalize = JS_ToBool(c, value) ? GF_TRUE : GF_FALSE;
break;
}
return JS_UNDEFINED;
}
static void va_finalize(JSRuntime *rt, JSValue obj)
{
EVG_VA *va = JS_GetOpaque(obj, va_class_id);
if (!va) return;
JS_FreeValueRT(rt, va->ab);
gf_free(va);
}
static void va_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
{
#ifdef GPAC_ENABLE_COVERAGE
if (!rt) return;
#endif
EVG_VA *va = JS_GetOpaque(obj, va_class_id);
if (!va) return;
JS_MarkValue(rt, va->ab, mark_func);
}
JSClassDef va_class = {
.class_name = "VertexAttrib",
.finalizer = va_finalize,
.gc_mark = va_gc_mark
};
static const JSCFunctionListEntry va_funcs[] =
{
JS_CGETSET_MAGIC_DEF("normalize", NULL, va_setProperty, 0),
};
#ifdef EVG_USE_JS_SHADER
static JSValue vaires_getProperty(JSContext *c, JSValueConst obj, int magic)
{
EVG_VAIRes *vr = JS_GetOpaque(obj, vaires_class_id);
if (!vr) return GF_JS_EXCEPTION(c);
if (magic<MAX_ATTR_DIM) {
return JS_NewFloat64(c, vr->values[magic]);
}
return JS_UNDEFINED;
}
static const JSCFunctionListEntry vaires_funcs[] =
{
JS_CGETSET_MAGIC_DEF("x", vaires_getProperty, NULL, 0),
JS_CGETSET_MAGIC_DEF("y", vaires_getProperty, NULL, 1),
JS_CGETSET_MAGIC_DEF("z", vaires_getProperty, NULL, 2),
JS_CGETSET_MAGIC_DEF("w", vaires_getProperty, NULL, 3),
JS_ALIAS_DEF("r", "x"),
JS_ALIAS_DEF("g", "y"),
JS_ALIAS_DEF("b", "z"),
JS_ALIAS_DEF("a", "w"),
JS_ALIAS_DEF("s", "x"),
JS_ALIAS_DEF("t", "y"),
};
JSClassDef vaires_class = {
.class_name = "VAIResult",
};
#endif //EVG_USE_JS_SHADER
static void mx2d_finalize(JSRuntime *rt, JSValue obj)
{
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx) return;
gf_free(mx);
}
JSClassDef mx2d_class = {
"Matrix2D",
.finalizer = mx2d_finalize
};
enum
{
//these 6 magics map to m[x] of the matrix, DO NOT MODIFY
MX2D_XX = 0,
MX2D_XY,
MX2D_TX,
MX2D_YX,
MX2D_YY,
MX2D_TY,
MX2D_IDENTITY,
MX2D_3D,
};
static JSValue mx2d_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx) return GF_JS_EXCEPTION(c);
if ((magic>=MX2D_XX) && (magic<=MX2D_TY)) {
return JS_NewFloat64(c, FIX2FLT(mx->m[magic]));
}
if (magic==MX2D_IDENTITY)
return JS_NewBool(c, gf_mx2d_is_identity(*mx));
if (magic==MX2D_3D)
return JS_FALSE;
return JS_UNDEFINED;
}
static JSValue mx2d_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx) return GF_JS_EXCEPTION(c);
if ((magic>=MX2D_XX) && (magic<=MX2D_TY)) {
Double d;
if (JS_ToFloat64(c, &d, value))
return GF_JS_EXCEPTION(c);
mx->m[magic] = FIX2FLT(d);
return JS_UNDEFINED;
}
if (magic==MX2D_IDENTITY) {
if (JS_ToBool(c, value))
gf_mx2d_init(*mx);
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
static JSValue mx2d_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(c);
GF_Matrix2D *amx = JS_GetOpaque(argv[0], mx2d_class_id);
if (!mx) return GF_JS_EXCEPTION(c);
if ((argc>1) && JS_ToBool(c, argv[1]))
gf_mx2d_pre_multiply(mx, amx);
else
gf_mx2d_add_matrix(mx, amx);
return JS_DupValue(c, obj);
}
static JSValue mx2d_translate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double tx, ty;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || (argc<1)) return GF_JS_EXCEPTION(c);
if (JS_IsObject(argv[0])) {
JSValue v;
#define GETD(_arg, _name, _res)\
if (! JS_IsObject(_arg)) return GF_JS_EXCEPTION(c);\
v = JS_GetPropertyStr(c, _arg, _name);\
JS_ToFloat64(c, &_res, v);\
JS_FreeValue(c, v);\
GETD(argv[0], "x", tx);
GETD(argv[0], "y", ty);
#undef GETD
} else if (argc==2) {
if (JS_ToFloat64(c, &tx, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &ty, argv[1])) return GF_JS_EXCEPTION(c);
} else {
return GF_JS_EXCEPTION(c);
}
gf_mx2d_add_translation(mx, FLT2FIX(tx), FLT2FIX(ty));
return JS_DupValue(c, obj);
}
static JSValue mx2d_rotate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double cx, cy, a;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || (argc<3)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &cx, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &cy, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &a, argv[2])) return GF_JS_EXCEPTION(c);
gf_mx2d_add_rotation(mx, FLT2FIX(cx), FLT2FIX(cy), FLT2FIX(a) );
return JS_DupValue(c, obj);
}
static JSValue mx2d_scale(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double sx, sy;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || (argc<2)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &sx, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &sy, argv[1])) return GF_JS_EXCEPTION(c);
if (argc==2) {
gf_mx2d_add_scale(mx, FLT2FIX(sx), FLT2FIX(sy));
return JS_DupValue(c, obj);
} else if (argc==5) {
Double cx, cy, a;
if (JS_ToFloat64(c, &cx, argv[2])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &cy, argv[3])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &a, argv[4])) return GF_JS_EXCEPTION(c);
gf_mx2d_add_scale_at(mx, FLT2FIX(sx), FLT2FIX(sy), FLT2FIX(cx), FLT2FIX(cy), FLT2FIX(a));
return JS_DupValue(c, obj);
}
return GF_JS_EXCEPTION(c);
}
static JSValue mx2d_skew(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double sx, sy;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || (argc<2)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &sx, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &sy, argv[1])) return GF_JS_EXCEPTION(c);
gf_mx2d_add_skew(mx, FLT2FIX(sx), FLT2FIX(sy));
return JS_DupValue(c, obj);
}
static JSValue mx2d_skew_x(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double s;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &s, argv[0])) return GF_JS_EXCEPTION(c);
gf_mx2d_add_skew_x(mx, FLT2FIX(s));
return JS_DupValue(c, obj);
}
static JSValue mx2d_skew_y(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double s;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &s, argv[0])) return GF_JS_EXCEPTION(c);
gf_mx2d_add_skew_y(mx, FLT2FIX(s));
return JS_DupValue(c, obj);
}
static JSValue mx2d_apply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Fixed x, y, w=0, h=0;
Double d;
JSValue v;
int res;
u32 is_rect=0;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx || !argc || !JS_IsObject(argv[0])) return GF_JS_EXCEPTION(c);
v = JS_GetPropertyStr(c, argv[0], "x");
res = JS_ToFloat64(c, &d, v);
JS_FreeValue(c, v);
if (res) return GF_JS_EXCEPTION(c);
x = FLT2FIX(d);
v = JS_GetPropertyStr(c, argv[0], "y");
res = JS_ToFloat64(c, &d, v);
JS_FreeValue(c, v);
if (res) return GF_JS_EXCEPTION(c);
y = FLT2FIX(d);
v = JS_GetPropertyStr(c, argv[0], "w");
if (!JS_IsUndefined(v) && JS_IsNumber(v)) {
res = JS_ToFloat64(c, &d, v);
JS_FreeValue(c, v);
if (res) return GF_JS_EXCEPTION(c);
is_rect++;
w = FLT2FIX(d);
}
JS_FreeValue(c, v);
v = JS_GetPropertyStr(c, argv[0], "h");
if (!JS_IsUndefined(v) && JS_IsNumber(v)) {
res = JS_ToFloat64(c, &d, v);
JS_FreeValue(c, v);
if (res) return GF_JS_EXCEPTION(c);
is_rect++;
h = FLT2FIX(d);
}
JS_FreeValue(c, v);
if (is_rect) {
GF_Rect rc;
rc.x = x;
rc.y = y;
rc.width = w;
rc.height = h;
gf_mx2d_apply_rect(mx, &rc);
v = JS_NewObject(c);
JS_SetPropertyStr(c, v, "x", JS_NewFloat64(c, FIX2FLT(rc.x)));
JS_SetPropertyStr(c, v, "y", JS_NewFloat64(c, FIX2FLT(rc.y)));
JS_SetPropertyStr(c, v, "w", JS_NewFloat64(c, FIX2FLT(rc.width)));
JS_SetPropertyStr(c, v, "h", JS_NewFloat64(c, FIX2FLT(rc.height)));
return v;
} else {
GF_Point2D pt;
pt.x = x;
pt.y = y;
gf_mx2d_apply_point(mx, &pt);
v = JS_NewObject(c);
JS_SetPropertyStr(c, v, "x", JS_NewFloat64(c, FIX2FLT(pt.x)));
JS_SetPropertyStr(c, v, "y", JS_NewFloat64(c, FIX2FLT(pt.y)));
return v;
}
return GF_JS_EXCEPTION(c);
}
static JSValue mx2d_inverse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx) return GF_JS_EXCEPTION(c);
gf_mx2d_inverse(mx);
return JS_DupValue(c, obj);
}
static JSValue mx2d_copy(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Matrix2D *nmx;
JSValue nobj;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx) return GF_JS_EXCEPTION(c);
if (argc) {
GF_Matrix2D *mx_from = JS_GetOpaque(argv[0], mx2d_class_id);
if (!mx_from) return GF_JS_EXCEPTION(c);
gf_mx2d_copy(*mx, *mx_from);
return JS_UNDEFINED;
}
GF_SAFEALLOC(nmx, GF_Matrix2D);
if (!nmx)
return js_throw_err(c, GF_OUT_OF_MEM);
gf_mx2d_copy(*nmx, *mx);
nobj = JS_NewObjectClass(c, mx2d_class_id);
JS_SetOpaque(nobj, nmx);
return nobj;
}
static JSValue mx2d_decompose_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 opt)
{
GF_Point2D scale;
GF_Point2D translate;
Fixed rotate;
GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
if (!mx) return GF_JS_EXCEPTION(c);
if (!gf_mx2d_decompose(mx, &scale, &rotate, &translate))
return JS_NULL;
if (opt==0) {
JSValue nobj = JS_NewObject(c);
JS_SetPropertyStr(c, nobj, "x", JS_NewFloat64(c, FIX2FLT(scale.x)) );
JS_SetPropertyStr(c, nobj, "y", JS_NewFloat64(c, FIX2FLT(scale.y)) );
return nobj;
}
if (opt==1) {
JSValue nobj = JS_NewObject(c);
JS_SetPropertyStr(c, nobj, "x", JS_NewFloat64(c, FIX2FLT(translate.x)) );
JS_SetPropertyStr(c, nobj, "y", JS_NewFloat64(c, FIX2FLT(translate.y)) );
return nobj;
}
if (opt==2) {
return JS_NewFloat64(c, FIX2FLT(rotate) );
}
return GF_JS_EXCEPTION(c);
}
static JSValue mx2d_get_scale(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return mx2d_decompose_ex(c, obj, argc, argv, 0);
}
static JSValue mx2d_get_translate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return mx2d_decompose_ex(c, obj, argc, argv, 1);
}
static JSValue mx2d_get_rotate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return mx2d_decompose_ex(c, obj, argc, argv, 2);
}
static const JSCFunctionListEntry mx2d_funcs[] =
{
JS_CGETSET_MAGIC_DEF("xx", mx2d_getProperty, mx2d_setProperty, MX2D_XX),
JS_CGETSET_MAGIC_DEF("xy", mx2d_getProperty, mx2d_setProperty, MX2D_XY),
JS_CGETSET_MAGIC_DEF("tx", mx2d_getProperty, mx2d_setProperty, MX2D_TX),
JS_CGETSET_MAGIC_DEF("yx", mx2d_getProperty, mx2d_setProperty, MX2D_YX),
JS_CGETSET_MAGIC_DEF("yy", mx2d_getProperty, mx2d_setProperty, MX2D_YY),
JS_CGETSET_MAGIC_DEF("ty", mx2d_getProperty, mx2d_setProperty, MX2D_TY),
JS_CGETSET_MAGIC_DEF("identity", mx2d_getProperty, mx2d_setProperty, MX2D_IDENTITY),
JS_CGETSET_MAGIC_DEF("is3D", mx2d_getProperty, NULL, MX2D_3D),
JS_CFUNC_DEF("get_scale", 0, mx2d_get_scale),
JS_CFUNC_DEF("get_translate", 0, mx2d_get_translate),
JS_CFUNC_DEF("get_rotate", 0, mx2d_get_rotate),
JS_CFUNC_DEF("inverse", 0, mx2d_inverse),
JS_CFUNC_DEF("copy", 0, mx2d_copy),
JS_CFUNC_DEF("add", 0, mx2d_multiply),
JS_CFUNC_DEF("translate", 0, mx2d_translate),
JS_CFUNC_DEF("rotate", 0, mx2d_rotate),
JS_CFUNC_DEF("scale", 0, mx2d_scale),
JS_CFUNC_DEF("skew", 0, mx2d_skew),
JS_CFUNC_DEF("skew_x", 0, mx2d_skew_x),
JS_CFUNC_DEF("skew_y", 0, mx2d_skew_y),
JS_CFUNC_DEF("apply", 0, mx2d_apply),
};
static JSValue mx2d_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
JSValue obj;
GF_Matrix2D *mx;
GF_SAFEALLOC(mx, GF_Matrix2D);
if (!mx)
return js_throw_err(c, GF_OUT_OF_MEM);
mx->m[MX2D_XX] = mx->m[MX2D_YY] = FIX_ONE;
obj = JS_NewObjectClass(c, mx2d_class_id);
JS_SetOpaque(obj, mx);
if ((argc==1) && JS_IsObject(argv[0])) {
GF_Matrix2D *amx = JS_GetOpaque(argv[0], mx2d_class_id);
if (amx) gf_mx2d_copy(*mx, *amx);
}
else if (argc==6) {
u32 i;
Double d;
for (i=0; i<6; i++) {
if (JS_ToFloat64(c, &d, argv[i])) return GF_JS_EXCEPTION(c);
mx->m[i] = FLT2FIX(d);
}
}
return obj;
}
static void colmx_finalize(JSRuntime *rt, JSValue obj)
{
GF_ColorMatrix *mx = JS_GetOpaque(obj, colmx_class_id);
if (!mx) return;
gf_free(mx);
}
JSClassDef colmx_class = {
"ColorMatrix",
.finalizer = colmx_finalize
};
static JSValue colmx_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
JSValue obj;
GF_ColorMatrix *cmx;
GF_SAFEALLOC(cmx, GF_ColorMatrix);
if (!cmx)
return js_throw_err(c, GF_OUT_OF_MEM);
gf_cmx_init(cmx);
obj = JS_NewObjectClass(c, colmx_class_id);
JS_SetOpaque(obj, cmx);
if ((argc==1) && JS_IsObject(argv[0])) {
GF_ColorMatrix *acmx = JS_GetOpaque(argv[0], colmx_class_id);
if (acmx) gf_cmx_copy(cmx, acmx);
else if (JS_IsArray(c, argv[0])) {
u32 i;
Double d;
for (i=0; i<20; i++) {
JSValue v = JS_GetPropertyUint32(c, argv[0], i);
if (JS_IsException(v)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &d, v)) {
JS_FreeValue(c, v);
return GF_JS_EXCEPTION(c);
}
cmx->m[i] = FLT2FIX(d);
JS_FreeValue(c, v);
}
cmx->identity = 0;
}
}
else if (argc==20) {
u32 i;
Double d;
for (i=0; i<20; i++) {
if (JS_ToFloat64(c, &d, argv[i])) return GF_JS_EXCEPTION(c);
cmx->m[i] = FLT2FIX(d);
}
cmx->identity = 0;
}
return obj;
}
enum
{
//these 6 magics map to m[x] of the matrix, DO NOT MODIFY
CMX_MRR = 0,
CMX_MRG,
CMX_MRB,
CMX_MRA,
CMX_TR,
CMX_MGR,
CMX_MGG,
CMX_MGB,
CMX_MGA,
CMX_TG,
CMX_MBR,
CMX_MBG,
CMX_MBB,
CMX_MBA,
CMX_TB,
CMX_MAR,
CMX_MAG,
CMX_MAB,
CMX_MAA,
CMX_TA,
CMX_IDENTITY,
};
static JSValue colmx_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
if (!cmx) return GF_JS_EXCEPTION(c);
if ((magic>=CMX_MRR) && (magic<=CMX_TA)) {
return JS_NewFloat64(c, FIX2FLT(cmx->m[magic]));
}
if (magic==CMX_IDENTITY)
return JS_NewBool(c, cmx->identity);
return JS_UNDEFINED;
}
static JSValue colmx_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
if (!cmx) return GF_JS_EXCEPTION(c);
if ((magic>=CMX_MRR) && (magic<=CMX_TA)) {
Double d;
if (JS_ToFloat64(c, &d, value))
return GF_JS_EXCEPTION(c);
cmx->m[magic] = FIX2FLT(d);
cmx->identity = GF_FALSE;
return JS_UNDEFINED;
}
if (magic==CMX_IDENTITY) {
gf_cmx_init(cmx);
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
static JSValue colmx_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
if (!cmx || !argc) return GF_JS_EXCEPTION(c);
GF_ColorMatrix *with = JS_GetOpaque(argv[0], colmx_class_id);
if (!cmx) return GF_JS_EXCEPTION(c);
gf_cmx_multiply(cmx, with);
return JS_DupValue(c, obj);
}
static Bool get_color(JSContext *c, JSValueConst obj, Double *a, Double *r, Double *g, Double *b)
{
JSValue v;
int res;
if (JS_IsArray(c, obj)) {
u32 i, len;
v = JS_GetPropertyStr(c, obj, "length");
res = JS_ToInt32(c, &len, v);
JS_FreeValue(c, v);
if (res) return GF_FALSE;
if (len>4) len=4;
for (i=0; i<len; i++) {
Double d;
v = JS_GetPropertyUint32(c, obj, i);
res = JS_ToFloat64(c, &d, v);
if (res) return GF_FALSE;
JS_FreeValue(c, v);
if (!i) *r = d;
else if (i==1) *g = d;
else if (i==2) *b = d;
else *a = d;
}
return GF_TRUE;
}
v = JS_GetPropertyStr(c, obj, "r");
res = JS_ToFloat64(c, r, v);
JS_FreeValue(c, v);
if (res) return GF_FALSE;
v = JS_GetPropertyStr(c, obj, "g");
res = JS_ToFloat64(c, g, v);
JS_FreeValue(c, v);
if (res) return GF_FALSE;
v = JS_GetPropertyStr(c, obj, "b");
res = JS_ToFloat64(c, b, v);
JS_FreeValue(c, v);
if (res) return GF_FALSE;
v = JS_GetPropertyStr(c, obj, "a");
JS_ToFloat64(c, a, v);
JS_FreeValue(c, v);
if (*r<0) *r=0;
if (*g<0) *g=0;
if (*b<0) *b=0;
if (*a<0) *a=0;
return GF_TRUE;
}
static JSValue colmx_apply_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
{
GF_Color col;
JSValue nobj;
Double r=0, g=0, b=0, a=1.0;
GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
if (!cmx || !argc) return GF_JS_EXCEPTION(c);
if (JS_IsString(argv[0])) {
const char *str = JS_ToCString(c, argv[0]);
col = gf_color_parse(str);
JS_FreeCString(c, str);
if (!use_int) {
a = GF_COL_A(col);
r = GF_COL_R(col);
g = GF_COL_G(col);
b = GF_COL_B(col);
r/=255;
g/=255;
b/=255;
a/=255;
}
} else if (JS_IsObject(argv[0])) {
if (!get_color(c, argv[0], &a, &r, &g, &b))
return GF_JS_EXCEPTION(c);
if (use_int) {
r*=255;
g*=255;
b*=255;
a*=255;
if (a>255) a=255;
if (r>255) r=255;
if (g>255) g=255;
if (b>255) b=255;
col = GF_COL_ARGB(a, r, g, b);
}
} else {
return GF_JS_EXCEPTION(c);
}
if (use_int) {
GF_Color res = gf_cmx_apply(cmx, col);
nobj = JS_NewObject(c);
JS_SetPropertyStr(c, nobj, "r", JS_NewInt32(c, GF_COL_R(res)) );
JS_SetPropertyStr(c, nobj, "g", JS_NewInt32(c, GF_COL_G(res)) );
JS_SetPropertyStr(c, nobj, "b", JS_NewInt32(c, GF_COL_B(res)) );
JS_SetPropertyStr(c, nobj, "a", JS_NewInt32(c, GF_COL_A(res)) );
return nobj;
} else {
Fixed fr=FLT2FIX(r), fg=FLT2FIX(g), fb=FLT2FIX(b), fa=FLT2FIX(a);
gf_cmx_apply_fixed(cmx, &fa, &fr, &fg, &fb);
nobj = JS_NewObject(c);
JS_SetPropertyStr(c, nobj, "r", JS_NewFloat64(c, FIX2FLT(fr)) );
JS_SetPropertyStr(c, nobj, "g", JS_NewFloat64(c, FIX2FLT(fg)) );
JS_SetPropertyStr(c, nobj, "b", JS_NewFloat64(c, FIX2FLT(fb)) );
JS_SetPropertyStr(c, nobj, "a", JS_NewFloat64(c, FIX2FLT(fa)) );
return nobj;
}
return GF_JS_EXCEPTION(c);
}
static JSValue colmx_apply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return colmx_apply_color(c, obj, argc, argv, GF_TRUE);
}
static JSValue colmx_applyf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return colmx_apply_color(c, obj, argc, argv, GF_FALSE);
}
static const JSCFunctionListEntry colmx_funcs[] =
{
JS_CGETSET_MAGIC_DEF("rr", colmx_getProperty, colmx_setProperty, CMX_MRR),
JS_CGETSET_MAGIC_DEF("rg", colmx_getProperty, colmx_setProperty, CMX_MRG),
JS_CGETSET_MAGIC_DEF("rb", colmx_getProperty, colmx_setProperty, CMX_MRB),
JS_CGETSET_MAGIC_DEF("ra", colmx_getProperty, colmx_setProperty, CMX_MRA),
JS_CGETSET_MAGIC_DEF("tr", colmx_getProperty, colmx_setProperty, CMX_TR),
JS_CGETSET_MAGIC_DEF("gr", colmx_getProperty, colmx_setProperty, CMX_MGR),
JS_CGETSET_MAGIC_DEF("gg", colmx_getProperty, colmx_setProperty, CMX_MGG),
JS_CGETSET_MAGIC_DEF("gb", colmx_getProperty, colmx_setProperty, CMX_MGB),
JS_CGETSET_MAGIC_DEF("ga", colmx_getProperty, colmx_setProperty, CMX_MGA),
JS_CGETSET_MAGIC_DEF("tg", colmx_getProperty, colmx_setProperty, CMX_TG),
JS_CGETSET_MAGIC_DEF("br", colmx_getProperty, colmx_setProperty, CMX_MBR),
JS_CGETSET_MAGIC_DEF("bg", colmx_getProperty, colmx_setProperty, CMX_MBG),
JS_CGETSET_MAGIC_DEF("bb", colmx_getProperty, colmx_setProperty, CMX_MBB),
JS_CGETSET_MAGIC_DEF("ba", colmx_getProperty, colmx_setProperty, CMX_MBA),
JS_CGETSET_MAGIC_DEF("tb", colmx_getProperty, colmx_setProperty, CMX_TB),
JS_CGETSET_MAGIC_DEF("ar", colmx_getProperty, colmx_setProperty, CMX_MAR),
JS_CGETSET_MAGIC_DEF("ag", colmx_getProperty, colmx_setProperty, CMX_MAG),
JS_CGETSET_MAGIC_DEF("ab", colmx_getProperty, colmx_setProperty, CMX_MAB),
JS_CGETSET_MAGIC_DEF("aa", colmx_getProperty, colmx_setProperty, CMX_MAA),
JS_CGETSET_MAGIC_DEF("ta", colmx_getProperty, colmx_setProperty, CMX_TA),
JS_CGETSET_MAGIC_DEF("identity", colmx_getProperty, colmx_setProperty, CMX_IDENTITY),
JS_CFUNC_DEF("multiply", 0, colmx_multiply),
JS_CFUNC_DEF("apply", 0, colmx_apply),
JS_CFUNC_DEF("applyf", 0, colmx_applyf),
};
static void path_finalize(JSRuntime *rt, JSValue obj)
{
GF_Path *path = JS_GetOpaque(obj, path_class_id);
if (!path) return;
gf_path_del(path);
}
JSClassDef path_class = {
"Path",
.finalizer = path_finalize
};
static JSValue path_close_reset(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 opt)
{
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp) return GF_JS_EXCEPTION(c);
if (opt==0) {
gf_path_close(gp);
return JS_DupValue(c, obj);
}
if (opt==1) {
gf_path_reset(gp);
return JS_DupValue(c, obj);
}
if (opt==2) {
JSValue nobj = JS_NewObjectClass(c, path_class_id);
if (JS_IsException(nobj)) return nobj;
JS_SetOpaque(nobj, gf_path_clone(gp));
return nobj;
}
return GF_JS_EXCEPTION(c);
}
static JSValue path_reset(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return path_close_reset(c, obj, argc, argv, 1);
}
static JSValue path_close(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return path_close_reset(c, obj, argc, argv, 0);
}
static JSValue path_clone(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return path_close_reset(c, obj, argc, argv, 2);
}
static JSValue path_line_move(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_line)
{
Double x=0, y=0;
GF_Err e;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<2)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[1])) return GF_JS_EXCEPTION(c);
if (is_line)
e = gf_path_add_line_to(gp, FLT2FIX(x), FLT2FIX(y));
else
e = gf_path_add_move_to(gp, FLT2FIX(x), FLT2FIX(y));
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_line_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return path_line_move(c, obj, argc, argv, GF_TRUE);
}
static JSValue path_move_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return path_line_move(c, obj, argc, argv, GF_FALSE);
}
static JSValue path_cubic_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double c1_x=0, c1_y=0, c2_x=0, c2_y=0, x=0, y=0;
GF_Err e;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<6)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &c1_x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &c1_y, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &c2_x, argv[2])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &c2_y, argv[3])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x, argv[4])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[5])) return GF_JS_EXCEPTION(c);
e = gf_path_add_cubic_to(gp, FLT2FIX(c1_x), FLT2FIX(c1_y), FLT2FIX(c2_x), FLT2FIX(c2_y), FLT2FIX(x), FLT2FIX(y));
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_quadratic_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double c_x=0, c_y=0, x=0, y=0;
GF_Err e;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<4)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &c_x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &c_y, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &x, argv[2])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[3])) return GF_JS_EXCEPTION(c);
e = gf_path_add_quadratic_to(gp, FLT2FIX(c_x), FLT2FIX(c_y), FLT2FIX(x), FLT2FIX(y));
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_rect(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double ox=0, oy=0, w=0, h=0;
s32 idx=0;
GF_Err e;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<3)) return GF_JS_EXCEPTION(c);
if (JS_IsObject(argv[0])) {
JSValue v;
#define GETD(_arg, _name, _res)\
if (! JS_IsObject(_arg)) return GF_JS_EXCEPTION(c);\
v = JS_GetPropertyStr(c, _arg, _name);\
JS_ToFloat64(c, &_res, v);\
JS_FreeValue(c, v);\
GETD(argv[0], "x", ox);
GETD(argv[0], "y", oy);
#undef GETD
idx=1;
} else if (argc>=4) {
if (JS_ToFloat64(c, &ox, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &oy, argv[1])) return GF_JS_EXCEPTION(c);
idx=2;
} else {
return GF_JS_EXCEPTION(c);
}
if (JS_ToFloat64(c, &w, argv[idx])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &h, argv[idx+1])) return GF_JS_EXCEPTION(c);
if ((argc>idx+2) && JS_ToBool(c, argv[idx+2])) {
e = gf_path_add_rect_center(gp, FLT2FIX(ox), FLT2FIX(oy), FLT2FIX(w), FLT2FIX(h));
} else {
e = gf_path_add_rect(gp, FLT2FIX(ox), FLT2FIX(oy), FLT2FIX(w), FLT2FIX(h));
}
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_ellipse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double cx=0, cy=0, a_axis=0, b_axis=0;
GF_Err e;
Bool valid = GF_TRUE;
u32 idx=0;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp) return GF_JS_EXCEPTION(c);
if (argc==3) {
JSValue v;
#define GETD(_arg, _name, _res)\
if (! JS_IsObject(_arg)) return GF_JS_EXCEPTION(c);\
v = JS_GetPropertyStr(c, _arg, _name);\
if (JS_ToFloat64(c, &_res, v)) valid = GF_FALSE;\
JS_FreeValue(c, v);\
GETD(argv[0], "x", cx);
GETD(argv[0], "y", cy);
#undef GETD
idx=1;
} else if (argc==4) {
if (JS_ToFloat64(c, &cx, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &cy, argv[1])) return GF_JS_EXCEPTION(c);
idx=2;
} else {
return GF_JS_EXCEPTION(c);
}
if (!valid) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &a_axis, argv[idx])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &b_axis, argv[idx+1])) return GF_JS_EXCEPTION(c);
e = gf_path_add_ellipse(gp, FLT2FIX(cx), FLT2FIX(cy), FLT2FIX(a_axis), FLT2FIX(b_axis));
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_bezier(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
s32 i;
GF_Point2D *pts;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<3)) return GF_JS_EXCEPTION(c);
pts = gf_malloc(sizeof(GF_Point2D)* argc);
memset(pts, 0, sizeof(GF_Point2D)* argc);
for (i=0; i<argc; i++) {
JSValue v;
Double d;
if (! JS_IsObject(argv[i]))
continue;
v = JS_GetPropertyStr(c, argv[i], "x");
JS_ToFloat64(c, &d, v);
pts[i].x = FLT2FIX(d);
JS_FreeValue(c, v);
v = JS_GetPropertyStr(c, argv[i], "y");
JS_ToFloat64(c, &d, v);
pts[i].x = FLT2FIX(d);
JS_FreeValue(c, v);
}
e = gf_path_add_bezier(gp, pts, argc);
gf_free(pts);
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_arc_bifs(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double end_x=0, end_y=0, fa_x=0, fa_y=0, fb_x=0, fb_y=0;
Bool cw_flag = GF_FALSE;
GF_Err e;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<6)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &end_x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &end_y, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &fa_x, argv[2])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &fa_y, argv[3])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &fb_x, argv[4])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &fb_y, argv[5])) return GF_JS_EXCEPTION(c);
if (argc>6) cw_flag = JS_ToBool(c, argv[6]);
e = gf_path_add_svg_arc_to(gp, FLT2FIX(end_x), FLT2FIX(end_y), FLT2FIX(fa_x), FLT2FIX(fa_y), FLT2FIX(fb_x), FLT2FIX(fb_y), cw_flag);
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_arc_svg(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double end_x=0, end_y=0, r_x=0, r_y=0, x_axis_rotation=0;
Bool large_arc_flag=GF_FALSE, sweep_flag=GF_FALSE;
GF_Err e;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<4)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &end_x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &end_y, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &r_x, argv[2])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &r_y, argv[3])) return GF_JS_EXCEPTION(c);
if (argc>4) {
if (JS_ToFloat64(c, &x_axis_rotation, argv[4])) return GF_JS_EXCEPTION(c);
if (argc>5) large_arc_flag = JS_ToBool(c, argv[5]);
if (argc>6) sweep_flag = JS_ToBool(c, argv[6]);
}
e = gf_path_add_svg_arc_to(gp, FLT2FIX(end_x), FLT2FIX(end_y), FLT2FIX(r_x), FLT2FIX(r_y), FLT2FIX(x_axis_rotation), large_arc_flag, sweep_flag);
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_arc(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double radius=0, start=0, end=0;
u32 close=0;
GF_Err e;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || (argc<3)) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &radius, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &start, argv[1])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &end, argv[2])) return GF_JS_EXCEPTION(c);
if (argc>3)
if (JS_ToInt32(c, &close, argv[3])) return GF_JS_EXCEPTION(c);
e = gf_path_add_arc(gp, FLT2FIX(radius), FLT2FIX(start), FLT2FIX(end), close);
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_add_path(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
GF_Matrix2D *mx=NULL;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || !argc) return GF_JS_EXCEPTION(c);
GF_Path *subgp = JS_GetOpaque(argv[0], path_class_id);
if (!subgp) return GF_JS_EXCEPTION(c);
if (argc>1)
mx = JS_GetOpaque(argv[1], mx2d_class_id);
e = gf_path_add_subpath(gp, subgp, mx);
if (e) return GF_JS_EXCEPTION(c);
return JS_DupValue(c, obj);
}
static JSValue path_flatten(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp) return GF_JS_EXCEPTION(c);
gf_path_flatten(gp);
return JS_DupValue(c, obj);
}
static JSValue path_get_flat(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
JSValue nobj;
GF_Path *gp_flat;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp) return GF_JS_EXCEPTION(c);
gp_flat = gf_path_get_flatten(gp);
if (!gp) return JS_NULL;
nobj = JS_NewObjectClass(c, path_class_id);
if (JS_IsException(nobj)) return nobj;
JS_SetOpaque(nobj, gp_flat);
return nobj;
}
static JSValue path_point_over(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
Double x, y;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || !argc) return GF_JS_EXCEPTION(c);
if (argc==1) {
Double d;
int res;
JSValue v;
if (!JS_IsObject(argv[0])) return GF_JS_EXCEPTION(c);
#define GETIT(_arg, _name, _var) \
v = JS_GetPropertyStr(c, _arg, _name);\
res = JS_ToFloat64(c, &d, v);\
JS_FreeValue(c, v);\
if (res) return GF_JS_EXCEPTION(c);\
_var = FLT2FIX(d);\
GETIT(argv[0], "x", x)
GETIT(argv[0], "y", y)
#undef GETIT
} else if (argc==2) {
if (JS_ToFloat64(c, &x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[1])) return GF_JS_EXCEPTION(c);
} else {
return GF_JS_EXCEPTION(c);
}
return gf_path_point_over(gp, FLT2FIX(x), FLT2FIX(y)) ? JS_TRUE : JS_FALSE;
}
static JSValue path_outline(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Path *outline;
GF_PenSettings pen;
GF_DashSettings dash;
JSValue v, dashes;
Double d;
int i, res;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || !argc || !JS_IsObject(argv[0]) ) return GF_JS_EXCEPTION(c);
memset(&pen, 0, sizeof(GF_PenSettings));
#define GETIT_F(_arg, _name, _var) \
v = JS_GetPropertyStr(c, _arg, _name);\
if (!JS_IsUndefined(v)) {\
res = JS_ToFloat64(c, &d, v);\
JS_FreeValue(c, v);\
if (res) return GF_JS_EXCEPTION(c);\
_var = FLT2FIX(d);\
}\
#define GETIT_I(_arg, _name, _var) \
v = JS_GetPropertyStr(c, _arg, _name);\
if (!JS_IsUndefined(v)) {\
res = JS_ToInt32(c, &i, v);\
JS_FreeValue(c, v);\
if (res) return GF_JS_EXCEPTION(c);\
_var = i;\
}\
GETIT_F(argv[0], "width", pen.width);
GETIT_F(argv[0], "miter", pen.miterLimit);
GETIT_F(argv[0], "offset", pen.dash_offset);
GETIT_F(argv[0], "length", pen.path_length);
GETIT_I(argv[0], "cap", pen.cap);
GETIT_I(argv[0], "join", pen.join);
GETIT_I(argv[0], "align", pen.align);
GETIT_I(argv[0], "dash", pen.dash);
#undef GETTIT_F
#undef GETTIT_I
dash.num_dash = 0;
dash.dashes = NULL;
dashes = JS_GetPropertyStr(c, argv[0], "dashes");
if (JS_IsArray(c, dashes) && !JS_IsNull(v)) {
v = JS_GetPropertyStr(c, dashes, "length");
JS_ToInt32(c, &dash.num_dash, v);
JS_FreeValue(c, v);
if (dash.num_dash) {
pen.dash_set = &dash;
dash.dashes = gf_malloc(sizeof(Fixed)*dash.num_dash);
for (i=0; i<(int) dash.num_dash; i++) {
v = JS_GetPropertyUint32(c, dashes, i);
JS_ToFloat64(c, &d, v);
dash.dashes[i] = FLT2FIX(d);
JS_FreeValue(c, v);
}
if (!pen.dash)
pen.dash = GF_DASH_STYLE_CUSTOM;
}
}
JS_FreeValue(c, dashes);
outline = gf_path_get_outline(gp, pen);
if (dash.dashes) gf_free(dash.dashes);
if (!outline) return GF_JS_EXCEPTION(c);
v = JS_NewObjectClass(c, path_class_id);
JS_SetOpaque(v, outline);
return v;
}
static JSValue path_transform(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
GF_Matrix2D *mx=NULL;
GF_Path *path;
JSValue v;
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp || !argc) return GF_JS_EXCEPTION(c);
mx = JS_GetOpaque(argv[0], mx2d_class_id);
path = gf_path_new();
if (!path) return GF_JS_EXCEPTION(c);
e = gf_path_add_subpath(path, gp, mx);
if (e) {
gf_path_del(path);
return GF_JS_EXCEPTION(c);
}
v = JS_NewObjectClass(c, path_class_id);
JS_SetOpaque(v, path);
return v;
}
enum
{
PATH_EMPTY=0,
PATH_ZERO_NONZERO,
PATH_FILL_EVEN,
PATH_BOUNDS,
PATH_CONTROL_BOUNDS,
PATH_IS_RECTANGLE
};
static JSValue path_bounds_ex(JSContext *c, GF_Path *gp, Bool is_ctrl)
{
JSValue nobj;
GF_Err e;
GF_Rect rc;
if (is_ctrl)
e = gf_path_get_control_bounds(gp, &rc);
else
e = gf_path_get_bounds(gp, &rc);
if (e) return GF_JS_EXCEPTION(c);
nobj = JS_NewObject(c);
JS_SetPropertyStr(c, nobj, "x", JS_NewFloat64(c, rc.x));
JS_SetPropertyStr(c, nobj, "y", JS_NewFloat64(c, rc.y));
JS_SetPropertyStr(c, nobj, "w", JS_NewFloat64(c, rc.width));
JS_SetPropertyStr(c, nobj, "h", JS_NewFloat64(c, rc.height));
return nobj;
}
static Bool path_check_rect(GF_Path *gp)
{
u32 i;
if (gp->n_contours != 1) return GF_FALSE;
if (gp->contours[0] != 4) return GF_FALSE;
for (i=0; i<4; i++) {
if ((gp->points[i].x != gp->points[0].x) && (gp->points[i].x != gp->points[2].x)) return GF_FALSE;
if ((gp->points[i].y != gp->points[0].y) && (gp->points[i].y != gp->points[2].y)) return GF_FALSE;
}
return GF_TRUE;
}
static JSValue path_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp) return GF_JS_EXCEPTION(c);
switch (magic) {
case PATH_EMPTY: return JS_NewBool(c, gf_path_is_empty(gp));
case PATH_ZERO_NONZERO: return JS_NewBool(c, gp->flags & GF_PATH_FILL_ZERO_NONZERO);
case PATH_FILL_EVEN: return JS_NewBool(c, gp->flags & GF_PATH_FILL_EVEN);
case PATH_BOUNDS: return path_bounds_ex(c, gp, GF_FALSE);
case PATH_CONTROL_BOUNDS: return path_bounds_ex(c, gp, GF_TRUE);
case PATH_IS_RECTANGLE: return path_check_rect(gp) ? JS_TRUE : JS_FALSE;
}
return JS_UNDEFINED;
}
static JSValue path_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
GF_Path *gp = JS_GetOpaque(obj, path_class_id);
if (!gp) return GF_JS_EXCEPTION(c);
switch (magic) {
case PATH_ZERO_NONZERO:
if (JS_ToBool(c, value))
gp->flags |= GF_PATH_FILL_ZERO_NONZERO;
else
gp->flags &= ~GF_PATH_FILL_ZERO_NONZERO;
break;
case PATH_FILL_EVEN:
if (JS_ToBool(c, value))
gp->flags |= GF_PATH_FILL_EVEN;
else
gp->flags &= ~GF_PATH_FILL_EVEN;
break;
}
return JS_UNDEFINED;
}
static JSValue path_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
JSValue obj;
GF_Path *path = gf_path_new();
if (!path) return GF_JS_EXCEPTION(c);
obj = JS_NewObjectClass(c, path_class_id);
if (JS_IsException(obj)) return obj;
JS_SetOpaque(obj, path);
return obj;
}
static const JSCFunctionListEntry path_funcs[] =
{
JS_CGETSET_MAGIC_DEF("empty", path_getProperty, NULL, PATH_EMPTY),
JS_CGETSET_MAGIC_DEF("zero_fill", path_getProperty, path_setProperty, PATH_ZERO_NONZERO),
JS_CGETSET_MAGIC_DEF("even_fill", path_getProperty, path_setProperty, PATH_FILL_EVEN),
JS_CGETSET_MAGIC_DEF("bounds", path_getProperty, NULL, PATH_BOUNDS),
JS_CGETSET_MAGIC_DEF("ctrl_bounds", path_getProperty, NULL, PATH_CONTROL_BOUNDS),
JS_CGETSET_MAGIC_DEF("is_rectangle", path_getProperty, NULL, PATH_IS_RECTANGLE),
JS_CFUNC_DEF("point_over", 0, path_point_over),
JS_CFUNC_DEF("get_flatten", 0, path_get_flat),
JS_CFUNC_DEF("flatten", 0, path_flatten),
JS_CFUNC_DEF("add_path", 0, path_add_path),
JS_CFUNC_DEF("arc", 0, path_arc),
JS_CFUNC_DEF("arc_svg", 0, path_arc_svg),
JS_CFUNC_DEF("arc_bifs", 0, path_arc_bifs),
JS_CFUNC_DEF("bezier", 0, path_bezier),
JS_CFUNC_DEF("ellipse", 0, path_ellipse),
JS_CFUNC_DEF("rectangle", 0, path_rect),
JS_CFUNC_DEF("quadratic_to", 0, path_quadratic_to),
JS_CFUNC_DEF("cubic_to", 0, path_cubic_to),
JS_CFUNC_DEF("line_to", 0, path_line_to),
JS_CFUNC_DEF("move_to", 0, path_move_to),
JS_CFUNC_DEF("clone", 0, path_clone),
JS_CFUNC_DEF("reset", 0, path_reset),
JS_CFUNC_DEF("close", 0, path_close),
JS_CFUNC_DEF("outline", 0, path_outline),
JS_CFUNC_DEF("transform", 0, path_transform),
};
static void stencil_finalize(JSRuntime *rt, JSValue obj)
{
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) return;
gf_evg_stencil_delete(stencil);
}
JSClassDef stencil_class = {
"Stencil",
.finalizer = stencil_finalize
};
enum
{
STENCIL_SOLID,
STENCIL_CMX,
STENCIL_MAT,
STENCIL_GRADMOD,
STENCIL_MAT_AUTO,
};
static JSValue stencil_set_linear(JSContext *c, GF_EVGStencil *stencil, int argc, JSValueConst *argv)
{
int res;
JSValue v;
Double d;
Fixed start_x=0, start_y=0, end_x=0, end_y=0;
s32 idx=0;
if (argc<2) return GF_JS_EXCEPTION(c);
#define GETIT(_arg, _name, _var) \
v = JS_GetPropertyStr(c, _arg, _name);\
res = JS_ToFloat64(c, &d, v);\
JS_FreeValue(c, v);\
if (res) return GF_JS_EXCEPTION(c);\
_var = FLT2FIX(d);\
if (JS_IsObject(argv[0])) {
GETIT(argv[0], "x", start_x)
GETIT(argv[0], "y", start_y)
idx=1;
} else {
if (JS_ToFloat64(c, &d, argv[0])) return GF_JS_EXCEPTION(c);
start_x = FLT2FIX(d);
if (JS_ToFloat64(c, &d, argv[1])) return GF_JS_EXCEPTION(c);
start_y = FLT2FIX(d);
idx=2;
}
if (argc<=idx) {
end_x = start_x;
end_y = start_y;
start_x = 0;
start_y = 0;
} else if (JS_IsObject(argv[idx])) {
GETIT(argv[idx], "x", end_x)
GETIT(argv[idx], "y", end_y)
} else if (argc>idx+1) {
if (JS_ToFloat64(c, &d, argv[idx])) return GF_JS_EXCEPTION(c);
end_x = FLT2FIX(d);
if (JS_ToFloat64(c, &d, argv[idx+1])) return GF_JS_EXCEPTION(c);
end_y = FLT2FIX(d);
}
#undef GETIT
gf_evg_stencil_set_linear_gradient(stencil, start_x, start_y, end_x, end_y);
return JS_UNDEFINED;
}
static JSValue stencil_set_radial(JSContext *c, GF_EVGStencil *stencil, int argc, JSValueConst *argv)
{
int res;
JSValue v;
Double d;
Fixed cx=0, cy=0, fx=0, fy=0, rx=0, ry=0;
s32 idx=0;
if (argc<3) return GF_JS_EXCEPTION(c);
#define GETIT(_arg, _name, _var) \
v = JS_GetPropertyStr(c, _arg, _name);\
res = JS_ToFloat64(c, &d, v);\
JS_FreeValue(c, v);\
if (res) return GF_JS_EXCEPTION(c);\
_var = FLT2FIX(d);\
if (JS_IsObject(argv[0])) {
GETIT(argv[0], "x", cx)
GETIT(argv[0], "y", cy)
idx+=1;
} else {
if (JS_ToFloat64(c, &d, argv[0])) return GF_JS_EXCEPTION(c);
cx = FLT2FIX(d);
if (JS_ToFloat64(c, &d, argv[1])) return GF_JS_EXCEPTION(c);
cy = FLT2FIX(d);
idx+=2;
}
if (JS_IsObject(argv[idx])) {
GETIT(argv[idx], "x", fx)
GETIT(argv[idx], "y", fy)
idx+=1;
} else if (argc>idx+1) {
if (JS_ToFloat64(c, &d, argv[idx])) return GF_JS_EXCEPTION(c);
fx = FLT2FIX(d);
if (JS_ToFloat64(c, &d, argv[idx+1])) return GF_JS_EXCEPTION(c);
fy = FLT2FIX(d);
idx+=2;
}
if (JS_IsObject(argv[idx])) {
GETIT(argv[idx], "x", rx)
GETIT(argv[idx], "y", ry)
} else if (argc>idx+1) {
if (JS_ToFloat64(c, &d, argv[idx])) return GF_JS_EXCEPTION(c);
rx = FLT2FIX(d);
if (JS_ToFloat64(c, &d, argv[idx+1])) return GF_JS_EXCEPTION(c);
ry = FLT2FIX(d);
}
#undef GETIT
gf_evg_stencil_set_radial_gradient(stencil, cx, cy, fx, fy, rx, ry);
return JS_UNDEFINED;
}
static JSValue stencil_set_points(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_StencilType type;
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) return GF_JS_EXCEPTION(c);
type = gf_evg_stencil_type(stencil);
if (type==GF_STENCIL_LINEAR_GRADIENT)
return stencil_set_linear(c, stencil, argc, argv);
if (type==GF_STENCIL_RADIAL_GRADIENT)
return stencil_set_radial(c, stencil, argc, argv);
return GF_JS_EXCEPTION(c);
}
Bool get_color_from_args(JSContext *c, int argc, JSValueConst *argv, u32 idx, Double *a, Double *r, Double *g, Double *b)
{
if (argc<(s32) idx) return GF_FALSE;
if (JS_IsString(argv[idx])) {
GF_Color col;
const char *str = JS_ToCString(c, argv[idx]);
col = gf_color_parse(str);
JS_FreeCString(c, str);
*a = ((Double)GF_COL_A(col)) / 255;
*r = ((Double)GF_COL_R(col)) / 255;
*g = ((Double)GF_COL_G(col)) / 255;
*b = ((Double)GF_COL_B(col)) / 255;
} else if (JS_IsNull(argv[idx])) {
*a = *r = *g = *b = 0;
} else if (JS_IsObject(argv[idx])) {
if (!get_color(c, argv[idx], a, r, g, b)) {
return GF_FALSE;
}
} else if (argc>(s32) idx) {
if (JS_ToFloat64(c, r, argv[idx]))
return GF_FALSE;
if (argc>(s32)idx+1) {
if (JS_ToFloat64(c, g, argv[idx+1]))
return GF_FALSE;
if (argc>(s32) idx+2) {
if (JS_ToFloat64(c, b, argv[idx+2]))
return GF_FALSE;
if (argc>(s32)idx+3) {
if (JS_ToFloat64(c, a, argv[idx+3]))
return GF_FALSE;
}
}
}
}
return GF_TRUE;
}
static JSValue stencil_set_stop_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
{
Double pos=0;
GF_StencilType type;
GF_Color col;
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) return GF_JS_EXCEPTION(c);
type = gf_evg_stencil_type(stencil);
if ((type!=GF_STENCIL_LINEAR_GRADIENT) && (type!=GF_STENCIL_RADIAL_GRADIENT)) return GF_JS_EXCEPTION(c);
if (!argc) {
gf_evg_stencil_set_gradient_interpolation(stencil, NULL, NULL, 0);
return JS_UNDEFINED;
}
if (JS_ToFloat64(c, &pos, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_IsString(argv[1])) {
const char *str = JS_ToCString(c, argv[1]);
col = gf_color_parse(str);
JS_FreeCString(c, str);
} else {
Double r=0, g=0, b=0, a=1.0;
if (!get_color_from_args(c, argc, argv, 1, &a, &r, &g, &b)) {
return GF_JS_EXCEPTION(c);
}
if (!use_int) {
a *= 255;
r *= 255;
g *= 255;
b *= 255;
}
col = GF_COL_ARGB(a, r, g, b);
}
gf_evg_stencil_push_gradient_interpolation(stencil, FLT2FIX(pos), col);
return JS_UNDEFINED;
}
static JSValue stencil_set_stop(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return stencil_set_stop_ex(c, obj, argc, argv, GF_TRUE);
}
static JSValue stencil_set_stopf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return stencil_set_stop_ex(c, obj, argc, argv, GF_FALSE);
}
static JSValue stencil_set_color_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
{
Double r=0, g=0, b=0, a=1.0;
GF_StencilType type;
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) return GF_JS_EXCEPTION(c);
type = gf_evg_stencil_type(stencil);
if (type!=GF_STENCIL_SOLID) return GF_JS_EXCEPTION(c);
if (JS_IsString(argv[0])) {
GF_Color col;
const char *str = JS_ToCString(c, argv[0]);
col = gf_color_parse(str);
JS_FreeCString(c, str);
gf_evg_stencil_set_brush_color(stencil, col);
return JS_UNDEFINED;
} else if (!get_color_from_args(c, argc, argv, 0, &a, &r, &g, &b)) {
return GF_JS_EXCEPTION(c);
}
if (!use_int) {
r*=255;
g*=255;
b*=255;
a*=255;
}
gf_evg_stencil_set_brush_color(stencil, GF_COL_ARGB(a, r, g, b));
return JS_UNDEFINED;
}
static JSValue stencil_set_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return stencil_set_color_ex(c, obj, argc, argv, GF_TRUE);
}
static JSValue stencil_set_colorf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return stencil_set_color_ex(c, obj, argc, argv, GF_FALSE);
}
static JSValue stencil_get_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_StencilType type;
GF_Color col;
char szCol[11];
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) return GF_JS_EXCEPTION(c);
type = gf_evg_stencil_type(stencil);
if (type!=GF_STENCIL_SOLID) return GF_JS_EXCEPTION(c);
col = gf_evg_stencil_get_brush_color(stencil);
sprintf(szCol, "0x%02X%02X%02X%02X", GF_COL_A(col), GF_COL_R(col), GF_COL_G(col), GF_COL_B(col) );
return JS_NewString(c, szCol);
}
static JSValue stencil_set_alpha_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
{
Double a=1.0;
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) {
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil)
return GF_JS_EXCEPTION(c);
stencil = tx->stencil;
}
if (argc) {
JS_ToFloat64(c, &a, argv[0]);
}
if (a<0) a=0;
if (!use_int) {
a*=255;
}
if (a>255) a = 255;
gf_evg_stencil_set_alpha(stencil, (u8) a);
return JS_UNDEFINED;
}
static JSValue stencil_set_alpha(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return stencil_set_alpha_ex(c, obj, argc, argv, GF_TRUE);
}
static JSValue stencil_set_alphaf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return stencil_set_alpha_ex(c, obj, argc, argv, GF_FALSE);
}
static JSValue stencil_get_alphaf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) {
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil)
return GF_JS_EXCEPTION(c);
stencil = tx->stencil;
}
Double res = gf_evg_stencil_get_alpha(stencil);
return JS_NewFloat64(c, res/255.0);
}
static JSValue stencil_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) return GF_JS_EXCEPTION(c);
switch (magic) {
case STENCIL_SOLID:
if (gf_evg_stencil_type(stencil) == GF_STENCIL_SOLID) return JS_TRUE;
return JS_FALSE;
case STENCIL_MAT:
{
GF_Matrix2D mx, *mxp;
if (! gf_evg_stencil_get_matrix(stencil, &mx)) return JS_NULL;
GF_SAFEALLOC(mxp, GF_Matrix2D);
if (!mxp) return js_throw_err(c, GF_OUT_OF_MEM);
gf_mx2d_copy(*mxp, mx);
JSValue res = JS_NewObjectClass(c, mx2d_class_id);
JS_SetOpaque(res, mxp);
return res;
}
break;
case STENCIL_MAT_AUTO:
if (gf_evg_stencil_get_auto_matrix(stencil)) return JS_TRUE;
return JS_FALSE;
case STENCIL_CMX:
{
GF_ColorMatrix *cmx;
GF_SAFEALLOC(cmx, GF_ColorMatrix);
if (!cmx) return js_throw_err(c, GF_OUT_OF_MEM);
gf_evg_stencil_get_color_matrix(stencil, cmx);
obj = JS_NewObjectClass(c, colmx_class_id);
JS_SetOpaque(obj, cmx);
return obj;
}
}
return JS_UNDEFINED;
}
static JSValue stencil_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
u32 v;
GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
if (!stencil) return GF_JS_EXCEPTION(c);
switch (magic) {
case STENCIL_CMX:
if (JS_IsNull(value)) {
gf_evg_stencil_set_color_matrix(stencil, NULL);
} else {
GF_ColorMatrix *cmx = JS_GetOpaque(value, colmx_class_id);
gf_evg_stencil_set_color_matrix(stencil, cmx);
}
return JS_UNDEFINED;
case STENCIL_GRADMOD:
if (JS_ToInt32(c, &v, value)) return GF_JS_EXCEPTION(c);
gf_evg_stencil_set_gradient_mode(stencil, v);
return JS_UNDEFINED;
case STENCIL_MAT:
if (JS_IsNull(value)) {
gf_evg_stencil_set_matrix(stencil, NULL);
} else {
GF_Matrix2D *mx = JS_GetOpaque(value, mx2d_class_id);
gf_evg_stencil_set_matrix(stencil, mx);
}
return JS_UNDEFINED;
case STENCIL_MAT_AUTO:
gf_evg_stencil_set_auto_matrix(stencil, JS_ToBool(c, value) ? GF_TRUE : GF_FALSE);
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
static const JSCFunctionListEntry stencil_funcs[] =
{
JS_CGETSET_MAGIC_DEF("solid_brush", stencil_getProperty, NULL, STENCIL_SOLID),
JS_CGETSET_MAGIC_DEF("pad", NULL, stencil_setProperty, STENCIL_GRADMOD),
JS_CGETSET_MAGIC_DEF("cmx", stencil_getProperty, stencil_setProperty, STENCIL_CMX),
JS_CGETSET_MAGIC_DEF("mx", stencil_getProperty, stencil_setProperty, STENCIL_MAT),
JS_CGETSET_MAGIC_DEF("auto_mx", stencil_getProperty, stencil_setProperty, STENCIL_MAT_AUTO),
JS_CFUNC_DEF("set_color", 0, stencil_set_color),
JS_CFUNC_DEF("set_colorf", 0, stencil_set_colorf),
JS_CFUNC_DEF("set_alpha", 0, stencil_set_alpha),
JS_CFUNC_DEF("set_alphaf", 0, stencil_set_alphaf),
JS_CFUNC_DEF("get_alphaf", 0, stencil_get_alphaf),
JS_CFUNC_DEF("get_color", 0, stencil_get_color),
JS_CFUNC_DEF("set_points", 0, stencil_set_points),
JS_CFUNC_DEF("set_stop", 0, stencil_set_stop),
JS_CFUNC_DEF("set_stopf", 0, stencil_set_stopf),
};
static JSValue stencil_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv, GF_StencilType type)
{
JSValue obj;
GF_EVGStencil *stencil = gf_evg_stencil_new(type);
if (!stencil) return GF_JS_EXCEPTION(c);
obj = JS_NewObjectClass(c, stencil_class_id);
if (JS_IsException(obj)) return obj;
JS_SetOpaque(obj, stencil);
return obj;
}
static JSValue solid_brush_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return stencil_constructor(c, new_target, argc, argv, GF_STENCIL_SOLID);
}
static JSValue linear_gradient_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return stencil_constructor(c, new_target, argc, argv, GF_STENCIL_LINEAR_GRADIENT);
}
static JSValue radial_gradient_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
return stencil_constructor(c, new_target, argc, argv, GF_STENCIL_RADIAL_GRADIENT);
}
static void texture_reset(GF_JSTexture *tx)
{
if (tx->owns_data && tx->data) {
gf_free(tx->data);
}
tx->data = NULL;
tx->data_size = 0;
tx->owns_data = GF_FALSE;
}
static void texture_finalize(JSRuntime *rt, JSValue obj)
{
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx) return;
texture_reset(tx);
if (tx->stencil)
gf_evg_stencil_delete(tx->stencil);
JS_FreeValueRT(rt, tx->param_fun);
JS_FreeValueRT(rt, tx->par_obj);
#ifndef GPAC_DISABLE_3D
if (tx->named_tx) {
gf_free(tx->named_tx);
}
#endif
#ifdef GPAC_HAS_FFMPEG
if (tx->swscaler)
sws_freeContext(tx->swscaler);
#endif
gf_free(tx);
}
static void texture_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
{
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx) return;
JS_MarkValue(rt, tx->param_fun, mark_func);
JS_MarkValue(rt, tx->par_obj, mark_func);
}
JSClassDef texture_class = {
"Texture",
.finalizer = texture_finalize,
.gc_mark = texture_gc_mark
};
enum
{
TX_MAPPING = 0,
TX_FILTER,
TX_CMX,
TX_REPEAT_S,
TX_REPEAT_T,
TX_FLIP_X,
TX_FLIP_Y,
TX_MAT,
TX_MAT_AUTO,
TX_WIDTH,
TX_HEIGHT,
TX_NB_COMP,
TX_PIXFMT,
TX_DATA,
};
static JSValue texture_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil) return GF_JS_EXCEPTION(c);
switch (magic) {
case TX_REPEAT_S:
return JS_NewBool(c, (tx->flags & GF_TEXTURE_REPEAT_S));
case TX_REPEAT_T:
return JS_NewBool(c, (tx->flags & GF_TEXTURE_REPEAT_T));
case TX_FLIP_X:
return JS_NewBool(c, (tx->flags & GF_TEXTURE_FLIP_X));
case TX_FLIP_Y:
return JS_NewBool(c, (tx->flags & GF_TEXTURE_FLIP_Y));
case TX_WIDTH:
return JS_NewInt32(c, tx->width);
case TX_HEIGHT:
return JS_NewInt32(c, tx->height);
case TX_PIXFMT:
return JS_NewInt32(c, tx->pf);
case TX_NB_COMP:
return JS_NewInt32(c, tx->nb_comp);
case TX_DATA:
if (tx->owns_data)
return JS_NewArrayBuffer(c, (u8 *) tx->data, tx->data_size, NULL, NULL, 0/*1*/);
return JS_NULL;
case TX_MAT:
{
GF_Matrix2D mx, *mxp;
if (! gf_evg_stencil_get_matrix(tx->stencil, &mx)) return JS_NULL;
GF_SAFEALLOC(mxp, GF_Matrix2D);
if (!mxp) return js_throw_err(c, GF_OUT_OF_MEM);
gf_mx2d_copy(*mxp, mx);
JSValue res = JS_NewObjectClass(c, mx2d_class_id);
JS_SetOpaque(res, mxp);
return res;
}
case TX_MAT_AUTO:
if (gf_evg_stencil_get_auto_matrix(tx->stencil)) return JS_TRUE;
return JS_FALSE;
case TX_CMX:
{
GF_ColorMatrix *cmx;
GF_SAFEALLOC(cmx, GF_ColorMatrix);
if (!cmx) return js_throw_err(c, GF_OUT_OF_MEM);
gf_evg_stencil_get_color_matrix(tx->stencil, cmx);
obj = JS_NewObjectClass(c, colmx_class_id);
JS_SetOpaque(obj, cmx);
return obj;
}
}
return JS_UNDEFINED;
}
static JSValue texture_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
u32 v;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil) return GF_JS_EXCEPTION(c);
switch (magic) {
case TX_FILTER:
if (JS_ToInt32(c, &v, value)) return GF_JS_EXCEPTION(c);
gf_evg_stencil_set_filter(tx->stencil, v);
return JS_UNDEFINED;
case TX_CMX:
if (JS_IsNull(value)) {
gf_evg_stencil_set_color_matrix(tx->stencil, NULL);
} else {
GF_ColorMatrix *cmx = JS_GetOpaque(value, colmx_class_id);
gf_evg_stencil_set_color_matrix(tx->stencil, cmx);
}
return JS_UNDEFINED;
case TX_REPEAT_S:
if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_REPEAT_S;
else tx->flags &= ~GF_TEXTURE_REPEAT_S;
gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
return JS_UNDEFINED;
case TX_REPEAT_T:
if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_REPEAT_T;
else tx->flags &= ~GF_TEXTURE_REPEAT_T;
gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
return JS_UNDEFINED;
case TX_FLIP_X:
if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_FLIP_X;
else tx->flags &= ~GF_TEXTURE_FLIP_X;
gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
return JS_UNDEFINED;
case TX_FLIP_Y:
if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_FLIP_Y;
else tx->flags &= ~GF_TEXTURE_FLIP_Y;
gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
return JS_UNDEFINED;
case TX_MAT:
if (JS_IsNull(value)) {
gf_evg_stencil_set_matrix(tx->stencil, NULL);
} else {
GF_Matrix2D *mx = JS_GetOpaque(value, mx2d_class_id);
gf_evg_stencil_set_matrix(tx->stencil, mx);
}
return JS_UNDEFINED;
case TX_MAT_AUTO:
gf_evg_stencil_set_auto_matrix(tx->stencil, JS_ToBool(c, value) ? GF_TRUE : GF_FALSE);
return JS_UNDEFINED;
}
return JS_UNDEFINED;
}
#define min_f(a, b, c) (fminf(a, fminf(b, c)))
#define max_f(a, b, c) (fmaxf(a, fmaxf(b, c)))
void rgb2hsv(const u8 src_r, const u8 src_g, const u8 src_b, u8 *dst_h, u8 *dst_s, u8 *dst_v)
{
float h, s, v; // h:0-360.0, s:0.0-1.0, v:0.0-1.0
float r = src_r / 255.0f;
float g = src_g / 255.0f;
float b = src_b / 255.0f;
float max = max_f(r, g, b);
float min = min_f(r, g, b);
v = max;
if (max == 0.0f) {
s = 0;
h = 0;
} else if (max - min == 0.0f) {
s = 0;
h = 0;
} else {
s = (max - min) / max;
if (max == r) {
h = 60 * ((g - b) / (max - min)) + 0;
} else if (max == g) {
h = 60 * ((b - r) / (max - min)) + 120;
} else {
h = 60 * ((r - g) / (max - min)) + 240;
}
}
if (h < 0) h += 360.0f;
*dst_h = (u8)(h / 2); // dst_h : 0-180
*dst_s = (u8)(s * 255); // dst_s : 0-255
*dst_v = (u8)(v * 255); // dst_v : 0-255
}
void hsv2rgb(u8 src_h, u8 src_s, u8 src_v, u8 *dst_r, u8 *dst_g, u8 *dst_b)
{
float r, g, b; // 0.0-1.0
float h = src_h * 2.0f; // 0-360
float s = src_s / 255.0f; // 0.0-1.0
float v = src_v / 255.0f; // 0.0-1.0
int hi = (int)(h / 60.0f) % 6;
float f = (h / 60.0f) - hi;
float p = v * (1.0f - s);
float q = v * (1.0f - s * f);
float t = v * (1.0f - s * (1.0f - f));
switch(hi) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5:
default:
r = v; g = p; b = q; break;
}
*dst_r = (u8)(r * 255); // dst_r : 0-255
*dst_g = (u8)(g * 255); // dst_r : 0-255
*dst_b = (u8)(b * 255); // dst_r : 0-255
}
enum
{
EVG_CONV_RGB_TO_HSV=0,
EVG_CONV_HSV_TO_RGB,
EVG_CONV_YUV_TO_RGB,
EVG_CONV_RGB_TO_YUV,
};
static JSValue texture_convert(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 conv_type)
{
JSValue nobj;
GF_Err e;
u32 i, j, dst_pf, nb_comp;
GF_JSCanvas *canvas=NULL;
GF_JSTexture *tx_conv;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil) return GF_JS_EXCEPTION(c);
if (argc) {
canvas = JS_GetOpaque(argv[0], canvas_class_id);
}
if ((conv_type==EVG_CONV_YUV_TO_RGB) || (conv_type==EVG_CONV_RGB_TO_YUV)) {
if (!canvas) return js_throw_err_msg(c, GF_BAD_PARAM, "Missing canvas parameter for RBG/YUV conversion");
}
switch (tx->pf) {
case GF_PIXEL_ARGB:
case GF_PIXEL_RGBA:
case GF_PIXEL_BGRA:
case GF_PIXEL_ABGR:
case GF_PIXEL_ALPHAGREY:
case GF_PIXEL_GREYALPHA:
if (conv_type == EVG_CONV_RGB_TO_YUV ) {
dst_pf = GF_PIXEL_YUVA444_PACK;
} else {
dst_pf = GF_PIXEL_RGBA;
}
nb_comp = 4;
break;
default:
if (conv_type == EVG_CONV_RGB_TO_YUV ) {
dst_pf = GF_PIXEL_YUV444_PACK;
} else {
dst_pf = GF_PIXEL_RGB;
}
nb_comp = 3;
break;
}
GF_SAFEALLOC(tx_conv, GF_JSTexture);
if (!tx_conv)
return js_throw_err(c, GF_OUT_OF_MEM);
tx_conv->width = tx->width;
tx_conv->height = tx->height;
tx_conv->pf = dst_pf;
tx_conv->nb_comp = nb_comp;
gf_pixel_get_size_info(tx_conv->pf, tx_conv->width, tx_conv->height, &tx_conv->data_size, &tx_conv->stride, &tx_conv->stride_uv, NULL, NULL);
tx_conv->data = gf_malloc(sizeof(char)*tx_conv->data_size);
tx_conv->owns_data = GF_TRUE;
for (j=0; j<tx_conv->height; j++) {
u8 *dst = tx_conv->data + j*tx_conv->stride;
for (i=0; i<tx_conv->width; i++) {
u8 a, r, g, b;
u32 col = gf_evg_stencil_get_pixel(tx->stencil, i, j);
if (conv_type == EVG_CONV_RGB_TO_HSV ) {
a = GF_COL_A(col);
r = GF_COL_R(col);
g = GF_COL_G(col);
b = GF_COL_B(col);
rgb2hsv(r, g, b, &r, &g, &b);
} else if (conv_type == EVG_CONV_HSV_TO_RGB ) {
a = GF_COL_A(col);
r = GF_COL_R(col);
g = GF_COL_G(col);
b = GF_COL_B(col);
hsv2rgb(r, g, b, &r, &g, &b);
} else if (conv_type == EVG_CONV_RGB_TO_YUV ) {
col = gf_evg_argb_to_ayuv(canvas->surface, col);
a = GF_COL_A(col);
r = GF_COL_R(col);
g = GF_COL_G(col);
b = GF_COL_B(col);
} else {
col = gf_evg_ayuv_to_argb(canvas->surface, col);
a = GF_COL_A(col);
r = GF_COL_R(col);
g = GF_COL_G(col);
b = GF_COL_B(col);
}
dst[0] = r;
dst[1] = g;
dst[2] = b;
if (nb_comp==4) {
dst[3] = a;
dst += 4;
} else {
dst += 3;
}
}
}
tx_conv->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
if (!tx_conv->stencil) {
e = GF_OUT_OF_MEM;
} else {
e = gf_evg_stencil_set_texture(tx_conv->stencil, tx_conv->data, tx_conv->width, tx_conv->height, tx_conv->stride, tx_conv->pf);
}
if (e) {
gf_evg_stencil_delete(tx_conv->stencil);
gf_free(tx_conv->data);
gf_free(tx_conv);
return js_throw_err_msg(c, e, "Failed to convert image: %s", gf_error_to_string(e));
}
nobj = JS_NewObjectClass(c, texture_class_id);
JS_SetOpaque(nobj, tx_conv);
return nobj;
}
static JSValue texture_rgb2hsv(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return texture_convert(c, obj, argc, argv, EVG_CONV_RGB_TO_HSV);
}
static JSValue texture_hsv2rgb(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return texture_convert(c, obj, argc, argv, EVG_CONV_HSV_TO_RGB);
}
static JSValue texture_rgb2yuv(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return texture_convert(c, obj, argc, argv, EVG_CONV_RGB_TO_YUV);
}
static JSValue texture_yuv2rgb(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return texture_convert(c, obj, argc, argv, EVG_CONV_YUV_TO_RGB);
}
static JSValue texture_split(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
JSValue nobj;
u32 i, j, idx, pix_shift=0;
GF_IRect src;
GF_JSTexture *tx_split;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil || !argc) return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &idx, argv[0])) return GF_JS_EXCEPTION(c);
if (idx>=tx->nb_comp) return GF_JS_EXCEPTION(c);
src.x = src.y = 0;
src.width = tx->width;
src.height = tx->height;
if (argc>1) {
JSValue v;
int res;
if (!JS_IsObject(argv[1])) return GF_JS_EXCEPTION(c);
#define GETIT(_name, _var) \
v = JS_GetPropertyStr(c, argv[1], _name);\
res = JS_ToInt32(c, &(src._var), v);\
JS_FreeValue(c, v);\
if (res) return GF_JS_EXCEPTION(c);\
if (src._var<0) return GF_JS_EXCEPTION(c);\
GETIT("x", x)
GETIT("y", y)
GETIT("w", width)
GETIT("h", height)
#undef GETIT
}
GF_SAFEALLOC(tx_split, GF_JSTexture);
if (!tx_split)
return js_throw_err(c, GF_OUT_OF_MEM);
tx_split->width = src.width;
tx_split->height = src.height;
tx_split->pf = GF_PIXEL_GREYSCALE;
tx_split->nb_comp = 1;
tx_split->stride = tx_split->width;
tx_split->data_size = tx_split->width * tx_split->height;
tx_split->data = gf_malloc(sizeof(char) * tx_split->data_size);
tx_split->owns_data = GF_TRUE;
pix_shift = 0;
if (idx==0) {
pix_shift = 16; //R component
} else if (idx==1) {
if ((tx->pf == GF_PIXEL_ALPHAGREY) || (tx->pf == GF_PIXEL_GREYALPHA)) pix_shift = 24; //alpha
else pix_shift = 8; //green
} else if (idx==2) {
pix_shift = 0; //blue
} else if (idx==3) {
pix_shift = 24; //alpha
}
for (j=0; j<tx_split->height; j++) {
u8 *dst = tx_split->data + j*tx_split->stride;
for (i=0; i<tx_split->width; i++) {
u32 col = gf_evg_stencil_get_pixel(tx->stencil, src.x + i, src.y + j);
*dst++ = (col >> pix_shift) & 0xFF;
}
}
tx_split->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
gf_evg_stencil_set_texture(tx_split->stencil, tx_split->data, tx_split->width, tx_split->height, tx_split->stride, tx_split->pf);
nobj = JS_NewObjectClass(c, texture_class_id);
JS_SetOpaque(nobj, tx_split);
return nobj;
}
static JSValue texture_convolution(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
JSValue v, kernv, nobj;
u32 i, j, kw=0, kh=0, kl=0, hkh, hkw;
s32 *kdata;
s32 knorm=0;
GF_JSTexture *tx_conv;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil || !argc) return GF_JS_EXCEPTION(c);
if (!JS_IsObject(argv[0]))
return GF_JS_EXCEPTION(c);
v = JS_GetPropertyStr(c, argv[0], "w");
JS_ToInt32(c, &kw, v);
JS_FreeValue(c, v);
v = JS_GetPropertyStr(c, argv[0], "h");
JS_ToInt32(c, &kh, v);
JS_FreeValue(c, v);
v = JS_GetPropertyStr(c, argv[0], "norm");
if (!JS_IsUndefined(v))
JS_ToInt32(c, &knorm, v);
JS_FreeValue(c, v);
if (!kh || !kw)
return GF_JS_EXCEPTION(c);
if (!(kh%2) || !(kw%2))
return GF_JS_EXCEPTION(c);
kernv = JS_GetPropertyStr(c, argv[0], "k");
if (JS_IsUndefined(kernv))
return GF_JS_EXCEPTION(c);
v = JS_GetPropertyStr(c, kernv, "length");
JS_ToInt32(c, &kl, v);
JS_FreeValue(c, v);
if (kl < kw * kh) {
JS_FreeValue(c, kernv);
return GF_JS_EXCEPTION(c);
}
kl = kw*kh;
kdata = gf_malloc(sizeof(s32)*kl);
for (j=0; j<kh; j++) {
for (i=0; i<kw; i++) {
u32 idx = j*kw + i;
v = JS_GetPropertyUint32(c, kernv, idx);
JS_ToInt32(c, &kdata[idx] , v);
JS_FreeValue(c, v);
}
}
JS_FreeValue(c, kernv);
GF_SAFEALLOC(tx_conv, GF_JSTexture);
if (!tx_conv)
return js_throw_err(c, GF_OUT_OF_MEM);
tx_conv->width = tx->width;
tx_conv->height = tx->height;
tx_conv->pf = GF_PIXEL_RGB;
tx_conv->nb_comp = 3;
gf_pixel_get_size_info(tx_conv->pf, tx_conv->width, tx_conv->height, &tx_conv->data_size, &tx_conv->stride, &tx_conv->stride_uv, NULL, NULL);
tx_conv->data = gf_malloc(sizeof(char)*tx_conv->data_size);
tx_conv->owns_data = GF_TRUE;
hkh = kh/2;
hkw = kw/2;
for (j=0; j<tx_conv->height; j++) {
u8 *dst = tx_conv->data + j*tx_conv->stride;
for (i=0; i<tx_conv->width; i++) {
u32 k, l, nb_pix=0;
s32 kr = 0;
s32 kg = 0;
s32 kb = 0;
for (k=0; k<kh; k++) {
if (j+k < hkh) continue;
if (j+k >= tx_conv->height + hkh) continue;
for (l=0; l<kw; l++) {
s32 kv;
if (i+l < hkw) continue;
else if (i+l >= tx_conv->width + hkw) continue;
u32 col = gf_evg_stencil_get_pixel(tx->stencil, i+l-hkw, j+k-hkh);
kv = kdata[k*kw + l];
kr += kv * (s32) GF_COL_R(col);
kg += kv * (s32) GF_COL_G(col);
kb += kv * (s32) GF_COL_B(col);
nb_pix++;
}
}
if (nb_pix!=kl) {
u32 n = knorm ? knorm : 1;
if (nb_pix) n *= nb_pix;
kr = (kr * kl / n);
kg = (kg * kl / n);
kb = (kb * kl / n);
} else if (knorm) {
kr /= knorm;
kg /= knorm;
kb /= knorm;
}
#define SET_CLAMP(_d, _s)\
if (_s<0) _d = 0;\
else if (_s>255) _d = 255;\
else _d = (u8) _s;
SET_CLAMP(dst[0], kr)
SET_CLAMP(dst[1], kg)
SET_CLAMP(dst[2], kb)
dst += 3;
}
}
gf_free(kdata);
tx_conv->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
gf_evg_stencil_set_texture(tx_conv->stencil, tx_conv->data, tx_conv->width, tx_conv->height, tx_conv->stride, tx_conv->pf);
nobj = JS_NewObjectClass(c, texture_class_id);
JS_SetOpaque(nobj, tx_conv);
return nobj;
}
static JSValue texture_update(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
u32 width=0, height=0, pf=0, stride=0, stride_uv=0;
u8 *data=NULL;
u8 *p_u=NULL;
u8 *p_v=NULL;
u8 *p_a=NULL;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil || !argc) return GF_JS_EXCEPTION(c);
JS_FreeValue(c, tx->par_obj);
tx->par_obj = JS_UNDEFINED;
if (JS_IsObject(argv[0])) {
//create from canvas object
GF_JSCanvas *canvas = JS_GetOpaque(argv[0], canvas_class_id);
if (canvas) {
width = canvas->width;
height = canvas->height;
stride = canvas->stride;
stride_uv = canvas->stride_uv;
data = canvas->data;
pf = canvas->pf;
tx->par_obj = JS_DupValue(c, argv[0]);
}
//create from filter packet
else if (jsf_is_packet(c, argv[0])) {
e = jsf_get_filter_packet_planes(c, argv[0], &width, &height, &pf, &stride, &stride_uv, (const u8 **)&data, (const u8 **)&p_u, (const u8 **)&p_v, (const u8 **)&p_a);
if (e) return js_throw_err(c, e);
#ifndef GPAC_DISABLE_3D
if (tx->gl_named_tx) {
JSValue wgl_named_texture_upload(JSContext *c, JSValueConst pck_obj, void *named_tx, Bool force_resetup);
if (pf != tx->pf) {
tx->pf = pf;
tx->force_resetup = GF_TRUE;
}
JSValue res = wgl_named_texture_upload(c, argv[0], tx->gl_named_tx, tx->force_resetup);
tx->force_resetup = GF_FALSE;
if (JS_IsException(res)) return res;
}
#endif //GPAC_DISABLE_3D
} else {
return js_throw_err(c, GF_BAD_PARAM);
}
} else {
return js_throw_err(c, GF_BAD_PARAM);
}
tx->owns_data = GF_FALSE;
tx->width = width;
tx->height = height;
tx->pf = pf;
tx->stride = stride;
tx->stride_uv = stride_uv;
tx->data = data;
if (p_u || p_v) {
e = gf_evg_stencil_set_texture_planes(tx->stencil, tx->width, tx->height, tx->pf, data, tx->stride, p_u, p_v, tx->stride_uv, p_a, tx->stride);
if (e) return js_throw_err(c, e);
} else {
gf_assert(data);
e = gf_evg_stencil_set_texture(tx->stencil, tx->data, tx->width, tx->height, tx->stride, tx->pf);
if (e) return js_throw_err(c, e);
}
if (tx->pf) {
tx->nb_comp = gf_pixel_get_nb_comp(tx->pf);
gf_pixel_get_size_info(tx->pf, tx->width, tx->height, NULL, NULL, NULL, NULL, NULL);
}
return JS_UNDEFINED;
}
static JSValue texture_get_pixel_internal(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_float)
{
Bool as_array=GF_FALSE;
JSValue ret;
Double x, y;
GF_Vec4 col;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil || (argc<2) ) return GF_JS_EXCEPTION(c);
if (is_float) {
if (JS_ToFloat64(c, &x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToFloat64(c, &y, argv[1])) return GF_JS_EXCEPTION(c);
} else {
s32 _x, _y;
if (JS_ToInt32(c, &_x, argv[0])) return GF_JS_EXCEPTION(c);
if (JS_ToInt32(c, &_y, argv[1])) return GF_JS_EXCEPTION(c);
x = ((Float)_x) / tx->width;
y = ((Float)_y) / tx->height;
}
if ((argc>2) && JS_ToBool(c, argv[2]))
as_array = GF_TRUE;
col = gf_evg_stencil_get_pixel_f(tx->stencil, (Float) x, (Float) y);
if (as_array) {
ret = JS_NewArray(c);
JS_SetPropertyStr(c, ret, "length", JS_NewInt32(c, 4) );
JS_SetPropertyUint32(c, ret, 0, JS_NewFloat64(c, col.x) );
JS_SetPropertyUint32(c, ret, 1, JS_NewFloat64(c, col.y) );
JS_SetPropertyUint32(c, ret, 2, JS_NewFloat64(c, col.z) );
JS_SetPropertyUint32(c, ret, 3, JS_NewFloat64(c, col.q) );
} else {
ret = JS_NewObject(c);
JS_SetPropertyStr(c, ret, "r", JS_NewFloat64(c, col.x) );
JS_SetPropertyStr(c, ret, "g", JS_NewFloat64(c, col.y) );
JS_SetPropertyStr(c, ret, "b", JS_NewFloat64(c, col.z) );
JS_SetPropertyStr(c, ret, "a", JS_NewFloat64(c, col.q) );
}
return ret;
}
static JSValue texture_get_pixel(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return texture_get_pixel_internal(c, obj, argc, argv, GF_FALSE);
}
static JSValue texture_get_pixelf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
return texture_get_pixel_internal(c, obj, argc, argv, GF_TRUE);
}
static GF_Err texture_load_data(JSContext *c, GF_JSTexture *tx, u8 *data, u32 size)
{
#ifndef GPAC_DISABLE_AV_PARSERS
GF_Err e;
GF_BitStream *bs;
u8 *dsi=NULL;
u32 codecid, width, height, pf, dsi_len, osize;
bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
gf_img_parse(bs, &codecid, &width, &height, &dsi, &dsi_len);
gf_bs_del(bs);
e = GF_NOT_SUPPORTED;
if (codecid==GF_CODECID_PNG) {
e = gf_img_png_dec(data, size, &width, &height, &pf, NULL, &osize);
if (e ==GF_BUFFER_TOO_SMALL) {
tx->owns_data = GF_TRUE;
tx->data_size = osize;
tx->data = gf_malloc(sizeof(char) * osize);
e = gf_img_png_dec(data, size, &width, &height, &pf, tx->data, &osize);
if (e==GF_OK) {
tx->width = width;
tx->height = height;
tx->pf = pf;
}
}
}
if (codecid==GF_CODECID_JPEG) {
e = gf_img_jpeg_dec(data, size, &width, &height, &pf, NULL, &osize, 0);
if (e ==GF_BUFFER_TOO_SMALL) {
tx->owns_data = GF_TRUE;
tx->data_size = osize;
tx->data = gf_malloc(sizeof(char) * osize);
e = gf_img_jpeg_dec(data, size, &width, &height, &pf, tx->data, &osize, 0);
if (e==GF_OK) {
tx->width = width;
tx->height = height;
tx->pf = pf;
}
}
}
if (dsi) gf_free(dsi);
if (e != GF_OK) {
return e;
}
tx->nb_comp = gf_pixel_get_nb_comp(tx->pf);
gf_pixel_get_size_info(tx->pf, tx->width, tx->height, NULL, &tx->stride, NULL, NULL, NULL);
e = gf_evg_stencil_set_texture(tx->stencil, tx->data, tx->width, tx->height, tx->stride, tx->pf);
if (e != GF_OK) {
return e;
}
return GF_OK;
#else
return GF_NOT_SUPPORTED;
#endif
}
static GF_Err texture_load_file(JSContext *c, GF_JSTexture *tx, const char *fileName, Bool rel_to_script)
{
char szPath[GF_MAX_PATH];
u8 *data;
u32 size;
GF_Err e;
char *full_url = NULL;
if (rel_to_script) {
const char *par_url = jsf_get_script_filename(c);
full_url = gf_url_concatenate(par_url, fileName);
fileName = full_url;
}
if (!strncmp(fileName, "$GSHARE/", 8)) {
gf_opts_default_shared_directory(szPath);
gf_strcat(szPath, fileName + 7);
fileName = szPath;
}
if (!gf_file_exists(fileName) || (gf_file_load_data(fileName, &data, &size) != GF_OK)) {
if (full_url) gf_free(full_url);
return GF_URL_ERROR;
}
if (full_url) gf_free(full_url);
e = texture_load_data(c, tx, data, size);
gf_free(data);
return e;
}
static JSValue texture_load(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_Err e;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil || (argc<1) ) return GF_JS_EXCEPTION(c);
if (JS_IsString(argv[0])) {
JSValue ret = JS_UNDEFINED;
Bool rel_to_script = GF_FALSE;
const char *str = JS_ToCString(c, argv[0]);
if (argc>1) rel_to_script = JS_ToBool(c, argv[1]);
e = texture_load_file(c, tx, str, rel_to_script);
if (e) {
ret = js_throw_err_msg(c, e, "Failed to load texture file %s: %s", str, gf_error_to_string(e));
}
JS_FreeCString(c, str);
return ret;
}
if (JS_IsArrayBuffer(c, argv[0])) {
size_t data_size;
u8 *data = JS_GetArrayBuffer(c, &data_size, argv[0]);
if (!data) {
e = GF_BAD_PARAM;
} else {
e = texture_load_data(c, tx, data, (u32) data_size);
}
if (e) {
return js_throw_err_msg(c, e, "Failed to load texture: %s", gf_error_to_string(e));
}
return JS_UNDEFINED;
}
return GF_JS_EXCEPTION(c);
}
static JSValue texture_set_named(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil || (argc<1) ) return GF_JS_EXCEPTION(c);
#ifndef GPAC_DISABLE_3D
const char *str = JS_ToCString(c, argv[0]);
if (tx->named_tx) gf_free(tx->named_tx);
tx->named_tx = str ? gf_strdup(str) : NULL;
JS_FreeCString(c, str);
#endif
return JS_UNDEFINED;
}
static JSValue texture_set_pad_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 color = 0;
GF_Err e;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil) return GF_JS_EXCEPTION(c);
if (argc) {
Double a, r, g, b;
if (!get_color_from_args(c, argc, argv, 0, &a, &r, &g, &b))
return GF_JS_EXCEPTION(c);
a*=255;
r*=255;
g*=255;
b*=255;
color = GF_COL_ARGB(a, r, g, b);
}
e = gf_evg_stencil_set_pad_color(tx->stencil, color);
if (e) {
return js_throw_err_msg(c, e, "Failed to set texture pad color: %s", gf_error_to_string(e));
}
return JS_UNDEFINED;
}
static JSValue texture_get_pad_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
char szCol[12];
u32 color = 0;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil) return GF_JS_EXCEPTION(c);
color = gf_evg_stencil_get_pad_color(tx->stencil);
if (!color) return JS_NewString(c, "none");
sprintf(szCol, "0x%02X%02X%02X%02X", GF_COL_A(color), GF_COL_R(color), GF_COL_G(color), GF_COL_B(color) );
return JS_NewString(c, szCol);
}
u32 gf_evg_stencil_get_pixel_fast(GF_EVGStencil *st, s32 x, s32 y);
static JSValue texture_diff_score(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
u32 i, j;
Bool is_yuv=GF_FALSE;
Bool has_alpha=GF_FALSE;
Bool split_sums=GF_FALSE;
Bool do_mae = GF_FALSE, do_mse = GF_FALSE;
Double mae_r=0, mae_g=0, mae_b=0, mae_a=0;
Double mse_r=0, mse_g=0, mse_b=0, mse_a=0;
GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
if (!tx || !tx->stencil || !argc) return GF_JS_EXCEPTION(c);
GF_JSTexture *tx_with = JS_GetOpaque(argv[0], texture_class_id);
if (!tx_with || !tx_with->stencil) return GF_JS_EXCEPTION(c);
if (tx_with->width != tx->width) return GF_JS_EXCEPTION(c);
if (tx_with->height != tx->height) return GF_JS_EXCEPTION(c);
if (tx_with->pf != tx->pf) return GF_JS_EXCEPTION(c);
if (argc>1) {
int idx=1;
if (JS_IsString(argv[1])) {
const char * str = JS_ToCString(c, argv[1]);
if (strstr(str, "mae")) do_mae = GF_TRUE;
if (strstr(str, "mse")) do_mse = GF_TRUE;
idx++;
JS_FreeCString(c, str);
}
if ((argc>idx) && JS_ToBool(c, argv[idx])) {
split_sums = GF_TRUE;
}
}
if (!do_mae && !do_mse) do_mae = GF_TRUE;
if (!split_sums && gf_pixel_fmt_is_yuv(tx->pf)) {
is_yuv = GF_TRUE;
}
has_alpha = gf_pixel_fmt_is_transparent(tx->pf);
for (j=0; j<tx->height; j++) {
u64 mae_line_r=0, mae_line_g=0, mae_line_b=0, mae_line_a=0;
u64 mse_line_r=0, mse_line_g=0, mse_line_b=0, mse_line_a=0;
for (i=0; i<tx->width; i++) {
u32 pval, pval2;
if (is_yuv) {
pval = gf_evg_stencil_get_pixel_fast(tx->stencil, i, j);
pval2 = gf_evg_stencil_get_pixel_fast(tx_with->stencil, i, j);
} else {
pval = gf_evg_stencil_get_pixel_fast(tx->stencil, i, j);
pval2 = gf_evg_stencil_get_pixel_fast(tx_with->stencil, i, j);
}
s32 this_c = GF_COL_R(pval);
s32 prev_c = GF_COL_R(pval2);
if (do_mse) mse_line_r += (this_c-prev_c)*(this_c-prev_c);
if (do_mae) {
if (prev_c<this_c) mae_line_r += this_c-prev_c;
else mae_line_r += prev_c-this_c;
}
if (has_alpha) {
this_c = GF_COL_A(pval);
prev_c = GF_COL_A(pval2);
if (do_mse) mse_line_a += (this_c-prev_c)*(this_c-prev_c);
if (do_mae) {
if (prev_c<this_c) mae_line_a += this_c-prev_c;
else mae_line_a += prev_c-this_c;
}
}
if (is_yuv) continue;
this_c = GF_COL_G(pval);
prev_c = GF_COL_G(pval2);
if (do_mse) mse_line_g += (this_c-prev_c)*(this_c-prev_c);
if (do_mae) {
if (prev_c<this_c) mae_line_g += this_c-prev_c;
else mae_line_g += prev_c-this_c;
}
this_c = GF_COL_B(pval);
prev_c = GF_COL_B(pval2);
if (do_mse) mse_line_b += (this_c-prev_c)*(this_c-prev_c);
if (do_mae) {
if (prev_c<this_c) mae_line_b += this_c-prev_c;
else mae_line_b += prev_c-this_c;
}
}
mae_r += mae_line_r;
mae_g += mae_line_g;
mae_b += mae_line_b;
mae_a += mae_line_a;
mse_r += mse_line_r;
mse_g += mse_line_g;
mse_b += mse_line_b;
mse_a += mse_line_a;
}
u64 div = tx->height * tx->width * 255;
u64 div_mse = div * 255;
u32 nb_p=1;
Double mae = mae_r;
Double mse = mse_r;
mae_r /= div;
mse_r /= div_mse;
if (has_alpha) {
mae += mae_a;
mse += mse_a;
nb_p=2;
mae_a /= div;
mse_a /= div_mse;
}
if (!is_yuv) {
mae += mae_g + mae_b;
mse += mse_g + mse_b;
nb_p = 4;
mae_g /= div;
mae_b /= div;
mse_g /= div_mse;
mse_b /= div_mse;
}
#define FCLAMP(_v)\
if (_v<0) _v = 0;\
else if (_v>1.0) _v = 1.0;\
if (!split_sums) {
JSValue ar = JS_NewArray(c);
if (JS_IsException(ar)) return GF_JS_EXCEPTION(c);
JSValue v = JS_NewObject(c);
if (JS_IsException(v)) return GF_JS_EXCEPTION(c);
mae /= div * nb_p;
mse /= div_mse * nb_p;
FCLAMP(mae)
FCLAMP(mse)
JS_SetPropertyStr(c, v, "mae", JS_NewFloat64(c, mae*100));
JS_SetPropertyStr(c, v, "mse", JS_NewFloat64(c, mse*100));
JS_SetPropertyUint32(c, ar, 0, v);
return ar;
}
JSValue ar = JS_NewArray(c);
if (JS_IsException(ar)) return GF_JS_EXCEPTION(c);
FCLAMP(mae_r)
FCLAMP(mae_g)
FCLAMP(mae_b)
FCLAMP(mae_a)
FCLAMP(mse_r)
FCLAMP(mse_g)
FCLAMP(mse_b)
FCLAMP(mse_a)
JSValue v = JS_NewObject(c);
if (JS_IsException(v)) return GF_JS_EXCEPTION(c);
JS_SetPropertyStr(c, v, "mae", JS_NewFloat64(c, mae_r*100));
JS_SetPropertyStr(c, v, "mse", JS_NewFloat64(c, mse_r*100));
JS_SetPropertyUint32(c, ar, 0, v);
v = JS_NewObject(c);
if (JS_IsException(v)) return GF_JS_EXCEPTION(c);
JS_SetPropertyStr(c, v, "mae", JS_NewFloat64(c, mae_g*100));
JS_SetPropertyStr(c, v, "mse", JS_NewFloat64(c, mse_g*100));
JS_SetPropertyUint32(c, ar, 1, v);
v = JS_NewObject(c);
if (JS_IsException(v)) return GF_JS_EXCEPTION(c);
JS_SetPropertyStr(c, v, "mae", JS_NewFloat64(c, mae_b*100));
JS_SetPropertyStr(c, v, "mse", JS_NewFloat64(c, mse_b*100));
JS_SetPropertyUint32(c, ar, 2, v);
if (has_alpha) {
v = JS_NewObject(c);
if (JS_IsException(v)) return GF_JS_EXCEPTION(c);
JS_SetPropertyStr(c, v, "mae", JS_NewFloat64(c, mae_a*100));
JS_SetPropertyStr(c, v, "mse", JS_NewFloat64(c, mse_a*100));
JS_SetPropertyUint32(c, ar, 3, v);
}
return ar;
}
static const JSCFunctionListEntry texture_funcs[] =
{
JS_CGETSET_MAGIC_DEF("filtering", NULL, texture_setProperty, TX_FILTER),
JS_CGETSET_MAGIC_DEF("cmx", texture_getProperty, texture_setProperty, TX_CMX),
JS_CGETSET_MAGIC_DEF("mx", texture_getProperty, texture_setProperty, TX_MAT),
JS_CGETSET_MAGIC_DEF("auto_mx", texture_getProperty, texture_setProperty, TX_MAT_AUTO),
JS_CGETSET_MAGIC_DEF("repeat_s", texture_getProperty, texture_setProperty, TX_REPEAT_S),
JS_CGETSET_MAGIC_DEF("repeat_t", texture_getProperty, texture_setProperty, TX_REPEAT_T),
JS_CGETSET_MAGIC_DEF("flip_h", texture_getProperty, texture_setProperty, TX_FLIP_X),
JS_CGETSET_MAGIC_DEF("flip_v", texture_getProperty, texture_setProperty, TX_FLIP_Y),
JS_CGETSET_MAGIC_DEF("width", texture_getProperty, NULL, TX_WIDTH),
JS_CGETSET_MAGIC_DEF("height", texture_getProperty, NULL, TX_HEIGHT),
JS_CGETSET_MAGIC_DEF("pixfmt", texture_getProperty, NULL, TX_PIXFMT),
JS_CGETSET_MAGIC_DEF("comp", texture_getProperty, NULL, TX_NB_COMP),
JS_CGETSET_MAGIC_DEF("data", texture_getProperty, NULL, TX_DATA),
JS_CFUNC_DEF("set_alpha", 0, stencil_set_alpha),
JS_CFUNC_DEF("set_alphaf", 0, stencil_set_alphaf),
JS_CFUNC_DEF("get_alphaf", 0, stencil_get_alphaf),
JS_CFUNC_DEF("rgb2hsv", 0, texture_rgb2hsv),
JS_CFUNC_DEF("hsv2rgb", 0, texture_hsv2rgb),
JS_CFUNC_DEF("rgb2yuv", 0, texture_rgb2yuv),
JS_CFUNC_DEF("yuv2rgb", 0, texture_yuv2rgb),
JS_CFUNC_DEF("convolution", 0, texture_convolution),
JS_CFUNC_DEF("split", 0, texture_split),
JS_CFUNC_DEF("update", 0, texture_update),
JS_CFUNC_DEF("get_pixelf", 0, texture_get_pixelf),
JS_CFUNC_DEF("get_pixel", 0, texture_get_pixel),
JS_CFUNC_DEF("load", 0, texture_load),
JS_CFUNC_DEF("set_named", 0, texture_set_named),
JS_CFUNC_DEF("set_pad_color", 0, texture_set_pad_color),
JS_CFUNC_DEF("get_pad_color", 0, texture_get_pad_color),
JS_CFUNC_DEF("diff_score", 0, texture_diff_score),
};
static void evg_param_tex_callback(void *cbk, u32 x, u32 y, Float *r, Float *g, Float *b, Float *a)
{
Double compv;
JSValue ret, v, argv[2];
GF_JSTexture *tx = (GF_JSTexture *)cbk;
if (!gf_js_try_lock(tx->ctx)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONSOLE, ("[JS] Failed to lock JS context in parametric texture, cannot fetch pixel\n" ));
return;
}
argv[0] = JS_NewInt32(tx->ctx, x);
argv[1] = JS_NewInt32(tx->ctx, y);
ret = JS_Call(tx->ctx, tx->param_fun, tx->obj, 2, argv);
JS_FreeValue(tx->ctx, argv[0]);
JS_FreeValue(tx->ctx, argv[1]);
*r = *g = *b = 0.0;
*a = 1.0;
#define GETCOMP(_comp)\
v = JS_GetPropertyStr(tx->ctx, ret, #_comp);\
if (!JS_IsUndefined(v)) {\
JS_ToFloat64(tx->ctx, &compv, v);\
JS_FreeValue(tx->ctx, v);\
*_comp = (Float) compv;\
}\
GETCOMP(r)
GETCOMP(g)
GETCOMP(b)
GETCOMP(a)
#undef GETCOMP
JS_FreeValue(tx->ctx, ret);
gf_js_lock(tx->ctx, GF_FALSE);
}
static JSValue texture_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
Bool use_screen_coords=GF_FALSE;
JSValue obj;
JSValue tx_fun=JS_UNDEFINED;
u32 width=0, height=0, pf=0, stride=0, stride_uv=0;
size_t data_size=0;
u8 *data=NULL;
u8 *p_u=NULL;
u8 *p_v=NULL;
u8 *p_a=NULL;
GF_JSTexture *tx;
GF_SAFEALLOC(tx, GF_JSTexture);
if (!tx)
return js_throw_err(c, GF_OUT_OF_MEM);
tx->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
if (!tx->stencil) {
gf_free(tx);
return GF_JS_EXCEPTION(c);
}
tx->param_fun = JS_UNDEFINED;
tx->obj = JS_UNDEFINED;
tx->par_obj = JS_UNDEFINED;
if (!argc) goto done;
if (JS_IsString(argv[0])) {
GF_Err e;
Bool rel_to_script = GF_FALSE;
const char *str = JS_ToCString(c, argv[0]);
if (argc>1) rel_to_script = JS_ToBool(c, argv[1]);
e = texture_load_file(c, tx, str, rel_to_script);
JS_FreeCString(c, str);
if (e) {
if (tx->stencil) gf_evg_stencil_delete(tx->stencil);
gf_free(tx);
return js_throw_err_msg(c, e, "Failed to load texture: %s", gf_error_to_string(e));
}
goto done;
}
if (JS_IsObject(argv[0])) {
//create from canvas object
GF_JSCanvas *canvas = JS_GetOpaque(argv[0], canvas_class_id);
if (canvas) {
width = canvas->width;
height = canvas->height;
stride = canvas->stride;
stride_uv = canvas->stride_uv;
data = canvas->data;
pf = canvas->pf;
tx->par_obj = JS_DupValue(c, argv[0]);
}
//create from filter packet
else if (jsf_is_packet(c, argv[0])) {
GF_Err e = jsf_get_filter_packet_planes(c, argv[0], &width, &height, &pf, &stride, &stride_uv, (const u8 **)&data, (const u8 **)&p_u, (const u8 **)&p_v, (const u8 **)&p_a);
if (e) goto error;
if (!stride) {
gf_pixel_get_size_info(pf, width, height, NULL, &stride, &stride_uv, NULL, NULL);
}
} else {
data = JS_GetArrayBuffer(c, &data_size, argv[0]);
if (data) {
GF_Err e = texture_load_data(c, tx, data, (u32) data_size);
if (e) {
if (tx->stencil) gf_evg_stencil_delete(tx->stencil);
gf_free(tx);
return js_throw_err_msg(c, e, "Failed to load texture: %s", gf_error_to_string(e));
}
tx->par_obj = JS_DupValue(c, argv[0]);
goto done;
}
goto error;
}
}
//arraybuffer
else {
Bool is_ab = GF_FALSE;
if (argc<4) goto error;
if (JS_ToInt32(c, &width, argv[0])) goto error;
if (JS_ToInt32(c, &height, argv[1])) goto error;
if (JS_IsString(argv[2])) {
const char *str = JS_ToCString(c, argv[2]);
pf = gf_pixel_fmt_parse(str);
JS_FreeCString(c, str);
} else if (JS_ToInt32(c, &pf, argv[2])) {
goto error;
}
if (JS_IsFunction(c, argv[3])) {
tx_fun = argv[3];
}
else if (JS_IsObject(argv[3])) {
data = JS_GetArrayBuffer(c, &data_size, argv[3]);
is_ab = GF_TRUE;
}
if (!width || !height || !pf || (!data && JS_IsUndefined(tx_fun)))
goto error;
if (argc>4) {
if (JS_ToInt32(c, &stride, argv[4]))
goto error;
if (argc>5) {
if (JS_ToInt32(c, &stride_uv, argv[5]))
goto error;
}
}
if (is_ab) {
tx->par_obj = JS_DupValue(c, argv[3]);
}
}
tx->owns_data = GF_FALSE;
tx->width = width;
tx->height = height;
tx->pf = pf;
tx->stride = stride;
tx->stride_uv = stride_uv;
tx->data = data;
if (p_u || p_v) {
if (gf_evg_stencil_set_texture_planes(tx->stencil, tx->width, tx->height, tx->pf, data, tx->stride, p_u, p_v, tx->stride_uv, p_a, tx->stride) != GF_OK)
goto error;
} else if (data) {
if (gf_evg_stencil_set_texture(tx->stencil, tx->data, tx->width, tx->height, tx->stride, tx->pf) != GF_OK)
goto error;
} else {
use_screen_coords = stride ? GF_TRUE : GF_FALSE;
if (gf_evg_stencil_set_texture_parametric(tx->stencil, tx->width, tx->height, tx->pf, evg_param_tex_callback, tx, use_screen_coords) != GF_OK)
goto error;
tx->param_fun = JS_DupValue(c, tx_fun);
tx->ctx = c;
}
done:
if (tx->pf) {
tx->nb_comp = gf_pixel_get_nb_comp(tx->pf);
tx->wide = (gf_pixel_is_wide_depth(tx->pf)>8) ? 1 : 0;
gf_pixel_get_size_info(tx->pf, tx->width, tx->height, NULL, NULL, NULL, NULL, NULL);
}
obj = JS_NewObjectClass(c, texture_class_id);
if (JS_IsException(obj)) return obj;
JS_SetOpaque(obj, tx);
tx->obj = obj;
return obj;
error:
if (tx->stencil) gf_evg_stencil_delete(tx->stencil);
gf_free(tx);
return js_throw_err_msg(c, GF_BAD_PARAM, "Failed to create texture");
}
Bool js_evg_is_texture(JSContext *ctx, JSValue this_obj)
{
GF_JSTexture *tx = JS_GetOpaque(this_obj, texture_class_id);
if (!tx) return GF_FALSE;
return GF_TRUE;
}
Bool js_evg_get_texture_info(JSContext *ctx, JSValue this_obj, u32 *width, u32 *height, u32 *pixfmt, u8 **p_data, u32 *stride, u8 **p_u, u8 **p_v, u32 *stride_uv, u8 **p_a)
{
GF_JSTexture *tx = JS_GetOpaque(this_obj, texture_class_id);
if (!tx) return GF_FALSE;
if (width) *width = tx->width;
if (height) *height = tx->height;
if (pixfmt) *pixfmt = tx->pf;
if (stride) *stride = tx->stride;
if (stride_uv) *stride_uv = tx->stride_uv;
if (!tx->data) return GF_TRUE;
if (p_data) *p_data = tx->data;
if (p_u) *p_u = NULL;
if (p_v) *p_v = NULL;
if (p_a) *p_a = NULL;
return GF_TRUE;
}
#ifndef GPAC_DISABLE_3D
const char *js_evg_get_texture_named(JSContext *ctx, JSValue this_obj)
{
GF_JSTexture *tx = JS_GetOpaque(this_obj, texture_class_id);
if (!tx) return NULL;
return tx->named_tx;
}
void js_evg_set_named_texture_gl(JSContext *ctx, JSValue this_obj, void *gl_named_tx)
{
GF_JSTexture *tx = JS_GetOpaque(this_obj, texture_class_id);
if (!tx) return;
tx->gl_named_tx = gl_named_tx;
tx->force_resetup = GF_TRUE;
}
#endif
#ifndef GPAC_DISABLE_FONTS
static void text_reset(GF_JSText *txt)
{
if (txt->path) gf_path_del(txt->path);
txt->path = NULL;
while (gf_list_count(txt->spans)) {
GF_TextSpan *s = gf_list_pop_back(txt->spans);
gf_font_manager_delete_span(txt->fm, s);
}
txt->min_x = txt->min_y = txt->max_x = txt->max_y = txt->max_w = txt->max_h = 0;
}
static void text_reset_fonts(GF_JSText *txt)
{
if (txt->fontnames) {
u32 i;
for (i=0; i<txt->nb_fonts; i++) {
gf_free(txt->fontnames[i]);
}
gf_free(txt->fontnames);
txt->fontnames = NULL;
txt->nb_fonts = 0;
}
}
static void text_finalize(JSRuntime *rt, JSValue obj)
{
GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
if (!txt) return;
text_reset(txt);
text_reset_fonts(txt);
gf_list_del(txt->spans);
gf_free(txt);
}
enum{
TXT_FONT=0,
TXT_FONTSIZE,
TXT_ALIGN,
TXT_BASELINE,
TXT_HORIZ,
TXT_FLIP,
TXT_UNDERLINED,
TXT_BOLD,
TXT_ITALIC,
TXT_SMALLCAP,
TXT_STRIKEOUT,
TXT_MAX_WIDTH,
TXT_LINESPACING,
};
enum
{
TXT_BL_TOP=0,
TXT_BL_HANGING,
TXT_BL_MIDDLE,
TXT_BL_ALPHABETIC,
TXT_BL_IDEOGRAPHIC,
TXT_BL_BOTTOM,
};
enum
{
TXT_AL_START=0,
TXT_AL_END,
TXT_AL_LEFT,
TXT_AL_RIGHT,
TXT_AL_CENTER
};
static JSValue text_getProperty(JSContext *c, JSValueConst obj, int magic)
{
GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
if (!txt) return GF_JS_EXCEPTION(c);
switch (magic) {
case TXT_FONT:
{
u32 i;
JSValue res = JS_NewArray(c);
JS_SetPropertyStr(c, res, "length", JS_NewInt32(c, txt->nb_fonts) );
for (i=0; i<txt->nb_fonts; i++) {
JS_SetPropertyUint32(c, res, i, JS_NewString(c, txt->fontnames[i]) );
}
return res;
}
case TXT_BASELINE: return JS_NewInt32(c, txt->baseline);
case TXT_ALIGN: return JS_NewInt32(c, txt->align);
case TXT_FONTSIZE: return JS_NewFloat64(c, txt->font_size);
case TXT_HORIZ: return JS_NewBool(c, txt->horizontal);
case TXT_FLIP: return JS_NewBool(c, txt->flip);
case TXT_MAX_WIDTH: return JS_NewFloat64(c, txt->maxWidth);
case TXT_LINESPACING: return JS_NewFloat64(c, txt->lineSpacing);
case TXT_BOLD: return JS_NewBool(c, txt->styles & GF_FONT_WEIGHT_100);
case TXT_ITALIC: return JS_NewBool(c, txt->styles & GF_FONT_ITALIC);
case TXT_UNDERLINED: return JS_NewBool(c, txt->styles & GF_FONT_UNDERLINED);
case TXT_SMALLCAP: return JS_NewBool(c, txt->styles & GF_FONT_SMALLCAPS);
case TXT_STRIKEOUT: return JS_NewBool(c, txt->styles & GF_FONT_STRIKEOUT);
}
return JS_UNDEFINED;
}
#ifndef GPAC_DISABLE_FONTS
static void text_update_path(GF_JSText *txt, Bool for_centered)
{
Fixed cy, off_x, ascent, descent, scale_x, ls;
u32 i, nb_lines;
if ((txt->path_for_centered == for_centered) && txt->path) {
return;
}
if (txt->path) gf_path_del(txt->path);
txt->path = NULL;
if (!txt->font)
return;
txt->path_for_centered = for_centered;
cy = FLT2FIX((txt->font_size * txt->font->baseline) / txt->font->em_size);
ascent = FLT2FIX((txt->font_size*txt->font->ascent) / txt->font->em_size);
if (txt->lineSpacing)
ls = FLT2FIX((txt->lineSpacing*txt->font->line_spacing) / txt->font->em_size);
else
ls = FLT2FIX((txt->font_size*txt->font->line_spacing) / txt->font->em_size);
descent = -FLT2FIX((txt->font_size*txt->font->descent) / txt->font->em_size);
scale_x = 0;
if (txt->maxWidth && txt->max_w && (txt->max_w > txt->maxWidth)) {
scale_x = gf_divfix(FLT2FIX(txt->maxWidth), INT2FIX(txt->max_w) );
}
if (txt->baseline==TXT_BL_TOP) {
cy = ascent;
} else if (txt->baseline==TXT_BL_BOTTOM) {
cy = -descent;
} else if (txt->baseline==TXT_BL_MIDDLE) {
Fixed mid = (ascent + descent)/2;
cy = mid;
}
off_x = 0;
txt->right_to_left = GF_FALSE;
txt->path = gf_path_new();
nb_lines = gf_list_count(txt->spans);
for (i=0; i<nb_lines; i++) {
Fixed cx=0;
u32 flags;
GF_Path *path;
GF_Matrix2D mx;
GF_TextSpan *span = gf_list_get(txt->spans, i);
if (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT)
txt->right_to_left = GF_TRUE;
if ((txt->align == TXT_AL_LEFT)
|| ((txt->align == TXT_AL_START) && !(span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
|| ((txt->align == TXT_AL_END) && (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
) {
cx = 0;
}
else if ((txt->align == TXT_AL_RIGHT)
|| ((txt->align == TXT_AL_END) && !(span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
|| ((txt->align == TXT_AL_START) && (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
) {
cx = txt->max_w - span->bounds.width;
} else if (txt->align == TXT_AL_CENTER) {
cx = (txt->max_w - span->bounds.width) / 2;
}
gf_mx2d_init(mx);
gf_mx2d_add_translation(&mx, off_x+cx, cy);
if (scale_x)
gf_mx2d_add_scale(&mx, scale_x, FIX_ONE);
flags = span->flags;
if (txt->path_for_centered) {
if (txt->flip)
span->flags |= GF_TEXT_SPAN_FLIP;
} else {
if (!txt->flip)
span->flags |= GF_TEXT_SPAN_FLIP;
}
path = gf_font_span_create_path(span);
gf_path_add_subpath(txt->path, path, &mx);
gf_path_del(path);
span->flags = flags;
if (!txt->horizontal) {
off_x += ls;
} else {
if (txt->path_for_centered)
cy -= ls;
else
cy += ls;
}
}
}
static void text_set_path(GF_JSCanvas *canvas, GF_JSText *txt)
{
text_update_path(txt, canvas->center_coords);
gf_evg_surface_set_path(canvas->surface, txt->path);
gf_evg_surface_force_aa(canvas->surface);
}
#endif
static void text_set_text_from_value(GF_JSText *txt, GF_Font *font, JSContext *c, JSValueConst value)
{
const char *str = JS_ToCString(c, value);
char *start = (char *) str;
while (start) {
GF_TextSpan *span;
char *nline = strchr(start, '\n');
if (nline) nline[0] = 0;
span = gf_font_manager_create_span(txt->fm, font, (char *) start, FLT2FIX(txt->font_size), GF_FALSE, GF_FALSE, GF_FALSE, NULL, GF_FALSE, 0, NULL);
if (span) {
if (txt->horizontal)
span->flags |= GF_TEXT_SPAN_HORIZONTAL;
gf_list_add(txt->spans, span);
}
if (!nline) break;
nline[0] = '\n';
start = nline + 1;
}
JS_FreeCString(c, str);
}
static JSValue text_get_path(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
JSValue nobj;
Bool is_center = GF_TRUE;
GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
if (!txt) return GF_JS_EXCEPTION(c);
if (argc) is_center = JS_ToBool(c, argv[0]);
text_update_path(txt, is_center);
if (!txt->path) return JS_NULL;
nobj = JS_NewObjectClass(c, path_class_id);
JS_SetOpaque(nobj, gf_path_clone(txt->path));
return nobj;
}
static JSValue text_set_text(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
s32 i, j, nb_lines;
GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
if (!txt) return GF_JS_EXCEPTION(c);
text_reset(txt);
if (!argc) return JS_UNDEFINED;
txt->font = gf_font_manager_set_font(txt->fm, txt->fontnames, txt->nb_fonts, txt->styles);
if (!txt->font)
return js_throw_err_msg(c, GF_NOT_FOUND, "Fonts not found (first was %s) and no default font available!\n"
"Check your GPAC configuration, or use `-rescan-fonts` to refresh font directory.\n", txt->fontnames ? txt->fontnames[0] : "none");
for (i=0; i<argc; i++) {
if (JS_IsArray(c, argv[i])) {
JSValue v = JS_GetPropertyStr(c, argv[i], "length");
JS_ToInt32(c, &nb_lines, v);
JS_FreeValue(c, v);
for (j=0; j<nb_lines; j++) {
v = JS_GetPropertyUint32(c, argv[i], j);
text_set_text_from_value(txt, txt->font, c, v);
JS_FreeValue(c, v);
}
} else {
text_set_text_from_value(txt, txt->font, c, argv[i]);
}
}
nb_lines = gf_list_count(txt->spans);
for (i=0; i<nb_lines; i++) {
GF_TextSpan *span = gf_list_get(txt->spans, i);
gf_font_manager_refresh_span_bounds(span);
span->bounds.y += FLT2FIX( i*txt->lineSpacing );
if (!txt->max_h && !txt->max_w) {
txt->max_w = span->bounds.width;
txt->max_h = span->bounds.height;
txt->min_x = span->bounds.x;
txt->min_y = span->bounds.y;
txt->max_x = txt->min_x + span->bounds.width;
txt->max_y = txt->min_y + span->bounds.height;
} else {
if (txt->min_x > span->bounds.x)
txt->min_x = span->bounds.x;
if (txt->min_y > span->bounds.y)
txt->min_y = span->bounds.y;
if (txt->max_w < span->bounds.width)
txt->max_w = span->bounds.width;
if (txt->max_h < span->bounds.height)
txt->max_h = span->bounds.height;
if (txt->max_x < span->bounds.x + span->bounds.width)
txt->max_x = span->bounds.x + span->bounds.width;
if (txt->max_y < span->bounds.y + span->bounds.height)
txt->max_y = span->bounds.y + span->bounds.height;
}
}
return JS_UNDEFINED;
}
static JSValue text_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
{
const char *str;
GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
if (!txt) return GF_JS_EXCEPTION(c);
switch (magic) {
case TXT_FONT:
{
text_reset_fonts(txt);
if (JS_IsArray(c, value)) {
u32 i, nb_fonts;
JSValue res = JS_GetPropertyStr(c, value, "length");
if (JS_ToInt32(c, &nb_fonts, res)) {
JS_FreeValue(c, res);
return GF_JS_EXCEPTION(c);
}
JS_FreeValue(c, res);
txt->nb_fonts = nb_fonts;
txt->fontnames = gf_malloc(sizeof(char *) * nb_fonts);
for (i=0; i<nb_fonts; i++) {
res = JS_GetPropertyUint32(c, value, i);
str = JS_ToCString(c, res);
txt->fontnames[i] = gf_strdup(str ? str : "SANS");
JS_FreeCString(c, str);
JS_FreeValue(c, res);
}
} else {
txt->nb_fonts = 1;
txt->fontnames = gf_malloc(sizeof(char *));
str = JS_ToCString(c, value);
txt->fontnames[0] = gf_strdup(str ? str : "SANS");
JS_FreeCString(c, str);
}
}
break;
case TXT_BASELINE:
if (JS_ToInt32(c, &txt->baseline, value)) return GF_JS_EXCEPTION(c);
return JS_UNDEFINED;
case TXT_ALIGN:
if (JS_ToInt32(c, &txt->align, value)) return GF_JS_EXCEPTION(c);
return JS_UNDEFINED;
case TXT_FONTSIZE:
if (JS_ToFloat64(c, &txt->font_size, value)) return GF_JS_EXCEPTION(c);
break;
case TXT_HORIZ:
txt->horizontal = JS_ToBool(c, value);
break;
case TXT_FLIP:
txt->flip = JS_ToBool(c, value);
break;
#define TOG_FLAG(_val) \
if (JS_ToBool(c, value)) \
txt->styles |= _val;\
else\
txt->styles &= ~_val;\
case TXT_UNDERLINED:
TOG_FLAG(GF_FONT_UNDERLINED);
break;
case TXT_ITALIC:
TOG_FLAG(GF_FONT_ITALIC);
break;
case TXT_BOLD:
TOG_FLAG(GF_FONT_WEIGHT_100);
break;
case TXT_STRIKEOUT:
TOG_FLAG(GF_FONT_STRIKEOUT);
break;
case TXT_SMALLCAP:
TOG_FLAG(GF_FONT_SMALLCAPS);
break;
#undef TOG_FLAG
case TXT_MAX_WIDTH:
JS_ToFloat64(c, &txt->maxWidth, value);
break;
case TXT_LINESPACING:
JS_ToFloat64(c, &txt->lineSpacing, value);
break;
}
return JS_UNDEFINED;
}
static JSValue text_measure(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
{
JSValue res;
GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
if (!txt) return GF_JS_EXCEPTION(c);
res = JS_NewObject(c);
if (txt->horizontal) {
JS_SetPropertyStr(c, res, "width", JS_NewFloat64(c, txt->max_w) );
JS_SetPropertyStr(c, res, "height", JS_NewFloat64(c, txt->max_y-txt->min_y) );
} else {
GF_Rect rc;
if (txt->path) gf_path_del(txt->path);
txt->path = NULL;
text_update_path(txt, GF_TRUE);
gf_path_get_bounds(txt->path, &rc);
if (txt->path) gf_path_del(txt->path);
txt->path = NULL;
JS_SetPropertyStr(c, res, "width", JS_NewFloat64(c, rc.width) );
JS_SetPropertyStr(c, res, "height", JS_NewFloat64(c, rc.height) );
}
JS_SetPropertyStr(c, res, "right_to_left", JS_NewBool(c, txt->right_to_left) );
if (txt->font) {
JS_SetPropertyStr(c, res, "em_size", JS_NewInt32(c, txt->font->em_size) );
JS_SetPropertyStr(c, res, "ascent", JS_NewInt32(c, txt->font->ascent) );
JS_SetPropertyStr(c, res, "descent", JS_NewInt32(c, txt->font->descent) );
JS_SetPropertyStr(c, res, "line_spacing", JS_NewInt32(c, txt->font->line_spacing) );
JS_SetPropertyStr(c, res, "underlined", JS_NewInt32(c, txt->font->underline) );
JS_SetPropertyStr(c, res, "baseline", JS_NewInt32(c, txt->font->baseline) );
JS_SetPropertyStr(c, res, "max_advance_h", JS_NewInt32(c, txt->font->max_advance_h) );
JS_SetPropertyStr(c, res, "max_advance_v", JS_NewInt32(c, txt->font->max_advance_v) );
}
return res;
}
static const JSCFunctionListEntry text_funcs[] =
{
JS_CGETSET_MAGIC_DEF("font", text_getProperty, text_setProperty, TXT_FONT),
JS_CGETSET_MAGIC_DEF("fontsize", text_getProperty, text_setProperty, TXT_FONTSIZE),
JS_CGETSET_MAGIC_DEF("align", text_getProperty, text_setProperty, TXT_ALIGN),
JS_CGETSET_MAGIC_DEF("baseline", text_getProperty, text_setProperty, TXT_BASELINE),
JS_CGETSET_MAGIC_DEF("horizontal", text_getProperty, text_setProperty, TXT_HORIZ),
JS_CGETSET_MAGIC_DEF("flip", text_getProperty, text_setProperty, TXT_FLIP),
JS_CGETSET_MAGIC_DEF("underline", text_getProperty, text_setProperty, TXT_UNDERLINED),
JS_CGETSET_MAGIC_DEF("bold", text_getProperty, text_setProperty, TXT_BOLD),
JS_CGETSET_MAGIC_DEF("italic", text_getProperty, text_setProperty, TXT_ITALIC),
JS_CGETSET_MAGIC_DEF("maxWidth", text_getProperty, text_setProperty, TXT_MAX_WIDTH),
JS_CGETSET_MAGIC_DEF("lineSpacing", text_getProperty, text_setProperty, TXT_LINESPACING),
JS_CFUNC_DEF("set_text", 0, text_set_text),
JS_CFUNC_DEF("measure", 0, text_measure),
JS_CFUNC_DEF("get_path", 0, text_get_path),
};
JSClassDef text_class = {
"Text",
.finalizer = text_finalize
};
static JSValue text_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
{
JSValue obj;
GF_JSText *txt;
GF_SAFEALLOC(txt, GF_JSText);
if (!txt)
return js_throw_err(c, GF_OUT_OF_MEM);
txt->fm = jsf_get_font_manager(c);
if (!txt->fm) {
gf_free(txt);
return js_throw_err_msg(c, GF_IO_ERR, "Failed to load font manager\n");
}
txt->spans = gf_list_new();
if (!txt->spans) {
gf_free(txt);
return GF_JS_EXCEPTION(c);
}
txt->font_size = 12.0;
txt->horizontal = GF_TRUE;
txt->align = TXT_AL_START;
txt->baseline = TXT_BL_ALPHABETIC;
obj = JS_NewObjectClass(c, text_class_id);
if (JS_IsException(obj)) return obj;
JS_SetOpaque(obj, txt);
return obj;
}
#endif // GPAC_DISABLE_FONTS
enum
{
MX_PROP_IDENTITY=0,
MX_PROP_3D,
MX_PROP_YAW,
MX_PROP_PITCH,
MX_PROP_ROLL,
MX_PROP_TRANLATE,
MX_PROP_SCALE,
MX_PROP_ROTATE,
MX_PROP_SHEAR,
MX_PROP_M,
};
static void mx_finalize(JSRuntime *rt, JSValue obj)
{
GF_Matrix *mx = JS_GetOpaque(obj, matrix_class_id);
if (mx) gf_free(mx);
}
JSClassDef matrix_class = {
.class_name = "Matrix",
.finalizer = mx_finalize
};
#define MAKEVEC(_v) \
res = JS_NewObject(ctx);\
JS_SetPropertyStr(ctx, res, "x", JS_NewFloat64(ctx, FIX2FLT(_v.x) ));\
JS_SetPropertyStr(ctx, res, "y", JS_NewFloat64(ctx, FIX2FLT(_v.y) ));\
JS_SetPropertyStr(ctx, res, "z", JS_NewFloat64(ctx, FIX2FLT(_v.z) ));\
return res;\
#define MAKEVEC4(_v) \
res = JS_NewObject(ctx);\
JS_SetPropertyStr(ctx, res, "x", JS_NewFloat64(ctx, FIX2FLT(_v.x) ));\
JS_SetPropertyStr(ctx, res, "y", JS_NewFloat64(ctx, FIX2FLT(_v.y) ));\
JS_SetPropertyStr(ctx, res, "z", JS_NewFloat64(ctx, FIX2FLT(_v.z) ));\
JS_SetPropertyStr(ctx, res, "w", JS_NewFloat64(ctx, FIX2FLT(_v.q) ));\
return res;\
#define MAKERECT(_v) \
res = JS_NewObject(ctx);\
JS_SetPropertyStr(ctx, res, "x", JS_NewFloat64(ctx, FIX2FLT(_v.x) ));\
JS_SetPropertyStr(ctx, res, "y", JS_NewFloat64(ctx, FIX2FLT(_v.y) ));\
JS_SetPropertyStr(ctx, res, "width", JS_NewFloat64(ctx, FIX2FLT(_v.width) ));\
JS_SetPropertyStr(ctx, res, "height", JS_NewFloat64(ctx, FIX2FLT(_v.height) ));\
return res;\
static JSValue mx_getProperty(JSContext *ctx, JSValueConst this_val, int magic)
{
JSValue res;
Fixed yaw, pitch, roll;
GF_Vec tr, sc, sh;
GF_Vec4 ro;
u32 i;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx) return GF_JS_EXCEPTION(ctx);
switch (magic) {
case MX_PROP_IDENTITY:
return JS_NewBool(ctx, gf_mx_is_identity(*mx));
case MX_PROP_3D:
return JS_TRUE;
case MX_PROP_YAW:
gf_mx_get_yaw_pitch_roll(mx, &yaw, &pitch, &roll);
return JS_NewFloat64(ctx, FIX2FLT(yaw));
case MX_PROP_PITCH:
gf_mx_get_yaw_pitch_roll(mx, &yaw, &pitch, &roll);
return JS_NewFloat64(ctx, FIX2FLT(pitch));
case MX_PROP_ROLL:
gf_mx_get_yaw_pitch_roll(mx, &yaw, &pitch, &roll);
return JS_NewFloat64(ctx, FIX2FLT(roll));
case MX_PROP_TRANLATE:
gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
MAKEVEC(tr)
case MX_PROP_SCALE:
gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
MAKEVEC(sc)
case MX_PROP_ROTATE:
gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
MAKEVEC4(ro)
case MX_PROP_SHEAR:
gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
MAKEVEC(sh)
case MX_PROP_M:
res = JS_NewArray(ctx);
for (i=0; i<16; i++)
JS_SetPropertyUint32(ctx, res, i, JS_NewFloat64(ctx, mx->m[i]));
return res;
}
return JS_UNDEFINED;
}
static JSValue mx_setProperty(JSContext *ctx, JSValueConst this_val, JSValueConst value, int magic)
{
JSValue v;
u32 i, len;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx) return GF_JS_EXCEPTION(ctx);
switch (magic) {
case MX_PROP_IDENTITY:
gf_mx_init(*mx);
break;
case MX_PROP_M:
if (!JS_IsArray(ctx, value)) return GF_JS_EXCEPTION(ctx);
v = JS_GetPropertyStr(ctx, value, "length");
len=0;
JS_ToInt32(ctx, &len, v);
JS_FreeValue(ctx, v);
if (len != 16) return GF_JS_EXCEPTION(ctx);
for (i=0; i<len; i++) {
Double _d;
v = JS_GetPropertyUint32(ctx, value, i);
JS_ToFloat64(ctx, &_d, v);
JS_FreeValue(ctx, v);
mx->m[i] = FLT2FIX(_d);
}
break;
}
return JS_UNDEFINED;
}
static JSValue mx_copy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Matrix *mx2;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx)
return GF_JS_EXCEPTION(ctx);
if (argc) {
mx2 = JS_GetOpaque(argv[0], matrix_class_id);
if (!mx2)
return GF_JS_EXCEPTION(ctx);
gf_mx_copy(*mx, *mx2);
return JS_DupValue(ctx, this_val);
}
GF_SAFEALLOC(mx2, GF_Matrix);
if (!mx2) return GF_JS_EXCEPTION(ctx);
JSValue res = JS_NewObjectClass(ctx, matrix_class_id);
JS_SetOpaque(res, mx2);
gf_mx_copy(*mx2, *mx);
return res;
}
static JSValue mx_equal(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(ctx);
GF_Matrix *mx2 = JS_GetOpaque(argv[0], matrix_class_id);
if (!mx2) return GF_JS_EXCEPTION(ctx);
if (gf_mx_equal(mx, mx2)) return JS_TRUE;
return JS_FALSE;
}
#define WGL_GET_VEC3(_x, _y, _z, _arg)\
{\
JSValue v;\
v = JS_GetPropertyStr(ctx, _arg, "x");\
EVG_GET_FLOAT(_x, v);\
JS_FreeValue(ctx, v);\
v = JS_GetPropertyStr(ctx, _arg, "y");\
EVG_GET_FLOAT(_y, v);\
JS_FreeValue(ctx, v);\
v = JS_GetPropertyStr(ctx, _arg, "z");\
EVG_GET_FLOAT(_z, v);\
JS_FreeValue(ctx, v);\
}
#define WGL_GET_VEC3F(_v, _arg)\
{\
JSValue v;\
Double _f;\
v = JS_GetPropertyStr(ctx, _arg, "x");\
EVG_GET_FLOAT(_f, v);\
_v.x = FLT2FIX(_f);\
JS_FreeValue(ctx, v);\
v = JS_GetPropertyStr(ctx, _arg, "y");\
EVG_GET_FLOAT(_f, v);\
_v.y = FLT2FIX(_f);\
JS_FreeValue(ctx, v);\
v = JS_GetPropertyStr(ctx, _arg, "z");\
EVG_GET_FLOAT(_f, v);\
_v.z = FLT2FIX(_f);\
JS_FreeValue(ctx, v);\
}
#define WGL_GET_VEC4(_x, _y, _z, _q, _arg)\
{\
JSValue v;\
v = JS_GetPropertyStr(ctx, _arg, "x");\
EVG_GET_FLOAT(_x, v);\
JS_FreeValue(ctx, v);\
v = JS_GetPropertyStr(ctx, _arg, "y");\
EVG_GET_FLOAT(_y, v);\
JS_FreeValue(ctx, v);\
v = JS_GetPropertyStr(ctx, _arg, "z");\
EVG_GET_FLOAT(_z, v);\
JS_FreeValue(ctx, v);\
v = JS_GetPropertyStr(ctx, _arg, "w");\
if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, _arg, "q");\
if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, _arg, "angle");\
EVG_GET_FLOAT(_q, v);\
JS_FreeValue(ctx, v);\
}
static JSValue mx_translate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
Double vx, vy, vz;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(ctx);
if (!JS_IsObject(argv[0])) {
if (argc<2) return GF_JS_EXCEPTION(ctx);
EVG_GET_FLOAT(vx, argv[0])
EVG_GET_FLOAT(vy, argv[1])
if (argc==3) {
EVG_GET_FLOAT(vz, argv[2])
} else {
vz = 0;
}
} else {
WGL_GET_VEC3(vx, vy, vz, argv[0])
}
gf_mx_add_translation(mx, FLT2FIX(vx), FLT2FIX(vy), FLT2FIX(vz));
return JS_DupValue(ctx, this_val);
}
static JSValue mx_scale(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
Double vx, vy, vz;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(ctx);
if (!JS_IsObject(argv[0])) {
if (argc<3) return GF_JS_EXCEPTION(ctx);
EVG_GET_FLOAT(vx, argv[0])
EVG_GET_FLOAT(vy, argv[1])
EVG_GET_FLOAT(vz, argv[2])
} else {
WGL_GET_VEC3(vx, vy, vz, argv[0])
}
gf_mx_add_scale(mx, FLT2FIX(vx), FLT2FIX(vy), FLT2FIX(vz));
return JS_DupValue(ctx, this_val);
}
static JSValue mx_rotate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
Double vx, vy, vz, angle;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(ctx);
if (!JS_IsObject(argv[0])) {
if (argc<4) return GF_JS_EXCEPTION(ctx);
EVG_GET_FLOAT(vx, argv[0])
EVG_GET_FLOAT(vy, argv[1])
EVG_GET_FLOAT(vz, argv[2])
EVG_GET_FLOAT(angle, argv[3])
} else {
WGL_GET_VEC4(vx, vy, vz, angle, argv[0])
}
gf_mx_add_rotation(mx, FLT2FIX(angle), FLT2FIX(vx), FLT2FIX(vy), FLT2FIX(vz));
return JS_DupValue(ctx, this_val);
}
static JSValue mx_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Matrix *_mx2 = NULL;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || !argc)
return GF_JS_EXCEPTION(ctx);
GF_Matrix *mx2 = JS_GetOpaque(argv[0], matrix_class_id);
if (!mx2) {
GF_Matrix2D *mx2_2D = JS_GetOpaque(argv[0], mx2d_class_id);
if (!mx2_2D)
return GF_JS_EXCEPTION(ctx);
GF_SAFEALLOC(_mx2, GF_Matrix);
if (!_mx2)
return GF_JS_EXCEPTION(ctx);
gf_mx_from_mx2d(_mx2, mx2_2D);
mx2 = _mx2;
}
if ((argc>1) && JS_ToBool(ctx, argv[1])) {
gf_mx_add_matrix_4x4(mx, mx2);
} else {
gf_mx_add_matrix(mx, mx2);
}
if (_mx2) gf_free(_mx2);
return JS_DupValue(ctx, this_val);
}
static JSValue mx_inverse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx) return GF_JS_EXCEPTION(ctx);
if (argc && JS_ToBool(ctx, argv[0])) {
gf_mx_inverse_4x4(mx);
} else {
gf_mx_inverse(mx);
}
return JS_DupValue(ctx, this_val);
}
static JSValue mx_transpose(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx) return GF_JS_EXCEPTION(ctx);
gf_mx_transpose(mx);
return JS_DupValue(ctx, this_val);
}
static JSValue mx_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
Fixed width, height, x, y;
GF_Vec pt;
GF_Vec4 pt4;
JSValue v, res;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || !argc) return GF_JS_EXCEPTION(ctx);
if (!JS_IsObject(argv[0])) return GF_JS_EXCEPTION(ctx);
/*try rect*/
v = JS_GetPropertyStr(ctx, argv[0], "width");
if (JS_IsUndefined(v))
v = JS_GetPropertyStr(ctx, argv[0], "w");
if (!JS_IsUndefined(v)) {
GF_Rect rc;
EVG_GET_FLOAT(width, v);
JS_FreeValue(ctx, v);
v = JS_GetPropertyStr(ctx, argv[0], "height");
if (JS_IsUndefined(v))
v = JS_GetPropertyStr(ctx, argv[0], "h");
EVG_GET_FLOAT(height, v);
JS_FreeValue(ctx, v);
v = JS_GetPropertyStr(ctx, argv[0], "x");
EVG_GET_FLOAT(x, v);
JS_FreeValue(ctx, v);
v = JS_GetPropertyStr(ctx, argv[0], "y");
EVG_GET_FLOAT(y, v);
JS_FreeValue(ctx, v);
rc.x = FLT2FIX(x);
rc.y = FLT2FIX(y);
rc.width = FLT2FIX(width);
rc.height = FLT2FIX(height);
gf_mx_apply_rect(mx, &rc);
MAKERECT(rc)
}
JS_FreeValue(ctx, v);
v = JS_GetPropertyStr(ctx, argv[0], "q");
if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, argv[0], "w");
if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, argv[0], "angle");
if (!JS_IsUndefined(v)) {
Double _v;
if (JS_ToFloat64(ctx, &_v, v)) return GF_JS_EXCEPTION(ctx);
pt4.q = FLT2FIX(_v);
WGL_GET_VEC3F(pt4, argv[0])
gf_mx_apply_vec_4x4(mx, &pt4);
MAKEVEC4(pt4)
}
JS_FreeValue(ctx, v);
/*try bbox ?*/
/*try vec*/
WGL_GET_VEC3F(pt, argv[0])
gf_mx_apply_vec(mx, &pt);
MAKEVEC(pt)
/*
void gf_mx_apply_vec(GF_Matrix *mx, GF_Vec *pt);
void gf_mx_apply_rect(GF_Matrix *_this, GF_Rect *rc);
void gf_mx_apply_bbox(GF_Matrix *mx, GF_BBox *b);
void gf_mx_apply_bbox_sphere(GF_Matrix *mx, GF_BBox *box);
void gf_mx_apply_vec_4x4(GF_Matrix *mx, GF_Vec4 *vec);
*/
return JS_UNDEFINED;
}
static JSValue mx_ortho(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
Double left, right, bottom, top, z_near, z_far;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || (argc<6)) return GF_JS_EXCEPTION(ctx);
EVG_GET_FLOAT(left, argv[0])
EVG_GET_FLOAT(right, argv[1])
EVG_GET_FLOAT(bottom, argv[2])
EVG_GET_FLOAT(top, argv[3])
EVG_GET_FLOAT(z_near, argv[4])
EVG_GET_FLOAT(z_far, argv[5])
if ((argc>6) && JS_ToBool(ctx, argv[6])) {
gf_mx_ortho_reverse_z(mx, FLT2FIX(left), FLT2FIX(right), FLT2FIX(bottom), FLT2FIX(top), FLT2FIX(z_near), FLT2FIX(z_far));
} else {
gf_mx_ortho(mx, FLT2FIX(left), FLT2FIX(right), FLT2FIX(bottom), FLT2FIX(top), FLT2FIX(z_near), FLT2FIX(z_far));
}
return JS_DupValue(ctx, this_val);
}
static JSValue mx_perspective(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
Double fov, ar, z_near, z_far;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || (argc<4)) return GF_JS_EXCEPTION(ctx);
EVG_GET_FLOAT(fov, argv[0])
EVG_GET_FLOAT(ar, argv[1])
EVG_GET_FLOAT(z_near, argv[2])
EVG_GET_FLOAT(z_far, argv[3])
if ((argc>4) && JS_ToBool(ctx, argv[4])) {
gf_mx_perspective_reverse_z(mx, FLT2FIX(fov), FLT2FIX(ar), FLT2FIX(z_near), FLT2FIX(z_far));
} else {
gf_mx_perspective(mx, FLT2FIX(fov), FLT2FIX(ar), FLT2FIX(z_near), FLT2FIX(z_far));
}
return JS_DupValue(ctx, this_val);
}
static JSValue mx_lookat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Vec pos, target, up;
GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
if (!mx || (argc!=3) ) return GF_JS_EXCEPTION(ctx);
WGL_GET_VEC3F(pos, argv[0]);
WGL_GET_VEC3F(target, argv[1]);
WGL_GET_VEC3F(up, argv[2]);
gf_mx_lookat(mx, pos, target, up);
return JS_DupValue(ctx, this_val);
}
static const JSCFunctionListEntry mx_funcs[] =
{
JS_CGETSET_MAGIC_DEF("identity", mx_getProperty, mx_setProperty, MX_PROP_IDENTITY),
JS_CGETSET_MAGIC_DEF("is3D", mx_getProperty, mx_setProperty, MX_PROP_3D),
JS_CGETSET_MAGIC_DEF("m", mx_getProperty, mx_setProperty, MX_PROP_M),
JS_CGETSET_MAGIC_DEF("yaw", mx_getProperty, NULL, MX_PROP_YAW),
JS_CGETSET_MAGIC_DEF("pitch", mx_getProperty, NULL, MX_PROP_PITCH),
JS_CGETSET_MAGIC_DEF("roll", mx_getProperty, NULL, MX_PROP_ROLL),
JS_CGETSET_MAGIC_DEF("dec_translate", mx_getProperty, NULL, MX_PROP_TRANLATE),
JS_CGETSET_MAGIC_DEF("dec_scale", mx_getProperty, NULL, MX_PROP_SCALE),
JS_CGETSET_MAGIC_DEF("dec_rotate", mx_getProperty, NULL, MX_PROP_ROTATE),
JS_CGETSET_MAGIC_DEF("dec_shear", mx_getProperty, NULL, MX_PROP_SHEAR),
JS_CFUNC_DEF("copy", 0, mx_copy),
JS_CFUNC_DEF("equal", 0, mx_equal),
JS_CFUNC_DEF("translate", 0, mx_translate),
JS_CFUNC_DEF("scale", 0, mx_scale),
JS_CFUNC_DEF("rotate", 0, mx_rotate),
JS_CFUNC_DEF("add", 0, mx_add),
JS_CFUNC_DEF("inverse", 0, mx_inverse),
JS_CFUNC_DEF("transpose", 0, mx_transpose),
JS_CFUNC_DEF("apply", 0, mx_apply),
JS_CFUNC_DEF("ortho", 0, mx_ortho),
JS_CFUNC_DEF("perspective", 0, mx_perspective),
JS_CFUNC_DEF("lookat", 0, mx_lookat),
};
/*
void gf_mx_rotate_vector(GF_Matrix *mx, GF_Vec *pt);
*/
static JSValue mx_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
{
JSValue res;
GF_Matrix *mx;
GF_SAFEALLOC(mx, GF_Matrix);
if (!mx)
return js_throw_err(ctx, GF_OUT_OF_MEM);
gf_mx_init(*mx);
res = JS_NewObjectClass(ctx, matrix_class_id);
JS_SetOpaque(res, mx);
if (argc) {
GF_Matrix *from = JS_GetOpaque(argv[0], matrix_class_id);
GF_Matrix2D *from2D = JS_GetOpaque(argv[0], mx2d_class_id);
if (from) {
gf_mx_copy(*mx, *from);
} else if (from2D) {
gf_mx_from_mx2d(mx, from2D);
} else if (argc>=3) {
GF_Vec x_axis, y_axis, z_axis;
WGL_GET_VEC3F(x_axis, argv[0])
WGL_GET_VEC3F(y_axis, argv[1])
WGL_GET_VEC3F(z_axis, argv[2])
gf_mx_rotation_matrix_from_vectors(mx, x_axis, y_axis,z_axis);
}
}
return res;
}
#ifndef GPAC_DISABLE_3D
static void mesh_finalize(JSRuntime *rt, JSValue obj)
{
GF_Mesh *mesh = JS_GetOpaque(obj, mesh_class_id);
if (mesh) mesh_free(mesh);
}
JSClassDef mesh_class = {
.class_name = "Mesh",
.finalizer = mesh_finalize
};
Bool mesh_gl_update_buffers(GF_Mesh *mesh);
static JSValue mesh_update_gl(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Mesh *mesh = JS_GetOpaque(this_val, mesh_class_id);
if (!mesh) return GF_JS_EXCEPTION(ctx);
Bool res = mesh_gl_update_buffers(mesh);
if (!res) return GF_JS_EXCEPTION(ctx);
return JS_UNDEFINED;
}
JSValue mesh_gl_draw(JSContext *ctx, GF_Mesh *mesh, int argc, JSValueConst *argv);
static JSValue mesh_draw(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
GF_Mesh *mesh = JS_GetOpaque(this_val, mesh_class_id);
if (!mesh) return GF_JS_EXCEPTION(ctx);
return mesh_gl_draw(ctx, mesh, argc, argv);
}
static const JSCFunctionListEntry mesh_funcs[] =
{
JS_CFUNC_DEF("update_gl", 0, mesh_update_gl),
JS_CFUNC_DEF("draw", 0, mesh_draw),
};
static JSValue mesh_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
{
JSValue res;
GF_Mesh *mesh;
GF_Path *gp = NULL;
if (argc==1) {
gp = JS_GetOpaque(argv[0], path_class_id);
if (!gp) return GF_JS_EXCEPTION(ctx);
}
mesh = new_mesh();
if (!mesh)
return js_throw_err(ctx, GF_OUT_OF_MEM);
res = JS_NewObjectClass(ctx, mesh_class_id);
JS_SetOpaque(res, mesh);
if (gp)
mesh_from_path(mesh, gp);
return res;
}
#endif //GPAC_DISABLE_3D
static JSValue evg_pixel_size(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
u32 pfmt=0;
if (!argc) return js_throw_err_msg(ctx, GF_BAD_PARAM, "missing pixel format parameter");
if (JS_IsString(argv[0])) {
const char *s = JS_ToCString(ctx, argv[0]);
if (s) {
pfmt = gf_pixel_fmt_parse(s);
JS_FreeCString(ctx, s);
}
} else if (JS_IsNumber(argv[0])) {
JS_ToInt32(ctx, &pfmt, argv[0]);
}
if (!pfmt) return js_throw_err_msg(ctx, GF_BAD_PARAM, "missing pixel format parameter");
return JS_NewInt32(ctx, gf_pixel_get_bytes_per_pixel(pfmt));
}
static int js_evg_load_module(JSContext *c, JSModuleDef *m)
{
JSValue ctor;
JSValue proto;
JSValue global;
if (!canvas_class_id) {
JSRuntime *rt = JS_GetRuntime(c);
JS_NewClassID(&canvas_class_id);
JS_NewClass(rt, canvas_class_id, &canvas_class);
JS_NewClassID(&path_class_id);
JS_NewClass(rt, path_class_id, &path_class);
JS_NewClassID(&mx2d_class_id);
JS_NewClass(rt, mx2d_class_id, &mx2d_class);
JS_NewClassID(&colmx_class_id);
JS_NewClass(rt, colmx_class_id, &colmx_class);
JS_NewClassID(&stencil_class_id);
JS_NewClass(rt, stencil_class_id, &stencil_class);
JS_NewClassID(&texture_class_id);
JS_NewClass(rt, texture_class_id, &texture_class);
#ifndef GPAC_DISABLE_FONTS
JS_NewClassID(&text_class_id);
JS_NewClass(rt, text_class_id, &text_class);
#endif
JS_NewClassID(&matrix_class_id);
JS_NewClass(rt, matrix_class_id, &matrix_class);
#ifndef GPAC_DISABLE_3D
JS_NewClassID(&mesh_class_id);
JS_NewClass(rt, mesh_class_id, &mesh_class);
#endif
JS_NewClassID(&shader_class_id);
JS_NewClass(rt, shader_class_id, &shader_class);
JS_NewClassID(&vai_class_id);
JS_NewClass(rt, vai_class_id, &vai_class);
JS_NewClassID(&va_class_id);
JS_NewClass(rt, va_class_id, &va_class);
#ifdef GPAC_ENABLE_COVERAGE
if (gf_sys_is_cov_mode())
va_gc_mark(NULL, JS_NULL, NULL);
#endif
#ifdef EVG_USE_JS_SHADER
JS_NewClassID(&fragment_class_id);
JS_NewClass(rt, fragment_class_id, &fragment_class);
JS_NewClassID(&vertex_class_id);
JS_NewClass(rt, vertex_class_id, &vertex_class);
JS_NewClassID(&vaires_class_id);
JS_NewClass(rt, vaires_class_id, &vaires_class);
#endif// EVG_USE_JS_SHADER
}
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, canvas_funcs, countof(canvas_funcs));
JS_SetClassProto(c, canvas_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, path_funcs, countof(path_funcs));
JS_SetClassProto(c, path_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, mx2d_funcs, countof(mx2d_funcs));
JS_SetClassProto(c, mx2d_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, colmx_funcs, countof(colmx_funcs));
JS_SetClassProto(c, colmx_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, stencil_funcs, countof(stencil_funcs));
JS_SetClassProto(c, stencil_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, texture_funcs, countof(texture_funcs));
JS_SetClassProto(c, texture_class_id, proto);
#ifndef GPAC_DISABLE_FONTS
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, text_funcs, countof(text_funcs));
JS_SetClassProto(c, text_class_id, proto);
#endif
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, mx_funcs, countof(mx_funcs));
JS_SetClassProto(c, matrix_class_id, proto);
#ifdef EVG_USE_JS_SHADER
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, fragment_funcs, countof(fragment_funcs));
JS_SetClassProto(c, fragment_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, vertex_funcs, countof(vertex_funcs));
JS_SetClassProto(c, vertex_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, vaires_funcs, countof(vaires_funcs));
JS_SetClassProto(c, vaires_class_id, proto);
#endif
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, shader_funcs, countof(shader_funcs));
JS_SetClassProto(c, shader_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, vai_funcs, countof(vai_funcs));
JS_SetClassProto(c, vai_class_id, proto);
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, va_funcs, countof(va_funcs));
JS_SetClassProto(c, va_class_id, proto);
#ifndef GPAC_DISABLE_3D
proto = JS_NewObject(c);
JS_SetPropertyFunctionList(c, proto, mesh_funcs, countof(mesh_funcs));
JS_SetClassProto(c, mesh_class_id, proto);
#endif
global = JS_GetGlobalObject(c);
JS_SetPropertyStr(c, global, "GF_GRADIENT_MODE_PAD", JS_NewInt32(c, GF_GRADIENT_MODE_PAD));
JS_SetPropertyStr(c, global, "GF_GRADIENT_MODE_SPREAD", JS_NewInt32(c, GF_GRADIENT_MODE_SPREAD));
JS_SetPropertyStr(c, global, "GF_GRADIENT_MODE_REPEAT", JS_NewInt32(c, GF_GRADIENT_MODE_REPEAT));
JS_SetPropertyStr(c, global, "GF_TEXTURE_FILTER_HIGH_SPEED", JS_NewInt32(c, GF_TEXTURE_FILTER_HIGH_SPEED));
JS_SetPropertyStr(c, global, "GF_TEXTURE_FILTER_MID", JS_NewInt32(c, GF_TEXTURE_FILTER_MID));
JS_SetPropertyStr(c, global, "GF_TEXTURE_FILTER_HIGH_QUALITY", JS_NewInt32(c, GF_TEXTURE_FILTER_HIGH_QUALITY));
JS_SetPropertyStr(c, global, "GF_PATH2D_ARC_OPEN", JS_NewInt32(c, GF_PATH2D_ARC_OPEN));
JS_SetPropertyStr(c, global, "GF_PATH2D_ARC_OPEN", JS_NewInt32(c, GF_PATH2D_ARC_OPEN));
JS_SetPropertyStr(c, global, "GF_PATH2D_ARC_PIE", JS_NewInt32(c, GF_PATH2D_ARC_PIE));
JS_SetPropertyStr(c, global, "GF_PATH_LINE_CENTER", JS_NewInt32(c, GF_PATH_LINE_CENTER));
JS_SetPropertyStr(c, global, "GF_PATH_LINE_INSIDE", JS_NewInt32(c, GF_PATH_LINE_INSIDE));
JS_SetPropertyStr(c, global, "GF_PATH_LINE_OUTSIDE", JS_NewInt32(c, GF_PATH_LINE_OUTSIDE));
JS_SetPropertyStr(c, global, "GF_LINE_CAP_FLAT", JS_NewInt32(c, GF_LINE_CAP_FLAT));
JS_SetPropertyStr(c, global, "GF_LINE_CAP_ROUND", JS_NewInt32(c, GF_LINE_CAP_ROUND));
JS_SetPropertyStr(c, global, "GF_LINE_CAP_SQUARE", JS_NewInt32(c, GF_LINE_CAP_SQUARE));
JS_SetPropertyStr(c, global, "GF_LINE_CAP_TRIANGLE", JS_NewInt32(c, GF_LINE_CAP_TRIANGLE));
JS_SetPropertyStr(c, global, "GF_LINE_JOIN_MITER", JS_NewInt32(c, GF_LINE_JOIN_MITER));
JS_SetPropertyStr(c, global, "GF_LINE_JOIN_ROUND", JS_NewInt32(c, GF_LINE_JOIN_ROUND));
JS_SetPropertyStr(c, global, "GF_LINE_JOIN_BEVEL", JS_NewInt32(c, GF_LINE_JOIN_BEVEL));
JS_SetPropertyStr(c, global, "GF_LINE_JOIN_MITER_SVG", JS_NewInt32(c, GF_LINE_JOIN_MITER_SVG));
JS_SetPropertyStr(c, global, "GF_DASH_STYLE_PLAIN", JS_NewInt32(c, GF_DASH_STYLE_PLAIN));
JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH", JS_NewInt32(c, GF_DASH_STYLE_DASH));
JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DOT", JS_NewInt32(c, GF_DASH_STYLE_DOT));
JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH_DOT", JS_NewInt32(c, GF_DASH_STYLE_DASH_DOT));
JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH_DASH_DOT", JS_NewInt32(c, GF_DASH_STYLE_DASH_DASH_DOT));
JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH_DOT_DOT", JS_NewInt32(c, GF_DASH_STYLE_DASH_DOT_DOT));
JS_SetPropertyStr(c, global, "GF_DASH_STYLE_SVG", JS_NewInt32(c, GF_DASH_STYLE_SVG));
#ifndef GPAC_DISABLE_FONTS
JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_TOP", JS_NewInt32(c, TXT_BL_TOP));
JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_HANGING", JS_NewInt32(c, TXT_BL_HANGING));
JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_MIDDLE", JS_NewInt32(c, TXT_BL_MIDDLE));
JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_ALPHABETIC", JS_NewInt32(c, TXT_BL_ALPHABETIC));
JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_IDEOGRAPHIC", JS_NewInt32(c, TXT_BL_IDEOGRAPHIC));
JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_BOTTOM", JS_NewInt32(c, TXT_BL_BOTTOM));
JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_START", JS_NewInt32(c, TXT_AL_START));
JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_END", JS_NewInt32(c, TXT_AL_END));
JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_LEFT", JS_NewInt32(c, TXT_AL_LEFT));
JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_RIGHT", JS_NewInt32(c, TXT_AL_RIGHT));
JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_CENTER", JS_NewInt32(c, TXT_AL_CENTER));
#endif
JS_SetPropertyStr(c, global, "GF_EVG_SRC_ATOP", JS_NewInt32(c, GF_EVG_SRC_ATOP));
JS_SetPropertyStr(c, global, "GF_EVG_SRC_IN", JS_NewInt32(c, GF_EVG_SRC_IN));
JS_SetPropertyStr(c, global, "GF_EVG_SRC_OUT", JS_NewInt32(c, GF_EVG_SRC_OUT));
JS_SetPropertyStr(c, global, "GF_EVG_SRC_OVER", JS_NewInt32(c, GF_EVG_SRC_OVER));
JS_SetPropertyStr(c, global, "GF_EVG_DST_ATOP", JS_NewInt32(c, GF_EVG_DST_ATOP));
JS_SetPropertyStr(c, global, "GF_EVG_DST_IN", JS_NewInt32(c, GF_EVG_DST_IN));
JS_SetPropertyStr(c, global, "GF_EVG_DST_OUT", JS_NewInt32(c, GF_EVG_DST_OUT));
JS_SetPropertyStr(c, global, "GF_EVG_DST_OVER", JS_NewInt32(c, GF_EVG_DST_OVER));
JS_SetPropertyStr(c, global, "GF_EVG_LIGHTER", JS_NewInt32(c, GF_EVG_LIGHTER));
JS_SetPropertyStr(c, global, "GF_EVG_COPY", JS_NewInt32(c, GF_EVG_COPY));
JS_SetPropertyStr(c, global, "GF_EVG_XOR", JS_NewInt32(c, GF_EVG_XOR));
JS_SetPropertyStr(c, global, "GF_EVG_POINTS", JS_NewInt32(c, GF_EVG_POINTS));
JS_SetPropertyStr(c, global, "GF_EVG_POLYGON", JS_NewInt32(c, GF_EVG_POLYGON));
JS_SetPropertyStr(c, global, "GF_EVG_LINES", JS_NewInt32(c, GF_EVG_LINES));
JS_SetPropertyStr(c, global, "GF_EVG_TRIANGLES", JS_NewInt32(c, GF_EVG_TRIANGLES));
JS_SetPropertyStr(c, global, "GF_EVG_QUADS", JS_NewInt32(c, GF_EVG_QUADS));
JS_SetPropertyStr(c, global, "GF_EVG_LINE_STRIP", JS_NewInt32(c, GF_EVG_LINE_STRIP));
JS_SetPropertyStr(c, global, "GF_EVG_TRIANGLE_STRIP", JS_NewInt32(c, GF_EVG_TRIANGLE_STRIP));
JS_SetPropertyStr(c, global, "GF_EVG_TRIANGLE_FAN", JS_NewInt32(c, GF_EVG_TRIANGLE_FAN));
JS_SetPropertyStr(c, global, "GF_EVG_SHADER_FRAGMENT", JS_NewInt32(c, GF_EVG_SHADER_FRAGMENT));
JS_SetPropertyStr(c, global, "GF_EVG_SHADER_VERTEX", JS_NewInt32(c, GF_EVG_SHADER_VERTEX));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEVER", JS_NewInt32(c, GF_EVGDEPTH_NEVER));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_ALWAYS", JS_NewInt32(c, GF_EVGDEPTH_ALWAYS));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_ALWAYS", JS_NewInt32(c, GF_EVGDEPTH_ALWAYS));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_EQUAL));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEQUAL", JS_NewInt32(c, GF_EVGDEPTH_NEQUAL));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS", JS_NewInt32(c, GF_EVGDEPTH_LESS));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_LESS_EQUAL));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER", JS_NewInt32(c, GF_EVGDEPTH_GREATER));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_GREATER_EQUAL));
JS_SetPropertyStr(c, global, "GF_EVG_VAI_VERTEX_INDEX", JS_NewInt32(c, GF_EVG_VAI_VERTEX_INDEX));
JS_SetPropertyStr(c, global, "GF_EVG_VAI_VERTEX", JS_NewInt32(c, GF_EVG_VAI_VERTEX));
JS_SetPropertyStr(c, global, "GF_EVG_VAI_PRIMITIVE", JS_NewInt32(c, GF_EVG_VAI_PRIMITIVE));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_DISABLE", JS_NewInt32(c, GF_EVGDEPTH_DISABLE));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEVER", JS_NewInt32(c, GF_EVGDEPTH_NEVER));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_ALWAYS", JS_NewInt32(c, GF_EVGDEPTH_ALWAYS));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_EQUAL));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEQUAL", JS_NewInt32(c, GF_EVGDEPTH_NEQUAL));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS", JS_NewInt32(c, GF_EVGDEPTH_LESS));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_LESS_EQUAL));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER", JS_NewInt32(c, GF_EVGDEPTH_GREATER));
JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_GREATER_EQUAL));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_NONE", JS_NewInt32(c, GF_EVG_OPERAND_NONE));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_MIX", JS_NewInt32(c, GF_EVG_OPERAND_MIX));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_MIX_ALPHA", JS_NewInt32(c, GF_EVG_OPERAND_MIX_ALPHA));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_REPLACE_ALPHA", JS_NewInt32(c, GF_EVG_OPERAND_REPLACE_ALPHA));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_REPLACE_ONE_MINUS_ALPHA", JS_NewInt32(c, GF_EVG_OPERAND_REPLACE_ONE_MINUS_ALPHA));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_MIX_DYN", JS_NewInt32(c, GF_EVG_OPERAND_MIX_DYN));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_MIX_DYN_ALPHA", JS_NewInt32(c, GF_EVG_OPERAND_MIX_DYN_ALPHA));
JS_SetPropertyStr(c, global, "GF_EVG_OPERAND_ODD_FILL", JS_NewInt32(c, GF_EVG_OPERAND_ODD_FILL));
JS_SetPropertyStr(c, global, "GF_RASTER_HIGH_SPEED", JS_NewInt32(c, GF_RASTER_HIGH_SPEED));
JS_SetPropertyStr(c, global, "GF_RASTER_MID", JS_NewInt32(c, GF_RASTER_MID));
JS_SetPropertyStr(c, global, "GF_RASTER_HIGH_QUALITY", JS_NewInt32(c, GF_RASTER_HIGH_QUALITY));
JS_SetPropertyStr(c, global, "GF_EVGMASK_NONE", JS_NewInt32(c, GF_EVGMASK_NONE));
JS_SetPropertyStr(c, global, "GF_EVGMASK_DRAW", JS_NewInt32(c, GF_EVGMASK_DRAW));
JS_SetPropertyStr(c, global, "GF_EVGMASK_DRAW_NO_CLEAR", JS_NewInt32(c, GF_EVGMASK_DRAW_NO_CLEAR));
JS_SetPropertyStr(c, global, "GF_EVGMASK_USE", JS_NewInt32(c, GF_EVGMASK_USE));
JS_SetPropertyStr(c, global, "GF_EVGMASK_USE_INV", JS_NewInt32(c, GF_EVGMASK_USE_INV));
JS_SetPropertyStr(c, global, "GF_EVGMASK_RECORD", JS_NewInt32(c, GF_EVGMASK_RECORD));
JS_FreeValue(c, global);
/*export constructors*/
ctor = JS_NewCFunction2(c, canvas_constructor, "Canvas", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "Canvas", ctor);
ctor = JS_NewCFunction2(c, path_constructor, "Path", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "Path", ctor);
ctor = JS_NewCFunction2(c, mx2d_constructor, "Matrix2D", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "Matrix2D", ctor);
ctor = JS_NewCFunction2(c, colmx_constructor, "ColorMatrix", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "ColorMatrix", ctor);
ctor = JS_NewCFunction2(c, solid_brush_constructor, "SolidBrush", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "SolidBrush", ctor);
ctor = JS_NewCFunction2(c, linear_gradient_constructor, "LinearGradient", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "LinearGradient", ctor);
ctor = JS_NewCFunction2(c, radial_gradient_constructor, "RadialGradient", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "RadialGradient", ctor);
ctor = JS_NewCFunction2(c, texture_constructor, "Texture", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "Texture", ctor);
#ifndef GPAC_DISABLE_FONTS
ctor = JS_NewCFunction2(c, text_constructor, "Text", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "Text", ctor);
#endif
ctor = JS_NewCFunction2(c, mx_constructor, "Matrix", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "Matrix", ctor);
ctor = JS_NewCFunction2(c, vai_constructor, "VertexAttribInterpolator", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "VertexAttribInterpolator", ctor);
ctor = JS_NewCFunction2(c, va_constructor, "VertexAttrib", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "VertexAttrib", ctor);
#ifndef GPAC_DISABLE_3D
ctor = JS_NewCFunction2(c, mesh_constructor, "Mesh", 1, JS_CFUNC_constructor, 0);
JS_SetModuleExport(c, m, "Mesh", ctor);
#endif
ctor = JS_NewCFunction2(c, evg_pixel_size, "PixelSize", 1, JS_CFUNC_generic, 0);
JS_SetModuleExport(c, m, "PixelSize", ctor);
#ifdef GPAC_HAS_FFMPEG
JS_SetModuleExport(c, m, "BlitEnabled", JS_TRUE);
#else
JS_SetModuleExport(c, m, "BlitEnabled", JS_FALSE);
#endif
return 0;
}
void qjs_module_init_evg(JSContext *ctx)
{
JSModuleDef *m;
m = JS_NewCModule(ctx, "evg", js_evg_load_module);
if (!m) return;
JS_AddModuleExport(ctx, m, "Canvas");
JS_AddModuleExport(ctx, m, "Path");
JS_AddModuleExport(ctx, m, "Matrix2D");
JS_AddModuleExport(ctx, m, "ColorMatrix");
JS_AddModuleExport(ctx, m, "SolidBrush");
JS_AddModuleExport(ctx, m, "LinearGradient");
JS_AddModuleExport(ctx, m, "RadialGradient");
JS_AddModuleExport(ctx, m, "Texture");
JS_AddModuleExport(ctx, m, "Text");
JS_AddModuleExport(ctx, m, "Matrix");
JS_AddModuleExport(ctx, m, "Mesh");
JS_AddModuleExport(ctx, m, "VertexAttribInterpolator");
JS_AddModuleExport(ctx, m, "VertexAttrib");
JS_AddModuleExport(ctx, m, "PixelSize");
JS_AddModuleExport(ctx, m, "BlitEnabled");
return;
}
#else // defined(GPAC_HAS_QJS) && !defined(GPAC_DISABLE_EVG)
void qjs_module_init_evg(void *ctx)
{
}
#endif