LCOV - code coverage report
Current view: top level - jsmods - evg.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2517 3196 78.8 %
Date: 2021-04-29 23:48:07 Functions: 178 179 99.4 %

          Line data    Source code
       1             : /*
       2             :  *                      GPAC - Multimedia Framework C SDK
       3             :  *
       4             :  *                      Authors: Jean Le Feuvre
       5             :  *                      Copyright (c) Telecom ParisTech 2019
       6             :  *                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / JavaScript vector graphics bindings
       9             :  *
      10             :  *  GPAC is free software; you can redistribute it and/or modify
      11             :  *  it under the terms of the GNU Lesser General Public License as published by
      12             :  *  the Free Software Foundation; either version 2, or (at your option)
      13             :  *  any later version.
      14             :  *
      15             :  *  GPAC is distributed in the hope that it will be useful,
      16             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      17             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      18             :  *  GNU Lesser General Public License for more details.
      19             :  *
      20             :  *  You should have received a copy of the GNU Lesser General Public
      21             :  *  License along with this library; see the file COPYING.  If not, write to
      22             :  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
      23             :  *
      24             :  */
      25             : 
      26             : 
      27             : /*
      28             :         ANY CHANGE TO THE API MUST BE REFLECTED IN THE DOCUMENTATION IN gpac/share/doc/idl/evg.idl
      29             :         (no way to define inline JS doc with doxygen)
      30             : */
      31             : 
      32             : 
      33             : #include <gpac/setup.h>
      34             : 
      35             : #if defined(GPAC_HAS_QJS)
      36             : 
      37             : /*base SVG type*/
      38             : #include <gpac/nodes_svg.h>
      39             : #include <gpac/nodes_mpeg4.h>
      40             : #include <gpac/nodes_x3d.h>
      41             : /*dom events*/
      42             : #include <gpac/evg.h>
      43             : 
      44             : #include "../scenegraph/qjs_common.h"
      45             : 
      46             : #include <gpac/internal/compositor_dev.h>
      47             : 
      48             : /*for texture from png/rgb*/
      49             : #include <gpac/bitstream.h>
      50             : #include <gpac/avparse.h>
      51             : #include <gpac/network.h>
      52             : 
      53             : 
      54             : #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; }
      55             : #define CLAMPCOLF(_name) if (_name<0) _name=0; else if (_name>1.0) _name=1.0;
      56             : 
      57             : uint8_t *evg_get_array(JSContext *ctx, JSValueConst obj, u32 *size);
      58             : 
      59             : //not used, wau too slow - we kept the code for future testing
      60             : //#define EVG_USE_JS_SHADER
      61             : 
      62             : typedef struct
      63             : {
      64             :         u32 width, height, pf, stride, stride_uv, nb_comp;
      65             :         char *data;
      66             :         u32 data_size;
      67             :         Bool owns_data;
      68             :         u32 flags;
      69             :         GF_EVGStencil *stencil;
      70             :         JSValue param_fun, obj;
      71             :         JSContext *ctx;
      72             : } GF_JSTexture;
      73             : 
      74             : #define MAX_ATTR_DIM    4
      75             : typedef struct
      76             : {
      77             :         Float values[MAX_ATTR_DIM];
      78             :         u32 dim;
      79             :         u8 comp_type;
      80             : } EVG_VAIRes;
      81             : 
      82             : enum
      83             : {
      84             :         GF_EVG_VAI_VERTEX_INDEX=0,
      85             :         GF_EVG_VAI_VERTEX,
      86             :         GF_EVG_VAI_PRIMITIVE,
      87             : };
      88             : 
      89             : typedef struct
      90             : {
      91             :         u32 prim_idx;
      92             :         u32 nb_comp;
      93             :         Float *values;
      94             :         u32 nb_values;
      95             :         JSValue ab;
      96             : #ifdef EVG_USE_JS_SHADER
      97             :         JSValue res;
      98             : #endif
      99             :         u32 interp_type;
     100             : 
     101             :         Float anchors[3][MAX_ATTR_DIM];
     102             :         EVG_VAIRes result;
     103             : 
     104             :         Bool normalize;
     105             : } EVG_VAI;
     106             : 
     107             : typedef struct
     108             : {
     109             :         u32 nb_comp, att_type;
     110             :         Float *values;
     111             :         u32 nb_values;
     112             :         JSValue ab;
     113             :         JSValue res;
     114             :         u32 interp_type;
     115             : 
     116             :         Bool normalize;
     117             : } EVG_VA;
     118             : 
     119             : enum
     120             : {
     121             :         COMP_X = 1,//x or r or s
     122             :         COMP_Y = 1<<1,//y or g or t
     123             :         COMP_Z = 1<<2, //z or b
     124             :         COMP_Q = 1<<3, //w/q or a
     125             :         COMP_V2_XY = COMP_X|COMP_Y,
     126             :         COMP_V2_XZ = COMP_X|COMP_Z,
     127             :         COMP_V2_YZ = COMP_Y|COMP_Z,
     128             :         COMP_V3 = COMP_X|COMP_Y|COMP_Z,
     129             :         COMP_V4 = COMP_X|COMP_Y|COMP_Z|COMP_Q,
     130             :         COMP_BOOL,
     131             :         COMP_INT,
     132             :         COMP_FLOAT
     133             : };
     134             : 
     135             : typedef struct
     136             : {
     137             :         u8 op_type;
     138             :         u8 cond_type;
     139             :         u8 left_value_type;
     140             :         u8 right_value_type;
     141             :         s32 left_value, right_value, right_value_second;
     142             :         char *uni_name;
     143             :         JSValue tx_ref;
     144             :         GF_JSTexture *tx;
     145             : 
     146             :         //we separate VAI/MX from base value in order to be able to assign a VAI value
     147             :         union {
     148             :         struct {
     149             :                         EVG_VAI *vai;
     150             :                         JSValue ref;
     151             :                 } vai;
     152             :         struct {
     153             :                         EVG_VA *va;
     154             :                         JSValue ref;
     155             :                 } va;
     156             :         struct {
     157             :                         GF_Matrix *mx;
     158             :                         JSValue ref;
     159             :                 } mx;
     160             :         };
     161             : 
     162             :         union {
     163             :                 Float vec[4];
     164             :                 s32 ival;
     165             :                 Bool bval;
     166             :         };
     167             : } ShaderOp;
     168             : 
     169             : enum
     170             : {
     171             :         GF_EVG_SHADER_FRAGMENT=1,
     172             :         GF_EVG_SHADER_VERTEX,
     173             : };
     174             : 
     175             : typedef struct
     176             : {
     177             :         char *name;
     178             :         union {
     179             :                 GF_Vec4 vecval;
     180             :                 s32 ival;
     181             :                 Bool bval;
     182             :         };
     183             :         u8 value_type;
     184             : } ShaderVar;
     185             : 
     186             : typedef struct
     187             : {
     188             :         u32 mode;
     189             :         u32 nb_ops, alloc_ops;
     190             :         ShaderOp *ops;
     191             :         u32 nb_vars, alloc_vars;
     192             :         ShaderVar *vars;
     193             :         Bool invalid, disable_early_z;
     194             : } EVGShader;
     195             : 
     196             : typedef struct
     197             : {
     198             :         u32 width, height, pf, stride, stride_uv, mem_size;
     199             :         char *data;
     200             :         Bool owns_data;
     201             :         Bool center_coords;
     202             :         GF_EVGSurface *surface;
     203             :         u32 composite_op;
     204             :         JSValue alpha_cbk;
     205             :         JSValue frag_shader;
     206             :         Bool frag_is_cbk;
     207             :         JSValue vert_shader;
     208             :         Bool vert_is_cbk;
     209             : 
     210             :         JSValue depth_buffer;
     211             : 
     212             : 
     213             :         EVGShader *frag, *vert;
     214             : 
     215             :         JSContext *ctx;
     216             :         JSValue obj;
     217             : #ifdef EVG_USE_JS_SHADER
     218             :         JSValue frag_obj;
     219             :         JSValue vert_obj;
     220             : #endif
     221             : 
     222             : } GF_JSCanvas;
     223             : 
     224             : 
     225             : 
     226             : typedef struct
     227             : {
     228             :         GF_FontManager *fm;
     229             :         GF_Path *path;
     230             :         char *fontname;
     231             :         Double font_size;
     232             :         u32 align;
     233             :         u32 baseline;
     234             :         u32 styles;
     235             :         GF_List *spans;
     236             :         Bool horizontal;
     237             :         Bool flip;
     238             :         Double maxWidth;
     239             :         Double lineSpacing;
     240             :         Fixed min_x, min_y, max_x, max_y, max_w, max_h;
     241             :         GF_Font *font;
     242             :         Bool path_for_centered;
     243             : } GF_JSText;
     244             : 
     245             : 
     246             : JSClassID canvas_class_id;
     247             : JSClassID canvas3d_class_id;
     248             : JSClassID path_class_id;
     249             : JSClassID mx2d_class_id;
     250             : JSClassID colmx_class_id;
     251             : JSClassID stencil_class_id;
     252             : JSClassID texture_class_id;
     253             : JSClassID text_class_id;
     254             : JSClassID matrix_class_id;
     255             : #ifdef EVG_USE_JS_SHADER
     256             : JSClassID fragment_class_id;
     257             : JSClassID vertex_class_id;
     258             : JSClassID vaires_class_id;
     259             : #endif
     260             : JSClassID vai_class_id;
     261             : JSClassID va_class_id;
     262             : JSClassID shader_class_id;
     263             : 
     264             : static void text_set_path(GF_JSCanvas *canvas, GF_JSText *text);
     265             : 
     266             : Bool get_color_from_args(JSContext *c, int argc, JSValueConst *argv, u32 idx, Double *a, Double *r, Double *g, Double *b);
     267             : static Bool get_color(JSContext *c, JSValueConst obj, Double *a, Double *r, Double *g, Double *b);
     268             : 
     269          10 : static void canvas_finalize(JSRuntime *rt, JSValue obj)
     270             : {
     271          10 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
     272          10 :         if (!canvas) return;
     273             :         JS_FreeValueRT(rt, canvas->alpha_cbk);
     274             :         JS_FreeValueRT(rt, canvas->frag_shader);
     275             :         JS_FreeValueRT(rt, canvas->vert_shader);
     276             : 
     277          10 :         if (canvas->owns_data)
     278           3 :                 gf_free(canvas->data);
     279          10 :         if (canvas->surface)
     280          10 :                 gf_evg_surface_delete(canvas->surface);
     281          10 :         gf_free(canvas);
     282             : }
     283             : 
     284           8 : static void canvas_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
     285             : {
     286           8 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
     287           8 :         if (!canvas) return;
     288           8 :         JS_MarkValue(rt, canvas->alpha_cbk, mark_func);
     289           8 :         JS_MarkValue(rt, canvas->frag_shader, mark_func);
     290           8 :         JS_MarkValue(rt, canvas->vert_shader, mark_func);
     291             : }
     292             : 
     293             : JSClassDef canvas_class = {
     294             :         "Canvas",
     295             :         .finalizer = canvas_finalize,
     296             :         .gc_mark = canvas_gc_mark
     297             : };
     298             : 
     299          19 : static void canvas3d_finalize(JSRuntime *rt, JSValue obj)
     300             : {
     301          19 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
     302          19 :         if (!canvas) return;
     303             :         JS_FreeValueRT(rt, canvas->alpha_cbk);
     304             :         JS_FreeValueRT(rt, canvas->frag_shader);
     305             :         JS_FreeValueRT(rt, canvas->vert_shader);
     306             : #ifdef EVG_USE_JS_SHADER
     307             :         JS_FreeValueRT(rt, canvas->frag_obj);
     308             :         JS_FreeValueRT(rt, canvas->vert_obj);
     309             : #endif
     310             :         JS_FreeValueRT(rt, canvas->depth_buffer);
     311             : 
     312          19 :         if (canvas->owns_data)
     313           1 :                 gf_free(canvas->data);
     314          19 :         if (canvas->surface)
     315          19 :                 gf_evg_surface_delete(canvas->surface);
     316          19 :         gf_free(canvas);
     317             : }
     318             : 
     319         108 : static void canvas3d_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
     320             : {
     321         108 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
     322         108 :         if (!canvas) return;
     323         108 :         JS_MarkValue(rt, canvas->alpha_cbk, mark_func);
     324         108 :         JS_MarkValue(rt, canvas->frag_shader, mark_func);
     325         108 :         JS_MarkValue(rt, canvas->vert_shader, mark_func);
     326             : #ifdef EVG_USE_JS_SHADER
     327             :         JS_MarkValue(rt, canvas->frag_obj, mark_func);
     328             :         JS_MarkValue(rt, canvas->vert_obj, mark_func);
     329             : #endif
     330         108 :         JS_MarkValue(rt, canvas->depth_buffer, mark_func);
     331             : }
     332             : 
     333             : JSClassDef canvas3d_class = {
     334             :         "Canvas3D",
     335             :         .finalizer = canvas3d_finalize,
     336             :         .gc_mark = canvas3d_gc_mark
     337             : };
     338             : 
     339             : enum
     340             : {
     341             :         GF_EVG_CENTERED = 0,
     342             :         GF_EVG_PATH,
     343             :         GF_EVG_MATRIX,
     344             :         GF_EVG_MATRIX_3D,
     345             :         GF_EVG_CLIPPER,
     346             :         GF_EVG_COMPOSITE_OP,
     347             :         GF_EVG_ALPHA_FUN,
     348             :         GF_EVG_FRAG_SHADER,
     349             :         GF_EVG_VERT_SHADER,
     350             :         GF_EVG_CCW,
     351             :         GF_EVG_BACKCULL,
     352             :         GF_EVG_MINDEPTH,
     353             :         GF_EVG_MAXDEPTH,
     354             :         GF_EVG_DEPTHTEST,
     355             :         GF_EVG_ANTIALIAS,
     356             :         GF_EVG_POINTSIZE,
     357             :         GF_EVG_POINTSMOOTH,
     358             :         GF_EVG_LINESIZE,
     359             :         GF_EVG_CLIP_ZERO,
     360             :         GF_EVG_DEPTH_BUFFER,
     361             :         GF_EVG_DEPTH_TEST,
     362             :         GF_EVG_WRITE_DEPTH,
     363             : };
     364             : 
     365         307 : u8 evg_get_alpha(void *cbk, u8 src_alpha, s32 x, s32 y)
     366             : {
     367         307 :         u32 res_a=0;
     368             :         GF_JSCanvas *canvas = cbk;
     369             :         JSValue ret, args[3];
     370         614 :         args[0] = JS_NewInt32(canvas->ctx, src_alpha);
     371         307 :         args[1] = JS_NewInt32(canvas->ctx, x);
     372         307 :         args[2] = JS_NewInt32(canvas->ctx, y);
     373         307 :         ret = JS_Call(canvas->ctx, canvas->alpha_cbk, canvas->obj, 3, args);
     374         307 :         JS_ToInt32(canvas->ctx, &res_a, ret);
     375         307 :         return res_a;
     376             : 
     377             : }
     378             : 
     379          29 : static JSValue canvas_constructor_internal(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv, Bool is_3d)
     380             : {
     381          29 :         u32 width, height, pf=0, osize;
     382          29 :         size_t data_size=0;
     383             :         u8 *data=NULL;
     384          29 :         u32 stride = 0;
     385          29 :         u32 stride_uv = 0;
     386             :         GF_JSCanvas *canvas;
     387             :         GF_Err e;
     388             : 
     389          29 :         if (argc<3)
     390           0 :                 return JS_EXCEPTION;
     391          29 :         if (JS_ToInt32(c, &width, argv[0]))
     392           0 :                 return JS_EXCEPTION;
     393          29 :         if (JS_ToInt32(c, &height, argv[1]))
     394           0 :                 return JS_EXCEPTION;
     395          58 :         if (JS_IsString(argv[2])) {
     396             :                 const char *str = JS_ToCString(c, argv[2]);
     397          27 :                 pf = gf_pixel_fmt_parse(str);
     398          27 :                 JS_FreeCString(c, str);
     399           2 :         } else if (JS_ToInt32(c, &pf, argv[2])) {
     400           0 :                 return JS_EXCEPTION;
     401             :         }
     402          29 :         if (!width || !height || !pf)
     403           0 :                 return JS_EXCEPTION;
     404             : 
     405          29 :         if (argc>3) {
     406             :                 s32 idx=0;
     407          50 :                 if (JS_IsObject(argv[3])) {
     408          25 :                         data = JS_GetArrayBuffer(c, &data_size, argv[3]);
     409          25 :                         if (!data) return JS_EXCEPTION;
     410             :                         idx=1;
     411           0 :                 } else if (!JS_IsInteger(argv[3]))
     412           0 :                         return JS_EXCEPTION;
     413             : 
     414          25 :                 if (argc>3+idx) {
     415           0 :                         if (JS_ToInt32(c, &stride, argv[3+idx]))
     416           0 :                                 return JS_EXCEPTION;
     417           0 :                         if (argc>4+idx) {
     418           0 :                                 if (JS_ToInt32(c, &stride_uv, argv[4+idx]))
     419           0 :                                         return JS_EXCEPTION;
     420             :                         }
     421             :                 }
     422             :         }
     423          29 :         GF_SAFEALLOC(canvas, GF_JSCanvas);
     424             : 
     425          29 :         if (!canvas)
     426           0 :                 return JS_EXCEPTION;
     427             : 
     428          29 :         if (!gf_pixel_get_size_info(pf, width, height, &osize, &stride, &stride_uv, NULL, NULL)) {
     429           0 :                 gf_free(canvas);
     430           0 :                 return JS_EXCEPTION;
     431             :         }
     432          29 :         if (data && (data_size<osize)) {
     433           0 :                 gf_free(canvas);
     434           0 :                 return JS_EXCEPTION;
     435             :         }
     436             : 
     437          29 :         canvas->mem_size = osize;
     438          29 :         canvas->width = width;
     439          29 :         canvas->height = height;
     440          29 :         canvas->pf = pf;
     441          29 :         canvas->stride = stride;
     442          29 :         canvas->alpha_cbk = JS_UNDEFINED;
     443          29 :         canvas->frag_shader = JS_UNDEFINED;
     444          29 :         canvas->vert_shader = JS_UNDEFINED;
     445             : #ifdef EVG_USE_JS_SHADER
     446             :         canvas->frag_obj = JS_UNDEFINED;
     447             :         canvas->vert_obj = JS_UNDEFINED;
     448             : #endif
     449          29 :         canvas->depth_buffer = JS_UNDEFINED;
     450          29 :         canvas->ctx = c;
     451          29 :         if (data) {
     452          25 :                 canvas->data = data;
     453          25 :                 canvas->owns_data = GF_FALSE;
     454             :         } else {
     455           4 :                 canvas->data = gf_malloc(sizeof(u8)*osize);
     456           4 :                 canvas->owns_data = GF_TRUE;
     457             :         }
     458          29 :         if (is_3d) {
     459          19 :                 canvas->surface = gf_evg_surface3d_new();
     460             : #ifdef EVG_USE_JS_SHADER
     461             :                 canvas->frag_obj = JS_NewObjectClass(c, fragment_class_id);
     462             :                 JS_SetOpaque(canvas->frag_obj, NULL);
     463             :                 canvas->vert_obj = JS_NewObjectClass(c, vertex_class_id);
     464             :                 JS_SetOpaque(canvas->vert_obj, NULL);
     465             : #endif
     466             :         } else {
     467          10 :                 canvas->surface = gf_evg_surface_new(GF_TRUE);
     468          10 :                 canvas->center_coords = GF_TRUE;
     469             :         }
     470          29 :         if (!canvas->surface)
     471             :                 e = GF_BAD_PARAM;
     472             :         else
     473          29 :                 e = gf_evg_surface_attach_to_buffer(canvas->surface, canvas->data, canvas->width, canvas->height, 0, canvas->stride, canvas->pf);
     474          29 :         if (e) {
     475           0 :                 if (canvas->owns_data) gf_free(canvas->data);
     476           0 :                 gf_evg_surface_delete(canvas->surface);
     477           0 :                 gf_free(canvas);
     478           0 :                 return JS_EXCEPTION;
     479             :         }
     480          29 :         canvas->obj = JS_NewObjectClass(c, is_3d ? canvas3d_class_id : canvas_class_id);
     481          58 :         if (JS_IsException(canvas->obj)) return canvas->obj;
     482             : 
     483          29 :         JS_SetOpaque(canvas->obj, canvas);
     484          29 :         return canvas->obj;
     485             : }
     486             : 
     487          10 : static JSValue canvas_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
     488             : {
     489          10 :         return canvas_constructor_internal(c, new_target, argc, argv, GF_FALSE);
     490             : }
     491             : 
     492           1 : static JSValue canvas_getProperty(JSContext *c, JSValueConst obj, int magic)
     493             : {
     494           1 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
     495           1 :         if (!canvas) return JS_EXCEPTION;
     496           1 :         switch (magic) {
     497           1 :         case GF_EVG_CENTERED: return JS_NewBool(c, canvas->center_coords);
     498           0 :         case GF_EVG_COMPOSITE_OP: return JS_NewInt32(c, canvas->composite_op);
     499           0 :         case GF_EVG_ALPHA_FUN: return JS_DupValue(c, canvas->alpha_cbk);
     500             :         }
     501           0 :         return JS_UNDEFINED;
     502             : }
     503             : 
     504         200 : Bool canvas_get_irect(JSContext *c, JSValueConst obj, GF_IRect *rc)
     505             : {
     506             :         JSValue v;
     507             :         int res;
     508             :         memset(rc, 0, sizeof(GF_IRect));
     509             : 
     510             : #define GET_PROP( _n, _f)\
     511             :         v = JS_GetPropertyStr(c, obj, _n);\
     512             :         res = JS_ToInt32(c, &(rc->_f), v);\
     513             :         JS_FreeValue(c, v);\
     514             :         if (res) return GF_FALSE;\
     515             : 
     516         400 :         GET_PROP("x", x)
     517         400 :         GET_PROP("y", y)
     518         400 :         GET_PROP("w", width)
     519         400 :         GET_PROP("h", height)
     520             : #undef GET_PROP
     521         200 :         return GF_TRUE;
     522             : }
     523             : 
     524        1853 : static JSValue canvas_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
     525             : {
     526        1853 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
     527        1853 :         if (!canvas) return JS_EXCEPTION;
     528        1853 :         switch (magic) {
     529         106 :         case GF_EVG_CENTERED:
     530         106 :                 canvas->center_coords = JS_ToBool(c, value) ? GF_TRUE : GF_FALSE;
     531         106 :                 gf_evg_surface_set_center_coords(canvas->surface, canvas->center_coords);
     532         106 :                 return JS_UNDEFINED;
     533             : 
     534         722 :         case GF_EVG_PATH:
     535         722 :                 if (JS_IsNull(value)) {
     536           0 :                         gf_evg_surface_set_path(canvas->surface, NULL);
     537             :                 } else {
     538         722 :                         GF_Path *gp = JS_GetOpaque(value, path_class_id);
     539         722 :                         if (gp)
     540         513 :                                 gf_evg_surface_set_path(canvas->surface, gp);
     541             :                         else {
     542         209 :                                 GF_JSText *text = JS_GetOpaque(value, text_class_id);
     543         209 :                                 if (text) {
     544         209 :                                         text_set_path(canvas, text);
     545             :                                 }
     546             :                         }
     547             :                 }
     548         722 :                 return JS_UNDEFINED;
     549             : 
     550         621 :         case GF_EVG_MATRIX:
     551         621 :                 if (JS_IsNull(value)) {
     552           0 :                         gf_evg_surface_set_matrix(canvas->surface, NULL);
     553             :                 } else {
     554         621 :                         GF_Matrix2D *mx = JS_GetOpaque(value, mx2d_class_id);
     555         621 :                         gf_evg_surface_set_matrix(canvas->surface, mx);
     556             :                 }
     557         621 :                 return JS_UNDEFINED;
     558             : 
     559           1 :         case GF_EVG_MATRIX_3D:
     560           1 :                 if (JS_IsNull(value)) {
     561           0 :                         gf_evg_surface_set_matrix(canvas->surface, NULL);
     562             :                 } else {
     563           1 :                         GF_Matrix *mx = JS_GetOpaque(value, matrix_class_id);
     564           1 :                         gf_evg_surface_set_matrix_3d(canvas->surface, mx);
     565             :                 }
     566           1 :                 return JS_UNDEFINED;
     567         301 :         case GF_EVG_CLIPPER:
     568         301 :                 if (JS_IsNull(value)) {
     569         201 :                         gf_evg_surface_set_clipper(canvas->surface, NULL);
     570             :                 } else {
     571             :                         GF_IRect rc;
     572         100 :                         canvas_get_irect(c, value, &rc);
     573         100 :                         gf_evg_surface_set_clipper(canvas->surface, &rc);
     574             :                 }
     575         301 :                 return JS_UNDEFINED;
     576           1 :         case GF_EVG_COMPOSITE_OP:
     577           1 :                 if (JS_ToInt32(c, &canvas->composite_op, value)) return JS_EXCEPTION;
     578           1 :                 gf_evg_surface_set_composite_mode(canvas->surface, canvas->composite_op);
     579           1 :                 break;
     580         101 :         case GF_EVG_ALPHA_FUN:
     581             :                 JS_FreeValue(c, canvas->alpha_cbk);
     582         101 :                 if (JS_IsNull(value) || !JS_IsFunction(c, value)) {
     583         100 :                         canvas->alpha_cbk = JS_UNDEFINED;
     584         100 :                         gf_evg_surface_set_alpha_callback(canvas->surface, NULL, NULL);
     585             :                 } else {
     586           1 :                         canvas->alpha_cbk = JS_DupValue(c, value);
     587           1 :                         gf_evg_surface_set_alpha_callback(canvas->surface, evg_get_alpha, canvas);
     588             :                 }
     589         101 :                 return JS_UNDEFINED;
     590             :         }
     591           1 :         return JS_UNDEFINED;
     592             : }
     593             : 
     594             : 
     595         432 : static JSValue canvas_clear_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_float, Bool is_3d)
     596             : {
     597             :         s32 i;
     598             :         s32 idx=0;
     599             :         GF_Err e;
     600             :         GF_IRect rc, *irc;
     601             :         u32 r=0, g=0, b=0, a=255;
     602             :         GF_Color col;
     603         432 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, is_3d ? canvas3d_class_id : canvas_class_id);
     604         432 :         if (!canvas)
     605           0 :                 return JS_EXCEPTION;
     606             : 
     607             :         irc = NULL;
     608         864 :         if (argc && JS_IsObject(argv[0])) {
     609             :                 irc = &rc;
     610             :                 idx=1;
     611         100 :                 if (!canvas_get_irect(c, argv[0], &rc))
     612           0 :                         return JS_EXCEPTION;
     613             :         }
     614        1096 :         if ((argc>idx) && JS_IsString(argv[idx])) {
     615             :                 const char *str = JS_ToCString(c, argv[idx]);
     616         232 :                 col = gf_color_parse(str);
     617         232 :                 JS_FreeCString(c, str);
     618             :         } else {
     619         200 :                 if (argc>4+idx) argc = 4+idx;
     620         600 :                 for (i=idx; i<argc; i++) {
     621             :                         s32 v;
     622         400 :                         if (use_float) {
     623             :                                 Double d;
     624         400 :                                 if (JS_ToFloat64(c, &d, argv[i]))
     625           0 :                                         return JS_EXCEPTION;
     626         400 :                                 v = (s32) (d*255);
     627           0 :                         } else if (JS_ToInt32(c, &v, argv[i])) {
     628           0 :                                 return JS_EXCEPTION;
     629             :                         }
     630             : 
     631         400 :                         if (v<0) v = 0;
     632         400 :                         else if (v>255) v = 255;
     633             : 
     634         400 :                         if (i==idx) r=v;
     635         200 :                         else if (i==idx+1) g=v;
     636         100 :                         else if (i==idx+2) b=v;
     637           0 :                         else a=v;
     638             :                 }
     639         200 :                 col = GF_COL_ARGB(a, r, g, b) ;
     640             :         }
     641         432 :         e = gf_evg_surface_clear(canvas->surface, irc, col);
     642         432 :         if (e)
     643           0 :                 return JS_EXCEPTION;
     644         432 :         return JS_UNDEFINED;
     645             : }
     646           1 : static JSValue canvas_clear(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     647             : {
     648           1 :         return canvas_clear_ex(c, obj, argc, argv, GF_FALSE, GF_FALSE);
     649             : }
     650         305 : static JSValue canvas_clearf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     651             : {
     652         305 :         return canvas_clear_ex(c, obj, argc, argv, GF_TRUE, GF_FALSE);
     653             : }
     654             : 
     655           4 : static JSValue canvas_rgb_yuv(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool to_rgb, Bool is_3d)
     656             : {
     657             :         GF_Err e;
     658           4 :         Double _r=0, _g=0, _b=0, _a=1.0;
     659             :         Float r=0, g=0, b=0, a=1.0;
     660             :         Bool as_array = GF_FALSE;
     661             :         u32 arg_idx=0;
     662             :         Float y, u, v;
     663             :         JSValue ret;
     664           4 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, is_3d ? canvas3d_class_id : canvas_class_id);
     665           4 :         if (!canvas || !argc)
     666           0 :                 return JS_EXCEPTION;
     667             : 
     668           8 :         if (JS_IsBool(argv[0])) {
     669           0 :                 as_array = JS_ToBool(c, argv[0]);
     670             :                 arg_idx=1;
     671             :         }
     672           4 :         if (!get_color_from_args(c, argc, argv, arg_idx, &_a, &_r, &_g, &_b))
     673           0 :                 return JS_EXCEPTION;
     674           4 :         r = (Float) _r;
     675           4 :         g = (Float) _g;
     676           4 :         b = (Float) _b;
     677           4 :         a = (Float) _a;
     678           4 :         if (to_rgb) {
     679           2 :                 e = gf_evg_yuv_to_rgb_f(canvas->surface, r, g, b, &y, &u, &v);
     680             :         } else {
     681           2 :                 e = gf_gf_evg_rgb_to_yuv_f(canvas->surface, r, g, b, &y, &u, &v);
     682             :         }
     683           4 :         if (e)
     684           0 :                 return JS_EXCEPTION;
     685           4 :         if (as_array) {
     686           0 :                 ret = JS_NewArray(c);
     687           0 :                 JS_SetPropertyStr(c, ret, "length", JS_NewInt32(c, 4) );
     688           0 :                 JS_SetPropertyUint32(c, ret, 0, JS_NewFloat64(c, y) );
     689           0 :                 JS_SetPropertyUint32(c, ret, 1, JS_NewFloat64(c, u) );
     690           0 :                 JS_SetPropertyUint32(c, ret, 2, JS_NewFloat64(c, v) );
     691           0 :                 JS_SetPropertyUint32(c, ret, 3, JS_NewFloat64(c, a) );
     692             :         } else {
     693           4 :                 ret = JS_NewObject(c);
     694           8 :                 JS_SetPropertyStr(c, ret, "r", JS_NewFloat64(c, y) );
     695           8 :                 JS_SetPropertyStr(c, ret, "g", JS_NewFloat64(c, u) );
     696           8 :                 JS_SetPropertyStr(c, ret, "b", JS_NewFloat64(c, v) );
     697           8 :                 JS_SetPropertyStr(c, ret, "a", JS_NewFloat64(c, a) );
     698             :         }
     699           4 :         return ret;
     700             : }
     701             : 
     702         407 : static JSValue canvas_reassign_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_3d)
     703             : {
     704             :         GF_Err e;
     705             :         u8 *data;
     706         407 :         size_t data_size=0;
     707         407 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, is_3d ? canvas3d_class_id : canvas_class_id);
     708         407 :         if (!canvas || !argc) return JS_EXCEPTION;
     709         814 :         if (!JS_IsObject(argv[0])) return JS_EXCEPTION;
     710             : 
     711         407 :         if (canvas->owns_data) {
     712           0 :                 gf_free(canvas->data);
     713           0 :                 canvas->owns_data = GF_FALSE;
     714             :         }
     715         407 :         canvas->data = NULL;
     716         407 :         data = JS_GetArrayBuffer(c, &data_size, argv[0]);
     717         407 :         if (!data || (data_size<canvas->mem_size)) {
     718             :                 e = GF_BAD_PARAM;
     719             :         } else {
     720         407 :                 canvas->data = data;
     721         407 :                 e = gf_evg_surface_attach_to_buffer(canvas->surface, canvas->data, canvas->width, canvas->height, 0, canvas->stride, canvas->pf);
     722             :         }
     723         407 :         if (e) return JS_EXCEPTION;
     724         407 :         return JS_UNDEFINED;
     725             : }
     726             : 
     727         199 : static JSValue canvas_reassign(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     728             : {
     729         199 :         return canvas_reassign_ex(c, obj, argc, argv, GF_FALSE);
     730             : }
     731             : 
     732           1 : static JSValue canvas_toYUV(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     733             : {
     734           1 :         return canvas_rgb_yuv(c, obj, argc, argv, GF_FALSE, GF_FALSE);
     735             : }
     736             : 
     737           1 : static JSValue canvas_toRGB(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     738             : {
     739           1 :         return canvas_rgb_yuv(c, obj, argc, argv, GF_TRUE, GF_FALSE);
     740             : }
     741             : 
     742         934 : static JSValue canvas_fill(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     743             : {
     744             :         GF_EVGStencil *stencil;
     745             :         GF_JSTexture *tx;
     746         934 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas_class_id);
     747         934 :         if (!canvas || !argc) return JS_EXCEPTION;
     748         934 :         stencil = JS_GetOpaque(argv[0], stencil_class_id);
     749         934 :         if (stencil) {
     750         614 :                 gf_evg_surface_fill(canvas->surface, stencil);
     751         614 :                 return JS_UNDEFINED;
     752             :         }
     753         320 :         tx = JS_GetOpaque(argv[0], texture_class_id);
     754         320 :         if (tx) {
     755         320 :                 gf_evg_surface_fill(canvas->surface, tx->stencil);
     756         320 :                 return JS_UNDEFINED;
     757             :         }
     758           0 :         return JS_EXCEPTION;
     759             : }
     760             : 
     761             : static const JSCFunctionListEntry canvas_funcs[] =
     762             : {
     763             :         JS_CGETSET_MAGIC_DEF("centered", canvas_getProperty, canvas_setProperty, GF_EVG_CENTERED),
     764             :         JS_CGETSET_MAGIC_DEF("path", NULL, canvas_setProperty, GF_EVG_PATH),
     765             :         JS_CGETSET_MAGIC_DEF("clipper", NULL, canvas_setProperty, GF_EVG_CLIPPER),
     766             :         JS_CGETSET_MAGIC_DEF("matrix", NULL, canvas_setProperty, GF_EVG_MATRIX),
     767             :         JS_CGETSET_MAGIC_DEF("matrix3d", NULL, canvas_setProperty, GF_EVG_MATRIX_3D),
     768             :         JS_CGETSET_MAGIC_DEF("compositeOperation", canvas_getProperty, canvas_setProperty, GF_EVG_COMPOSITE_OP),
     769             :         JS_CGETSET_MAGIC_DEF("on_alpha", canvas_getProperty, canvas_setProperty, GF_EVG_ALPHA_FUN),
     770             :         JS_CFUNC_DEF("clear", 0, canvas_clear),
     771             :         JS_CFUNC_DEF("clearf", 0, canvas_clearf),
     772             :         JS_CFUNC_DEF("fill", 0, canvas_fill),
     773             :         JS_CFUNC_DEF("reassign", 0, canvas_reassign),
     774             :         JS_CFUNC_DEF("toYUV", 0, canvas_toYUV),
     775             :         JS_CFUNC_DEF("toRGB", 0, canvas_toRGB),
     776             : };
     777             : 
     778             : 
     779           1 : static JSValue canvas3d_clear(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     780             : {
     781           1 :         return canvas_clear_ex(c, obj, argc, argv, GF_FALSE, GF_TRUE);
     782             : }
     783         125 : static JSValue canvas3d_clearf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     784             : {
     785         125 :         return canvas_clear_ex(c, obj, argc, argv, GF_TRUE, GF_TRUE);
     786             : }
     787         208 : static JSValue canvas3d_reassign(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     788             : {
     789         208 :         return canvas_reassign_ex(c, obj, argc, argv, GF_TRUE);
     790             : }
     791             : 
     792           1 : static JSValue canvas3d_toYUV(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     793             : {
     794           1 :         return canvas_rgb_yuv(c, obj, argc, argv, GF_FALSE, GF_TRUE);
     795             : }
     796             : 
     797           1 : static JSValue canvas3d_toRGB(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
     798             : {
     799           1 :         return canvas_rgb_yuv(c, obj, argc, argv, GF_TRUE, GF_TRUE);
     800             : }
     801             : 
     802             : Bool vai_call_lerp(EVG_VAI *vai, GF_EVGFragmentParam *frag);
     803             : 
     804             : #ifdef EVG_USE_JS_SHADER
     805             : static Bool evg_frag_shader_fun(void *udta, GF_EVGFragmentParam *frag)
     806             : {
     807             :         Bool frag_valid;
     808             :         JSValue res;
     809             :         GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
     810             :         if (!canvas) return GF_FALSE;
     811             : 
     812             :         JS_SetOpaque(canvas->frag_obj, frag);
     813             :         res = JS_Call(canvas->ctx, canvas->frag_shader, canvas->obj, 1, &canvas->frag_obj);
     814             :         frag_valid = frag->frag_valid ? 1 : 0;
     815             :         if (JS_IsException(res)) frag_valid = GF_FALSE;
     816             :         else if (!JS_IsUndefined(res)) frag_valid = JS_ToBool(canvas->ctx, res) ? GF_TRUE : GF_FALSE;
     817             :         JS_FreeValue(canvas->ctx, res);
     818             :         return frag_valid;
     819             : }
     820             : 
     821             : static Bool evg_vert_shader_fun(void *udta, GF_EVGVertexParam *vertex)
     822             : {
     823             :         Bool vert_valid=GF_TRUE;
     824             :         JSValue res;
     825             :         GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
     826             :         if (!canvas) return GF_FALSE;
     827             : 
     828             :         JS_SetOpaque(canvas->vert_obj, vertex);
     829             :         res = JS_Call(canvas->ctx, canvas->frag_shader, canvas->obj, 1, &canvas->vert_obj);
     830             :         if (JS_IsException(res)) vert_valid = GF_FALSE;
     831             :         else if (!JS_IsUndefined(res)) vert_valid = JS_ToBool(canvas->ctx, res) ? GF_TRUE : GF_FALSE;
     832             :         JS_FreeValue(canvas->ctx, res);
     833             :         return vert_valid;
     834             : }
     835             : #endif
     836             : 
     837           1 : static JSValue canvas3d_getProperty(JSContext *c, JSValueConst obj, int magic)
     838             : {
     839           1 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
     840           1 :         if (!canvas) return JS_EXCEPTION;
     841           1 :         switch (magic) {
     842           0 :         case GF_EVG_FRAG_SHADER: return JS_DupValue(c, canvas->frag_shader);
     843           0 :         case GF_EVG_VERT_SHADER: return JS_DupValue(c, canvas->vert_shader);
     844           1 :         case GF_EVG_DEPTH_BUFFER: return JS_DupValue(c, canvas->depth_buffer);
     845             :         }
     846           0 :         return JS_UNDEFINED;
     847             : }
     848             : 
     849             : static Bool evg_frag_shader_ops(void *udta, GF_EVGFragmentParam *frag);
     850             : static Bool evg_vert_shader_ops(void *udta, GF_EVGVertexParam *frag);
     851             : 
     852        1443 : static JSValue canvas3d_setProperty(JSContext *ctx, JSValueConst obj, JSValueConst value, int magic)
     853             : {
     854             :         Float f;
     855             :         s32 ival;
     856             :         GF_Err e = GF_OK;
     857        1443 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
     858        1443 :         if (!canvas) return JS_EXCEPTION;
     859        1443 :         switch (magic) {
     860           0 :         case GF_EVG_CLIPPER:
     861           0 :                 if (JS_IsNull(value)) {
     862           0 :                         e = gf_evg_surface_set_clipper(canvas->surface, NULL);
     863             :                 } else {
     864             :                         GF_IRect rc;
     865           0 :                         canvas_get_irect(ctx, value, &rc);
     866           0 :                         e = gf_evg_surface_set_clipper(canvas->surface, &rc);
     867             :                 }
     868             :                 break;
     869         326 :         case GF_EVG_FRAG_SHADER:
     870             :                 JS_FreeValue(ctx, canvas->frag_shader);
     871         326 :                 canvas->frag_shader = JS_UNDEFINED;
     872         326 :                 canvas->frag = NULL;
     873         326 :                 if (JS_IsNull(value)) {
     874           0 :                         canvas->frag_shader = JS_UNDEFINED;
     875           0 :                         e = gf_evg_surface_set_fragment_shader(canvas->surface, NULL, NULL);
     876             : #ifdef EVG_USE_JS_SHADER
     877             :                 } else if (JS_IsFunction(ctx, value)) {
     878             :                         canvas->frag_shader = JS_DupValue(ctx, value);
     879             :                         e = gf_evg_surface_set_fragment_shader(canvas->surface, evg_frag_shader_fun, canvas);
     880             : #endif
     881         326 :                 } else if (JS_IsObject(value)) {
     882         326 :                         canvas->frag = JS_GetOpaque(value, shader_class_id);
     883         326 :                         if (!canvas->frag || (canvas->frag->mode != GF_EVG_SHADER_FRAGMENT))
     884           0 :                                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "Invalid fragment shader object");
     885         326 :                         canvas->frag_shader = JS_DupValue(ctx, value);
     886         326 :                         e = gf_evg_surface_set_fragment_shader(canvas->surface, evg_frag_shader_ops, canvas);
     887         326 :                         if (!e) e = gf_evg_surface_disable_early_depth(canvas->surface, canvas->frag->disable_early_z);
     888             :                 } else {
     889           0 :                         canvas->frag_shader = JS_UNDEFINED;
     890           0 :                         e = gf_evg_surface_set_fragment_shader(canvas->surface, NULL, NULL);
     891             :                 }
     892             :                 break;
     893         225 :         case GF_EVG_VERT_SHADER:
     894             :                 JS_FreeValue(ctx, canvas->vert_shader);
     895         225 :                 canvas->vert_shader = JS_UNDEFINED;
     896         225 :                 canvas->vert = NULL;
     897         225 :                 if (JS_IsNull(value)) {
     898         100 :                         canvas->vert_shader = JS_UNDEFINED;
     899         100 :                         e = gf_evg_surface_set_vertex_shader(canvas->surface, NULL, NULL);
     900             : #ifdef EVG_USE_JS_SHADER
     901             :                 } else if (JS_IsFunction(ctx, value)) {
     902             :                         canvas->vert_shader = JS_DupValue(ctx, value);
     903             :                         e = gf_evg_surface_set_vertex_shader(canvas->surface, evg_vert_shader_fun, canvas);
     904             : #endif
     905         125 :                 } else if (JS_IsObject(value)) {
     906         125 :                         canvas->vert = JS_GetOpaque(value, shader_class_id);
     907         125 :                         if (!canvas->vert || (canvas->vert->mode != GF_EVG_SHADER_VERTEX))
     908           0 :                                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "Invalid fragment shader object");
     909         125 :                         canvas->vert_shader = JS_DupValue(ctx, value);
     910         125 :                         e = gf_evg_surface_set_vertex_shader(canvas->surface, evg_vert_shader_ops, canvas);
     911             :                 } else {
     912           0 :                         canvas->frag_shader = JS_UNDEFINED;
     913           0 :                         e = gf_evg_surface_set_fragment_shader(canvas->surface, NULL, NULL);
     914             :                 }
     915             :                 break;
     916          10 :         case GF_EVG_CCW:
     917          10 :                 e = gf_evg_surface_set_ccw(canvas->surface, JS_ToBool(ctx, value) );
     918          10 :                 break;
     919         226 :         case GF_EVG_BACKCULL:
     920         226 :                 e = gf_evg_surface_set_backcull(canvas->surface, JS_ToBool(ctx, value) );
     921         226 :                 break;
     922         226 :         case GF_EVG_ANTIALIAS:
     923         226 :                 e = gf_evg_surface_set_antialias(canvas->surface, JS_ToBool(ctx, value) );
     924         226 :                 break;
     925             : 
     926          19 :         case GF_EVG_DEPTH_BUFFER:
     927             :         {
     928             :                 Float *depthb;
     929             :                 u32 dsize;
     930             :                 JS_FreeValue(ctx, canvas->depth_buffer);
     931          19 :                 canvas->depth_buffer = JS_UNDEFINED;
     932          19 :                 depthb = (Float *) evg_get_array(ctx, value, &dsize);
     933          19 :                 if (!depthb) {
     934           0 :                         canvas->depth_buffer = JS_NULL;
     935           0 :                         e = gf_evg_surface_set_depth_buffer(canvas->surface, NULL);
     936             :                 } else {
     937          19 :                         u32 req_size = canvas->width*canvas->height*sizeof(Float);
     938          19 :                         if (req_size>dsize) {
     939             :                                 e = GF_BAD_PARAM;
     940           0 :                                 gf_evg_surface_set_depth_buffer(canvas->surface, NULL);
     941             :                         } else {
     942          19 :                                 canvas->depth_buffer = JS_DupValue(ctx, value);
     943          19 :                                 e = gf_evg_surface_set_depth_buffer(canvas->surface, depthb);
     944             :                         }
     945             :                 }
     946             :                 break;
     947             :         }
     948             : 
     949          10 :         case GF_EVG_MINDEPTH:
     950          10 :                 EVG_GET_FLOAT(f, value);
     951          10 :                 e = gf_evg_surface_set_min_depth(canvas->surface, f);
     952          10 :                 break;
     953          10 :         case GF_EVG_MAXDEPTH:
     954          10 :                 EVG_GET_FLOAT(f, value);
     955          10 :                 e = gf_evg_surface_set_max_depth(canvas->surface, f);
     956          10 :                 break;
     957           0 :         case GF_EVG_DEPTHTEST:
     958           0 :                 if (JS_ToInt32(ctx, &ival, value))
     959           0 :                         return js_throw_err(ctx, GF_BAD_PARAM);
     960           0 :                 e = gf_evg_set_depth_test(canvas->surface, ival);
     961           0 :                 break;
     962          10 :         case GF_EVG_POINTSIZE:
     963          10 :                 EVG_GET_FLOAT(f, value);
     964          10 :                 e = gf_evg_surface_set_point_size(canvas->surface, f);
     965          10 :                 break;
     966          10 :         case GF_EVG_POINTSMOOTH:
     967          10 :                 e = gf_evg_surface_set_point_smooth(canvas->surface, JS_ToBool(ctx, value) );
     968          10 :                 break;
     969          10 :         case GF_EVG_LINESIZE:
     970          10 :                 EVG_GET_FLOAT(f, value);
     971          10 :                 e = gf_evg_surface_set_line_size(canvas->surface, f);
     972          10 :                 break;
     973         125 :         case GF_EVG_CLIP_ZERO:
     974         125 :                 e = gf_evg_surface_set_clip_zero(canvas->surface, JS_ToBool(ctx, value) ? GF_TRUE : GF_FALSE);
     975         125 :                 break;
     976         226 :         case GF_EVG_DEPTH_TEST:
     977         226 :                 if (JS_ToInt32(ctx, &ival, value)) return JS_EXCEPTION;
     978         226 :                 e = gf_evg_set_depth_test(canvas->surface, ival);
     979         226 :                 break;
     980          10 :         case GF_EVG_WRITE_DEPTH:
     981          10 :                 e = gf_evg_surface_write_depth(canvas->surface, JS_ToBool(ctx, value) ? GF_TRUE : GF_FALSE);
     982          10 :                 break;
     983             :         }
     984        1443 :         if (e) return js_throw_err(ctx, e);
     985             : 
     986        1443 :         return JS_UNDEFINED;
     987             : }
     988             : 
     989         402 : static JSValue canvas3d_set_matrix(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_proj)
     990             : {
     991             :         GF_Err e;
     992             :         GF_Matrix mx;
     993         402 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
     994         402 :         if (!canvas) return JS_EXCEPTION;
     995             : 
     996         402 :         gf_mx_init(mx);
     997         402 :         if (argc) {
     998         402 :                 if (JS_IsArray(c, argv[0])) {
     999         402 :                         u32 i, len=0;
    1000         402 :                         JSValue v = JS_GetPropertyStr(c, argv[0], "length");
    1001         402 :                         JS_ToInt32(c, &len, v);
    1002             :                         JS_FreeValue(c, v);
    1003         402 :                         if (len < 16) return JS_EXCEPTION;
    1004        6432 :                         for (i=0; i<16; i++) {
    1005        6432 :                                 Double val=0;
    1006        6432 :                                 v = JS_GetPropertyUint32(c, argv[0], i);
    1007        6432 :                                 s32 res = JS_ToFloat64(c, &val, v);
    1008             :                                 JS_FreeValue(c, v);
    1009        6432 :                                 if (res) return JS_EXCEPTION;
    1010        6432 :                                 mx.m[i] =  FLT2FIX(val);
    1011             :                         }
    1012             :                 } else {
    1013           0 :                         return js_throw_err_msg(c, GF_NOT_SUPPORTED, "only float array currently supported for matrices");
    1014             :                 }
    1015             :         }
    1016         402 :         if (is_proj)
    1017         201 :                 e = gf_evg_surface_set_projection(canvas->surface, &mx);
    1018             :         else
    1019         201 :                 e = gf_evg_surface_set_modelview(canvas->surface, &mx);
    1020         402 :         return e ? JS_EXCEPTION : JS_UNDEFINED;
    1021             : }
    1022         201 : static JSValue canvas3d_projection(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    1023             : {
    1024         201 :         return canvas3d_set_matrix(c, obj, argc, argv, GF_TRUE);
    1025             : }
    1026         201 : static JSValue canvas3d_modelview(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    1027             : {
    1028         201 :         return canvas3d_set_matrix(c, obj, argc, argv, GF_FALSE);
    1029             : }
    1030             : 
    1031         473 : uint8_t *evg_get_array(JSContext *ctx, JSValueConst obj, u32 *size)
    1032             : {
    1033             :         JSValue v;
    1034             :         /*ArrayBuffer*/
    1035             :         size_t psize;
    1036         473 :         uint8_t *res = JS_GetArrayBuffer(ctx, &psize, obj);
    1037         473 :         if (res) {
    1038          19 :                 *size = (u32) psize;
    1039          19 :                 return res;
    1040             :         }
    1041             :         /*ArrayView*/
    1042         454 :         v = JS_GetPropertyStr(ctx, obj, "buffer");
    1043         454 :         if (JS_IsUndefined(v)) return NULL;
    1044         454 :         res = JS_GetArrayBuffer(ctx, &psize, v);
    1045             :         JS_FreeValue(ctx, v);
    1046         454 :         *size = (u32) psize;
    1047         454 :         return res;
    1048             : }
    1049         226 : static JSValue canvas3d_draw_array(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    1050             : {
    1051             :         uint8_t *indices=NULL;
    1052             :         uint8_t *vertices=NULL;
    1053         226 :         u32 idx_size=0, vx_size, nb_comp=3;
    1054             :         GF_Err e;
    1055         226 :         GF_EVGPrimitiveType prim_type=GF_EVG_TRIANGLES;
    1056         226 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
    1057         226 :         if (!canvas || argc<2) return JS_EXCEPTION;
    1058             : 
    1059         226 :         indices = evg_get_array(c, argv[0], &idx_size);
    1060         226 :         vertices = evg_get_array(c, argv[1], &vx_size);
    1061         226 :         if (!indices || ! vertices) return JS_EXCEPTION;
    1062         226 :         if (argc>2) {
    1063         226 :                 JS_ToInt32(c, (s32 *)&prim_type, argv[2]);
    1064         226 :                 if (!prim_type) return JS_EXCEPTION;
    1065         226 :                 if (argc>3) {
    1066           0 :                         JS_ToInt32(c, &nb_comp, argv[3]);
    1067           0 :                         if ((nb_comp<2) || (nb_comp>4)) return JS_EXCEPTION;
    1068             :                 }
    1069             :         }
    1070         226 :         if (vx_size % nb_comp)
    1071           0 :                 return JS_EXCEPTION;
    1072         226 :         idx_size /= sizeof(s32);
    1073         226 :         vx_size /= sizeof(Float);
    1074         226 :         e = gf_evg_surface_draw_array(canvas->surface, (u32 *)indices, idx_size, (Float *)vertices, vx_size, nb_comp, prim_type);
    1075         226 :         if (e) return JS_EXCEPTION;
    1076             : 
    1077         226 :         return JS_UNDEFINED;
    1078             : }
    1079             : 
    1080             : static void text_update_path(GF_JSText *txt, Bool for_centered);
    1081             : 
    1082         100 : static JSValue canvas3d_draw_path(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
    1083             : {
    1084             :         GF_Err e = GF_OK;
    1085             :         Float z = 0;
    1086         100 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
    1087         100 :         if (!canvas || argc<1) return JS_EXCEPTION;
    1088         100 :         if (argc>1) {
    1089           0 :                 EVG_GET_FLOAT(z, argv[1]);
    1090             :         }
    1091             : 
    1092         100 :         GF_Path *gp = JS_GetOpaque(argv[0], path_class_id);
    1093         100 :         if (gp) {
    1094           0 :                 e = gf_evg_surface_draw_path(canvas->surface, gp, z);
    1095             :         } else {
    1096         100 :                 GF_JSText *text = JS_GetOpaque(argv[0], text_class_id);
    1097         100 :                 if (text) {
    1098         100 :                         text_update_path(text, GF_TRUE);
    1099         100 :                         e = gf_evg_surface_draw_path(canvas->surface, text->path, z);
    1100             :                 }
    1101             :         }
    1102         100 :         if (e) return js_throw_err(ctx, e);
    1103         100 :         return JS_UNDEFINED;
    1104             : }
    1105         226 : static JSValue canvas3d_clear_depth(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
    1106             : {
    1107             :         GF_Err e;
    1108             :         Float depth = 1.0;
    1109         226 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
    1110         226 :         if (!canvas) return JS_EXCEPTION;
    1111         226 :         if (argc)
    1112         226 :                 EVG_GET_FLOAT(depth, argv[0]);
    1113             : 
    1114         226 :         e = gf_evg_surface_clear_depth(canvas->surface, depth);
    1115         226 :         if (e) return JS_EXCEPTION;
    1116         226 :         return JS_UNDEFINED;
    1117             : }
    1118         226 : static JSValue canvas3d_viewport(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
    1119             : {
    1120             :         s32 x, y, w, h;
    1121             :         GF_Err e;
    1122         226 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
    1123         226 :         if (!canvas) return JS_EXCEPTION;
    1124         226 :         if (argc) {
    1125         226 :                 if (argc<4) return js_throw_err(ctx, GF_BAD_PARAM);
    1126         226 :                 if (JS_ToInt32(ctx, &x, argv[0])) return js_throw_err(ctx, GF_BAD_PARAM);;
    1127         226 :                 if (JS_ToInt32(ctx, &y, argv[1])) return js_throw_err(ctx, GF_BAD_PARAM);;
    1128         226 :                 if (JS_ToInt32(ctx, &w, argv[2])) return js_throw_err(ctx, GF_BAD_PARAM);;
    1129         226 :                 if (JS_ToInt32(ctx, &h, argv[3])) return js_throw_err(ctx, GF_BAD_PARAM);;
    1130             :         } else {
    1131           0 :                 x = y = 0;
    1132           0 :                 w = canvas->width;
    1133           0 :                 h = canvas->height;
    1134             :         }
    1135         226 :         e = gf_evg_surface_viewport(canvas->surface, x, y, w, h);
    1136         226 :         if (e) return JS_EXCEPTION;
    1137         226 :         return JS_UNDEFINED;
    1138             : }
    1139             : 
    1140             : 
    1141             : enum
    1142             : {
    1143             :         EVG_OP_IF=1,
    1144             :         EVG_OP_ELSE,
    1145             :         EVG_OP_ELSEIF,
    1146             :         EVG_OP_END,
    1147             :         EVG_OP_GOTO,
    1148             :         EVG_OP_ASSIGN,
    1149             :         EVG_OP_DISCARD,
    1150             :         EVG_OP_ADD,
    1151             :         EVG_OP_SUB,
    1152             :         EVG_OP_MUL,
    1153             :         EVG_OP_DIV,
    1154             :         EVG_OP_NEG,
    1155             :         EVG_OP_LESS,
    1156             :         EVG_OP_LESS_EQUAL,
    1157             :         EVG_OP_GREATER,
    1158             :         EVG_OP_GREATER_EQUAL,
    1159             :         EVG_OP_EQUAL,
    1160             :         EVG_OP_NOT_EQUAL,
    1161             :         EVG_OP_SAMPLER,
    1162             :         EVG_OP_SAMPLER_YUV,
    1163             : 
    1164             :         EVG_OP_PRINT,
    1165             : 
    1166             :         EVG_OP_NORMALIZE,
    1167             :         EVG_OP_LENGTH,
    1168             :         EVG_OP_DISTANCE,
    1169             :         EVG_OP_DOT,
    1170             :         EVG_OP_CROSS,
    1171             :         EVG_OP_POW,
    1172             :         EVG_OP_SIN,
    1173             :         EVG_OP_ASIN,
    1174             :         EVG_OP_COS,
    1175             :         EVG_OP_ACOS,
    1176             :         EVG_OP_TAN,
    1177             :         EVG_OP_ATAN,
    1178             :         EVG_OP_LOG,
    1179             :         EVG_OP_EXP,
    1180             :         EVG_OP_LOG2,
    1181             :         EVG_OP_EXP2,
    1182             :         EVG_OP_SINH,
    1183             :         EVG_OP_COSH,
    1184             :         EVG_OP_SQRT,
    1185             :         EVG_OP_INVERSE_SQRT,
    1186             :         EVG_OP_ABS,
    1187             :         EVG_OP_SIGN,
    1188             :         EVG_OP_FLOOR,
    1189             :         EVG_OP_CEIL,
    1190             :         EVG_OP_FRACT,
    1191             :         EVG_OP_MOD,
    1192             :         EVG_OP_MIN,
    1193             :         EVG_OP_MAX,
    1194             :         EVG_OP_CLAMP,
    1195             :         EVG_OP_RGB2YUV,
    1196             :         EVG_OP_YUV2RGB,
    1197             : 
    1198             :         EVG_FIRST_VAR_ID
    1199             : };
    1200             : 
    1201             : enum
    1202             : {
    1203             :         VAR_FRAG_ARGB=1,
    1204             :         VAR_FRAG_YUV,
    1205             :         VAR_FRAG_X,
    1206             :         VAR_FRAG_Y,
    1207             :         VAR_FRAG_DEPTH,
    1208             :         VAR_FRAG_W,
    1209             :         VAR_UNIFORM,
    1210             :         VAR_VERTEX_IN,
    1211             :         VAR_VERTEX_OUT,
    1212             :         VAR_VAI,
    1213             :         VAR_VA,
    1214             :         VAR_MATRIX,
    1215             : };
    1216             : 
    1217             : 
    1218             : static GFINLINE Float isqrtf(Float v)
    1219             : {
    1220       10800 :         v = sqrtf(v);
    1221       10800 :         if (v) v = 1 / v;
    1222             :         return v;
    1223             : }
    1224             : static GFINLINE Float signf(Float v)
    1225             : {
    1226       10800 :         if (v==0) return 0;
    1227       10800 :         if (v>0) return 1.0;
    1228             :         return -1.0;
    1229             : }
    1230             : static GFINLINE Float fractf(Float v)
    1231             : {
    1232       10800 :         return v - floorf(v);
    1233             : }
    1234             : static GFINLINE Float _modf(Float x, Float y)
    1235             : {
    1236       10800 :         if (!y) return 0.0;
    1237       10800 :         return x - y * floorf(x/y);
    1238             : }
    1239             : 
    1240             : 
    1241             : 
    1242             : #if defined(WIN32) && !defined(__GNUC__)
    1243             : # include <intrin.h>
    1244             : # define GPAC_HAS_SSE2
    1245             : #else
    1246             : # ifdef __SSE2__
    1247             : #  include <emmintrin.h>
    1248             : #  define GPAC_HAS_SSE2
    1249             : # endif
    1250             : #endif
    1251             : 
    1252             : #ifdef GPAC_HAS_SSE2
    1253             : 
    1254             : static Float evg_float_clamp(Float val, Float minval, Float maxval)
    1255             : {
    1256             :     _mm_store_ss( &val, _mm_min_ss( _mm_max_ss(_mm_set_ss(val),_mm_set_ss(minval)), _mm_set_ss(maxval) ) );
    1257             :     return val;
    1258             : }
    1259             : #else
    1260             : 
    1261             : #define evg_float_clamp(_val, _minval, _maxval)\
    1262             :         (_val<_minval) ? _minval : (_val>_maxval) ? _maxval : _val;
    1263             : 
    1264             : #endif
    1265             : 
    1266             : 
    1267             : /*
    1268             : 
    1269             : */
    1270             : 
    1271     4817326 : static Bool evg_shader_ops(GF_JSCanvas *canvas, EVGShader *shader, GF_EVGFragmentParam *frag, GF_EVGVertexParam *vert)
    1272             : {
    1273             :         u32 op_idx, dim;
    1274             :         GF_Vec4 tmpl, tmpr;
    1275             :         GF_Vec4 *left_val, *right_val, *right2_val;
    1276             :         u32 if_level=0;
    1277             :         u32 nif_level=0;
    1278             :         Bool cond_res;
    1279             : 
    1280             :         //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
    1281     4817326 :         tmpl.x = tmpl.y = tmpl.z = tmpl.q = 0;
    1282             :         left_val = &tmpl;
    1283     4817326 :         tmpr.x = tmpr.y = tmpr.z = tmpr.q = 0;
    1284             :         right_val = &tmpr;
    1285             : 
    1286    83733378 :         for (op_idx=0; op_idx<shader->nb_ops; op_idx++) {
    1287             :                 u32 next_idx, idx, var_idx;
    1288             :                 Bool has_next, norm_result=GF_FALSE;
    1289             :                 u8 right_val_type, left_val_type=0;
    1290             :                 u8 *left_val_type_ptr=NULL;
    1291    83733378 :                 ShaderOp *op = &shader->ops[op_idx];
    1292             : 
    1293    83733378 :                 if (op->op_type==EVG_OP_GOTO) {
    1294     1204487 :                         u32 stack_idx = op->left_value;
    1295     1204487 :                         if (op->uni_name) stack_idx = op->ival;
    1296             : 
    1297     1204487 :                         if (!stack_idx || (stack_idx > shader->nb_ops)) {
    1298           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Shader] Invalid goto operation, stack index %d not in stack indices [1, %d]\n", op->left_value, shader->nb_ops));
    1299           0 :                                 shader->invalid = GF_TRUE;
    1300             :                                 return GF_FALSE;
    1301             :                         }
    1302     1204487 :                         op_idx = stack_idx - 1;
    1303     1204487 :                         op = &shader->ops[op_idx];
    1304             :                 }
    1305             : 
    1306    83733378 :                 if (op->op_type==EVG_OP_ELSE) {
    1307     1204487 :                         if (nif_level) {
    1308      395087 :                                 if (nif_level==1) {
    1309             :                                         nif_level=0;
    1310      395087 :                                         if_level++;
    1311             :                                 }
    1312      809400 :                         } else if (if_level) {
    1313      809400 :                                 if_level--;
    1314             :                                 nif_level++;
    1315             :                         }
    1316     1204487 :                         continue;
    1317             :                 }
    1318    82528891 :                 if (op->op_type==EVG_OP_END) {
    1319             :                         assert(nif_level || if_level);
    1320     4239973 :                         if (nif_level) nif_level--;
    1321     3430573 :                         else if (if_level) if_level--;
    1322     4239973 :                         continue;
    1323             :                 }
    1324    78288918 :                 if (nif_level) {
    1325     1204487 :                         if (op->op_type==EVG_OP_IF) {
    1326           0 :                                 nif_level++;
    1327             :                         }
    1328     1204487 :                         continue;
    1329             :                 }
    1330             : 
    1331             :                 dim=4;
    1332    77084431 :                 if (op->left_value==VAR_FRAG_ARGB) {
    1333     4313043 :                         left_val = &frag->color;
    1334             :                         left_val_type = COMP_V4;
    1335     4313043 :                         frag->frag_valid = GF_EVG_FRAG_RGB;
    1336    72771388 :                 } else if (op->left_value==VAR_FRAG_YUV) {
    1337     1204487 :                         left_val = &frag->color;
    1338             :                         left_val_type = COMP_V4;
    1339     1204487 :                         frag->frag_valid = GF_EVG_FRAG_YUV;
    1340    71566901 :                 } else if (op->left_value==VAR_FRAG_X) {
    1341             :                         left_val = &tmpl;
    1342           0 :                         tmpl.x = frag->screen_x;
    1343             :                         left_val_type = COMP_FLOAT;
    1344    71566901 :                 } else if (op->left_value==VAR_FRAG_Y) {
    1345             :                         left_val = &tmpl;
    1346           0 :                         tmpl.x = frag->screen_y;
    1347             :                         left_val_type = COMP_FLOAT;
    1348    71566901 :                 } else if (op->left_value==VAR_FRAG_W) {
    1349             :                         left_val = &tmpl;
    1350           0 :                         tmpl.x = frag->persp_denum;
    1351             :                         left_val_type = COMP_FLOAT;
    1352    71566901 :                 } else if (op->left_value==VAR_FRAG_DEPTH) {
    1353           0 :                         left_val = (GF_Vec4 *) &frag->depth;
    1354             :                         left_val_type = COMP_FLOAT;
    1355    71566901 :                 } else if (op->left_value==VAR_VERTEX_IN) {
    1356        4500 :                         left_val = (GF_Vec4 *) &vert->in_vertex;
    1357             :                         left_val_type = COMP_V4;
    1358    71562401 :                 } else if (op->left_value==VAR_VERTEX_OUT) {
    1359        4500 :                         left_val = (GF_Vec4 *) &vert->out_vertex;
    1360             :                         left_val_type = COMP_V4;
    1361    71557901 :                 } else if (op->left_value==VAR_VAI) {
    1362        7200 :                         left_val = (GF_Vec4 *) &op->vai.vai->anchors[vert->vertex_idx_in_prim];
    1363             :                         left_val_type = COMP_V4;
    1364        7200 :                         norm_result = op->vai.vai->normalize;
    1365    71550701 :                 } else if (op->left_value) {
    1366    71550701 :                         u32 l_var_idx = op->left_value - EVG_FIRST_VAR_ID-1;
    1367    71550701 :                         left_val = &shader->vars[l_var_idx].vecval;
    1368    71550701 :                         left_val_type = shader->vars[l_var_idx].value_type;
    1369    71550701 :                         left_val_type_ptr = & shader->vars[l_var_idx].value_type;
    1370             :                 }
    1371             : 
    1372    77084431 :                 if (op->right_value>EVG_FIRST_VAR_ID) {
    1373    31904375 :                         var_idx = op->right_value - EVG_FIRST_VAR_ID-1;
    1374    31904375 :                         right_val = &shader->vars[var_idx].vecval;
    1375    31904375 :                         right_val_type = shader->vars[var_idx].value_type;
    1376    45180056 :                 } else if ((op->right_value==VAR_VAI) && op->vai.vai) {
    1377     7275459 :                         vai_call_lerp(op->vai.vai, frag);
    1378     7275459 :                         dim = MIN(4, op->vai.vai->result.dim);
    1379     7275459 :                         right_val = (GF_Vec4 *) &op->vai.vai->result.values[0];
    1380     7275459 :                         right_val_type = op->vai.vai->result.comp_type;
    1381    37904597 :                 } else if ((op->right_value==VAR_VA) && op->va.va) {
    1382             :                         u32 va_idx, j, nb_v_per_prim=3;
    1383             :                         EVG_VA *va = op->va.va;
    1384             : 
    1385        3600 :                         if (vert->ptype == GF_EVG_LINES)
    1386             :                                 nb_v_per_prim=2;
    1387        3600 :                         else if (vert->ptype == GF_EVG_POINTS)
    1388             :                                 nb_v_per_prim=1;
    1389             : 
    1390        3600 :                         if (va->interp_type==GF_EVG_VAI_PRIMITIVE) {
    1391        3600 :                                 va_idx = vert->prim_index * va->nb_comp;
    1392             :                         }
    1393           0 :                         else if (va->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
    1394           0 :                                 va_idx = vert->vertex_idx * va->nb_comp;
    1395             :                         } else {
    1396           0 :                                 va_idx = vert->prim_index * nb_v_per_prim * va->nb_comp;
    1397             :                         }
    1398             : 
    1399        3600 :                         if (va_idx+va->nb_comp > va->nb_values)
    1400             :                                 return GF_FALSE;
    1401             : 
    1402             :                         right_val = &tmpr;
    1403        3600 :                         right_val->x = right_val->y = right_val->z = right_val->q = 0;
    1404             :                         assert(va->nb_comp<=4);
    1405       10800 :                         for (j=0; j<va->nb_comp; j++) {
    1406       10800 :                                 ((Float *)right_val)[j] = va->values[va_idx+j];
    1407             :                         }
    1408        3600 :                         if (va->normalize) {
    1409        3600 :                                 if (va->nb_comp==2) {
    1410             :                                         Float len;
    1411           0 :                                         if (!right_val->x) len = ABS(right_val->y);
    1412           0 :                                         else if (!right_val->y) len = ABS(right_val->x);
    1413           0 :                                         else len = sqrtf(right_val->x*right_val->x + right_val->y*right_val->y);
    1414           0 :                                         if (len) {
    1415           0 :                                                 right_val->x/=len;
    1416           0 :                                                 right_val->y/=len;
    1417             :                                         }
    1418             :                                 } else {
    1419        3600 :                                         gf_vec_norm((GF_Vec *) right_val);
    1420             :                                 }
    1421             : 
    1422             :                         }
    1423        3600 :                         right_val_type = va->att_type;
    1424    37900997 :                 } else if ((op->right_value==VAR_MATRIX) && op->mx.mx) {
    1425        8100 :                         if (op->op_type==EVG_OP_MUL) {
    1426        8100 :                                 gf_mx_apply_vec_4x4(op->mx.mx, left_val);
    1427        8100 :                                 continue;
    1428             :                         }
    1429           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Shader] Invalid operation for right value matrix\n"));
    1430           0 :                         shader->invalid = GF_TRUE;
    1431             :                         return GF_FALSE;
    1432    37892897 :                 } else if (op->right_value==VAR_FRAG_ARGB) {
    1433           0 :                         right_val = &frag->color;
    1434             :                         right_val_type = COMP_V4;
    1435    37892897 :                 } else if (op->right_value==VAR_FRAG_YUV) {
    1436           0 :                         right_val = &frag->color;
    1437             :                         right_val_type = COMP_V4;
    1438    37892897 :                 } else if (op->right_value==VAR_FRAG_X) {
    1439             :                         right_val = &tmpr;
    1440      352352 :                         tmpr.x = frag->screen_x;
    1441             :                         right_val_type = COMP_FLOAT;
    1442    37540545 :                 } else if (op->right_value==VAR_FRAG_Y) {
    1443             :                         right_val = &tmpr;
    1444           0 :                         tmpr.x = frag->screen_y;
    1445             :                         right_val_type = COMP_FLOAT;
    1446    37540545 :                 } else if (op->right_value==VAR_FRAG_W) {
    1447             :                         right_val = &tmpr;
    1448           0 :                         tmpr.x = frag->persp_denum;
    1449             :                         right_val_type = COMP_FLOAT;
    1450    37540545 :                 } else if (op->right_value==VAR_FRAG_DEPTH) {
    1451             :                         right_val = &tmpr;
    1452           0 :                         tmpr.x = frag->depth;
    1453             :                         right_val_type = COMP_FLOAT;
    1454    37540545 :                 } else if (op->right_value==VAR_VERTEX_IN) {
    1455        8100 :                         right_val = &vert->in_vertex;
    1456             :                         right_val_type = COMP_V4;
    1457    37532445 :                 } else if (op->right_value==VAR_VERTEX_OUT) {
    1458           0 :                         right_val = &vert->out_vertex;
    1459             :                         right_val_type = COMP_V4;
    1460    37532445 :                 } else if (!op->right_value || (op->right_value==VAR_UNIFORM) ) {
    1461    37532445 :                         right_val = (GF_Vec4 *) &op->vec[0];
    1462    37532445 :                         right_val_type = op->right_value_type;
    1463             :                 } else {
    1464           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Shader] Invalid right-value in operation %d\n", op_idx));
    1465           0 :                         shader->invalid = GF_TRUE;
    1466             :                         return GF_FALSE;
    1467             :                 }
    1468    77076331 :                 if (!right_val_type) {
    1469           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_PARSER, ("[Shader] Invalid right-value type in operation %d\n", op_idx));
    1470           0 :                         shader->invalid = GF_TRUE;
    1471             :                         return GF_FALSE;
    1472             :                 }
    1473             : 
    1474             : #define GET_FIRST_COMP\
    1475             :                                         idx=0;\
    1476             :                                         while (1) {\
    1477             :                                                 if (op->right_value_type & (1<<idx))\
    1478             :                                                         break;\
    1479             :                                                 if (idx==3)\
    1480             :                                                         break;\
    1481             :                                                 idx++;\
    1482             :                                         }\
    1483             : 
    1484             : #define GET_NEXT_COMP\
    1485             :                                         has_next = GF_FALSE;\
    1486             :                                         next_idx = idx;\
    1487             :                                         while (1) {\
    1488             :                                                 if (next_idx==3)\
    1489             :                                                         break;\
    1490             :                                                 if (op->right_value_type & (1<< (next_idx+1)) ) {\
    1491             :                                                         has_next = GF_TRUE;\
    1492             :                                                         next_idx = idx+1;\
    1493             :                                                         break;\
    1494             :                                                 }\
    1495             :                                                 next_idx++;\
    1496             :                                         }\
    1497             :                                         if (has_next) idx = next_idx;\
    1498             : 
    1499    77076331 :                 switch (op->op_type) {
    1500    29987160 :                 case EVG_OP_ASSIGN:
    1501             :                         //full assignment
    1502    29987160 :                         if (op->left_value_type==COMP_V4) {
    1503    24864717 :                                 if (left_val_type_ptr) {
    1504    20392543 :                                         left_val_type = *left_val_type_ptr = right_val_type;
    1505             :                                 }
    1506             : 
    1507    24864717 :                                 if (right_val_type==COMP_BOOL) {
    1508           0 :                                         if (left_val_type_ptr) {
    1509           0 :                                                 *((Bool *) left_val) = *((Bool *) right_val) ? GF_TRUE : GF_FALSE;
    1510             :                                         } else {
    1511           0 :                                                 left_val->x = (Float) ( *(Bool *) right_val ? 1.0 : 0.0 );
    1512           0 :                                                 left_val->y = left_val->z = left_val->q = left_val->x;
    1513             :                                         }
    1514    24864717 :                                 } else if (right_val_type==COMP_INT) {
    1515     3035486 :                                         if (left_val_type_ptr || (left_val_type==COMP_INT)) {
    1516     3035486 :                                                 *((s32 *) left_val) = *(s32 *) right_val;
    1517             :                                         } else {
    1518           0 :                                                 left_val->x = (Float) *(s32 *) right_val;
    1519           0 :                                                 left_val->y = left_val->z = left_val->q = left_val->x;
    1520             :                                         }
    1521    21829231 :                                 } else if (right_val_type==COMP_FLOAT) {
    1522      352352 :                                         if (left_val_type_ptr) {
    1523      352352 :                                                 *((Float *) left_val) = *(Float *) right_val;
    1524             :                                         } else {
    1525           0 :                                                 left_val->x = *(Float *) right_val;
    1526           0 :                                                 left_val->y = left_val->z = left_val->q = left_val->x;
    1527             :                                         }
    1528    21476879 :                                 } else if ((right_val_type==COMP_V4) || (op->right_value_type==COMP_V4)) {
    1529    21473279 :                                         *left_val = *right_val;
    1530    21473279 :                                         if (dim<4) left_val->q = 1.0;
    1531             :                                 } else {
    1532        3600 :                                         Float *srcs = (Float *) &right_val->x;
    1533             : 
    1534        3600 :                                         GET_FIRST_COMP
    1535        3600 :                                         if (left_val_type & COMP_X) { left_val->x = srcs[idx]; GET_NEXT_COMP }
    1536        3600 :                                         if (left_val_type & COMP_Y) { left_val->y = srcs[idx]; GET_NEXT_COMP }
    1537        3600 :                                         if (left_val_type & COMP_Z) { left_val->z = srcs[idx]; GET_NEXT_COMP }
    1538        3600 :                                         if (dim<4) left_val->q = 1.0;
    1539        3600 :                                         else if (left_val_type & COMP_Q) { left_val->q = srcs[idx]; }
    1540             :                                 }
    1541             :                         }
    1542             :                         //partial assignment, only valid for float sources
    1543             :                         else {
    1544             :                                 Bool use_const = GF_FALSE;
    1545             :                                 Float cval;
    1546     5122443 :                                 if (right_val_type==COMP_FLOAT) {
    1547             :                                         use_const = GF_TRUE;
    1548      352352 :                                         cval = right_val->x;
    1549     4770091 :                                 } else if (right_val_type==COMP_INT) {
    1550             :                                         use_const = GF_TRUE;
    1551     1734605 :                                         cval = (Float) ( *(s32*)right_val);
    1552             :                                 }
    1553             :                                 if (use_const) {
    1554     2086957 :                                         if (op->left_value_type & COMP_X) left_val->x = cval;
    1555     2086957 :                                         if (op->left_value_type & COMP_Y) left_val->y = cval;
    1556     2086957 :                                         if (op->left_value_type & COMP_Z) left_val->z = cval;
    1557     2086957 :                                         if (op->left_value_type & COMP_Q) left_val->q = cval;
    1558             :                                 } else {
    1559     3035486 :                                         Float *srcs = (Float *) &right_val->x;
    1560             : 
    1561     3035486 :                                         GET_FIRST_COMP
    1562     3035486 :                                         if (op->left_value_type & COMP_X) { left_val->x = srcs[idx]; GET_NEXT_COMP }
    1563     3035486 :                                         if (op->left_value_type & COMP_Y) { left_val->y = srcs[idx]; GET_NEXT_COMP }
    1564     3035486 :                                         if (op->left_value_type & COMP_Z) { left_val->z = srcs[idx]; GET_NEXT_COMP }
    1565     3035486 :                                         if (op->left_value_type & COMP_Q) { left_val->q = srcs[idx]; }
    1566             :                                 }
    1567             :                         }
    1568    29987160 :                         if (norm_result)
    1569        3600 :                                 gf_vec_norm((GF_Vec *)left_val);
    1570             :                         break;
    1571             : 
    1572             : #define BASE_OP(_opv, _opv2)\
    1573             :                         if (op->left_value_type==COMP_V4) {\
    1574             :                                 if (right_val_type==COMP_INT) {\
    1575             :                                         if (left_val_type == COMP_INT) {\
    1576             :                                                 *((s32 *) left_val) _opv *(s32 *) right_val;\
    1577             :                                         } else if (left_val_type == COMP_FLOAT) {\
    1578             :                                                 left_val->x _opv *(s32 *) right_val;\
    1579             :                                         } else {\
    1580             :                                                 left_val->x _opv *(s32 *) right_val;\
    1581             :                                                 left_val->y _opv *(s32 *) right_val;\
    1582             :                                                 left_val->z _opv *(s32 *) right_val;\
    1583             :                                                 left_val->q _opv *(s32 *) right_val;\
    1584             :                                         }\
    1585             :                                 } else if (right_val_type==COMP_FLOAT) {\
    1586             :                                         if (left_val_type == COMP_INT) {\
    1587             :                                                 left_val->x = *((s32 *) left_val) _opv2 right_val->x;\
    1588             :                                                 *left_val_type_ptr = COMP_FLOAT;\
    1589             :                                         } else if (left_val_type == COMP_FLOAT) {\
    1590             :                                                 left_val->x _opv right_val->x;\
    1591             :                                         } else {\
    1592             :                                                 left_val->x _opv right_val->x;\
    1593             :                                                 left_val->y _opv right_val->x;\
    1594             :                                                 left_val->z _opv right_val->x;\
    1595             :                                                 left_val->q _opv right_val->x;\
    1596             :                                         }\
    1597             :                                 } else if (right_val_type==COMP_BOOL) {\
    1598             :                                 } else {\
    1599             :                                         Float *srcs = (Float *) &right_val->x;\
    1600             :                                         GET_FIRST_COMP\
    1601             :                                         if (left_val_type & COMP_X) { left_val->x _opv srcs[idx]; GET_NEXT_COMP } \
    1602             :                                         if (left_val_type & COMP_Y) { left_val->y _opv srcs[idx]; GET_NEXT_COMP } \
    1603             :                                         if (left_val_type & COMP_Z) { left_val->z _opv srcs[idx]; GET_NEXT_COMP } \
    1604             :                                         if (left_val_type & COMP_Q) { left_val->q _opv srcs[idx]; }\
    1605             :                                 }\
    1606             :                         }\
    1607             :                         else {\
    1608             :                                 Bool use_const = GF_FALSE;\
    1609             :                                 Float cval;\
    1610             :                                 if (right_val_type==COMP_FLOAT) {\
    1611             :                                         use_const = GF_TRUE;\
    1612             :                                         cval = right_val->x;\
    1613             :                                 } else if (right_val_type==COMP_INT) {\
    1614             :                                         use_const = GF_TRUE;\
    1615             :                                         cval = (Float) ( *(s32*)right_val);\
    1616             :                                 }\
    1617             :                                 if (use_const) {\
    1618             :                                         if (op->left_value_type & COMP_X) left_val->x _opv cval;\
    1619             :                                         if (op->left_value_type & COMP_Y) left_val->y _opv cval;\
    1620             :                                         if (op->left_value_type & COMP_Z) left_val->z _opv cval;\
    1621             :                                         if (op->left_value_type & COMP_Q) left_val->q _opv cval;\
    1622             :                                 } else {\
    1623             :                                         Float *srcs = (Float *) &right_val->x;\
    1624             :                                         GET_FIRST_COMP\
    1625             :                                         if (op->left_value_type & COMP_X) { left_val->x _opv srcs[idx]; GET_NEXT_COMP }\
    1626             :                                         if (op->left_value_type & COMP_Y) { left_val->y _opv srcs[idx]; GET_NEXT_COMP }\
    1627             :                                         if (op->left_value_type & COMP_Z) { left_val->z _opv srcs[idx]; GET_NEXT_COMP }\
    1628             :                                         if (op->left_value_type & COMP_Q) left_val->q _opv srcs[idx];\
    1629             :                                 }\
    1630             :                         }\
    1631             : 
    1632    12754098 :                 case EVG_OP_MUL:
    1633    12754098 :                         BASE_OP(*=, *)
    1634             :                         break;
    1635      352352 :                 case EVG_OP_DIV:
    1636      352352 :                         BASE_OP(/=, /)
    1637             :                         break;
    1638     9106458 :                 case EVG_OP_ADD:
    1639     9106458 :                         BASE_OP(+=, +)
    1640             :                         break;
    1641           0 :                 case EVG_OP_SUB:
    1642           0 :                         BASE_OP(-=, -)
    1643             :                         break;
    1644           0 :                 case EVG_OP_NEG:
    1645           0 :                         BASE_OP(= !, *)
    1646             :                         break;
    1647     4239973 :                 case EVG_OP_IF:
    1648             : #define BASE_COND(_opv)\
    1649             :                 cond_res=GF_FALSE;\
    1650             :                 if (op->left_value_type==COMP_V4) {\
    1651             :                         if ((right_val_type==COMP_INT) || (right_val_type==COMP_BOOL)) {\
    1652             :                                 if (left_val_type == COMP_INT) {\
    1653             :                                         cond_res = ( *((s32 *) left_val) _opv *(s32 *) right_val) ? GF_TRUE : GF_FALSE;\
    1654             :                                 } else if (left_val_type == COMP_FLOAT) {\
    1655             :                                         cond_res = (left_val->x _opv *(s32 *) right_val) ? GF_TRUE : GF_FALSE;\
    1656             :                                 } else {\
    1657             :                                         cond_res = ( (left_val->x _opv *(s32 *) right_val) && \
    1658             :                                                         (left_val->y _opv *(s32 *) right_val) && \
    1659             :                                                         (left_val->z _opv *(s32 *) right_val) && \
    1660             :                                                         (left_val->q _opv *(s32 *) right_val) ) ? GF_TRUE : GF_FALSE;\
    1661             :                                 }\
    1662             :                         } else if (right_val_type==COMP_FLOAT) {\
    1663             :                                 if (left_val_type == COMP_INT) {\
    1664             :                                         cond_res = ( *((s32 *) left_val) _opv right_val->x) ? GF_TRUE : GF_FALSE;\
    1665             :                                 } else if (left_val_type == COMP_FLOAT) {\
    1666             :                                         cond_res = (left_val->x _opv right_val->x) ? GF_TRUE : GF_FALSE;\
    1667             :                                 } else {\
    1668             :                                         cond_res = ( (left_val->x _opv right_val->x) &&\
    1669             :                                                         (left_val->y _opv right_val->x) &&\
    1670             :                                                         (left_val->z _opv right_val->x) && \
    1671             :                                                         (left_val->q _opv right_val->x) ) ? GF_TRUE : GF_FALSE;\
    1672             :                                 }\
    1673             :                         } else {\
    1674             :                                 cond_res=GF_TRUE;\
    1675             :                                 Float *srcs = (Float *) &right_val->x;\
    1676             :                                 GET_FIRST_COMP\
    1677             :                                 if (left_val_type & COMP_X) { if (! (left_val->x _opv srcs[idx]) ) cond_res = GF_FALSE; GET_NEXT_COMP } \
    1678             :                                 if (left_val_type & COMP_Y) { if (! (left_val->y _opv srcs[idx]) ) cond_res = GF_FALSE; GET_NEXT_COMP } \
    1679             :                                 if (left_val_type & COMP_Z) { if (! (left_val->z _opv srcs[idx]) ) cond_res = GF_FALSE; GET_NEXT_COMP } \
    1680             :                                 if (left_val_type & COMP_Q) { if (! (left_val->q _opv srcs[idx]) ) cond_res = GF_FALSE; }\
    1681             :                         }\
    1682             :                 }\
    1683             :                 else {\
    1684             :                         Bool use_const = GF_FALSE;\
    1685             :                         Float cval;\
    1686             :                         if (right_val_type==COMP_FLOAT) {\
    1687             :                                 use_const = GF_TRUE;\
    1688             :                                 cval = right_val->x;\
    1689             :                         } else if (right_val_type==COMP_INT) {\
    1690             :                                 use_const = GF_TRUE;\
    1691             :                                 cval = (Float) ( *(s32*)right_val);\
    1692             :                         }\
    1693             :                         if (use_const) {\
    1694             :                                 cond_res=GF_TRUE;\
    1695             :                                 if (op->left_value_type & COMP_X) if (! (left_val->x _opv cval) ) cond_res=GF_FALSE;\
    1696             :                                 if (op->left_value_type & COMP_Y) if (! (left_val->y _opv cval) ) cond_res=GF_FALSE;\
    1697             :                                 if (op->left_value_type & COMP_Z) if (! (left_val->z _opv cval) ) cond_res=GF_FALSE;\
    1698             :                                 if (op->left_value_type & COMP_Q) if (! (left_val->q _opv cval) ) cond_res=GF_FALSE;\
    1699             :                         } else {\
    1700             :                                 Float *srcs = (Float *) &right_val->x;\
    1701             :                                 GET_FIRST_COMP\
    1702             :                                 if (op->left_value_type & COMP_X) { if (! (left_val->x _opv srcs[idx]) ) cond_res=GF_FALSE; GET_NEXT_COMP }\
    1703             :                                 if (op->left_value_type & COMP_Y) { if (! (left_val->y _opv srcs[idx]) ) cond_res=GF_FALSE; GET_NEXT_COMP }\
    1704             :                                 if (op->left_value_type & COMP_Z) { if (! (left_val->z _opv srcs[idx]) ) cond_res=GF_FALSE; GET_NEXT_COMP }\
    1705             :                                 if (op->left_value_type & COMP_Q) { if (! (left_val->q _opv srcs[idx]) ) cond_res=GF_FALSE; }\
    1706             :                         }\
    1707             :                 }
    1708             : 
    1709     4239973 :                         if (op->cond_type==EVG_OP_LESS) { BASE_COND(<) }
    1710     3035486 :                         else if (op->cond_type==EVG_OP_LESS_EQUAL) { BASE_COND(<=) }
    1711     3035486 :                         else if (op->cond_type==EVG_OP_GREATER) { BASE_COND(>) }
    1712           0 :                         else if (op->cond_type==EVG_OP_GREATER_EQUAL) { BASE_COND(>=) }
    1713           0 :                         else if (op->cond_type==EVG_OP_EQUAL) { BASE_COND(==) }
    1714           0 :                         else if (op->cond_type==EVG_OP_NOT_EQUAL) { BASE_COND(!=) }
    1715             :                         else break;
    1716             : 
    1717     4239973 :                         if (cond_res) if_level++;
    1718             :                         else nif_level++;
    1719             : 
    1720             :                         break;
    1721             : 
    1722     1204487 :                 case EVG_OP_SAMPLER:
    1723     1204487 :                         if (left_val_type_ptr) {
    1724     1204487 :                                 *left_val_type_ptr = COMP_V4;
    1725             :                         }
    1726     1204487 :                         if (op->left_value_type==COMP_V4) {
    1727     1204487 :                                 gf_evg_stencil_get_pixel_f(op->tx->stencil, right_val->x, right_val->y, &left_val->x, &left_val->y, &left_val->z, &left_val->q);
    1728             :                         } else {
    1729           0 :                                 gf_evg_stencil_get_pixel_f(op->tx->stencil, right_val->x, right_val->y, &tmpr.x, &tmpr.y, &tmpr.z, &tmpr.q);
    1730           0 :                                 if (op->left_value_type & COMP_X) left_val->x = tmpr.x;
    1731           0 :                                 if (op->left_value_type & COMP_Y) left_val->y = tmpr.y;
    1732           0 :                                 if (op->left_value_type & COMP_Z) left_val->z = tmpr.z;
    1733           0 :                                 if (op->left_value_type & COMP_Q) left_val->q = tmpr.q;
    1734             :                         }
    1735             :                         break;
    1736             : 
    1737     1204487 :                 case EVG_OP_SAMPLER_YUV:
    1738     1204487 :                         if (left_val_type_ptr) {
    1739     1204487 :                                 *left_val_type_ptr = COMP_V4;
    1740             :                         }
    1741     1204487 :                         if (op->left_value_type==COMP_V4) {
    1742     1204487 :                                 gf_evg_stencil_get_pixel_yuv_f(op->tx->stencil, right_val->x, right_val->y, &left_val->x, &left_val->y, &left_val->z, &left_val->q);
    1743             :                         } else {
    1744           0 :                                 gf_evg_stencil_get_pixel_yuv_f(op->tx->stencil, right_val->x, right_val->y, &tmpr.x, &tmpr.y, &tmpr.z, &tmpr.q);
    1745           0 :                                 if (op->left_value_type & COMP_X) left_val->x = tmpr.x;
    1746           0 :                                 if (op->left_value_type & COMP_Y) left_val->y = tmpr.y;
    1747           0 :                                 if (op->left_value_type & COMP_Z) left_val->z = tmpr.z;
    1748           0 :                                 if (op->left_value_type & COMP_Q) left_val->q = tmpr.q;
    1749             :                         }
    1750             :                         break;
    1751           0 :                 case EVG_OP_DISCARD:
    1752           0 :                         frag->frag_valid = 0;
    1753             :                         return GF_TRUE;
    1754     6070972 :                 case EVG_OP_NORMALIZE:
    1755     6070972 :                         if (left_val_type_ptr) {
    1756     6070972 :                                 *left_val_type_ptr = COMP_V4;
    1757             :                         }
    1758     6070972 :                         *left_val = *right_val;
    1759     6070972 :                         gf_vec_norm((GF_Vec *)left_val);
    1760             :                         break;
    1761           0 :                 case EVG_OP_LENGTH:
    1762           0 :                         if (left_val_type_ptr) {
    1763           0 :                                 *left_val_type_ptr = COMP_FLOAT;
    1764             :                         }
    1765           0 :                         left_val->x = gf_vec_len_p( (GF_Vec *) right_val);
    1766             :                         break;
    1767           0 :                 case EVG_OP_DISTANCE:
    1768           0 :                         if (left_val_type_ptr) {
    1769           0 :                                 *left_val_type_ptr = COMP_FLOAT;
    1770             :                         }
    1771           0 :                         right2_val = &shader->vars[op->right_value_second - EVG_FIRST_VAR_ID-1].vecval;
    1772             :                         //right2_val_type = shader->vars[op->right_value_second - EVG_FIRST_VAR_ID-1].value_type;
    1773           0 :                         gf_vec_diff(tmpr, *right_val, *right2_val);
    1774           0 :                         left_val->x = gf_vec_len_p( (GF_Vec *) &tmpr);
    1775             :                         break;
    1776             : 
    1777     6070972 :                 case EVG_OP_DOT:
    1778     6070972 :                         if (left_val_type_ptr) {
    1779     6070972 :                                 *left_val_type_ptr = COMP_FLOAT;
    1780             :                         }
    1781     6070972 :                         right2_val = &shader->vars[op->right_value_second - EVG_FIRST_VAR_ID-1].vecval;
    1782             :                         //right2_val_type = shader->vars[op->right_value_second - EVG_FIRST_VAR_ID-1].value_type;
    1783     6070972 :                         left_val->x = gf_vec_dot_p( (GF_Vec *) right_val, (GF_Vec *) right2_val);
    1784             :                         break;
    1785           0 :                 case EVG_OP_CROSS:
    1786           0 :                         if (left_val_type_ptr) {
    1787           0 :                                 *left_val_type_ptr = COMP_V4;
    1788             :                         }
    1789           0 :                         right2_val = &shader->vars[op->right_value_second - EVG_FIRST_VAR_ID-1].vecval;
    1790             :                         //right2_val_type = shader->vars[op->right_value_second - EVG_FIRST_VAR_ID-1].value_type;
    1791           0 :                         * (GF_Vec *) left_val = gf_vec_cross_p( (GF_Vec *) right_val, (GF_Vec *) right2_val);
    1792             :                         break;
    1793             : 
    1794             : #define BASE_FUN2(__fun) \
    1795             :         if (left_val_type_ptr) {\
    1796             :                 *left_val_type_ptr = right_val_type;\
    1797             :         }\
    1798             :         var_idx = op->right_value_second - EVG_FIRST_VAR_ID-1;\
    1799             :         right2_val = &shader->vars[var_idx].vecval;\
    1800             :         if (right_val_type==COMP_FLOAT) {\
    1801             :                 left_val->x = __fun(right_val->x, right2_val->x);\
    1802             :         } else {\
    1803             :                 if (right_val_type&COMP_X) {\
    1804             :                         left_val->x = __fun(right_val->x, right2_val->x);\
    1805             :                 }\
    1806             :                 if (right_val_type&COMP_Y) {\
    1807             :                         left_val->y = __fun(right_val->y, right2_val->y);\
    1808             :                 }\
    1809             :                 if (right_val_type&COMP_Z) {\
    1810             :                         left_val->z = __fun(right_val->z, right2_val->z);\
    1811             :                 }\
    1812             :                 if (right_val_type&COMP_Q) {\
    1813             :                         left_val->q = __fun(right_val->z, right2_val->q);\
    1814             :                 }\
    1815             :         }\
    1816             : 
    1817             : 
    1818             : #define BASE_FUN(__fun) \
    1819             :                 if (left_val_type_ptr) {\
    1820             :                         *left_val_type_ptr = right_val_type;\
    1821             :                 }\
    1822             :                 if (right_val_type==COMP_FLOAT) {\
    1823             :                         left_val->x = (Float) __fun(right_val->x);\
    1824             :                 } else {\
    1825             :                         if (right_val_type&COMP_X) {\
    1826             :                                 left_val->x = (Float) __fun(right_val->x);\
    1827             :                         }\
    1828             :                         if (right_val_type&COMP_Y) {\
    1829             :                                 left_val->y = (Float) __fun(right_val->y);\
    1830             :                         }\
    1831             :                         if (right_val_type&COMP_Z) {\
    1832             :                                 left_val->z = (Float) __fun(right_val->z);\
    1833             :                         }\
    1834             :                         if (right_val_type&COMP_Q) {\
    1835             :                                 left_val->q = (Float) __fun(right_val->z);\
    1836             :                         }\
    1837             :                 }\
    1838             : 
    1839     3035486 :                 case EVG_OP_POW:
    1840     3035486 :                         BASE_FUN2(powf)
    1841             :                         break;
    1842           0 :                 case EVG_OP_SIN:
    1843           0 :                         BASE_FUN(sinf)
    1844             :                         break;
    1845           0 :                 case EVG_OP_ASIN:
    1846           0 :                         BASE_FUN(asinf)
    1847             :                         break;
    1848           0 :                 case EVG_OP_COS:
    1849           0 :                         BASE_FUN(cosf)
    1850             :                         break;
    1851           0 :                 case EVG_OP_ACOS:
    1852           0 :                         BASE_FUN(acosf)
    1853             :                         break;
    1854           0 :                 case EVG_OP_TAN:
    1855           0 :                         BASE_FUN(tanf)
    1856             :                         break;
    1857           0 :                 case EVG_OP_ATAN:
    1858           0 :                         BASE_FUN2(atan2f)
    1859             :                         break;
    1860           0 :                 case EVG_OP_LOG:
    1861           0 :                         BASE_FUN(logf)
    1862             :                         break;
    1863           0 :                 case EVG_OP_EXP:
    1864           0 :                         BASE_FUN(expf)
    1865             :                         break;
    1866           0 :                 case EVG_OP_LOG2:
    1867           0 :                         BASE_FUN(log2f)
    1868             :                         break;
    1869           0 :                 case EVG_OP_EXP2:
    1870           0 :                         BASE_FUN(exp2f)
    1871             :                         break;
    1872           0 :                 case EVG_OP_SINH:
    1873           0 :                         BASE_FUN(sinhf)
    1874             :                         break;
    1875           0 :                 case EVG_OP_COSH:
    1876           0 :                         BASE_FUN(coshf)
    1877             :                         break;
    1878           0 :                 case EVG_OP_SQRT:
    1879           0 :                         BASE_FUN(sqrtf)
    1880             :                         break;
    1881        3600 :                 case EVG_OP_INVERSE_SQRT:
    1882       14400 :                         BASE_FUN(isqrtf)
    1883             :                         break;
    1884           0 :                 case EVG_OP_ABS:
    1885           0 :                         BASE_FUN(ABS)
    1886             :                         break;
    1887        3600 :                 case EVG_OP_SIGN:
    1888       14400 :                         BASE_FUN(signf)
    1889             :                         break;
    1890           0 :                 case EVG_OP_FLOOR:
    1891           0 :                         BASE_FUN(floorf)
    1892             :                         break;
    1893           0 :                 case EVG_OP_CEIL:
    1894           0 :                         BASE_FUN(ceilf)
    1895             :                         break;
    1896        3600 :                 case EVG_OP_FRACT:
    1897       14400 :                         BASE_FUN(fractf)
    1898             :                         break;
    1899        3600 :                 case EVG_OP_MOD:
    1900       14400 :                         BASE_FUN2(_modf)
    1901             :                         break;
    1902           0 :                 case EVG_OP_MIN:
    1903           0 :                         BASE_FUN2(MIN)
    1904             :                         break;
    1905           0 :                 case EVG_OP_MAX:
    1906           0 :                         BASE_FUN2(MAX)
    1907             :                         break;
    1908     3035486 :                 case EVG_OP_CLAMP:
    1909     3035486 :                         if (left_val_type_ptr) {
    1910     3035486 :                                 *left_val_type_ptr = right_val_type;
    1911             :                         }
    1912     3035486 :                         var_idx = op->right_value_second - EVG_FIRST_VAR_ID-1;
    1913     3035486 :                         right2_val = &shader->vars[var_idx].vecval;
    1914             :                         //right2_val_type = shader->vars[var_idx].value_type;
    1915     3035486 :                         if (right_val_type==COMP_FLOAT) {
    1916           0 :                                 left_val->x = evg_float_clamp(left_val->x, right_val->x, right2_val->x);
    1917             :                         } else {
    1918     3035486 :                                 if (right_val_type&COMP_X) {
    1919     6070972 :                                         left_val->x = evg_float_clamp(left_val->x, right_val->x, right2_val->x);
    1920             :                                 }
    1921     3035486 :                                 if (right_val_type&COMP_Y) {
    1922     6070972 :                                         left_val->y = evg_float_clamp(left_val->y, right_val->y, right2_val->x);
    1923             :                                 }
    1924     3035486 :                                 if (right_val_type&COMP_Z) {
    1925     6070972 :                                         left_val->z = evg_float_clamp(left_val->z, right_val->z, right2_val->x);
    1926             :                                 }
    1927     3035486 :                                 if (right_val_type&COMP_Q) {
    1928     6070972 :                                         left_val->q = evg_float_clamp(left_val->q, right_val->z, right2_val->x);
    1929             :                                 }
    1930             :                         }
    1931             :                         break;
    1932           0 :                 case EVG_OP_RGB2YUV:
    1933           0 :                         if (left_val_type_ptr) {
    1934           0 :                                 *left_val_type_ptr = right_val_type;
    1935             :                         }
    1936           0 :                         left_val->q = right_val->q;
    1937           0 :                         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);
    1938             :                         break;
    1939           0 :                 case EVG_OP_YUV2RGB:
    1940           0 :                         if (left_val_type_ptr) {
    1941           0 :                                 *left_val_type_ptr = right_val_type;
    1942             :                         }
    1943           0 :                         left_val->q = right_val->q;
    1944           0 :                         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);
    1945             :                         break;
    1946             : 
    1947           0 :                 case EVG_OP_PRINT:
    1948           0 :                         if (op->right_value>EVG_FIRST_VAR_ID) {
    1949           0 :                                 fprintf(stderr, "%s: ", shader->vars[op->right_value - EVG_FIRST_VAR_ID - 1].name);
    1950             :                         }
    1951           0 :                         if (right_val_type==COMP_FLOAT) {
    1952           0 :                                 fprintf(stderr, "%g\n", right_val->x);
    1953           0 :                         } else if (right_val_type==COMP_INT) {
    1954           0 :                                 fprintf(stderr, "%d\n", * (s32 *) &right_val);
    1955             :                         } else {
    1956           0 :                                 if (right_val_type&COMP_X) fprintf(stderr, "x=%g ", right_val->x);
    1957           0 :                                 if (right_val_type&COMP_Y) fprintf(stderr, "y=%g ", right_val->y);
    1958           0 :                                 if (right_val_type&COMP_Z) fprintf(stderr, "z=%g ", right_val->z);
    1959           0 :                                 if (right_val_type&COMP_Q) fprintf(stderr, "q=%g ", right_val->q);
    1960           0 :                                 fprintf(stderr, "\n");
    1961             :                         }
    1962             : 
    1963             :                         break;
    1964             :                 }
    1965             :         }
    1966             :         return GF_TRUE;
    1967             : }
    1968             : 
    1969     4812826 : static Bool evg_frag_shader_ops(void *udta, GF_EVGFragmentParam *frag)
    1970             : {
    1971             :         GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
    1972     4812826 :         if (!canvas->frag || canvas->frag->invalid) return GF_FALSE;
    1973     4812826 :         return evg_shader_ops(canvas, canvas->frag, frag, NULL);
    1974             : }
    1975        4500 : static Bool evg_vert_shader_ops(void *udta, GF_EVGVertexParam *vert)
    1976             : {
    1977             :         GF_JSCanvas *canvas = (GF_JSCanvas *)udta;
    1978        4500 :         if (!canvas->vert || canvas->vert->invalid) return GF_FALSE;
    1979        4500 :         return evg_shader_ops(canvas, canvas->vert, NULL, vert);
    1980             : }
    1981             : 
    1982          36 : static void shader_reset(JSRuntime *rt, EVGShader *shader)
    1983             : {
    1984             :         u32 i;
    1985         166 :         for (i=0; i<shader->nb_ops; i++) {
    1986         130 :                 if (shader->ops[i].right_value==VAR_VAI) {
    1987             :                         JS_FreeValueRT(rt, shader->ops[i].vai.ref);
    1988             :                 }
    1989         127 :                 else if (shader->ops[i].right_value==VAR_MATRIX) {
    1990             :                         JS_FreeValueRT(rt, shader->ops[i].mx.ref);
    1991             :                 }
    1992         109 :                 else if (shader->ops[i].right_value==VAR_VA) {
    1993             :                         JS_FreeValueRT(rt, shader->ops[i].va.ref);
    1994             :                 }
    1995         108 :                 else if (shader->ops[i].left_value==VAR_VAI) {
    1996             :                         JS_FreeValueRT(rt, shader->ops[i].vai.ref);
    1997             :                 }
    1998         106 :                 else if (shader->ops[i].left_value==VAR_MATRIX) {
    1999             :                         JS_FreeValueRT(rt, shader->ops[i].mx.ref);
    2000             :                 }
    2001             : 
    2002         130 :                 if (shader->ops[i].uni_name) {
    2003          25 :                         gf_free(shader->ops[i].uni_name);
    2004          25 :                         shader->ops[i].uni_name = NULL;
    2005             :                 }
    2006         130 :                 if (shader->ops[i].op_type==EVG_OP_SAMPLER) {
    2007             :                         JS_FreeValueRT(rt, shader->ops[i].tx_ref);
    2008           1 :                         shader->ops[i].tx_ref = JS_UNDEFINED;
    2009             :                 }
    2010         130 :                 shader->ops[i].right_value = 0;
    2011             :         }
    2012          36 :         shader->nb_ops = 0;
    2013          69 :         for (i=0; i<shader->nb_vars; i++) {
    2014          33 :                 if (shader->vars[i].name) gf_free(shader->vars[i].name);
    2015          33 :                 shader->vars[i].name = NULL;
    2016             :         }
    2017          36 :         shader->nb_vars = 0;
    2018          36 :         shader->invalid = GF_FALSE;
    2019          36 :         shader->disable_early_z = GF_FALSE;
    2020          36 : }
    2021          36 : static void shader_finalize(JSRuntime *rt, JSValue obj)
    2022             : {
    2023          36 :         EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
    2024          36 :         if (!shader) return;
    2025          36 :         shader_reset(rt, shader);
    2026          36 :         gf_free(shader->ops);
    2027          36 :         gf_free(shader->vars);
    2028          36 :         gf_free(shader);
    2029             : }
    2030             : 
    2031         136 : static void shader_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
    2032             : {
    2033             :         u32 i;
    2034         136 :         EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
    2035         136 :         if (!shader) return;
    2036         384 :         for (i=0; i<shader->nb_ops; i++) {
    2037         384 :                 if (shader->ops[i].tx)
    2038           8 :                         JS_MarkValue(rt, shader->ops[i].tx_ref, mark_func);
    2039             : 
    2040         384 :                 if (shader->ops[i].right_value==VAR_VAI) {
    2041           4 :                         JS_MarkValue(rt, shader->ops[i].vai.ref, mark_func);
    2042             :                 }
    2043         380 :                 else if (shader->ops[i].right_value==VAR_MATRIX) {
    2044          64 :                         JS_MarkValue(rt, shader->ops[i].mx.ref, mark_func);
    2045             :                 }
    2046         316 :                 else if (shader->ops[i].right_value==VAR_VA) {
    2047           0 :                         JS_MarkValue(rt, shader->ops[i].va.ref, mark_func);
    2048             :                 }
    2049         316 :                 else if (shader->ops[i].left_value==VAR_VAI) {
    2050           0 :                         JS_MarkValue(rt, shader->ops[i].vai.ref, mark_func);
    2051             :                 }
    2052         316 :                 else if (shader->ops[i].left_value==VAR_MATRIX) {
    2053           0 :                         JS_MarkValue(rt, shader->ops[i].mx.ref, mark_func);
    2054             :                 }
    2055             :         }
    2056             : }
    2057             : 
    2058             : JSClassDef shader_class = {
    2059             :         .class_name = "Shader",
    2060             :         .finalizer = shader_finalize,
    2061             :         .gc_mark = shader_gc_mark
    2062             : };
    2063             : 
    2064         203 : static u32 get_builtin_var_name(EVGShader *shader, const char *val_name)
    2065             : {
    2066             :         u32 i;
    2067         203 :         if (shader->mode==GF_EVG_SHADER_FRAGMENT) {
    2068         142 :                 if (!strcmp(val_name, "fragColor") || !strcmp(val_name, "fragRGBA")) return VAR_FRAG_ARGB;
    2069         122 :                 if (!strcmp(val_name, "fragYUVA")) return VAR_FRAG_YUV;
    2070         121 :                 if (!strcmp(val_name, "fragDepth") || !strcmp(val_name, "fragZ")) return VAR_FRAG_DEPTH;
    2071         121 :                 if (!strcmp(val_name, "fragX")) return VAR_FRAG_X;
    2072         120 :                 if (!strcmp(val_name, "fragY")) return VAR_FRAG_Y;
    2073         120 :                 if (!strcmp(val_name, "fragW")) return VAR_FRAG_W;
    2074             :         }
    2075         181 :         if (shader->mode==GF_EVG_SHADER_VERTEX) {
    2076          61 :                 if (!strcmp(val_name, "vertex")) return VAR_VERTEX_IN;
    2077          26 :                 if (!strcmp(val_name, "vertexOut")) return VAR_VERTEX_OUT;
    2078             :         }
    2079             : 
    2080         129 :         if (val_name[0] == '.') return VAR_UNIFORM;
    2081             : 
    2082         159 :         for (i=0; i<shader->nb_vars; i++) {
    2083         231 :                 if (!strcmp(shader->vars[i].name, val_name)) {
    2084          72 :                         return EVG_FIRST_VAR_ID+i+1;
    2085             :                 }
    2086             :         }
    2087             :         return 0;
    2088             : }
    2089          23 : static u8 get_value_type(const char *comp)
    2090             : {
    2091          23 :         if (!strcmp(comp, "x") || !strcmp(comp, "r") || !strcmp(comp, "s")) return COMP_X;
    2092          22 :         if (!strcmp(comp, "y") || !strcmp(comp, "g") || !strcmp(comp, "t")) return COMP_Y;
    2093          22 :         if (!strcmp(comp, "z") || !strcmp(comp, "b")) return COMP_Z;
    2094          22 :         if (!strcmp(comp, "q") || !strcmp(comp, "a")) return COMP_Q;
    2095           1 :         if (!strcmp(comp, "xyz") || !strcmp(comp, "rgb")) return COMP_V3;
    2096           1 :         if (!strcmp(comp, "xy") || !strcmp(comp, "rg") || !strcmp(comp, "st")) return COMP_V2_XY;
    2097           1 :         if (!strcmp(comp, "xz") || !strcmp(comp, "rb")) return COMP_V2_XZ;
    2098           1 :         if (!strcmp(comp, "yz") || !strcmp(comp, "gb")) return COMP_V2_YZ;
    2099           0 :         return COMP_V4;
    2100             : }
    2101             : 
    2102             : 
    2103         130 : static JSValue shader_push(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
    2104             : {
    2105             :         const char *val_name=NULL, *arg_str;
    2106             :         const char *op_name;
    2107             :         char *uni_name=NULL;
    2108             :         Bool dual_right_val = GF_FALSE;
    2109             :         u32 var_idx=0;
    2110         130 :         u32 left_op_idx=0, right_op_idx=0, right_op2_idx=0;
    2111             :         u8 left_value_type=COMP_V4, right_value_type=COMP_V4;
    2112             :         u8 op_type=0;
    2113             :         u8 cond_type=0;
    2114             :         ShaderOp new_op;
    2115         130 :         EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
    2116         130 :         if (!shader) return JS_EXCEPTION;
    2117         130 :         shader->invalid = GF_FALSE;
    2118         130 :         if (!argc) {
    2119           0 :                 shader_reset(JS_GetRuntime(ctx), shader);
    2120           0 :                 return JS_UNDEFINED;
    2121             :         }
    2122             : 
    2123             :         memset(&new_op, 0, sizeof(ShaderOp));
    2124             :         new_op.op_type = 0;
    2125         130 :         new_op.tx_ref = JS_UNDEFINED;
    2126             : 
    2127         260 :         if (JS_IsObject(argv[0])) {
    2128           2 :                 new_op.vai.vai = JS_GetOpaque(argv[0], vai_class_id);
    2129           2 :                 if (!new_op.vai.vai) {
    2130           0 :                         shader->invalid = GF_TRUE;
    2131           0 :                         return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid left-operand value, must be a VAI");
    2132             :                 }
    2133           2 :                 new_op.vai.ref = JS_DupValue(ctx, argv[0]);
    2134           2 :                 left_op_idx = VAR_VAI;
    2135             :         } else {
    2136             : 
    2137             :                 arg_str = JS_ToCString(ctx, argv[0]);
    2138         128 :                 if (!arg_str) {
    2139           0 :                         shader->invalid = GF_TRUE;
    2140           0 :                         return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid left-operand value, must be a string");
    2141             :                 }
    2142             :                 val_name = arg_str;
    2143         128 :                 while ((val_name[0]==' ') || (val_name[0]=='\t'))
    2144           0 :                         val_name++;
    2145             : 
    2146         128 :                 if (!strcmp(val_name, "if")) {
    2147             :                         op_type = EVG_OP_IF;
    2148           2 :                         JS_FreeCString(ctx, arg_str);
    2149             :                         arg_str = JS_ToCString(ctx, argv[1]);
    2150           2 :                         if (!arg_str) {
    2151           0 :                                 shader->invalid = GF_TRUE;
    2152           0 :                                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid if first var, must be a string");
    2153             :                         }
    2154             :                         var_idx=1;
    2155             :                         val_name = arg_str;
    2156             :                 }
    2157         126 :                 else if (!strcmp(val_name, "else")) {
    2158             :                         op_type = EVG_OP_ELSE;
    2159           1 :                         JS_FreeCString(ctx, arg_str);
    2160           1 :                         goto op_parsed;
    2161             :                 }
    2162         125 :                 else if (!strcmp(val_name, "discard")) {
    2163           0 :                         if (shader->mode==GF_EVG_SHADER_VERTEX) {
    2164           0 :                                 shader->invalid = GF_TRUE;
    2165           0 :                                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "discard is invalid in vertex shader");
    2166             :                         }
    2167             :                         op_type = EVG_OP_DISCARD;
    2168           0 :                         JS_FreeCString(ctx, arg_str);
    2169           0 :                         goto op_parsed;
    2170             :                 }
    2171         125 :                 else if (!strcmp(val_name, "elseif")) {
    2172             :                         op_type = EVG_OP_ELSEIF;
    2173             :                         var_idx=1;
    2174             :                 }
    2175         125 :                 else if (!strcmp(val_name, "goto")) {
    2176             :                         op_type = EVG_OP_GOTO;
    2177           1 :                         JS_FreeCString(ctx, arg_str);
    2178             : 
    2179           2 :                         if (JS_IsString(argv[1])) {
    2180             :                                 arg_str = JS_ToCString(ctx, argv[1]);
    2181           1 :                                 if (arg_str[0] != '.') {
    2182           0 :                                         shader->invalid = GF_TRUE;
    2183           0 :                                         return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid goto var, must be a uniform string");
    2184             :                                 }
    2185             :                                 right_op_idx = VAR_UNIFORM;
    2186           1 :                                 uni_name = gf_strdup(arg_str+1);
    2187           1 :                                 JS_FreeCString(ctx, arg_str);
    2188           0 :                         } else if (JS_ToInt32(ctx, &left_op_idx, argv[1]) ) {
    2189           0 :                                 shader->invalid = GF_TRUE;
    2190           0 :                                 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");
    2191             :                         }
    2192             :                         goto op_parsed;
    2193             :                 }
    2194         124 :                 else if (!strcmp(val_name, "end")) {
    2195             :                         op_type = EVG_OP_END;
    2196           2 :                         JS_FreeCString(ctx, arg_str);
    2197           2 :                         goto op_parsed;
    2198             :                 }
    2199         122 :                 else if (!strcmp(val_name, "print")) {
    2200             :                         op_type = EVG_OP_PRINT;
    2201           0 :                         JS_FreeCString(ctx, arg_str);
    2202             :                         var_idx=-1;
    2203           0 :                         goto parse_right_val;
    2204             :                 }
    2205         122 :                 else if (!strcmp(val_name, "toRGB")) {
    2206             :                         op_type = EVG_OP_YUV2RGB;
    2207           0 :                         JS_FreeCString(ctx, arg_str);
    2208             :                         var_idx=-1;
    2209           0 :                         goto parse_right_val;
    2210             :                 }
    2211         122 :                 else if (!strcmp(val_name, "toYUV")) {
    2212             :                         op_type = EVG_OP_RGB2YUV;
    2213           0 :                         JS_FreeCString(ctx, arg_str);
    2214             :                         var_idx=-1;
    2215           0 :                         goto parse_right_val;
    2216             :                 }
    2217             : 
    2218         124 :                 char *sep = strchr(val_name, '.');
    2219         124 :                 if (sep) sep[0] = 0;
    2220         124 :                 left_op_idx = get_builtin_var_name(shader, val_name);
    2221         124 :                 if (!left_op_idx) {
    2222          33 :                         if (shader->alloc_vars <= shader->nb_vars) {
    2223          33 :                                 shader->alloc_vars = shader->nb_vars+1;
    2224          33 :                                 shader->vars = gf_realloc(shader->vars, sizeof(ShaderVar)*shader->alloc_vars);
    2225             :                         }
    2226          33 :                         shader->vars[shader->nb_vars].name = gf_strdup(val_name);
    2227          33 :                         shader->nb_vars++;
    2228          33 :                         left_op_idx = EVG_FIRST_VAR_ID + shader->nb_vars;
    2229             :                 }
    2230         124 :                 if (sep) {
    2231          22 :                         left_value_type = get_value_type(sep+1);
    2232          22 :                         sep[0] = '.';
    2233             :                 }
    2234         124 :                 JS_FreeCString(ctx, arg_str);
    2235             :         }
    2236             : 
    2237         126 :         op_name = JS_ToCString(ctx, argv[var_idx+1]);
    2238         126 :         if (!op_name) {
    2239           0 :                 shader->invalid = GF_TRUE;
    2240           0 :                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid operand type, must be a string");
    2241             :         }
    2242         126 :         if (!strcmp(op_name, "=")) {
    2243             :                 op_type = EVG_OP_ASSIGN;
    2244          85 :                 if (left_op_idx==VAR_FRAG_DEPTH)
    2245           0 :                         shader->disable_early_z = GF_TRUE;
    2246          41 :         } else if (!strcmp(op_name, "+=")) {
    2247             :                 op_type = EVG_OP_ADD;
    2248           3 :                 if (left_op_idx==VAR_FRAG_DEPTH)
    2249           0 :                         shader->disable_early_z = GF_TRUE;
    2250          38 :         } else if (!strcmp(op_name, "-=")) {
    2251             :                 op_type = EVG_OP_SUB;
    2252           0 :                 if (left_op_idx==VAR_FRAG_DEPTH)
    2253           0 :                         shader->disable_early_z = GF_TRUE;
    2254          38 :         } else if (!strcmp(op_name, "*=")) {
    2255             :                 op_type = EVG_OP_MUL;
    2256          23 :                 if (left_op_idx==VAR_FRAG_DEPTH)
    2257           0 :                         shader->disable_early_z = GF_TRUE;
    2258          15 :         } else if (!strcmp(op_name, "/=")) {
    2259             :                 op_type = EVG_OP_DIV;
    2260           1 :                 if (left_op_idx==VAR_FRAG_DEPTH)
    2261           0 :                         shader->disable_early_z = GF_TRUE;
    2262          14 :         } else if (!strcmp(op_name, "=!")) {
    2263             :                 op_type = EVG_OP_NEG;
    2264           0 :                 if (left_op_idx==VAR_FRAG_DEPTH)
    2265           0 :                         shader->disable_early_z = GF_TRUE;
    2266          14 :         } else if (!strcmp(op_name, "<")) {
    2267             :                 cond_type = EVG_OP_LESS;
    2268          13 :         } else if (!strcmp(op_name, "<=")) {
    2269             :                 cond_type = EVG_OP_LESS_EQUAL;
    2270          13 :         } else if (!strcmp(op_name, ">")) {
    2271             :                 cond_type = EVG_OP_GREATER;
    2272          12 :         } else if (!strcmp(op_name, ">=")) {
    2273             :                 cond_type = EVG_OP_GREATER_EQUAL;
    2274          12 :         } else if (!strcmp(op_name, "==")) {
    2275             :                 cond_type = EVG_OP_EQUAL;
    2276          12 :         } else if (!strcmp(op_name, "!=")) {
    2277             :                 cond_type = EVG_OP_NOT_EQUAL;
    2278          12 :         } else if (!strcmp(op_name, "sampler") || !strcmp(op_name, "samplerYUV") ) {
    2279           2 :                 new_op.tx = JS_GetOpaque(argv[var_idx+2], texture_class_id);
    2280           2 :                 if (!strcmp(op_name, "samplerYUV")) op_type = EVG_OP_SAMPLER_YUV;
    2281             :                 else op_type = EVG_OP_SAMPLER;
    2282             : 
    2283           2 :                 if (!new_op.tx) {
    2284           0 :                         JS_FreeCString(ctx, val_name);
    2285           0 :                         shader->invalid = GF_TRUE;
    2286           0 :                         return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid texture object for 2D sampler");
    2287             :                 }
    2288           2 :                 new_op.tx_ref = argv[var_idx+2];
    2289           2 :                 var_idx++;
    2290          10 :         } else if (!strcmp(op_name, "normalize")) {
    2291             :                 op_type = EVG_OP_NORMALIZE;
    2292           8 :         } else if (!strcmp(op_name, "length")) {
    2293             :                 op_type = EVG_OP_LENGTH;
    2294           8 :         } else if (!strcmp(op_name, "distance")) {
    2295             :                 op_type = EVG_OP_DISTANCE;
    2296             :                 dual_right_val = GF_TRUE;
    2297           8 :         } else if (!strcmp(op_name, "dot")) {
    2298             :                 op_type = EVG_OP_DOT;
    2299             :                 dual_right_val = GF_TRUE;
    2300           6 :         } else if (!strcmp(op_name, "cross")) {
    2301             :                 op_type = EVG_OP_CROSS;
    2302             :                 dual_right_val = GF_TRUE;
    2303           6 :         } else if (!strcmp(op_name, "pow")) {
    2304             :                 op_type = EVG_OP_POW;
    2305             :                 dual_right_val = GF_TRUE;
    2306           5 :         } else if (!strcmp(op_name, "sin")) {
    2307             :                 op_type = EVG_OP_SIN;
    2308           5 :         } else if (!strcmp(op_name, "asin")) {
    2309             :                 op_type = EVG_OP_ASIN;
    2310           5 :         } else if (!strcmp(op_name, "cos")) {
    2311             :                 op_type = EVG_OP_COS;
    2312           5 :         } else if (!strcmp(op_name, "acos")) {
    2313             :                 op_type = EVG_OP_ACOS;
    2314           5 :         } else if (!strcmp(op_name, "tan")) {
    2315             :                 op_type = EVG_OP_TAN;
    2316           5 :         } else if (!strcmp(op_name, "atan")) {
    2317             :                 op_type = EVG_OP_ATAN;
    2318             :                 dual_right_val = GF_TRUE;
    2319           5 :         } else if (!strcmp(op_name, "log")) {
    2320             :                 op_type = EVG_OP_LOG;
    2321           5 :         } else if (!strcmp(op_name, "exp")) {
    2322             :                 op_type = EVG_OP_EXP;
    2323           5 :         } else if (!strcmp(op_name, "log2")) {
    2324             :                 op_type = EVG_OP_LOG2;
    2325           5 :         } else if (!strcmp(op_name, "exp2")) {
    2326             :                 op_type = EVG_OP_EXP2;
    2327           5 :         } else if (!strcmp(op_name, "sinh")) {
    2328             :                 op_type = EVG_OP_SINH;
    2329           5 :         } else if (!strcmp(op_name, "cosh")) {
    2330             :                 op_type = EVG_OP_COSH;
    2331           5 :         } else if (!strcmp(op_name, "sqrt")) {
    2332             :                 op_type = EVG_OP_SQRT;
    2333           5 :         } else if (!strcmp(op_name, "inversesqrt")) {
    2334             :                 op_type = EVG_OP_INVERSE_SQRT;
    2335           4 :         } else if (!strcmp(op_name, "abs")) {
    2336             :                 op_type = EVG_OP_ABS;
    2337           4 :         } else if (!strcmp(op_name, "sign")) {
    2338             :                 op_type = EVG_OP_SIGN;
    2339           3 :         } else if (!strcmp(op_name, "floor")) {
    2340             :                 op_type = EVG_OP_FLOOR;
    2341           3 :         } else if (!strcmp(op_name, "ceil")) {
    2342             :                 op_type = EVG_OP_CEIL;
    2343           3 :         } else if (!strcmp(op_name, "fract")) {
    2344             :                 op_type = EVG_OP_FRACT;
    2345           2 :         } else if (!strcmp(op_name, "mod")) {
    2346             :                 op_type = EVG_OP_MOD;
    2347             :                 dual_right_val = GF_TRUE;
    2348           1 :         } else if (!strcmp(op_name, "min")) {
    2349             :                 op_type = EVG_OP_MIN;
    2350             :                 dual_right_val = GF_TRUE;
    2351           1 :         } else if (!strcmp(op_name, "max")) {
    2352             :                 op_type = EVG_OP_MAX;
    2353             :                 dual_right_val = GF_TRUE;
    2354           1 :         } else if (!strcmp(op_name, "clamp")) {
    2355             :                 op_type = EVG_OP_CLAMP;
    2356             :                 dual_right_val = GF_TRUE;
    2357             :         } else {
    2358           0 :                 JS_FreeCString(ctx, val_name);
    2359           0 :                 shader->invalid = GF_TRUE;
    2360           0 :                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid operand type, must be a string");
    2361             :         }
    2362         126 :         JS_FreeCString(ctx, op_name);
    2363             : 
    2364         126 : parse_right_val:
    2365         252 :         if (JS_IsString(argv[var_idx+2])) {
    2366             :                 val_name = JS_ToCString(ctx, argv[var_idx+2]);
    2367          74 :                 if (!val_name) {
    2368           0 :                         shader->invalid = GF_TRUE;
    2369           0 :                         return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid right-operand value, must be a string");
    2370             :                 }
    2371             : 
    2372          74 :                 char *sep = strchr(val_name+1, '.');
    2373          74 :                 if (sep) sep[0] = 0;
    2374          74 :                 right_op_idx = get_builtin_var_name(shader, val_name);
    2375          74 :                 if (right_op_idx==VAR_UNIFORM) {
    2376          24 :                         uni_name = gf_strdup(val_name+1);
    2377             :                 }
    2378          74 :                 if (sep) sep[0] = '.';
    2379             : 
    2380          74 :                 if (!right_op_idx) {
    2381           0 :                         JSValue ret = js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid right-operand value, undefined variable %s", val_name);
    2382           0 :                         JS_FreeCString(ctx, val_name);
    2383           0 :                         shader->invalid = GF_TRUE;
    2384           0 :                         if (uni_name) gf_free(uni_name);
    2385           0 :                         return ret;
    2386             :                 }
    2387          74 :                 if (sep) {
    2388           1 :                         right_value_type = get_value_type(sep+1);
    2389             :                 }
    2390          74 :                 JS_FreeCString(ctx, val_name);
    2391             :         }
    2392          52 :         else if (JS_IsArray(ctx, argv[var_idx+2])) {
    2393             :                 u32 idx, length;
    2394          23 :                 JSValue comp = JS_GetPropertyStr(ctx, argv[var_idx+2], "length");
    2395          23 :                 if (JS_ToInt32(ctx, &length, comp)) return JS_EXCEPTION;
    2396             :                 JS_FreeValue(ctx, comp);
    2397          23 :                 if (length>4) length=4;
    2398             :                 right_value_type = 0;
    2399          87 :                 for (idx=0;idx<length; idx++) {
    2400          87 :                         comp = JS_GetPropertyUint32(ctx, argv[var_idx+2], idx);
    2401          87 :                         EVG_GET_FLOAT(new_op.vec[idx], comp);
    2402          87 :                         right_value_type |= 1<<idx;
    2403             :                         JS_FreeValue(ctx, comp);
    2404             :                 }
    2405             :         }
    2406          58 :         else if (JS_IsObject(argv[var_idx+2])) {
    2407          22 :                 EVG_VAI *vai = JS_GetOpaque(argv[var_idx+2], vai_class_id);
    2408          22 :                 GF_Matrix *mx = JS_GetOpaque(argv[var_idx+2], matrix_class_id);
    2409          22 :                 EVG_VA *va = (shader->mode==GF_EVG_SHADER_VERTEX) ? JS_GetOpaque(argv[var_idx+2], va_class_id) : NULL;
    2410             : 
    2411          22 :                 if (vai) {
    2412           3 :                         new_op.vai.vai = vai;
    2413           3 :                         new_op.vai.ref = JS_DupValue(ctx, argv[var_idx+2]);
    2414             :                         right_op_idx = VAR_VAI;
    2415          19 :                 } else if (mx) {
    2416          18 :                         new_op.mx.mx = mx;
    2417          18 :                         new_op.mx.ref = JS_DupValue(ctx, argv[var_idx+2]);
    2418             :                         right_op_idx = VAR_MATRIX;
    2419           1 :                 } else if (va) {
    2420           1 :                         new_op.va.va = va;
    2421           1 :                         new_op.va.ref = JS_DupValue(ctx, argv[var_idx+2]);
    2422             :                         right_op_idx = VAR_VA;
    2423             :                 } else {
    2424           0 :                         shader->invalid = GF_TRUE;
    2425             :                         if (uni_name) gf_free(uni_name);
    2426           0 :                         return js_throw_err_msg(ctx, GF_BAD_PARAM, "unknown object type for right operand");
    2427             :                 }
    2428             :         }
    2429           7 :         else if (JS_IsBool(argv[var_idx+2])) {
    2430           0 :                 new_op.bval = JS_ToBool(ctx, argv[var_idx+2]) ? GF_TRUE : GF_FALSE;
    2431             :                 right_value_type = COMP_BOOL;
    2432             :         }
    2433           7 :         else if (JS_IsNumber(argv[var_idx+2])) {
    2434           7 :                 if (JS_IsInteger(argv[var_idx+2])) {
    2435           6 :                         JS_ToInt32(ctx, &new_op.ival, argv[var_idx+2]);
    2436             :                         right_value_type = COMP_INT;
    2437             :                 } else {
    2438             :                         Double v;
    2439           1 :                         JS_ToFloat64(ctx, &v, argv[var_idx+2]);
    2440           1 :                         new_op.vec[0] = (Float) v;
    2441             :                         right_value_type = COMP_FLOAT;
    2442             :                 }
    2443             :         }
    2444             : 
    2445         126 :         if (dual_right_val) {
    2446             :                 Bool is_ok=GF_FALSE;
    2447           5 :                 if ((u32) argc<=var_idx+3) {}
    2448             :                 else {
    2449           5 :                         val_name = JS_ToCString(ctx, argv[var_idx+3]);
    2450           5 :                         if (val_name) {
    2451           5 :                                 right_op2_idx = get_builtin_var_name(shader, val_name);
    2452           5 :                                 if (right_op2_idx >= EVG_FIRST_VAR_ID) {
    2453             :                                         is_ok=GF_TRUE;
    2454             :                                 }
    2455             :                         }
    2456           5 :                         JS_FreeCString(ctx, val_name);
    2457             :                 }
    2458           5 :                 if (!is_ok) {
    2459           0 :                         JSValue ret = js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid second right-operand value, only local variable allowed");
    2460           0 :                         JS_FreeCString(ctx, val_name);
    2461           0 :                         shader->invalid = GF_TRUE;
    2462           0 :                         if (uni_name) gf_free(uni_name);
    2463           0 :                         return ret;
    2464             :                 }
    2465             :         }
    2466             : 
    2467         256 : op_parsed:
    2468             : 
    2469         130 :         if (dual_right_val && !right_op2_idx) {
    2470           0 :                 shader->invalid = GF_TRUE;
    2471           0 :                 if (uni_name) gf_free(uni_name);
    2472           0 :                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "invalid second right-operand value, only local variable allowed");
    2473             :         }
    2474         130 :         new_op.op_type = op_type;
    2475         130 :         if (!new_op.op_type) {
    2476           0 :                 shader->invalid = GF_TRUE;
    2477           0 :                 if (uni_name) gf_free(uni_name);
    2478           0 :                 return js_throw_err_msg(ctx, GF_BAD_PARAM, "unknown operation type");
    2479             :         }
    2480         130 :         new_op.cond_type = cond_type;
    2481         130 :         new_op.left_value = left_op_idx;
    2482         130 :         new_op.right_value = right_op_idx;
    2483         130 :         new_op.left_value_type = left_value_type;
    2484         130 :         new_op.right_value_type = right_value_type;
    2485         130 :         new_op.right_value_second = right_op2_idx;
    2486         130 :         if (new_op.tx) {
    2487           2 :                 new_op.tx_ref = JS_DupValue(ctx, new_op.tx_ref);
    2488             :         }
    2489             : 
    2490         130 :         if (shader->alloc_ops <= shader->nb_ops) {
    2491         130 :                 shader->alloc_ops = shader->nb_ops+1;
    2492         130 :                 shader->ops = gf_realloc(shader->ops, sizeof(ShaderOp)*shader->alloc_ops);
    2493         130 :                 shader->ops[shader->nb_ops].uni_name = NULL;
    2494             :         }
    2495         130 :         if (shader->ops[shader->nb_ops].uni_name) {
    2496           0 :                 gf_free(shader->ops[shader->nb_ops].uni_name);
    2497           0 :                 shader->ops[shader->nb_ops].uni_name = NULL;
    2498             :         }
    2499             : 
    2500         130 :         shader->ops[shader->nb_ops] = new_op;
    2501         130 :         shader->ops[shader->nb_ops].uni_name = uni_name;
    2502         130 :         shader->nb_ops++;
    2503         130 :         return JS_NewInt32(ctx, shader->nb_ops);
    2504             : }
    2505             : 
    2506         226 : static JSValue shader_update(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
    2507             : {
    2508             :         u32 i;
    2509         226 :         EVGShader *shader = JS_GetOpaque(obj, shader_class_id);
    2510         226 :         if (!shader) return JS_EXCEPTION;
    2511         226 :         if (shader->invalid) return JS_EXCEPTION;
    2512             : 
    2513        3486 :         for (i=0; i<shader->nb_ops; i++) {
    2514             :                 JSValue v;
    2515        3486 :                 ShaderOp *op = &shader->ops[i];
    2516        3486 :                 if (!op->uni_name) continue;
    2517         926 :                 v = JS_GetPropertyStr(ctx, obj, op->uni_name);
    2518         926 :                 if (JS_IsUndefined(v)) return js_throw_err_msg(ctx, GF_BAD_PARAM, "uniform %s cannot be found in shader", op->uni_name);
    2519         926 :                 if (JS_IsBool(v)) {
    2520           0 :                         op->right_value_type = COMP_BOOL;
    2521           0 :                         op->bval = JS_ToBool(ctx, v) ? GF_TRUE : GF_FALSE;
    2522             :                 }
    2523         926 :                 else if (JS_IsNumber(v)) {
    2524         226 :                         if (JS_IsInteger(v)) {
    2525         226 :                                 op->right_value_type = COMP_INT;
    2526         226 :                                 if (JS_ToInt32(ctx, &op->ival, v)) return JS_EXCEPTION;
    2527             :                         } else {
    2528           0 :                                 op->right_value_type = COMP_FLOAT;
    2529           0 :                                 EVG_GET_FLOAT(op->vec[0], v)
    2530             :                         }
    2531             :                 }
    2532         700 :                 else if (JS_IsArray(ctx, v)) {
    2533             :                         u32 idx, length;
    2534         700 :                         JSValue comp = JS_GetPropertyStr(ctx, v, "length");
    2535         700 :                         if (JS_ToInt32(ctx, &length, comp)) return JS_EXCEPTION;
    2536             :                         JS_FreeValue(ctx, comp);
    2537         700 :                         if (length>4) length=4;
    2538         700 :                         op->right_value_type = 0;
    2539        3400 :                         for (idx=0;idx<length; idx++) {
    2540        2700 :                                 comp = JS_GetPropertyUint32(ctx, v, idx);
    2541        2700 :                                 EVG_GET_FLOAT(op->vec[idx], comp);
    2542        2700 :                                 op->right_value_type |= 1<<idx;
    2543             :                                 JS_FreeValue(ctx, comp);
    2544             :                         }
    2545             :                 }
    2546           0 :                 else if (JS_IsObject(v)) {
    2547             :                         u32 data_size;
    2548             :                         Float *vals;
    2549             :                         u32 idx;
    2550           0 :                         u8 *data = evg_get_array(ctx, v, &data_size);
    2551           0 :                         if (data) {
    2552           0 :                                 if (data_size%4) return JS_EXCEPTION;
    2553             :                                 vals = (Float*)data;
    2554           0 :                                 data_size/= sizeof(Float);
    2555           0 :                                 if (data_size>4) data_size=4;
    2556           0 :                                 op->right_value_type = 0;
    2557           0 :                                 for (idx=0;idx<data_size; idx++) {
    2558           0 :                                         op->vec[idx] = vals[idx];
    2559           0 :                                         op->right_value_type |= 1<<idx;
    2560             :                                 }
    2561             :                         } else {
    2562             :                                 JSValue comp;
    2563           0 :                                 op->right_value_type = 0;
    2564             : #define GET_COMP(_name, _idx, _mask)\
    2565             :                                 comp = JS_GetPropertyStr(ctx, v, _name);\
    2566             :                                 if (!JS_IsUndefined(comp)) { EVG_GET_FLOAT(op->vec[_idx], comp); op->right_value_type |= _mask; }\
    2567             :                                 JS_FreeValue(ctx, comp);\
    2568             : 
    2569           0 :                                 GET_COMP("x", 0, COMP_X);
    2570           0 :                                 GET_COMP("r", 0, COMP_X);
    2571           0 :                                 GET_COMP("s", 0, COMP_X);
    2572             : 
    2573           0 :                                 GET_COMP("y", 1, COMP_Y);
    2574           0 :                                 GET_COMP("g", 1, COMP_Y);
    2575           0 :                                 GET_COMP("t", 1, COMP_Y);
    2576             : 
    2577           0 :                                 GET_COMP("z", 2, COMP_Z);
    2578           0 :                                 GET_COMP("b", 2, COMP_Z);
    2579             : 
    2580           0 :                                 GET_COMP("q", 3, COMP_Q);
    2581           0 :                                 GET_COMP("w", 3, COMP_Q);
    2582           0 :                                 GET_COMP("a", 3, COMP_Q);
    2583             : 
    2584             :                         }
    2585             :                 }
    2586             :                 JS_FreeValue(ctx, v);
    2587             : 
    2588             :         }
    2589         226 :         return JS_UNDEFINED;
    2590             : }
    2591             : 
    2592             : static const JSCFunctionListEntry shader_funcs[] =
    2593             : {
    2594             :         JS_CFUNC_DEF("push", 0, shader_push),
    2595             :         JS_CFUNC_DEF("update", 0, shader_update),
    2596             : };
    2597             : 
    2598          36 : static JSValue canvas3d_new_shader(JSContext *ctx, JSValueConst obj, int argc, JSValueConst *argv)
    2599             : {
    2600             :         EVGShader *shader;
    2601             :         u32 mode;
    2602             :         JSValue res;
    2603          36 :         GF_JSCanvas *canvas = JS_GetOpaque(obj, canvas3d_class_id);
    2604          36 :         if (!canvas) return JS_EXCEPTION;
    2605          36 :         if (!argc) return JS_EXCEPTION;
    2606          36 :         JS_ToInt32(ctx, &mode, argv[0]);
    2607          36 :         GF_SAFEALLOC(shader, EVGShader);
    2608          36 :         if (!shader)
    2609           0 :                 return js_throw_err(ctx, GF_OUT_OF_MEM);
    2610          36 :         shader->mode = mode;
    2611          36 :         res = JS_NewObjectClass(ctx, shader_class_id);
    2612          36 :         JS_SetOpaque(res, shader);
    2613          36 :         return res;
    2614             : }
    2615             : 
    2616             : static const JSCFunctionListEntry canvas3d_funcs[] =
    2617             : {
    2618             :         JS_CGETSET_MAGIC_DEF("clipper", NULL, canvas3d_setProperty, GF_EVG_CLIPPER),
    2619             :         JS_CGETSET_MAGIC_DEF("fragment", canvas3d_getProperty, canvas3d_setProperty, GF_EVG_FRAG_SHADER),
    2620             :         JS_CGETSET_MAGIC_DEF("vertex", canvas3d_getProperty, canvas3d_setProperty, GF_EVG_VERT_SHADER),
    2621             :         JS_CGETSET_MAGIC_DEF("ccw", NULL, canvas3d_setProperty, GF_EVG_CCW),
    2622             :         JS_CGETSET_MAGIC_DEF("backcull", NULL, canvas3d_setProperty, GF_EVG_BACKCULL),
    2623             :         JS_CGETSET_MAGIC_DEF("antialias", NULL, canvas3d_setProperty, GF_EVG_ANTIALIAS),
    2624             :         JS_CGETSET_MAGIC_DEF("min_depth", NULL, canvas3d_setProperty, GF_EVG_MINDEPTH),
    2625             :         JS_CGETSET_MAGIC_DEF("max_depth", NULL, canvas3d_setProperty, GF_EVG_MAXDEPTH),
    2626             :         JS_CGETSET_MAGIC_DEF("point_size", NULL, canvas3d_setProperty, GF_EVG_POINTSIZE),
    2627             :         JS_CGETSET_MAGIC_DEF("point_smooth", NULL, canvas3d_setProperty, GF_EVG_POINTSMOOTH),
    2628             :         JS_CGETSET_MAGIC_DEF("line_size", NULL, canvas3d_setProperty, GF_EVG_LINESIZE),
    2629             :         JS_CGETSET_MAGIC_DEF("clip_zero", NULL, canvas3d_setProperty, GF_EVG_CLIP_ZERO),
    2630             :         JS_CGETSET_MAGIC_DEF("depth_test", NULL, canvas3d_setProperty, GF_EVG_DEPTH_TEST),
    2631             :         JS_CGETSET_MAGIC_DEF("write_depth", NULL, canvas3d_setProperty, GF_EVG_WRITE_DEPTH),
    2632             :         JS_CGETSET_MAGIC_DEF("depth_buffer", canvas3d_getProperty, canvas3d_setProperty, GF_EVG_DEPTH_BUFFER),
    2633             : 
    2634             :         JS_CFUNC_DEF("clear", 0, canvas3d_clear),
    2635             :         JS_CFUNC_DEF("clearf", 0, canvas3d_clearf),
    2636             :         JS_CFUNC_DEF("reassign", 0, canvas3d_reassign),
    2637             :         JS_CFUNC_DEF("projection", 0, canvas3d_projection),
    2638             :         JS_CFUNC_DEF("modelview", 0, canvas3d_modelview),
    2639             :         JS_CFUNC_DEF("draw_array", 0, canvas3d_draw_array),
    2640             :         JS_CFUNC_DEF("draw_path", 0, canvas3d_draw_path),
    2641             :         JS_CFUNC_DEF("clear_depth", 0, canvas3d_clear_depth),
    2642             :         JS_CFUNC_DEF("viewport", 0, canvas3d_viewport),
    2643             :         JS_CFUNC_DEF("new_shader", 0, canvas3d_new_shader),
    2644             :         JS_CFUNC_DEF("toYUV", 0, canvas3d_toYUV),
    2645             :         JS_CFUNC_DEF("toRGB", 0, canvas3d_toRGB),
    2646             : };
    2647             : 
    2648          19 : static JSValue canvas3d_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    2649             : {
    2650          19 :         return canvas_constructor_internal(c, new_target, argc, argv, GF_TRUE);
    2651             : }
    2652             : 
    2653             : #ifdef EVG_USE_JS_SHADER
    2654             : 
    2655             : JSClassDef fragment_class = {
    2656             :         .class_name = "Fragment",
    2657             : };
    2658             : 
    2659             : enum {
    2660             :         EVG_FRAG_SCREENX,
    2661             :         EVG_FRAG_SCREENY,
    2662             :         EVG_FRAG_DEPTH,
    2663             :         EVG_FRAG_R,
    2664             :         EVG_FRAG_G,
    2665             :         EVG_FRAG_B,
    2666             :         EVG_FRAG_A,
    2667             :         EVG_FRAG_Y,
    2668             :         EVG_FRAG_U,
    2669             :         EVG_FRAG_V,
    2670             :         EVG_FRAG_RGBA,
    2671             :         EVG_FRAG_RGB,
    2672             :         EVG_FRAG_YUV,
    2673             :         EVG_FRAG_YUVA,
    2674             : };
    2675             : 
    2676             : static JSValue fragment_getProperty(JSContext *c, JSValueConst obj, int magic)
    2677             : {
    2678             :         GF_EVGFragmentParam *frag = JS_GetOpaque(obj, fragment_class_id);
    2679             :         if (!frag) return JS_EXCEPTION;
    2680             :         switch (magic) {
    2681             :         case EVG_FRAG_SCREENX: return JS_NewFloat64(c, frag->screen_x);
    2682             :         case EVG_FRAG_SCREENY: return JS_NewFloat64(c, frag->screen_y);
    2683             :         case EVG_FRAG_DEPTH: return JS_NewFloat64(c, frag->depth);
    2684             :         }
    2685             :         return JS_UNDEFINED;
    2686             : }
    2687             : static JSValue fragment_setProperty(JSContext *ctx, JSValueConst obj, JSValueConst value, int magic)
    2688             : {
    2689             :         EVG_VAIRes *vr;
    2690             :         GF_EVGFragmentType frag_type = GF_EVG_FRAG_RGB;
    2691             : 
    2692             :         GF_EVGFragmentParam *frag = JS_GetOpaque(obj, fragment_class_id);
    2693             :         if (!frag) return JS_EXCEPTION;
    2694             :         switch (magic) {
    2695             :         case EVG_FRAG_DEPTH:
    2696             :                 EVG_GET_FLOAT(frag->depth, value)
    2697             :                 break;
    2698             :         case EVG_FRAG_Y:
    2699             :                 frag_type = GF_EVG_FRAG_YUV;
    2700             :         case EVG_FRAG_R:
    2701             :                 EVG_GET_FLOAT(frag->color.x, value)
    2702             :                 CLAMPCOLF(frag->color.x)
    2703             :                 frag->frag_valid = frag_type;
    2704             :                 break;
    2705             :         case EVG_FRAG_U:
    2706             :                 frag_type = GF_EVG_FRAG_YUV;
    2707             :         case EVG_FRAG_G:
    2708             :                 EVG_GET_FLOAT(frag->color.y, value)
    2709             :                 CLAMPCOLF(frag->color.y)
    2710             :                 frag->frag_valid = frag_type;
    2711             :                 break;
    2712             :         case EVG_FRAG_V:
    2713             :                 frag_type = GF_EVG_FRAG_YUV;
    2714             :         case EVG_FRAG_B:
    2715             :                 EVG_GET_FLOAT(frag->color.z, value)
    2716             :                 CLAMPCOLF(frag->color.z)
    2717             :                 frag->frag_valid = frag_type;
    2718             :                 break;
    2719             :         case EVG_FRAG_A:
    2720             :                 EVG_GET_FLOAT(frag->color.q, value)
    2721             :                 CLAMPCOLF(frag->color.q)
    2722             :                 if (!frag->frag_valid)
    2723             :                         frag->frag_valid = GF_EVG_FRAG_RGB;
    2724             :                 break;
    2725             :         case EVG_FRAG_YUVA:
    2726             :                 frag_type = GF_EVG_FRAG_YUV;
    2727             :         case EVG_FRAG_RGBA:
    2728             :                 vr = JS_GetOpaque(value, vaires_class_id);
    2729             :                 if (vr) {
    2730             :                         frag->color.x = vr->values[0];
    2731             :                         frag->color.y = vr->values[1];
    2732             :                         frag->color.z = vr->values[2];
    2733             :                         frag->color.q = vr->values[3];
    2734             :                         frag->frag_valid = frag_type;
    2735             :                 } else {
    2736             :                         Double a, r, g, b;
    2737             :                         a=1.0;
    2738             :                         if (!get_color_from_args(ctx, 1, &value, 0, &a, &r, &g, &b))
    2739             :                                 return JS_EXCEPTION;
    2740             :                         frag->color.q = (Float) a;
    2741             :                         frag->color.x = (Float) r;
    2742             :                         frag->color.y = (Float) g;
    2743             :                         frag->color.z = (Float) b;
    2744             :                         frag->frag_valid = GF_EVG_FRAG_RGB;
    2745             :                 }
    2746             :                 break;
    2747             :         case EVG_FRAG_YUV:
    2748             :                 frag_type = GF_EVG_FRAG_YUV;
    2749             :         case EVG_FRAG_RGB:
    2750             :                 vr = JS_GetOpaque(value, vaires_class_id);
    2751             :                 if (vr) {
    2752             :                         frag->color.x = vr->values[0];
    2753             :                         frag->color.y = vr->values[1];
    2754             :                         frag->color.z = vr->values[2];
    2755             :                         frag->frag_valid = frag_type;
    2756             :                 } else
    2757             :                         return JS_EXCEPTION;
    2758             :         default:
    2759             :                 return JS_UNDEFINED;
    2760             :         }
    2761             :         return JS_UNDEFINED;
    2762             : }
    2763             : 
    2764             : static const JSCFunctionListEntry fragment_funcs[] =
    2765             : {
    2766             :         JS_CGETSET_MAGIC_DEF("x", fragment_getProperty, NULL, EVG_FRAG_SCREENX),
    2767             :         JS_CGETSET_MAGIC_DEF("y", fragment_getProperty, NULL, EVG_FRAG_SCREENY),
    2768             :         JS_CGETSET_MAGIC_DEF("z", fragment_getProperty, fragment_setProperty, EVG_FRAG_DEPTH),
    2769             :         JS_ALIAS_DEF("depth", "z"),
    2770             :         JS_CGETSET_MAGIC_DEF("r", NULL, fragment_setProperty, EVG_FRAG_R),
    2771             :         JS_CGETSET_MAGIC_DEF("g", NULL, fragment_setProperty, EVG_FRAG_G),
    2772             :         JS_CGETSET_MAGIC_DEF("b", NULL, fragment_setProperty, EVG_FRAG_B),
    2773             :         JS_CGETSET_MAGIC_DEF("a", NULL, fragment_setProperty, EVG_FRAG_A),
    2774             :         JS_ALIAS_DEF("Y", "r"),
    2775             :         JS_ALIAS_DEF("U", "g"),
    2776             :         JS_ALIAS_DEF("V", "b"),
    2777             :         JS_CGETSET_MAGIC_DEF("rgba", NULL, fragment_setProperty, EVG_FRAG_RGBA),
    2778             :         JS_CGETSET_MAGIC_DEF("rgb", NULL, fragment_setProperty, EVG_FRAG_RGB),
    2779             :         JS_CGETSET_MAGIC_DEF("yuv", NULL, fragment_setProperty, EVG_FRAG_YUV),
    2780             :         JS_CGETSET_MAGIC_DEF("yuva", NULL, fragment_setProperty, EVG_FRAG_YUVA),
    2781             : };
    2782             : 
    2783             : JSClassDef vertex_class = {
    2784             :         .class_name = "Vertex",
    2785             : };
    2786             : 
    2787             : enum {
    2788             :         EVG_VERTEX_IN,
    2789             :         EVG_VERTEX_OUT,
    2790             : };
    2791             : 
    2792             : static JSValue vertex_getProperty(JSContext *c, JSValueConst obj, int magic)
    2793             : {
    2794             :         JSValue res;
    2795             :         GF_EVGVertexParam *vert= JS_GetOpaque(obj, vertex_class_id);
    2796             :         if (!vert) return JS_EXCEPTION;
    2797             :         switch (magic) {
    2798             :         case EVG_VERTEX_IN: 
    2799             :                 res = JS_NewObject(c);
    2800             :                 JS_SetPropertyStr(c, res, "x", JS_NewFloat64(c, vert->in_vertex.x));
    2801             :                 JS_SetPropertyStr(c, res, "y", JS_NewFloat64(c, vert->in_vertex.y));
    2802             :                 JS_SetPropertyStr(c, res, "z", JS_NewFloat64(c, vert->in_vertex.z));
    2803             :                 JS_SetPropertyStr(c, res, "q", JS_NewFloat64(c, vert->in_vertex.q));
    2804             :                 return res;
    2805             :         }
    2806             :         return JS_UNDEFINED;
    2807             : }
    2808             : static JSValue vertex_setProperty(JSContext *ctx, JSValueConst obj, JSValueConst value, int magic)
    2809             : {
    2810             :         Double _f;
    2811             :         JSValue v;
    2812             :         GF_EVGVertexParam *vert= JS_GetOpaque(obj, vertex_class_id);
    2813             :         if (!vert) return JS_EXCEPTION;
    2814             :         switch (magic) {
    2815             :         case EVG_VERTEX_OUT:
    2816             :                 v = JS_GetPropertyStr(ctx, value, "x");
    2817             :                 EVG_GET_FLOAT(_f, v);
    2818             :                 vert->out_vertex.x = FLT2FIX(_f);
    2819             :                 v = JS_GetPropertyStr(ctx, value, "y");
    2820             :                 EVG_GET_FLOAT(_f, v);
    2821             :                 vert->out_vertex.y = FLT2FIX(_f);
    2822             :                 v = JS_GetPropertyStr(ctx, value, "z");
    2823             :                 EVG_GET_FLOAT(_f, v);
    2824             :                 vert->out_vertex.z = FLT2FIX(_f);
    2825             :                 v = JS_GetPropertyStr(ctx, value, "q");
    2826             :                 EVG_GET_FLOAT(_f, v);
    2827             :                 vert->out_vertex.q = FLT2FIX(_f);
    2828             : 
    2829             :                 break;
    2830             :         default:
    2831             :                 return JS_UNDEFINED;
    2832             :         }
    2833             :         return JS_UNDEFINED;
    2834             : }
    2835             : static const JSCFunctionListEntry vertex_funcs[] =
    2836             : {
    2837             :         JS_CGETSET_MAGIC_DEF("vertex", vertex_getProperty, NULL, EVG_VERTEX_IN),
    2838             :         JS_CGETSET_MAGIC_DEF("vertexOut", NULL, vertex_setProperty, EVG_VERTEX_OUT),
    2839             : };
    2840             : #endif // EVG_USE_JS_SHADER
    2841             : 
    2842     7275459 : Bool vai_call_lerp(EVG_VAI *vai, GF_EVGFragmentParam *frag)
    2843             : {
    2844             :         u32 i;
    2845             :         
    2846             :         //different primitive, setup inperpolation points
    2847     7275459 :         if (frag->prim_index != vai->prim_idx) {
    2848             :                 u32 idx;
    2849        1324 :                 vai->prim_idx = frag->prim_index;
    2850             :                 //no values, this is a VAI filled by the vertex shader
    2851        1324 :                 if (!vai->values) {
    2852         572 :                 } else if (vai->interp_type==GF_EVG_VAI_PRIMITIVE) {
    2853           0 :                         idx = frag->prim_index * vai->nb_comp;
    2854           0 :                         if (idx+vai->nb_comp > vai->nb_values)
    2855             :                                 return GF_FALSE;
    2856             : 
    2857           0 :                         for (i=0; i<vai->nb_comp; i++) {
    2858           0 :                                 vai->result.values[i] = vai->values[idx+i];
    2859             :                         }
    2860         572 :                 } else if (frag->ptype!=GF_EVG_POINTS) {
    2861             :                         u32 nb_v_per_prim = 3;
    2862         572 :                         if (frag->ptype==GF_EVG_LINES)
    2863             :                                 nb_v_per_prim=2;
    2864             : 
    2865         572 :                         if (vai->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
    2866           0 :                                 idx = frag->idx1 * vai->nb_comp;
    2867             :                         } else {
    2868         572 :                                 idx = frag->prim_index * nb_v_per_prim * vai->nb_comp;
    2869             :                         }
    2870         572 :                         if (idx+vai->nb_comp > vai->nb_values)
    2871             :                                 return GF_FALSE;
    2872        1144 :                         for (i=0; i<vai->nb_comp; i++) {
    2873        1144 :                                 vai->anchors[0][i] = vai->values[idx+i];
    2874             :                         }
    2875             : 
    2876         572 :                         if (vai->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
    2877           0 :                                 idx = frag->idx2 * vai->nb_comp;
    2878             :                         } else {
    2879         572 :                                 idx = (frag->prim_index * nb_v_per_prim + 1) * vai->nb_comp;
    2880             :                         }
    2881         572 :                         if (idx+vai->nb_comp > vai->nb_values)
    2882             :                                 return GF_FALSE;
    2883        1144 :                         for (i=0; i<vai->nb_comp; i++) {
    2884        1144 :                                 vai->anchors[1][i] = vai->values[idx+i];
    2885             :                         }
    2886         572 :                         if (frag->ptype!=GF_EVG_LINES) {
    2887         572 :                                 if (vai->interp_type==GF_EVG_VAI_VERTEX_INDEX) {
    2888           0 :                                         idx = frag->idx3 * vai->nb_comp;
    2889             :                                 } else {
    2890         572 :                                         idx = (frag->prim_index * nb_v_per_prim + 2) * vai->nb_comp;
    2891             :                                 }
    2892         572 :                                 if (idx+vai->nb_comp > vai->nb_values)
    2893             :                                         return GF_FALSE;
    2894        1144 :                                 for (i=0; i<vai->nb_comp; i++) {
    2895        1144 :                                         vai->anchors[2][i] = vai->values[idx+i];
    2896             :                                 }
    2897             :                         }
    2898             :                 }
    2899             :         }
    2900     7275459 :         if (vai->interp_type==GF_EVG_VAI_PRIMITIVE) {
    2901             :                 return GF_TRUE;
    2902             :         }
    2903             : 
    2904     7275459 :         if (frag->ptype==GF_EVG_LINES) {
    2905           0 :                 for (i=0; i<vai->nb_comp; i++) {
    2906           0 :                         Float v = frag->pbc1 * vai->anchors[0][i] + frag->pbc2 * vai->anchors[1][i];
    2907           0 :                         vai->result.values[i] = v / frag->persp_denum;
    2908             :                 }
    2909             :         } else {
    2910    20621890 :                 for (i=0; i<vai->nb_comp; i++) {
    2911    20621890 :                         Float v = (Float) ( frag->pbc1 * vai->anchors[0][i] + frag->pbc2 * vai->anchors[1][i] + frag->pbc3 * vai->anchors[2][i] );
    2912    20621890 :                         vai->result.values[i] = v / frag->persp_denum;
    2913             :                 }
    2914             :         }
    2915     7275459 :         if (vai->normalize) {
    2916     3035486 :                 if (vai->nb_comp==2) {
    2917             :                         Float len;
    2918           0 :                         if (!vai->result.values[0]) len = ABS(vai->result.values[1]);
    2919           0 :                         else if (!vai->result.values[1]) len = ABS(vai->result.values[0]);
    2920           0 :                         else len = sqrtf(vai->result.values[0]*vai->result.values[0] + vai->result.values[1]*vai->result.values[1]);
    2921           0 :                         if (len) {
    2922           0 :                                 vai->result.values[0]/=len;
    2923           0 :                                 vai->result.values[1]/=len;
    2924             :                         }
    2925             :                 } else {
    2926     3035486 :                         gf_vec_norm((GF_Vec *) &vai->result.values[0]);
    2927             :                 }
    2928             :         }
    2929             :         return GF_TRUE;
    2930             : }
    2931             : 
    2932             : #ifdef EVG_USE_JS_SHADER
    2933             : static JSValue vai_lerp(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    2934             : {
    2935             :         EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
    2936             :         if (!vai || !argc) return JS_EXCEPTION;
    2937             :         GF_EVGFragmentParam *frag = JS_GetOpaque(argv[0], fragment_class_id);
    2938             :         if (!frag) return JS_EXCEPTION;
    2939             : 
    2940             :         if (!vai_call_lerp(vai, frag))
    2941             :                 return JS_EXCEPTION;
    2942             :         return JS_DupValue(c, vai->res);
    2943             : }
    2944             : #endif
    2945             : 
    2946           1 : static JSValue vai_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    2947             : {
    2948           1 :         EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
    2949           1 :         if (!vai) return JS_EXCEPTION;
    2950           1 :         switch (magic) {
    2951           1 :         case 0:
    2952           1 :                 vai->normalize = JS_ToBool(c, value) ? GF_TRUE : GF_FALSE;
    2953           1 :                 break;
    2954             :         }
    2955           1 :         return JS_UNDEFINED;
    2956             : }
    2957             : 
    2958           3 : static JSValue vai_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    2959             : {
    2960             :         EVG_VAI *vai;
    2961             :         JSValue obj;
    2962             :         u8 *data=NULL;
    2963           3 :         u32 data_size=0;
    2964             :         u32 nb_comp;
    2965           3 :         s32 interp_type = GF_EVG_VAI_VERTEX_INDEX;
    2966           3 :         if (argc<1)
    2967           0 :                 return js_throw_err_msg(c, GF_BAD_PARAM, "Missing parameter for VertexAttribInterpolator");
    2968             : 
    2969           3 :         if (argc>=2) {
    2970           1 :                 data = evg_get_array(c, argv[0], &data_size);
    2971           1 :                 if (!data) return JS_EXCEPTION;
    2972           1 :                 if (JS_ToInt32(c, &nb_comp, argv[1])) return JS_EXCEPTION;
    2973           1 :                 if (nb_comp>MAX_ATTR_DIM)
    2974           0 :                         return js_throw_err_msg(c, GF_BAD_PARAM, "Dimension too big, max is %d", MAX_ATTR_DIM);
    2975             : 
    2976           1 :                 if (data_size % sizeof(Float)) return JS_EXCEPTION;
    2977           1 :                 data_size /= sizeof(Float);
    2978           1 :                 if (data_size % nb_comp) return JS_EXCEPTION;
    2979             : 
    2980           1 :                 if (argc>2) {
    2981           1 :                         if (JS_ToInt32(c, &interp_type, argv[2])) return JS_EXCEPTION;
    2982             :                 }
    2983             :         } else {
    2984           2 :                 if (JS_ToInt32(c, &nb_comp, argv[0])) return JS_EXCEPTION;
    2985             :         }
    2986             : 
    2987           3 :         GF_SAFEALLOC(vai, EVG_VAI);
    2988           3 :         if (!vai)
    2989           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    2990           3 :         vai->nb_comp = nb_comp;
    2991           3 :         vai->values = (Float *)data;
    2992           3 :         vai->nb_values = data_size;
    2993           3 :         if (data)
    2994           1 :                 vai->ab = JS_DupValue(c, argv[0]);
    2995             : 
    2996           3 :         vai->prim_idx = (u32) -1;
    2997           3 :         vai->interp_type = interp_type;
    2998             : 
    2999             : #ifdef EVG_USE_JS_SHADER
    3000             :         vai->res = JS_NewObjectClass(c, vaires_class_id);
    3001             :         JS_SetOpaque(vai->res, &vai->result);
    3002             :         JS_SetPropertyStr(c, vai->res, "length", JS_NewInt32(c, nb_comp));
    3003             : #endif
    3004           3 :         vai->result.dim = nb_comp;
    3005           3 :         if (nb_comp==1) vai->result.comp_type = COMP_X;
    3006           3 :         else if (nb_comp==2) vai->result.comp_type = COMP_V2_XY;
    3007           2 :         else if (nb_comp==3) vai->result.comp_type = COMP_V3;
    3008           0 :         else vai->result.comp_type = COMP_V4;
    3009             : 
    3010           3 :         obj = JS_NewObjectClass(c, vai_class_id);
    3011           3 :         JS_SetOpaque(obj, vai);
    3012           3 :         return obj;
    3013             : }
    3014           3 : static void vai_finalize(JSRuntime *rt, JSValue obj)
    3015             : {
    3016           3 :         EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
    3017           3 :         if (!vai) return;
    3018             :         JS_FreeValueRT(rt, vai->ab);
    3019             : #ifdef EVG_USE_JS_SHADER
    3020             :         JS_FreeValueRT(rt, vai->res);
    3021             : #endif
    3022           3 :         gf_free(vai);
    3023             : }
    3024             : 
    3025           4 : static void vai_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
    3026             : {
    3027           4 :         EVG_VAI *vai = JS_GetOpaque(obj, vai_class_id);
    3028           4 :         if (!vai) return;
    3029           4 :         JS_MarkValue(rt, vai->ab, mark_func);
    3030             : #ifdef EVG_USE_JS_SHADER
    3031             :         JS_MarkValue(rt, vai->res, mark_func);
    3032             : #endif
    3033             : }
    3034             : 
    3035             : JSClassDef vai_class = {
    3036             :         .class_name = "VertexAttribInterpolator",
    3037             :         .finalizer = vai_finalize,
    3038             :         .gc_mark = vai_gc_mark
    3039             : };
    3040             : static const JSCFunctionListEntry vai_funcs[] =
    3041             : {
    3042             :         JS_CGETSET_MAGIC_DEF("normalize", NULL, vai_setProperty, 0),
    3043             : #ifdef EVG_USE_JS_SHADER
    3044             :         JS_CFUNC_DEF("lerp", 0, vai_lerp),
    3045             : #endif
    3046             : };
    3047             : 
    3048           1 : static JSValue va_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    3049             : {
    3050             :         EVG_VA *va;
    3051             :         JSValue obj;
    3052             :         u8 *data=NULL;
    3053             :         u32 data_size;
    3054             :         u32 nb_comp;
    3055           1 :         s32 interp_type = GF_EVG_VAI_VERTEX_INDEX;
    3056           1 :         if (argc<2)
    3057           0 :                 return js_throw_err_msg(c, GF_BAD_PARAM, "Missing parameter / data for VertexAttrib");
    3058             : 
    3059           1 :         data = evg_get_array(c, argv[0], &data_size);
    3060           1 :         if (!data) return JS_EXCEPTION;
    3061           1 :         if (JS_ToInt32(c, &nb_comp, argv[1])) return JS_EXCEPTION;
    3062           1 :         if (nb_comp>MAX_ATTR_DIM)
    3063           0 :                 return js_throw_err_msg(c, GF_BAD_PARAM, "Dimension too big, max is %d", MAX_ATTR_DIM);
    3064             : 
    3065           1 :         if (data_size % sizeof(Float)) return JS_EXCEPTION;
    3066           1 :         data_size /= sizeof(Float);
    3067           1 :         if (data_size % nb_comp) return JS_EXCEPTION;
    3068             : 
    3069           1 :         if (argc>2) {
    3070           1 :                 if (JS_ToInt32(c, &interp_type, argv[2])) return JS_EXCEPTION;
    3071             :         }
    3072             : 
    3073           1 :         GF_SAFEALLOC(va, EVG_VA);
    3074           1 :         if (!va)
    3075           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    3076           1 :         va->nb_comp = nb_comp;
    3077           1 :         va->values = (Float *)data;
    3078           1 :         va->nb_values = data_size;
    3079           1 :         va->ab = JS_DupValue(c, argv[0]);
    3080           1 :         va->interp_type = interp_type;
    3081           1 :         if (va->nb_comp==1) va->att_type = COMP_FLOAT;
    3082           1 :         else if (va->nb_comp==2) va->att_type = COMP_V2_XY;
    3083           1 :         else if (va->nb_comp==3) va->att_type = COMP_V3;
    3084           0 :         else va->att_type = COMP_V4;
    3085             : 
    3086           1 :         obj = JS_NewObjectClass(c, va_class_id);
    3087           1 :         JS_SetOpaque(obj, va);
    3088           1 :         return obj;
    3089             : }
    3090             : 
    3091           1 : static JSValue va_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    3092             : {
    3093           1 :         EVG_VA *va = JS_GetOpaque(obj, va_class_id);
    3094           1 :         if (!va) return JS_EXCEPTION;
    3095           1 :         switch (magic) {
    3096           1 :         case 0:
    3097           1 :                 va->normalize = JS_ToBool(c, value) ? GF_TRUE : GF_FALSE;
    3098           1 :                 break;
    3099             :         }
    3100           1 :         return JS_UNDEFINED;
    3101             : }
    3102           1 : static void va_finalize(JSRuntime *rt, JSValue obj)
    3103             : {
    3104           1 :         EVG_VA *va = JS_GetOpaque(obj, va_class_id);
    3105           1 :         if (!va) return;
    3106             :         JS_FreeValueRT(rt, va->ab);
    3107           1 :         gf_free(va);
    3108             : }
    3109             : 
    3110           0 : static void va_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
    3111             : {
    3112           0 :         EVG_VA *va = JS_GetOpaque(obj, va_class_id);
    3113           0 :         if (!va) return;
    3114           0 :         JS_MarkValue(rt, va->ab, mark_func);
    3115             : }
    3116             : 
    3117             : JSClassDef va_class = {
    3118             :         .class_name = "VertexAttrib",
    3119             :         .finalizer = va_finalize,
    3120             :         .gc_mark = va_gc_mark
    3121             : };
    3122             : static const JSCFunctionListEntry va_funcs[] =
    3123             : {
    3124             :         JS_CGETSET_MAGIC_DEF("normalize", NULL, va_setProperty, 0),
    3125             : };
    3126             : 
    3127             : #ifdef EVG_USE_JS_SHADER
    3128             : 
    3129             : static JSValue vaires_getProperty(JSContext *c, JSValueConst obj, int magic)
    3130             : {
    3131             :         EVG_VAIRes *vr = JS_GetOpaque(obj, vaires_class_id);
    3132             :         if (!vr) return JS_EXCEPTION;
    3133             :         if (magic<MAX_ATTR_DIM) {
    3134             :                 return JS_NewFloat64(c, vr->values[magic]);
    3135             :         }
    3136             :         return JS_UNDEFINED;
    3137             : }
    3138             : static const JSCFunctionListEntry vaires_funcs[] =
    3139             : {
    3140             :         JS_CGETSET_MAGIC_DEF("x", vaires_getProperty, NULL, 0),
    3141             :         JS_CGETSET_MAGIC_DEF("y", vaires_getProperty, NULL, 1),
    3142             :         JS_CGETSET_MAGIC_DEF("z", vaires_getProperty, NULL, 2),
    3143             :         JS_CGETSET_MAGIC_DEF("w", vaires_getProperty, NULL, 3),
    3144             :         JS_ALIAS_DEF("r", "x"),
    3145             :         JS_ALIAS_DEF("g", "y"),
    3146             :         JS_ALIAS_DEF("b", "z"),
    3147             :         JS_ALIAS_DEF("a", "w"),
    3148             :         JS_ALIAS_DEF("s", "x"),
    3149             :         JS_ALIAS_DEF("t", "y"),
    3150             : };
    3151             : 
    3152             : JSClassDef vaires_class = {
    3153             :         .class_name = "VAIResult",
    3154             : };
    3155             : 
    3156             : #endif //EVG_USE_JS_SHADER
    3157             : 
    3158         218 : static void mx2d_finalize(JSRuntime *rt, JSValue obj)
    3159             : {
    3160         218 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3161         218 :         if (!mx) return;
    3162         218 :         gf_free(mx);
    3163             : }
    3164             : JSClassDef mx2d_class = {
    3165             :         "Matrix2D",
    3166             :         .finalizer = mx2d_finalize
    3167             : };
    3168             : 
    3169             : enum
    3170             : {
    3171             :         //these 6 magics map to m[x] of the matrix, DO NOT MODIFY
    3172             :         MX2D_XX = 0,
    3173             :         MX2D_XY,
    3174             :         MX2D_TX,
    3175             :         MX2D_YX,
    3176             :         MX2D_YY,
    3177             :         MX2D_TY,
    3178             :         MX2D_IDENTITY,
    3179             : };
    3180             : 
    3181           1 : static JSValue mx2d_getProperty(JSContext *c, JSValueConst obj, int magic)
    3182             : {
    3183           1 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3184           1 :         if (!mx) return JS_EXCEPTION;
    3185           1 :         if ((magic>=MX2D_XX) && (magic<=MX2D_TY)) {
    3186           1 :                 return JS_NewFloat64(c, FIX2FLT(mx->m[magic]));
    3187             :         }
    3188           0 :         if (magic==MX2D_IDENTITY)
    3189           0 :                 return JS_NewBool(c, gf_mx2d_is_identity(*mx));
    3190           0 :         return JS_UNDEFINED;
    3191             : }
    3192          17 : static JSValue mx2d_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    3193             : {
    3194          17 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3195          17 :         if (!mx) return JS_EXCEPTION;
    3196          17 :         if ((magic>=MX2D_XX) && (magic<=MX2D_TY)) {
    3197             :                 Double d;
    3198           1 :                 if (JS_ToFloat64(c, &d, value))
    3199           0 :                         return JS_EXCEPTION;
    3200           1 :                 mx->m[magic] = FIX2FLT(d);
    3201           1 :                 return JS_UNDEFINED;
    3202             :         }
    3203          16 :         if (magic==MX2D_IDENTITY) {
    3204          16 :                 if (JS_ToBool(c, value))
    3205          32 :                         gf_mx2d_init(*mx);
    3206          16 :                 return JS_UNDEFINED;
    3207             :         }
    3208           0 :         return JS_UNDEFINED;
    3209             : }
    3210             : 
    3211         201 : static JSValue mx2d_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3212             : {
    3213         201 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3214         201 :         if (!mx || !argc) return JS_EXCEPTION;
    3215         201 :         GF_Matrix2D *amx = JS_GetOpaque(argv[0], mx2d_class_id);
    3216         201 :         if (!mx) return JS_EXCEPTION;
    3217         201 :         if ((argc>1) && JS_ToBool(c, argv[1]))
    3218           0 :                 gf_mx2d_pre_multiply(mx, amx);
    3219             :         else
    3220         201 :                 gf_mx2d_add_matrix(mx, amx);
    3221             :         return JS_DupValue(c, obj);
    3222             : }
    3223             : 
    3224         219 : static JSValue mx2d_translate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3225             : {
    3226             :         Double tx, ty;
    3227         219 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3228         219 :         if (!mx || (argc<1)) return JS_EXCEPTION;
    3229             : 
    3230         438 :         if (JS_IsObject(argv[0])) {
    3231             :                 JSValue v;
    3232             : #define GETD(_arg, _name, _res)\
    3233             :                 if (! JS_IsObject(_arg)) return JS_EXCEPTION;\
    3234             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    3235             :                 JS_ToFloat64(c, &_res, v);\
    3236             :                 JS_FreeValue(c, v);\
    3237             : 
    3238           0 :                 GETD(argv[0], "x", tx);
    3239           0 :                 GETD(argv[0], "y", ty);
    3240             : #undef GETD
    3241             : 
    3242         219 :         } else if (argc==2) {
    3243         219 :                 if (JS_ToFloat64(c, &tx, argv[0])) return JS_EXCEPTION;
    3244         219 :                 if (JS_ToFloat64(c, &ty, argv[1])) return JS_EXCEPTION;
    3245             :         } else {
    3246           0 :                 return JS_EXCEPTION;
    3247             :         }
    3248         219 :         gf_mx2d_add_translation(mx, FLT2FIX(tx), FLT2FIX(ty));
    3249             :         return JS_DupValue(c, obj);
    3250             : }
    3251         201 : static JSValue mx2d_rotate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3252             : {
    3253             :         Double cx, cy, a;
    3254         201 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3255         201 :         if (!mx || (argc<3)) return JS_EXCEPTION;
    3256         201 :         if (JS_ToFloat64(c, &cx, argv[0])) return JS_EXCEPTION;
    3257         201 :         if (JS_ToFloat64(c, &cy, argv[1])) return JS_EXCEPTION;
    3258         201 :         if (JS_ToFloat64(c, &a, argv[2])) return JS_EXCEPTION;
    3259         201 :         gf_mx2d_add_rotation(mx, FLT2FIX(cx), FLT2FIX(cy), FLT2FIX(a) );
    3260             :         return JS_DupValue(c, obj);
    3261             : }
    3262         222 : static JSValue mx2d_scale(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3263             : {
    3264             :         Double sx, sy;
    3265         222 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3266         222 :         if (!mx || (argc<2)) return JS_EXCEPTION;
    3267         222 :         if (JS_ToFloat64(c, &sx, argv[0])) return JS_EXCEPTION;
    3268         222 :         if (JS_ToFloat64(c, &sy, argv[1])) return JS_EXCEPTION;
    3269         222 :         if (argc==2) {
    3270         222 :                 gf_mx2d_add_scale(mx, FLT2FIX(sx), FLT2FIX(sy));
    3271             :                 return JS_DupValue(c, obj);
    3272           0 :         } else if (argc==5) {
    3273             :                 Double cx, cy, a;
    3274           0 :                 if (JS_ToFloat64(c, &cx, argv[2])) return JS_EXCEPTION;
    3275           0 :                 if (JS_ToFloat64(c, &cy, argv[3])) return JS_EXCEPTION;
    3276           0 :                 if (JS_ToFloat64(c, &a, argv[4])) return JS_EXCEPTION;
    3277           0 :                 gf_mx2d_add_scale_at(mx, FLT2FIX(sx), FLT2FIX(sy), FLT2FIX(cx), FLT2FIX(cy), FLT2FIX(a));
    3278             :                 return JS_DupValue(c, obj);
    3279             :         }
    3280           0 :         return JS_EXCEPTION;
    3281             : }
    3282             : 
    3283           1 : static JSValue mx2d_skew(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3284             : {
    3285             :         Double sx, sy;
    3286           1 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3287           1 :         if (!mx || (argc<2)) return JS_EXCEPTION;
    3288           1 :         if (JS_ToFloat64(c, &sx, argv[0])) return JS_EXCEPTION;
    3289           1 :         if (JS_ToFloat64(c, &sy, argv[1])) return JS_EXCEPTION;
    3290           1 :         gf_mx2d_add_skew(mx, FLT2FIX(sx), FLT2FIX(sy));
    3291             :         return JS_DupValue(c, obj);
    3292             : }
    3293           1 : static JSValue mx2d_skew_x(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3294             : {
    3295             :         Double s;
    3296           1 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3297           1 :         if (!mx || !argc) return JS_EXCEPTION;
    3298           1 :         if (JS_ToFloat64(c, &s, argv[0])) return JS_EXCEPTION;
    3299           1 :         gf_mx2d_add_skew_x(mx, FLT2FIX(s));
    3300             :         return JS_DupValue(c, obj);
    3301             : }
    3302           1 : static JSValue mx2d_skew_y(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3303             : {
    3304             :         Double s;
    3305           1 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3306           1 :         if (!mx || !argc) return JS_EXCEPTION;
    3307           1 :         if (JS_ToFloat64(c, &s, argv[0])) return JS_EXCEPTION;
    3308           1 :         gf_mx2d_add_skew_y(mx, FLT2FIX(s));
    3309             :         return JS_DupValue(c, obj);
    3310             : }
    3311             : 
    3312           1 : static JSValue mx2d_apply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3313             : {
    3314             :         Fixed x, y, w=0, h=0;
    3315             :         Double d;
    3316             :         JSValue v;
    3317             :         int res;
    3318             :         u32 is_rect=0;
    3319           1 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3320           2 :         if (!mx || !argc || !JS_IsObject(argv[0])) return JS_EXCEPTION;
    3321           1 :         v = JS_GetPropertyStr(c, argv[0], "x");
    3322           1 :         res = JS_ToFloat64(c, &d, v);
    3323             :         JS_FreeValue(c, v);
    3324           1 :         if (res) return JS_EXCEPTION;
    3325           1 :         x = FLT2FIX(d);
    3326             : 
    3327           1 :         v = JS_GetPropertyStr(c, argv[0], "y");
    3328           1 :         res = JS_ToFloat64(c, &d, v);
    3329             :         JS_FreeValue(c, v);
    3330           1 :         if (res) return JS_EXCEPTION;
    3331           1 :         y = FLT2FIX(d);
    3332             : 
    3333           1 :         v = JS_GetPropertyStr(c, argv[0], "w");
    3334           1 :         if (!JS_IsUndefined(v) && JS_IsNumber(v)) {
    3335           0 :                 res = JS_ToFloat64(c, &d, v);
    3336             :                 JS_FreeValue(c, v);
    3337           0 :                 if (res) return JS_EXCEPTION;
    3338             :                 is_rect++;
    3339           0 :                 w = FLT2FIX(d);
    3340             :         }
    3341             :         JS_FreeValue(c, v);
    3342             : 
    3343           1 :         v = JS_GetPropertyStr(c, argv[0], "h");
    3344           1 :         if (!JS_IsUndefined(v) && JS_IsNumber(v)) {
    3345           0 :                 res = JS_ToFloat64(c, &d, v);
    3346             :                 JS_FreeValue(c, v);
    3347           0 :                 if (res) return JS_EXCEPTION;
    3348           0 :                 is_rect++;
    3349           0 :                 h = FLT2FIX(d);
    3350             :         }
    3351             :         JS_FreeValue(c, v);
    3352             : 
    3353           1 :         if (is_rect) {
    3354             :                 GF_Rect rc;
    3355           0 :                 rc.x = x;
    3356           0 :                 rc.y = y;
    3357           0 :                 rc.width = w;
    3358           0 :                 rc.height = h;
    3359           0 :                 gf_mx2d_apply_rect(mx, &rc);
    3360           0 :                 v = JS_NewObject(c);
    3361           0 :                 JS_SetPropertyStr(c, v, "x", JS_NewFloat64(c, FIX2FLT(rc.x)));
    3362           0 :                 JS_SetPropertyStr(c, v, "y", JS_NewFloat64(c, FIX2FLT(rc.y)));
    3363           0 :                 JS_SetPropertyStr(c, v, "w", JS_NewFloat64(c, FIX2FLT(rc.width)));
    3364           0 :                 JS_SetPropertyStr(c, v, "h", JS_NewFloat64(c, FIX2FLT(rc.height)));
    3365           0 :                 return v;
    3366             : 
    3367             :         } else {
    3368             :                 GF_Point2D pt;
    3369           1 :                 pt.x = x;
    3370           1 :                 pt.y = y;
    3371           1 :                 gf_mx2d_apply_point(mx, &pt);
    3372           1 :                 v = JS_NewObject(c);
    3373           2 :                 JS_SetPropertyStr(c, v, "x", JS_NewFloat64(c, FIX2FLT(pt.x)));
    3374           2 :                 JS_SetPropertyStr(c, v, "y", JS_NewFloat64(c, FIX2FLT(pt.y)));
    3375           1 :                 return v;
    3376             :         }
    3377             :         return JS_EXCEPTION;
    3378             : }
    3379             : 
    3380           1 : static JSValue mx2d_inverse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3381             : {
    3382           1 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3383           1 :         if (!mx) return JS_EXCEPTION;
    3384           1 :         gf_mx2d_inverse(mx);
    3385             :         return JS_DupValue(c, obj);
    3386             : }
    3387             : 
    3388           1 : static JSValue mx2d_copy(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3389             : {
    3390             :         GF_Matrix2D *nmx;
    3391             :         JSValue nobj;
    3392           1 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3393           1 :         if (!mx) return JS_EXCEPTION;
    3394           1 :         GF_SAFEALLOC(nmx, GF_Matrix2D);
    3395           1 :         if (!nmx)
    3396           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    3397           1 :         gf_mx2d_copy(*nmx, *mx);
    3398           1 :         nobj = JS_NewObjectClass(c, mx2d_class_id);
    3399           1 :         JS_SetOpaque(nobj, nmx);
    3400           1 :         return nobj;
    3401             : }
    3402             : 
    3403           3 : static JSValue mx2d_decompose_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 opt)
    3404             : {
    3405             :         GF_Point2D scale;
    3406             :         GF_Point2D translate;
    3407             :         Fixed rotate;
    3408           3 :         GF_Matrix2D *mx = JS_GetOpaque(obj, mx2d_class_id);
    3409           3 :         if (!mx) return JS_EXCEPTION;
    3410           3 :         if (!gf_mx2d_decompose(mx, &scale, &rotate, &translate))
    3411           3 :                 return JS_NULL;
    3412           0 :         if (opt==0) {
    3413           0 :                 JSValue nobj = JS_NewObject(c);
    3414           0 :                 JS_SetPropertyStr(c, nobj, "x", JS_NewFloat64(c, FIX2FLT(scale.x)) );
    3415           0 :                 JS_SetPropertyStr(c, nobj, "y", JS_NewFloat64(c, FIX2FLT(scale.y)) );
    3416           0 :                 return nobj;
    3417             :         }
    3418           0 :         if (opt==1) {
    3419           0 :                 JSValue nobj = JS_NewObject(c);
    3420           0 :                 JS_SetPropertyStr(c, nobj, "x", JS_NewFloat64(c, FIX2FLT(translate.x)) );
    3421           0 :                 JS_SetPropertyStr(c, nobj, "y", JS_NewFloat64(c, FIX2FLT(translate.y)) );
    3422           0 :                 return nobj;
    3423             :         }
    3424           0 :         if (opt==2) {
    3425           0 :                 return JS_NewFloat64(c, FIX2FLT(rotate) );
    3426             :         }
    3427           0 :         return JS_EXCEPTION;
    3428             : }
    3429             : 
    3430           1 : static JSValue mx2d_get_scale(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3431             : {
    3432           1 :         return mx2d_decompose_ex(c, obj, argc, argv, 0);
    3433             : }
    3434           1 : static JSValue mx2d_get_translate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3435             : {
    3436           1 :         return mx2d_decompose_ex(c, obj, argc, argv, 1);
    3437             : }
    3438           1 : static JSValue mx2d_get_rotate(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3439             : {
    3440           1 :         return mx2d_decompose_ex(c, obj, argc, argv, 2);
    3441             : }
    3442             : 
    3443             : static const JSCFunctionListEntry mx2d_funcs[] =
    3444             : {
    3445             :         JS_CGETSET_MAGIC_DEF("xx", mx2d_getProperty, mx2d_setProperty, MX2D_XX),
    3446             :         JS_CGETSET_MAGIC_DEF("xy", mx2d_getProperty, mx2d_setProperty, MX2D_XY),
    3447             :         JS_CGETSET_MAGIC_DEF("tx", mx2d_getProperty, mx2d_setProperty, MX2D_TX),
    3448             :         JS_CGETSET_MAGIC_DEF("yx", mx2d_getProperty, mx2d_setProperty, MX2D_YX),
    3449             :         JS_CGETSET_MAGIC_DEF("yy", mx2d_getProperty, mx2d_setProperty, MX2D_YY),
    3450             :         JS_CGETSET_MAGIC_DEF("ty", mx2d_getProperty, mx2d_setProperty, MX2D_TY),
    3451             :         JS_CGETSET_MAGIC_DEF("identity", mx2d_getProperty, mx2d_setProperty, MX2D_IDENTITY),
    3452             :         JS_CFUNC_DEF("get_scale", 0, mx2d_get_scale),
    3453             :         JS_CFUNC_DEF("get_translate", 0, mx2d_get_translate),
    3454             :         JS_CFUNC_DEF("get_rotate", 0, mx2d_get_rotate),
    3455             :         JS_CFUNC_DEF("inverse", 0, mx2d_inverse),
    3456             :         JS_CFUNC_DEF("copy", 0, mx2d_copy),
    3457             :         JS_CFUNC_DEF("add", 0, mx2d_multiply),
    3458             :         JS_CFUNC_DEF("translate", 0, mx2d_translate),
    3459             :         JS_CFUNC_DEF("rotate", 0, mx2d_rotate),
    3460             :         JS_CFUNC_DEF("scale", 0, mx2d_scale),
    3461             :         JS_CFUNC_DEF("skew", 0, mx2d_skew),
    3462             :         JS_CFUNC_DEF("skew_x", 0, mx2d_skew_x),
    3463             :         JS_CFUNC_DEF("skew_y", 0, mx2d_skew_y),
    3464             :         JS_CFUNC_DEF("apply", 0, mx2d_apply),
    3465             : };
    3466             : 
    3467         217 : static JSValue mx2d_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    3468             : {
    3469             :         JSValue obj;
    3470             :         GF_Matrix2D *mx;
    3471         217 :         GF_SAFEALLOC(mx, GF_Matrix2D);
    3472         217 :         if (!mx)
    3473           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    3474         217 :         mx->m[MX2D_XX] = mx->m[MX2D_YY] = FIX_ONE;
    3475         217 :         obj = JS_NewObjectClass(c, mx2d_class_id);
    3476         217 :         JS_SetOpaque(obj, mx);
    3477         218 :         if ((argc==1) && JS_IsObject(argv[0])) {
    3478           1 :                 GF_Matrix2D *amx = JS_GetOpaque(argv[0], mx2d_class_id);
    3479           1 :                 if (amx) gf_mx2d_copy(*mx, *amx);
    3480             :         }
    3481         216 :         else if (argc==6) {
    3482             :                 u32 i;
    3483             :                 Double d;
    3484           0 :                 for (i=0; i<6; i++) {
    3485           0 :                         if (JS_ToFloat64(c, &d, argv[i])) return JS_EXCEPTION;
    3486           0 :                         mx->m[i] = FLT2FIX(d);
    3487             :                 }
    3488             :         }
    3489         217 :         return obj;
    3490             : }
    3491             : 
    3492           3 : static void colmx_finalize(JSRuntime *rt, JSValue obj)
    3493             : {
    3494           3 :         GF_ColorMatrix *mx = JS_GetOpaque(obj, colmx_class_id);
    3495           3 :         if (!mx) return;
    3496           3 :         gf_free(mx);
    3497             : }
    3498             : JSClassDef colmx_class = {
    3499             :         "ColorMatrix",
    3500             :         .finalizer = colmx_finalize
    3501             : };
    3502             : 
    3503           3 : static JSValue colmx_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    3504             : {
    3505             :         JSValue obj;
    3506             :         GF_ColorMatrix *cmx;
    3507           3 :         GF_SAFEALLOC(cmx, GF_ColorMatrix);
    3508           3 :         if (!cmx)
    3509           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    3510           3 :         gf_cmx_init(cmx);
    3511           3 :         obj = JS_NewObjectClass(c, colmx_class_id);
    3512           3 :         JS_SetOpaque(obj, cmx);
    3513           3 :         if ((argc==1) && JS_IsObject(argv[0])) {
    3514           0 :                 GF_ColorMatrix *acmx = JS_GetOpaque(argv[0], colmx_class_id);
    3515           0 :                 if (acmx) gf_cmx_copy(cmx, acmx);
    3516             :         }
    3517           3 :         else if (argc==20) {
    3518             :                 u32 i;
    3519             :                 Double d;
    3520           0 :                 for (i=0; i<20; i++) {
    3521           0 :                         if (JS_ToFloat64(c, &d, argv[i])) return JS_EXCEPTION;
    3522           0 :                         cmx->m[i] = FLT2FIX(d);
    3523             :                 }
    3524           0 :                 cmx->identity = 0;
    3525             :         }
    3526           3 :         return obj;
    3527             : }
    3528             : enum
    3529             : {
    3530             :         //these 6 magics map to m[x] of the matrix, DO NOT MODIFY
    3531             :         CMX_MRR = 0,
    3532             :         CMX_MRG,
    3533             :         CMX_MRB,
    3534             :         CMX_MRA,
    3535             :         CMX_TR,
    3536             :         CMX_MGR,
    3537             :         CMX_MGG,
    3538             :         CMX_MGB,
    3539             :         CMX_MGA,
    3540             :         CMX_TG,
    3541             :         CMX_MBR,
    3542             :         CMX_MBG,
    3543             :         CMX_MBB,
    3544             :         CMX_MBA,
    3545             :         CMX_TB,
    3546             :         CMX_MAR,
    3547             :         CMX_MAG,
    3548             :         CMX_MAB,
    3549             :         CMX_MAA,
    3550             :         CMX_TA,
    3551             :         CMX_IDENTITY,
    3552             : };
    3553             : 
    3554           1 : static JSValue colmx_getProperty(JSContext *c, JSValueConst obj, int magic)
    3555             : {
    3556           1 :         GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
    3557           1 :         if (!cmx) return JS_EXCEPTION;
    3558           1 :         if ((magic>=CMX_MRR) && (magic<=CMX_TA)) {
    3559           1 :                 return JS_NewFloat64(c, FIX2FLT(cmx->m[magic]));
    3560             :         }
    3561           0 :         if (magic==CMX_IDENTITY)
    3562           0 :                 return JS_NewBool(c, cmx->identity);
    3563           0 :         return JS_UNDEFINED;
    3564             : }
    3565           4 : static JSValue colmx_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    3566             : {
    3567           4 :         GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
    3568           4 :         if (!cmx) return JS_EXCEPTION;
    3569           4 :         if ((magic>=CMX_MRR) && (magic<=CMX_TA)) {
    3570             :                 Double d;
    3571           4 :                 if (JS_ToFloat64(c, &d, value))
    3572           0 :                         return JS_EXCEPTION;
    3573           4 :                 cmx->m[magic] = FIX2FLT(d);
    3574           4 :                 cmx->identity = GF_FALSE;
    3575           4 :                 return JS_UNDEFINED;
    3576             :         }
    3577           0 :         return JS_UNDEFINED;
    3578             : }
    3579             : 
    3580           1 : static JSValue colmx_multiply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3581             : {
    3582           1 :         GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
    3583           1 :         if (!cmx || !argc) return JS_EXCEPTION;
    3584           1 :         GF_ColorMatrix *with = JS_GetOpaque(argv[0], colmx_class_id);
    3585           1 :         if (!cmx) return JS_EXCEPTION;
    3586           1 :         gf_cmx_multiply(cmx, with);
    3587             :         return JS_DupValue(c, obj);
    3588             : }
    3589             : 
    3590         103 : static Bool get_color(JSContext *c, JSValueConst obj, Double *a, Double *r, Double *g, Double *b)
    3591             : {
    3592             :         JSValue v;
    3593             :         int res;
    3594         103 :         if (JS_IsArray(c, obj)) {
    3595             :                 u32 i, len;
    3596           1 :                 v = JS_GetPropertyStr(c, obj, "length");
    3597           1 :                 res = JS_ToInt32(c, &len, v);
    3598             :                 JS_FreeValue(c, v);
    3599           1 :                 if (res) return GF_FALSE;
    3600           1 :                 if (len>4) len=4;
    3601           4 :                 for (i=0; i<len; i++) {
    3602             :                         Double d;
    3603           4 :                         v = JS_GetPropertyUint32(c, obj, i);
    3604           4 :                         res = JS_ToFloat64(c, &d, v);
    3605           4 :                         if (res) return GF_FALSE;
    3606             :                         JS_FreeValue(c, v);
    3607           4 :                         if (!i) *r = d;
    3608           3 :                         else if (i==1) *g = d;
    3609           2 :                         else if (i==2) *b = d;
    3610           1 :                         else *a = d;
    3611             :                 }
    3612             :                 return GF_TRUE;
    3613             :         }
    3614         102 :         v = JS_GetPropertyStr(c, obj, "r");
    3615         102 :         res = JS_ToFloat64(c, r, v);
    3616             :         JS_FreeValue(c, v);
    3617         102 :         if (res) return GF_FALSE;
    3618         102 :         v = JS_GetPropertyStr(c, obj, "g");
    3619         102 :         res = JS_ToFloat64(c, g, v);
    3620             :         JS_FreeValue(c, v);
    3621         102 :         if (res) return GF_FALSE;
    3622         102 :         v = JS_GetPropertyStr(c, obj, "b");
    3623         102 :         res = JS_ToFloat64(c, b, v);
    3624             :         JS_FreeValue(c, v);
    3625         102 :         if (res) return GF_FALSE;
    3626         102 :         v = JS_GetPropertyStr(c, obj, "a");
    3627         102 :         JS_ToFloat64(c, a, v);
    3628             :         JS_FreeValue(c, v);
    3629             : 
    3630         102 :         if (*r<0) *r=0;
    3631         102 :         if (*g<0) *g=0;
    3632         102 :         if (*b<0) *b=0;
    3633         102 :         if (*a<0) *a=0;
    3634             : 
    3635             :         return GF_TRUE;
    3636             : }
    3637         101 : static JSValue colmx_apply_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
    3638             : {
    3639             :         GF_Color col;
    3640             :         JSValue nobj;
    3641         101 :         Double r=0, g=0, b=0, a=1.0;
    3642         101 :         GF_ColorMatrix *cmx = JS_GetOpaque(obj, colmx_class_id);
    3643         101 :         if (!cmx || !argc) return JS_EXCEPTION;
    3644         202 :         if (JS_IsString(argv[0])) {
    3645             :                 const char *str = JS_ToCString(c, argv[0]);
    3646         100 :                 col = gf_color_parse(str);
    3647         100 :                 JS_FreeCString(c, str);
    3648             : 
    3649         100 :                 if (!use_int) {
    3650           0 :                         a = GF_COL_A(col);
    3651           0 :                         r = GF_COL_R(col);
    3652           0 :                         g = GF_COL_G(col);
    3653           0 :                         b = GF_COL_B(col);
    3654           0 :                         r/=255;
    3655           0 :                         g/=255;
    3656           0 :                         b/=255;
    3657           0 :                         a/=255;
    3658             :                 }
    3659             : 
    3660           1 :         } else if (JS_IsObject(argv[0])) {
    3661           1 :                 if (!get_color(c, argv[0], &a, &r, &g, &b))
    3662           0 :                         return JS_EXCEPTION;
    3663           1 :                 if (use_int) {
    3664           0 :                         r*=255;
    3665           0 :                         g*=255;
    3666           0 :                         b*=255;
    3667           0 :                         a*=255;
    3668           0 :                         if (a>255) a=255;
    3669           0 :                         if (r>255) r=255;
    3670           0 :                         if (g>255) g=255;
    3671           0 :                         if (b>255) b=255;
    3672           0 :                         col = GF_COL_ARGB(a, r, g, b);
    3673             :                 }
    3674             :         } else {
    3675           0 :                 return JS_EXCEPTION;
    3676             :         }
    3677         101 :         if (use_int) {
    3678         100 :                 GF_Color res = gf_cmx_apply(cmx, col);
    3679         100 :                 nobj = JS_NewObject(c);
    3680         200 :                 JS_SetPropertyStr(c, nobj, "r", JS_NewInt32(c, GF_COL_R(res)) );
    3681         200 :                 JS_SetPropertyStr(c, nobj, "g", JS_NewInt32(c, GF_COL_G(res)) );
    3682         200 :                 JS_SetPropertyStr(c, nobj, "b", JS_NewInt32(c, GF_COL_B(res)) );
    3683         200 :                 JS_SetPropertyStr(c, nobj, "a", JS_NewInt32(c, GF_COL_A(res)) );
    3684         100 :                 return nobj;
    3685             :         } else {
    3686           1 :                 Fixed fr=FLT2FIX(r), fg=FLT2FIX(g), fb=FLT2FIX(b), fa=FLT2FIX(a);
    3687             : 
    3688           1 :                 gf_cmx_apply_fixed(cmx, &fa, &fr, &fg, &fb);
    3689           1 :                 nobj = JS_NewObject(c);
    3690           2 :                 JS_SetPropertyStr(c, nobj, "r", JS_NewFloat64(c, FIX2FLT(fr)) );
    3691           2 :                 JS_SetPropertyStr(c, nobj, "g", JS_NewFloat64(c, FIX2FLT(fg)) );
    3692           2 :                 JS_SetPropertyStr(c, nobj, "b", JS_NewFloat64(c, FIX2FLT(fb)) );
    3693           2 :                 JS_SetPropertyStr(c, nobj, "a", JS_NewFloat64(c, FIX2FLT(fa)) );
    3694           1 :                 return nobj;
    3695             :         }
    3696             :         return JS_EXCEPTION;
    3697             : }
    3698         100 : static JSValue colmx_apply(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3699             : {
    3700         100 :         return colmx_apply_color(c, obj, argc, argv, GF_TRUE);
    3701             : }
    3702           1 : static JSValue colmx_applyf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3703             : {
    3704           1 :         return colmx_apply_color(c, obj, argc, argv, GF_FALSE);
    3705             : }
    3706             : 
    3707             : static const JSCFunctionListEntry colmx_funcs[] =
    3708             : {
    3709             :         JS_CGETSET_MAGIC_DEF("rr", colmx_getProperty, colmx_setProperty, CMX_MRR),
    3710             :         JS_CGETSET_MAGIC_DEF("rg", colmx_getProperty, colmx_setProperty, CMX_MRG),
    3711             :         JS_CGETSET_MAGIC_DEF("rb", colmx_getProperty, colmx_setProperty, CMX_MRB),
    3712             :         JS_CGETSET_MAGIC_DEF("ra", colmx_getProperty, colmx_setProperty, CMX_MRA),
    3713             :         JS_CGETSET_MAGIC_DEF("tr", colmx_getProperty, colmx_setProperty, CMX_TR),
    3714             :         JS_CGETSET_MAGIC_DEF("gr", colmx_getProperty, colmx_setProperty, CMX_MGR),
    3715             :         JS_CGETSET_MAGIC_DEF("gg", colmx_getProperty, colmx_setProperty, CMX_MGG),
    3716             :         JS_CGETSET_MAGIC_DEF("gb", colmx_getProperty, colmx_setProperty, CMX_MGB),
    3717             :         JS_CGETSET_MAGIC_DEF("ga", colmx_getProperty, colmx_setProperty, CMX_MGA),
    3718             :         JS_CGETSET_MAGIC_DEF("tg", colmx_getProperty, colmx_setProperty, CMX_TG),
    3719             :         JS_CGETSET_MAGIC_DEF("br", colmx_getProperty, colmx_setProperty, CMX_MBR),
    3720             :         JS_CGETSET_MAGIC_DEF("bg", colmx_getProperty, colmx_setProperty, CMX_MBG),
    3721             :         JS_CGETSET_MAGIC_DEF("bb", colmx_getProperty, colmx_setProperty, CMX_MBB),
    3722             :         JS_CGETSET_MAGIC_DEF("ba", colmx_getProperty, colmx_setProperty, CMX_MBA),
    3723             :         JS_CGETSET_MAGIC_DEF("tb", colmx_getProperty, colmx_setProperty, CMX_TB),
    3724             :         JS_CGETSET_MAGIC_DEF("ar", colmx_getProperty, colmx_setProperty, CMX_MAR),
    3725             :         JS_CGETSET_MAGIC_DEF("ag", colmx_getProperty, colmx_setProperty, CMX_MAG),
    3726             :         JS_CGETSET_MAGIC_DEF("ab", colmx_getProperty, colmx_setProperty, CMX_MAB),
    3727             :         JS_CGETSET_MAGIC_DEF("aa", colmx_getProperty, colmx_setProperty, CMX_MAA),
    3728             :         JS_CGETSET_MAGIC_DEF("ta", colmx_getProperty, colmx_setProperty, CMX_TA),
    3729             :         JS_CGETSET_MAGIC_DEF("identity", colmx_getProperty, NULL, CMX_IDENTITY),
    3730             : 
    3731             :         JS_CFUNC_DEF("multiply", 0, colmx_multiply),
    3732             :         JS_CFUNC_DEF("apply", 0, colmx_apply),
    3733             :         JS_CFUNC_DEF("applyf", 0, colmx_applyf),
    3734             : };
    3735             : 
    3736             : 
    3737             : 
    3738          19 : static void path_finalize(JSRuntime *rt, JSValue obj)
    3739             : {
    3740          19 :         GF_Path *path = JS_GetOpaque(obj, path_class_id);
    3741          19 :         if (!path) return;
    3742          19 :         gf_path_del(path);
    3743             : }
    3744             : JSClassDef path_class = {
    3745             :         "Path",
    3746             :         .finalizer = path_finalize
    3747             : };
    3748             : 
    3749           9 : static JSValue path_close_reset(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 opt)
    3750             : {
    3751           9 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3752           9 :         if (!gp) return JS_EXCEPTION;
    3753           9 :         if (opt==0) {
    3754           2 :                 gf_path_close(gp);
    3755             :                 return JS_DupValue(c, obj);
    3756             :         }
    3757           7 :         if (opt==1) {
    3758           6 :                 gf_path_reset(gp);
    3759             :                 return JS_DupValue(c, obj);
    3760             :         }
    3761           1 :         if (opt==2) {
    3762           1 :                 JSValue nobj = JS_NewObjectClass(c, path_class_id);
    3763           1 :                 if (JS_IsException(nobj)) return nobj;
    3764           1 :                 JS_SetOpaque(nobj, gf_path_clone(gp));
    3765           1 :                 gf_path_reset(gp);
    3766           1 :                 return nobj;
    3767             :         }
    3768           0 :         return JS_EXCEPTION;
    3769             : }
    3770           6 : static JSValue path_reset(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3771             : {
    3772           6 :         return path_close_reset(c, obj, argc, argv, 1);
    3773             : }
    3774           2 : static JSValue path_close(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3775             : {
    3776           2 :         return path_close_reset(c, obj, argc, argv, 0);
    3777             : }
    3778           1 : static JSValue path_clone(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3779             : {
    3780           1 :         return path_close_reset(c, obj, argc, argv, 2);
    3781             : }
    3782           5 : static JSValue path_line_move(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_line)
    3783             : {
    3784           5 :         Double x=0, y=0;
    3785             :         GF_Err e;
    3786           5 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3787           5 :         if (!gp || (argc<2)) return JS_EXCEPTION;
    3788           5 :         if (JS_ToFloat64(c, &x, argv[0])) return JS_EXCEPTION;
    3789           5 :         if (JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
    3790           5 :         if (is_line)
    3791           1 :                 e = gf_path_add_line_to(gp, FLT2FIX(x), FLT2FIX(y));
    3792             :         else
    3793           4 :                 e = gf_path_add_move_to(gp, FLT2FIX(x), FLT2FIX(y));
    3794           5 :         if (e) return JS_EXCEPTION;
    3795             :         return JS_DupValue(c, obj);
    3796             : }
    3797           1 : static JSValue path_line_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3798             : {
    3799           1 :         return path_line_move(c, obj, argc, argv, GF_TRUE);
    3800             : }
    3801           4 : static JSValue path_move_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3802             : {
    3803           4 :         return path_line_move(c, obj, argc, argv, GF_FALSE);
    3804             : }
    3805           2 : static JSValue path_cubic_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3806             : {
    3807           2 :         Double c1_x=0, c1_y=0, c2_x=0, c2_y=0, x=0, y=0;
    3808             :         GF_Err e;
    3809           2 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3810           2 :         if (!gp || (argc<6)) return JS_EXCEPTION;
    3811           2 :         if (JS_ToFloat64(c, &c1_x, argv[0])) return JS_EXCEPTION;
    3812           2 :         if (JS_ToFloat64(c, &c1_y, argv[1])) return JS_EXCEPTION;
    3813           2 :         if (JS_ToFloat64(c, &c2_x, argv[2])) return JS_EXCEPTION;
    3814           2 :         if (JS_ToFloat64(c, &c2_y, argv[3])) return JS_EXCEPTION;
    3815           2 :         if (JS_ToFloat64(c, &x, argv[4])) return JS_EXCEPTION;
    3816           2 :         if (JS_ToFloat64(c, &y, argv[5])) return JS_EXCEPTION;
    3817           2 :         e = gf_path_add_cubic_to(gp, FLT2FIX(c1_x), FLT2FIX(c1_y), FLT2FIX(c2_x), FLT2FIX(c2_y), FLT2FIX(x), FLT2FIX(y));
    3818           2 :         if (e) return JS_EXCEPTION;
    3819             :         return JS_DupValue(c, obj);
    3820             : }
    3821           1 : static JSValue path_quadratic_to(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3822             : {
    3823           1 :         Double c_x=0, c_y=0, x=0, y=0;
    3824             :         GF_Err e;
    3825           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3826           1 :         if (!gp || (argc<4)) return JS_EXCEPTION;
    3827           1 :         if (JS_ToFloat64(c, &c_x, argv[0])) return JS_EXCEPTION;
    3828           1 :         if (JS_ToFloat64(c, &c_y, argv[1])) return JS_EXCEPTION;
    3829           1 :         if (JS_ToFloat64(c, &x, argv[2])) return JS_EXCEPTION;
    3830           1 :         if (JS_ToFloat64(c, &y, argv[3])) return JS_EXCEPTION;
    3831           1 :         e = gf_path_add_quadratic_to(gp, FLT2FIX(c_x), FLT2FIX(c_y), FLT2FIX(x), FLT2FIX(y));
    3832           1 :         if (e) return JS_EXCEPTION;
    3833             :         return JS_DupValue(c, obj);
    3834             : }
    3835             : 
    3836          12 : static JSValue path_rect(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3837             : {
    3838          12 :         Double ox=0, oy=0, w=0, h=0;
    3839             :         s32 idx=0;
    3840             :         GF_Err e;
    3841             : 
    3842          12 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3843          12 :         if (!gp || (argc<3)) return JS_EXCEPTION;
    3844             : 
    3845          24 :         if (JS_IsObject(argv[0])) {
    3846             :                 JSValue v;
    3847             : #define GETD(_arg, _name, _res)\
    3848             :                 if (! JS_IsObject(_arg)) return JS_EXCEPTION;\
    3849             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    3850             :                 JS_ToFloat64(c, &_res, v);\
    3851             :                 JS_FreeValue(c, v);\
    3852             : 
    3853           0 :                 GETD(argv[0], "x", ox);
    3854           0 :                 GETD(argv[0], "y", oy);
    3855             : #undef GETD
    3856             : 
    3857             :                 idx=1;
    3858          12 :         } else if (argc>=4) {
    3859          12 :                 if (JS_ToFloat64(c, &ox, argv[0])) return JS_EXCEPTION;
    3860          12 :                 if (JS_ToFloat64(c, &oy, argv[1])) return JS_EXCEPTION;
    3861             :                 idx=2;
    3862             :         } else {
    3863           0 :                 return JS_EXCEPTION;
    3864             :         }
    3865          12 :         if (JS_ToFloat64(c, &w, argv[idx])) return JS_EXCEPTION;
    3866          12 :         if (JS_ToFloat64(c, &h, argv[idx+1])) return JS_EXCEPTION;
    3867          12 :         if ((argc>idx+2) && JS_ToBool(c, argv[idx+2])) {
    3868           0 :                 e = gf_path_add_rect_center(gp, FLT2FIX(ox), FLT2FIX(oy), FLT2FIX(w), FLT2FIX(h));
    3869             :         } else {
    3870          12 :                 e = gf_path_add_rect(gp, FLT2FIX(ox), FLT2FIX(oy), FLT2FIX(w), FLT2FIX(h));
    3871             :         }
    3872          12 :         if (e) return JS_EXCEPTION;
    3873             :         return JS_DupValue(c, obj);
    3874             : }
    3875             : 
    3876           3 : static JSValue path_ellipse(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3877             : {
    3878           3 :         Double cx=0, cy=0, a_axis=0, b_axis=0;
    3879             :         GF_Err e;
    3880             :         u32 idx=0;
    3881           3 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3882           3 :         if (!gp) return JS_EXCEPTION;
    3883           3 :         if (argc==3) {
    3884             :                 JSValue v;
    3885             : #define GETD(_arg, _name, _res)\
    3886             :                 if (! JS_IsObject(_arg)) return JS_EXCEPTION;\
    3887             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    3888             :                 JS_ToFloat64(c, &_res, v);\
    3889             :                 JS_FreeValue(c, v);\
    3890             : 
    3891           0 :                 GETD(argv[0], "x", cx);
    3892           0 :                 GETD(argv[0], "y", cy);
    3893             : #undef GETD
    3894             : 
    3895             :                 idx=1;
    3896           3 :         } else if (argc==4) {
    3897           3 :                 if (JS_ToFloat64(c, &cx, argv[0])) return JS_EXCEPTION;
    3898           3 :                 if (JS_ToFloat64(c, &cy, argv[1])) return JS_EXCEPTION;
    3899             :                 idx=2;
    3900             :         } else {
    3901           0 :                 return JS_EXCEPTION;
    3902             :         }
    3903           3 :         if (JS_ToFloat64(c, &a_axis, argv[idx])) return JS_EXCEPTION;
    3904           3 :         if (JS_ToFloat64(c, &b_axis, argv[idx+1])) return JS_EXCEPTION;
    3905           3 :         e = gf_path_add_ellipse(gp, FLT2FIX(cx), FLT2FIX(cy), FLT2FIX(a_axis), FLT2FIX(b_axis));
    3906           3 :         if (e) return JS_EXCEPTION;
    3907             :         return JS_DupValue(c, obj);
    3908             : }
    3909             : 
    3910           1 : static JSValue path_bezier(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3911             : {
    3912             :         GF_Err e;
    3913             :         s32 i;
    3914             :         GF_Point2D *pts;
    3915           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3916           1 :         if (!gp || (argc<3)) return JS_EXCEPTION;
    3917           1 :         pts = gf_malloc(sizeof(GF_Point2D)* argc);
    3918             :         memset(pts, 0, sizeof(GF_Point2D)* argc);
    3919           7 :         for (i=0; i<argc; i++) {
    3920             :                 JSValue v;
    3921             :                 Double d;
    3922          12 :                 if (! JS_IsObject(argv[i]))
    3923           0 :                         continue;
    3924           6 :                 v = JS_GetPropertyStr(c, argv[i], "x");
    3925           6 :                 JS_ToFloat64(c, &d, v);
    3926           6 :                 pts[i].x = FLT2FIX(d);
    3927             :                 JS_FreeValue(c, v);
    3928           6 :                 v = JS_GetPropertyStr(c, argv[i], "y");
    3929           6 :                 JS_ToFloat64(c, &d, v);
    3930           6 :                 pts[i].x = FLT2FIX(d);
    3931             :                 JS_FreeValue(c, v);
    3932             :         }
    3933           1 :         e = gf_path_add_bezier(gp, pts, argc);
    3934           1 :         gf_free(pts);
    3935           1 :         if (e) return JS_EXCEPTION;
    3936             :         return JS_DupValue(c, obj);
    3937             : }
    3938             : 
    3939           1 : static JSValue path_arc_bifs(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3940             : {
    3941           1 :         Double end_x=0, end_y=0, fa_x=0, fa_y=0, fb_x=0, fb_y=0;
    3942             :         Bool cw_flag = GF_FALSE;
    3943             :         GF_Err e;
    3944           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3945           1 :         if (!gp || (argc<6)) return JS_EXCEPTION;
    3946           1 :         if (JS_ToFloat64(c, &end_x, argv[0])) return JS_EXCEPTION;
    3947           1 :         if (JS_ToFloat64(c, &end_y, argv[1])) return JS_EXCEPTION;
    3948           1 :         if (JS_ToFloat64(c, &fa_x, argv[2])) return JS_EXCEPTION;
    3949           1 :         if (JS_ToFloat64(c, &fa_y, argv[3])) return JS_EXCEPTION;
    3950           1 :         if (JS_ToFloat64(c, &fb_x, argv[4])) return JS_EXCEPTION;
    3951           1 :         if (JS_ToFloat64(c, &fb_y, argv[5])) return JS_EXCEPTION;
    3952           1 :         if (argc>6) cw_flag = JS_ToBool(c, argv[6]);
    3953             : 
    3954           1 :         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);
    3955           1 :         if (e) return JS_EXCEPTION;
    3956             :         return JS_DupValue(c, obj);
    3957             : }
    3958             : 
    3959             : 
    3960           1 : static JSValue path_arc_svg(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3961             : {
    3962           1 :         Double end_x=0, end_y=0, r_x=0, r_y=0, x_axis_rotation=0;
    3963             :         Bool large_arc_flag=GF_FALSE, sweep_flag=GF_FALSE;
    3964             :         GF_Err e;
    3965           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3966           1 :         if (!gp || (argc<4)) return JS_EXCEPTION;
    3967           1 :         if (JS_ToFloat64(c, &end_x, argv[0])) return JS_EXCEPTION;
    3968           1 :         if (JS_ToFloat64(c, &end_y, argv[1])) return JS_EXCEPTION;
    3969           1 :         if (JS_ToFloat64(c, &r_x, argv[2])) return JS_EXCEPTION;
    3970           1 :         if (JS_ToFloat64(c, &r_y, argv[3])) return JS_EXCEPTION;
    3971           1 :         if (argc>4) {
    3972           0 :                 if (JS_ToFloat64(c, &x_axis_rotation, argv[4])) return JS_EXCEPTION;
    3973           0 :                 if (argc>5) large_arc_flag = JS_ToBool(c, argv[5]);
    3974           0 :                 if (argc>6) sweep_flag = JS_ToBool(c, argv[6]);
    3975             :         }
    3976           1 :         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);
    3977             : 
    3978           1 :         if (e) return JS_EXCEPTION;
    3979             :         return JS_DupValue(c, obj);
    3980             : }
    3981             : 
    3982           1 : static JSValue path_arc(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    3983             : {
    3984           1 :         Double radius=0, start=0, end=0;
    3985           1 :         u32 close=0;
    3986             :         GF_Err e;
    3987           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    3988           1 :         if (!gp || (argc<3)) return JS_EXCEPTION;
    3989           1 :         if (JS_ToFloat64(c, &radius, argv[0])) return JS_EXCEPTION;
    3990           1 :         if (JS_ToFloat64(c, &start, argv[1])) return JS_EXCEPTION;
    3991           1 :         if (JS_ToFloat64(c, &end, argv[2])) return JS_EXCEPTION;
    3992           1 :         if (argc>3)
    3993           0 :                 if (JS_ToInt32(c, &close, argv[3])) return JS_EXCEPTION;
    3994           1 :         e = gf_path_add_arc(gp, FLT2FIX(radius), FLT2FIX(start), FLT2FIX(end), close);
    3995           1 :         if (e) return JS_EXCEPTION;
    3996             :         return JS_DupValue(c, obj);
    3997             : }
    3998             : 
    3999           1 : static JSValue path_add_path(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4000             : {
    4001             :         GF_Err e;
    4002             :         GF_Matrix2D *mx=NULL;
    4003           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    4004           1 :         if (!gp || !argc) return JS_EXCEPTION;
    4005           1 :         GF_Path *subgp = JS_GetOpaque(argv[0], path_class_id);
    4006           1 :         if (!gp) return JS_EXCEPTION;
    4007           1 :         if (argc>1)
    4008           0 :                 mx = JS_GetOpaque(argv[1], mx2d_class_id);
    4009           1 :         e = gf_path_add_subpath(gp, subgp, mx);
    4010           1 :         if (e) return JS_EXCEPTION;
    4011             :         return JS_DupValue(c, obj);
    4012             : }
    4013             : 
    4014           1 : static JSValue path_flatten(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4015             : {
    4016           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    4017           1 :         if (!gp) return JS_EXCEPTION;
    4018           1 :         gf_path_flatten(gp);
    4019             :         return JS_DupValue(c, obj);
    4020             : }
    4021           1 : static JSValue path_get_flat(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4022             : {
    4023             :         JSValue nobj;
    4024             :         GF_Path *gp_flat;
    4025           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    4026           1 :         if (!gp) return JS_EXCEPTION;
    4027           1 :         gp_flat = gf_path_get_flatten(gp);
    4028             :         if (!gp) return JS_NULL;
    4029           1 :         nobj = JS_NewObjectClass(c, path_class_id);
    4030           1 :         if (JS_IsException(nobj)) return nobj;
    4031           1 :         JS_SetOpaque(nobj, gp_flat);
    4032           1 :         return nobj;
    4033             : }
    4034             : 
    4035           1 : static JSValue path_point_over(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4036             : {
    4037             :         Double x, y;
    4038           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    4039           1 :         if (!gp || !argc) return JS_EXCEPTION;
    4040           1 :         if (argc==1) {
    4041             :                 Double d;
    4042             :                 int res;
    4043             :                 JSValue v;
    4044           0 :                 if (!JS_IsObject(argv[0])) return JS_EXCEPTION;
    4045             : #define GETIT(_arg, _name, _var) \
    4046             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    4047             :                 res = JS_ToFloat64(c, &d, v);\
    4048             :                 JS_FreeValue(c, v);\
    4049             :                 if (res) return JS_EXCEPTION;\
    4050             :                 _var = FLT2FIX(d);\
    4051             : 
    4052           0 :                 GETIT(argv[0], "x", x)
    4053           0 :                 GETIT(argv[0], "y", y)
    4054             : #undef GETIT
    4055           1 :         } else if (argc==2) {
    4056           1 :                 if (JS_ToFloat64(c, &x, argv[0])) return JS_EXCEPTION;
    4057           1 :                 if (JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
    4058             :         } else {
    4059           0 :                 return JS_EXCEPTION;
    4060             :         }
    4061           1 :         return JS_NewBool(c, gf_path_point_over(gp, FLT2FIX(x), FLT2FIX(y)));
    4062             : }
    4063             : 
    4064             : 
    4065           3 : static JSValue path_outline(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4066             : {
    4067             :         GF_Path *outline;
    4068             :         GF_PenSettings pen;
    4069             :         GF_DashSettings dash;
    4070             :         JSValue v, dashes;
    4071             :         Double d;
    4072             :         int i, res;
    4073           3 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    4074           6 :         if (!gp || !argc || !JS_IsObject(argv[0]) ) return JS_EXCEPTION;
    4075             :         memset(&pen, 0, sizeof(GF_PenSettings));
    4076             : 
    4077             : #define GETIT_F(_arg, _name, _var) \
    4078             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    4079             :                 if (!JS_IsUndefined(v)) {\
    4080             :                         res = JS_ToFloat64(c, &d, v);\
    4081             :                         JS_FreeValue(c, v);\
    4082             :                         if (res) return JS_EXCEPTION;\
    4083             :                         _var = FLT2FIX(d);\
    4084             :                 }\
    4085             : 
    4086             : #define GETIT_I(_arg, _name, _var) \
    4087             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    4088             :                 if (!JS_IsUndefined(v)) {\
    4089             :                         res = JS_ToInt32(c, &i, v);\
    4090             :                         JS_FreeValue(c, v);\
    4091             :                         if (res) return JS_EXCEPTION;\
    4092             :                         _var = i;\
    4093             :                 }\
    4094             : 
    4095           9 :         GETIT_F(argv[0], "width", pen.width);
    4096           6 :         GETIT_F(argv[0], "miter", pen.miterLimit);
    4097           6 :         GETIT_F(argv[0], "offset", pen.dash_offset);
    4098           6 :         GETIT_F(argv[0], "length", pen.path_length);
    4099           6 :         GETIT_I(argv[0], "cap", pen.cap);
    4100           8 :         GETIT_I(argv[0], "join", pen.join);
    4101           8 :         GETIT_I(argv[0], "align", pen.align);
    4102           8 :         GETIT_I(argv[0], "dash", pen.dash);
    4103             : 
    4104             : 
    4105             : #undef GETTIT_F
    4106             : #undef GETTIT_I
    4107             : 
    4108           3 :         dash.num_dash = 0;
    4109           3 :         dash.dashes = NULL;
    4110           3 :         dashes = JS_GetPropertyStr(c, argv[0], "dashes");
    4111           3 :         if (JS_IsArray(c, dashes) && !JS_IsNull(v)) {
    4112           2 :                 v = JS_GetPropertyStr(c, dashes, "length");
    4113           2 :                 JS_ToInt32(c, &dash.num_dash, v);
    4114             :                 JS_FreeValue(c, v);
    4115           2 :                 pen.dash_set = &dash;
    4116           2 :                 dash.dashes = gf_malloc(sizeof(Fixed)*dash.num_dash);
    4117          10 :                 for (i=0; i<(int) dash.num_dash; i++) {
    4118           8 :                         v = JS_GetPropertyUint32(c, dashes, i);
    4119           8 :                         JS_ToFloat64(c, &d, v);
    4120           8 :                         dash.dashes[i] = FLT2FIX(d);
    4121             :                         JS_FreeValue(c, v);
    4122             :                 }
    4123             :         }
    4124             :         JS_FreeValue(c, dashes);
    4125             : 
    4126           3 :         outline = gf_path_get_outline(gp, pen);
    4127           3 :         if (dash.dashes) gf_free(dash.dashes);
    4128             : 
    4129           3 :         if (!outline) return JS_EXCEPTION;
    4130           3 :         v = JS_NewObjectClass(c, path_class_id);
    4131           3 :         JS_SetOpaque(v, outline);
    4132           3 :         return v;
    4133             : }
    4134             : 
    4135             : enum
    4136             : {
    4137             :         PATH_EMPTY=0,
    4138             :         PATH_ZERO_NONZERO,
    4139             :         PATH_BOUNDS,
    4140             :         PATH_CONTROL_BOUNDS,
    4141             : };
    4142             : 
    4143             : 
    4144           1 : static JSValue path_bounds_ex(JSContext *c, GF_Path *gp, Bool is_ctrl)
    4145             : {
    4146             :         JSValue nobj;
    4147             :         GF_Err e;
    4148             :         GF_Rect rc;
    4149           1 :         if (is_ctrl)
    4150           0 :                 e = gf_path_get_control_bounds(gp, &rc);
    4151             :         else
    4152           1 :                 e = gf_path_get_bounds(gp, &rc);
    4153           1 :         if (e) return JS_EXCEPTION;
    4154           1 :         nobj = JS_NewObject(c);
    4155           2 :         JS_SetPropertyStr(c, nobj, "x", JS_NewFloat64(c, rc.x));
    4156           2 :         JS_SetPropertyStr(c, nobj, "y", JS_NewFloat64(c, rc.y));
    4157           2 :         JS_SetPropertyStr(c, nobj, "w", JS_NewFloat64(c, rc.width));
    4158           2 :         JS_SetPropertyStr(c, nobj, "h", JS_NewFloat64(c, rc.height));
    4159           1 :         return nobj;
    4160             : }
    4161           1 : static JSValue path_getProperty(JSContext *c, JSValueConst obj, int magic)
    4162             : {
    4163           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    4164           1 :         if (!gp) return JS_EXCEPTION;
    4165           1 :         switch (magic) {
    4166           0 :         case PATH_EMPTY: return JS_NewBool(c, gf_path_is_empty(gp));
    4167           0 :         case PATH_ZERO_NONZERO: return JS_NewBool(c, gp->flags & GF_PATH_FILL_ZERO_NONZERO);
    4168           1 :         case PATH_BOUNDS: return path_bounds_ex(c, gp, GF_FALSE);
    4169           0 :         case PATH_CONTROL_BOUNDS: return path_bounds_ex(c, gp, GF_TRUE);
    4170             :         }
    4171           0 :         return JS_UNDEFINED;
    4172             : }
    4173           1 : static JSValue path_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    4174             : {
    4175           1 :         GF_Path *gp = JS_GetOpaque(obj, path_class_id);
    4176           1 :         if (!gp) return JS_EXCEPTION;
    4177           1 :         switch (magic) {
    4178           1 :         case PATH_ZERO_NONZERO:
    4179           1 :                 if (JS_ToBool(c, value))
    4180           0 :                         gp->flags |= GF_PATH_FILL_ZERO_NONZERO;
    4181             :                 else
    4182           1 :                         gp->flags &= ~GF_PATH_FILL_ZERO_NONZERO;
    4183             :         }
    4184           1 :         return JS_UNDEFINED;
    4185             : }
    4186          13 : static JSValue path_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    4187             : {
    4188             :         JSValue obj;
    4189          13 :         GF_Path *path = gf_path_new();
    4190          13 :         if (!path) return JS_EXCEPTION;
    4191          13 :         obj = JS_NewObjectClass(c, path_class_id);
    4192          13 :         if (JS_IsException(obj)) return obj;
    4193          13 :         JS_SetOpaque(obj, path);
    4194          13 :         return obj;
    4195             : }
    4196             : static const JSCFunctionListEntry path_funcs[] =
    4197             : {
    4198             :         JS_CGETSET_MAGIC_DEF("empty", path_getProperty, NULL, PATH_EMPTY),
    4199             :         JS_CGETSET_MAGIC_DEF("zero_fill", path_getProperty, path_setProperty, PATH_ZERO_NONZERO),
    4200             :         JS_CGETSET_MAGIC_DEF("bounds", path_getProperty, NULL, PATH_BOUNDS),
    4201             :         JS_CGETSET_MAGIC_DEF("ctrl_bounds", path_getProperty, NULL, PATH_CONTROL_BOUNDS),
    4202             : 
    4203             :         JS_CFUNC_DEF("point_over", 0, path_point_over),
    4204             :         JS_CFUNC_DEF("get_flatten", 0, path_get_flat),
    4205             :         JS_CFUNC_DEF("flatten", 0, path_flatten),
    4206             :         JS_CFUNC_DEF("add_path", 0, path_add_path),
    4207             :         JS_CFUNC_DEF("arc", 0, path_arc),
    4208             :         JS_CFUNC_DEF("arc_svg", 0, path_arc_svg),
    4209             :         JS_CFUNC_DEF("arc_bifs", 0, path_arc_bifs),
    4210             :         JS_CFUNC_DEF("bezier", 0, path_bezier),
    4211             :         JS_CFUNC_DEF("ellipse", 0, path_ellipse),
    4212             :         JS_CFUNC_DEF("rectangle", 0, path_rect),
    4213             :         JS_CFUNC_DEF("quadratic_to", 0, path_quadratic_to),
    4214             :         JS_CFUNC_DEF("cubic_to", 0, path_cubic_to),
    4215             :         JS_CFUNC_DEF("line_to", 0, path_line_to),
    4216             :         JS_CFUNC_DEF("move_to", 0, path_move_to),
    4217             :         JS_CFUNC_DEF("clone", 0, path_clone),
    4218             :         JS_CFUNC_DEF("reset", 0, path_reset),
    4219             :         JS_CFUNC_DEF("close", 0, path_close),
    4220             :         JS_CFUNC_DEF("outline", 0, path_outline),
    4221             : 
    4222             : };
    4223             : 
    4224             : 
    4225          44 : static void stencil_finalize(JSRuntime *rt, JSValue obj)
    4226             : {
    4227          44 :         GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
    4228          44 :         if (!stencil) return;
    4229          44 :         gf_evg_stencil_delete(stencil);
    4230             : }
    4231             : JSClassDef stencil_class = {
    4232             :         "Stencil",
    4233             :         .finalizer = stencil_finalize
    4234             : };
    4235             : 
    4236             : enum
    4237             : {
    4238             :         STENCIL_CMX,
    4239             :         STENCIL_MAT,
    4240             :         STENCIL_GRADMOD,
    4241             : };
    4242             : 
    4243           1 : static JSValue stencil_set_linear(JSContext *c, GF_EVGStencil *stencil, int argc, JSValueConst *argv)
    4244             : {
    4245             :         int res;
    4246             :         JSValue v;
    4247             :         Double d;
    4248             :         Fixed start_x=0, start_y=0, end_x=0, end_y=0;
    4249             :         s32 idx=0;
    4250           1 :         if (argc<2) return JS_EXCEPTION;
    4251             : 
    4252             : #define GETIT(_arg, _name, _var) \
    4253             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    4254             :                 res = JS_ToFloat64(c, &d, v);\
    4255             :                 JS_FreeValue(c, v);\
    4256             :                 if (res) return JS_EXCEPTION;\
    4257             :                 _var = FLT2FIX(d);\
    4258             : 
    4259           2 :         if (JS_IsObject(argv[0])) {
    4260           0 :                 GETIT(argv[0], "x", start_x)
    4261           0 :                 GETIT(argv[0], "y", start_y)
    4262             :                 idx=1;
    4263             :         } else {
    4264           1 :                 if (JS_ToFloat64(c, &d, argv[0])) return JS_EXCEPTION;
    4265           1 :                 start_x = FLT2FIX(d);
    4266           1 :                 if (JS_ToFloat64(c, &d, argv[1])) return JS_EXCEPTION;
    4267           1 :                 start_y = FLT2FIX(d);
    4268             :                 idx=2;
    4269             :         }
    4270           1 :         if (argc<=idx) {
    4271             :                 end_x = start_x;
    4272             :                 end_y = start_y;
    4273             :                 start_x = 0;
    4274             :                 start_y = 0;
    4275           2 :         } else if (JS_IsObject(argv[idx])) {
    4276           0 :                 GETIT(argv[idx], "x", end_x)
    4277           0 :                 GETIT(argv[idx], "y", end_y)
    4278           1 :         } else if (argc>idx+1) {
    4279           1 :                 if (JS_ToFloat64(c, &d, argv[idx])) return JS_EXCEPTION;
    4280           1 :                 end_x = FLT2FIX(d);
    4281           1 :                 if (JS_ToFloat64(c, &d, argv[idx+1])) return JS_EXCEPTION;
    4282           1 :                 end_y = FLT2FIX(d);
    4283             :         }
    4284             : #undef GETIT
    4285           1 :         gf_evg_stencil_set_linear_gradient(stencil, start_x, start_y, end_x, end_y);
    4286           1 :         return JS_UNDEFINED;
    4287             : }
    4288             : 
    4289           6 : static JSValue stencil_set_radial(JSContext *c, GF_EVGStencil *stencil, int argc, JSValueConst *argv)
    4290             : {
    4291             :         int res;
    4292             :         JSValue v;
    4293             :         Double d;
    4294             :         Fixed cx=0, cy=0, fx=0, fy=0, rx=0, ry=0;
    4295             :         s32 idx=0;
    4296           6 :         if (argc<3) return JS_EXCEPTION;
    4297             : 
    4298             : #define GETIT(_arg, _name, _var) \
    4299             :                 v = JS_GetPropertyStr(c, _arg, _name);\
    4300             :                 res = JS_ToFloat64(c, &d, v);\
    4301             :                 JS_FreeValue(c, v);\
    4302             :                 if (res) return JS_EXCEPTION;\
    4303             :                 _var = FLT2FIX(d);\
    4304             : 
    4305          12 :         if (JS_IsObject(argv[0])) {
    4306           0 :                 GETIT(argv[0], "x", cx)
    4307           0 :                 GETIT(argv[0], "y", cy)
    4308             :                 idx+=1;
    4309             :         } else {
    4310           6 :                 if (JS_ToFloat64(c, &d, argv[0])) return JS_EXCEPTION;
    4311           6 :                 cx = FLT2FIX(d);
    4312           6 :                 if (JS_ToFloat64(c, &d, argv[1])) return JS_EXCEPTION;
    4313           6 :                 cy = FLT2FIX(d);
    4314             :                 idx+=2;
    4315             :         }
    4316             : 
    4317          12 :         if (JS_IsObject(argv[idx])) {
    4318           0 :                 GETIT(argv[idx], "x", fx)
    4319           0 :                 GETIT(argv[idx], "y", fy)
    4320           0 :                 idx+=1;
    4321           6 :         } else if (argc>idx+1) {
    4322           6 :                 if (JS_ToFloat64(c, &d, argv[idx])) return JS_EXCEPTION;
    4323           6 :                 fx = FLT2FIX(d);
    4324           6 :                 if (JS_ToFloat64(c, &d, argv[idx+1])) return JS_EXCEPTION;
    4325           6 :                 fy = FLT2FIX(d);
    4326           6 :                 idx+=2;
    4327             :         }
    4328             : 
    4329          12 :         if (JS_IsObject(argv[idx])) {
    4330           0 :                 GETIT(argv[idx], "x", rx)
    4331           0 :                 GETIT(argv[idx], "y", ry)
    4332           6 :         } else if (argc>idx+1) {
    4333           6 :                 if (JS_ToFloat64(c, &d, argv[idx])) return JS_EXCEPTION;
    4334           6 :                 rx = FLT2FIX(d);
    4335           6 :                 if (JS_ToFloat64(c, &d, argv[idx+1])) return JS_EXCEPTION;
    4336           6 :                 ry = FLT2FIX(d);
    4337             :         }
    4338             : #undef GETIT
    4339           6 :         gf_evg_stencil_set_radial_gradient(stencil, cx, cy, fx, fy, rx, ry);
    4340           6 :         return JS_UNDEFINED;
    4341             : }
    4342             : 
    4343             : 
    4344           7 : static JSValue stencil_set_points(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4345             : {
    4346             :         GF_StencilType type;
    4347           7 :         GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
    4348           7 :         if (!stencil) return JS_EXCEPTION;
    4349             : 
    4350           7 :         type = gf_evg_stencil_type(stencil);
    4351           7 :         if (type==GF_STENCIL_LINEAR_GRADIENT)
    4352           1 :                 return stencil_set_linear(c, stencil, argc, argv);
    4353           6 :         if (type==GF_STENCIL_RADIAL_GRADIENT)
    4354           6 :                 return stencil_set_radial(c, stencil, argc, argv);
    4355           0 :         return JS_EXCEPTION;
    4356             : }
    4357             : 
    4358         132 : Bool get_color_from_args(JSContext *c, int argc, JSValueConst *argv, u32 idx, Double *a, Double *r, Double *g, Double *b)
    4359             : {
    4360         132 :         if (argc<(s32) idx) return GF_FALSE;
    4361         264 :         if (JS_IsString(argv[idx])) {
    4362             :                 GF_Color col;
    4363             :                 const char *str = JS_ToCString(c, argv[idx]);
    4364           0 :                 col = gf_color_parse(str);
    4365           0 :                 JS_FreeCString(c, str);
    4366           0 :                 *a = ((Double)GF_COL_A(col)) / 255;
    4367           0 :                 *r = ((Double)GF_COL_R(col)) / 255;
    4368           0 :                 *g = ((Double)GF_COL_G(col)) / 255;
    4369           0 :                 *b = ((Double)GF_COL_B(col)) / 255;
    4370         132 :         } else if (JS_IsObject(argv[idx])) {
    4371         102 :                 if (!get_color(c, argv[idx], a, r, g, b)) {
    4372             :                         return GF_FALSE;
    4373             :                 }
    4374          30 :         } else if (argc>(s32) idx) {
    4375          30 :                 if (JS_ToFloat64(c, r, argv[idx]))
    4376             :                         return GF_FALSE;
    4377          30 :                 if (argc>(s32)idx+1) {
    4378          30 :                         if (JS_ToFloat64(c, g, argv[idx+1]))
    4379             :                                 return GF_FALSE;
    4380          30 :                         if (argc>(s32) idx+2) {
    4381          30 :                                 if (JS_ToFloat64(c, b, argv[idx+2]))
    4382             :                                         return GF_FALSE;
    4383          30 :                                 if (argc>(s32)idx+3) {
    4384          28 :                                         if (JS_ToFloat64(c, a, argv[idx+3]))
    4385             :                                                 return GF_FALSE;
    4386             :                                 }
    4387             :                         }
    4388             :                 }
    4389             :         }
    4390             :         return GF_TRUE;
    4391             : }
    4392             : 
    4393          27 : static JSValue stencil_set_stop_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
    4394             : {
    4395          27 :         Double pos=0;
    4396             :         GF_StencilType type;
    4397             :         GF_Color col;
    4398          27 :         GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
    4399          27 :         if (!stencil) return JS_EXCEPTION;
    4400          27 :         type = gf_evg_stencil_type(stencil);
    4401          27 :         if ((type!=GF_STENCIL_LINEAR_GRADIENT) && (type!=GF_STENCIL_RADIAL_GRADIENT)) return JS_EXCEPTION;
    4402          27 :         if (!argc) {
    4403           0 :                 gf_evg_stencil_set_gradient_interpolation(stencil, NULL, NULL, 0);
    4404           0 :                 return JS_UNDEFINED;
    4405             :         }
    4406          27 :         if (JS_ToFloat64(c, &pos, argv[0])) return JS_EXCEPTION;
    4407             : 
    4408          54 :         if (JS_IsString(argv[1])) {
    4409             :                 const char *str = JS_ToCString(c, argv[1]);
    4410           1 :                 col = gf_color_parse(str);
    4411           1 :                 JS_FreeCString(c, str);
    4412             :         } else {
    4413          26 :                 Double r=0, g=0, b=0, a=1.0;
    4414          26 :                 if (!get_color_from_args(c, argc, argv, 1, &a, &r, &g, &b)) {
    4415           0 :                         return JS_EXCEPTION;
    4416             :                 }
    4417          26 :                 if (!use_int) {
    4418          26 :                         a *= 255;
    4419          26 :                         r *= 255;
    4420          26 :                         g *= 255;
    4421          26 :                         b *= 255;
    4422             :                 }
    4423          26 :                 col = GF_COL_ARGB(a, r, g, b);
    4424             :         }
    4425          27 :         gf_evg_stencil_push_gradient_interpolation(stencil, FLT2FIX(pos), col);
    4426          27 :         return JS_UNDEFINED;
    4427             : }
    4428           1 : static JSValue stencil_set_stop(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4429             : {
    4430           1 :         return stencil_set_stop_ex(c, obj, argc, argv, GF_TRUE);
    4431             : }
    4432          26 : static JSValue stencil_set_stopf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4433             : {
    4434          26 :         return stencil_set_stop_ex(c, obj, argc, argv, GF_FALSE);
    4435             : }
    4436             : 
    4437         538 : static JSValue stencil_set_color_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
    4438             : {
    4439         538 :         Double r=0, g=0, b=0, a=1.0;
    4440             :         GF_StencilType type;
    4441         538 :         GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
    4442         538 :         if (!stencil) return JS_EXCEPTION;
    4443         538 :         type = gf_evg_stencil_type(stencil);
    4444         538 :         if (type!=GF_STENCIL_SOLID) return JS_EXCEPTION;
    4445             : 
    4446        1076 :         if (JS_IsString(argv[0])) {
    4447             :                 GF_Color col;
    4448             :                 const char *str = JS_ToCString(c, argv[0]);
    4449         436 :                 col = gf_color_parse(str);
    4450         436 :                 JS_FreeCString(c, str);
    4451         436 :                 gf_evg_stencil_set_brush_color(stencil, col);
    4452         436 :                 return JS_UNDEFINED;
    4453         102 :         } else if (!get_color_from_args(c, argc, argv, 0, &a, &r, &g, &b)) {
    4454           0 :                 return JS_EXCEPTION;
    4455             :         }
    4456         102 :         if (!use_int) {
    4457           2 :                 r*=255;
    4458           2 :                 g*=255;
    4459           2 :                 b*=255;
    4460           2 :                 a*=255;
    4461             :         }
    4462         102 :         gf_evg_stencil_set_brush_color(stencil, GF_COL_ARGB(a, r, g, b));
    4463         102 :         return JS_UNDEFINED;
    4464             : }
    4465             : 
    4466         536 : static JSValue stencil_set_color(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4467             : {
    4468         536 :         return stencil_set_color_ex(c, obj, argc, argv, GF_TRUE);
    4469             : }
    4470           2 : static JSValue stencil_set_colorf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4471             : {
    4472           2 :         return stencil_set_color_ex(c, obj, argc, argv, GF_FALSE);
    4473             : }
    4474             : 
    4475         101 : static JSValue stencil_set_alpha_ex(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool use_int)
    4476             : {
    4477         101 :         Double a=1.0;
    4478         101 :         GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
    4479         101 :         if (!stencil) {
    4480         100 :                 GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4481         100 :                 if (!tx || !tx->stencil)
    4482           0 :                         return JS_EXCEPTION;
    4483             :                 stencil = tx->stencil;
    4484             :         }
    4485             : 
    4486         101 :         if (argc) {
    4487         101 :                 JS_ToFloat64(c, &a, argv[0]);
    4488             :         }
    4489         101 :         if (a<0) a=0;
    4490         101 :         if (!use_int) {
    4491         100 :                 a*=255;
    4492             :         }
    4493         101 :         if (a>255) a = 255;
    4494         101 :         gf_evg_stencil_set_alpha(stencil, (u8) a);
    4495         101 :         return JS_UNDEFINED;
    4496             : }
    4497             : 
    4498           1 : static JSValue stencil_set_alpha(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4499             : {
    4500           1 :         return stencil_set_alpha_ex(c, obj, argc, argv, GF_TRUE);
    4501             : }
    4502         100 : static JSValue stencil_set_alphaf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4503             : {
    4504         100 :         return stencil_set_alpha_ex(c, obj, argc, argv, GF_FALSE);
    4505             : }
    4506             : 
    4507           1 : static JSValue stencil_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    4508             : {
    4509             :         u32 v;
    4510           1 :         GF_EVGStencil *stencil = JS_GetOpaque(obj, stencil_class_id);
    4511           1 :         if (!stencil) return JS_EXCEPTION;
    4512             : 
    4513           1 :         switch (magic) {
    4514           0 :         case STENCIL_CMX:
    4515           0 :                 if (JS_IsNull(value)) {
    4516           0 :                         gf_evg_stencil_set_color_matrix(stencil, NULL);
    4517             :                 } else {
    4518           0 :                         GF_ColorMatrix *cmx = JS_GetOpaque(value, colmx_class_id);
    4519           0 :                         gf_evg_stencil_set_color_matrix(stencil, cmx);
    4520             :                 }
    4521           0 :                 return JS_UNDEFINED;
    4522           1 :         case STENCIL_GRADMOD:
    4523           1 :                 if (JS_ToInt32(c, &v, value)) return JS_EXCEPTION;
    4524           1 :                 gf_evg_stencil_set_gradient_mode(stencil, v);
    4525           1 :                 return JS_UNDEFINED;
    4526           0 :         case STENCIL_MAT:
    4527           0 :                 if (JS_IsNull(value)) {
    4528           0 :                         gf_evg_stencil_set_matrix(stencil, NULL);
    4529             :                 } else {
    4530           0 :                         GF_Matrix2D *mx = JS_GetOpaque(value, mx2d_class_id);
    4531           0 :                         gf_evg_stencil_set_matrix(stencil, mx);
    4532             :                 }
    4533           0 :                 return JS_UNDEFINED;
    4534             :         }
    4535           0 :         return JS_UNDEFINED;
    4536             : }
    4537             : 
    4538             : static const JSCFunctionListEntry stencil_funcs[] =
    4539             : {
    4540             :         JS_CGETSET_MAGIC_DEF("pad", NULL, stencil_setProperty, STENCIL_GRADMOD),
    4541             :         JS_CGETSET_MAGIC_DEF("cmx", NULL, stencil_setProperty, STENCIL_CMX),
    4542             :         JS_CGETSET_MAGIC_DEF("mx", NULL, stencil_setProperty, STENCIL_MAT),
    4543             :         JS_CFUNC_DEF("set_color", 0, stencil_set_color),
    4544             :         JS_CFUNC_DEF("set_colorf", 0, stencil_set_colorf),
    4545             :         JS_CFUNC_DEF("set_alpha", 0, stencil_set_alpha),
    4546             :         JS_CFUNC_DEF("set_alphaf", 0, stencil_set_alphaf),
    4547             :         JS_CFUNC_DEF("set_points", 0, stencil_set_points),
    4548             :         JS_CFUNC_DEF("set_stop", 0, stencil_set_stop),
    4549             :         JS_CFUNC_DEF("set_stopf", 0, stencil_set_stopf),
    4550             : };
    4551             : 
    4552             : 
    4553          44 : static JSValue stencil_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv, GF_StencilType type)
    4554             : {
    4555             :         JSValue obj;
    4556          44 :         GF_EVGStencil *stencil = gf_evg_stencil_new(type);
    4557          44 :         if (!stencil) return JS_EXCEPTION;
    4558          44 :         obj = JS_NewObjectClass(c, stencil_class_id);
    4559          44 :         if (JS_IsException(obj)) return obj;
    4560          44 :         JS_SetOpaque(obj, stencil);
    4561          44 :         return obj;
    4562             : }
    4563          37 : static JSValue solid_brush_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    4564             : {
    4565          37 :         return stencil_constructor(c, new_target, argc, argv, GF_STENCIL_SOLID);
    4566             : }
    4567           1 : static JSValue linear_gradient_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    4568             : {
    4569           1 :         return stencil_constructor(c, new_target, argc, argv, GF_STENCIL_LINEAR_GRADIENT);
    4570             : }
    4571           6 : static JSValue radial_gradient_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    4572             : {
    4573           6 :         return stencil_constructor(c, new_target, argc, argv, GF_STENCIL_RADIAL_GRADIENT);
    4574             : }
    4575             : 
    4576         133 : static void texture_reset(GF_JSTexture *tx)
    4577             : {
    4578         133 :         if (tx->owns_data && tx->data) {
    4579          22 :                 gf_free(tx->data);
    4580             :         }
    4581         133 :         tx->data = NULL;
    4582         133 :         tx->data_size = 0;
    4583         133 :         tx->owns_data = GF_FALSE;
    4584         133 : }
    4585             : 
    4586         133 : static void texture_finalize(JSRuntime *rt, JSValue obj)
    4587             : {
    4588         133 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4589         133 :         if (!tx) return;
    4590         133 :         texture_reset(tx);
    4591         133 :         if (tx->stencil)
    4592         133 :                 gf_evg_stencil_delete(tx->stencil);
    4593             :         JS_FreeValueRT(rt, tx->param_fun);
    4594         133 :         gf_free(tx);
    4595             : }
    4596             : 
    4597          16 : static void texture_gc_mark(JSRuntime *rt, JSValueConst obj, JS_MarkFunc *mark_func)
    4598             : {
    4599          16 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4600          16 :         if (!tx) return;
    4601          16 :         JS_MarkValue(rt, tx->param_fun, mark_func);
    4602             : }
    4603             : 
    4604             : JSClassDef texture_class = {
    4605             :         "Texture",
    4606             :         .finalizer = texture_finalize,
    4607             :         .gc_mark = texture_gc_mark
    4608             : };
    4609             : 
    4610             : enum
    4611             : {
    4612             :         TX_MAPPING = 0,
    4613             :         TX_FILTER,
    4614             :         TX_CMX,
    4615             :         TX_REPEAT_S,
    4616             :         TX_REPEAT_T,
    4617             :         TX_FLIP_X,
    4618             :         TX_FLIP_Y,
    4619             :         TX_MAT,
    4620             :         TX_WIDTH,
    4621             :         TX_HEIGHT,
    4622             :         TX_NB_COMP,
    4623             :         TX_PIXFMT,
    4624             :         TX_DATA,
    4625             : };
    4626             : 
    4627         432 : static JSValue texture_getProperty(JSContext *c, JSValueConst obj, int magic)
    4628             : {
    4629         432 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4630         432 :         if (!tx || !tx->stencil) return JS_EXCEPTION;
    4631         432 :         switch (magic) {
    4632           0 :         case TX_REPEAT_S:
    4633           0 :                 return JS_NewBool(c, (tx->flags & GF_TEXTURE_REPEAT_S));
    4634           0 :         case TX_REPEAT_T:
    4635           0 :                 return JS_NewBool(c, (tx->flags & GF_TEXTURE_REPEAT_T));
    4636           0 :         case TX_FLIP_X:
    4637           0 :                 return JS_NewBool(c, (tx->flags & GF_TEXTURE_FLIP_X));
    4638           0 :         case TX_FLIP_Y:
    4639           0 :                 return JS_NewBool(c, (tx->flags & GF_TEXTURE_FLIP_Y));
    4640         221 :         case TX_WIDTH:
    4641         221 :                 return JS_NewInt32(c, tx->width);
    4642         211 :         case TX_HEIGHT:
    4643         211 :                 return JS_NewInt32(c, tx->height);
    4644           0 :         case TX_PIXFMT:
    4645           0 :                 return JS_NewInt32(c, tx->pf);
    4646           0 :         case TX_NB_COMP:
    4647           0 :                 return JS_NewInt32(c, tx->nb_comp);
    4648           0 :         case TX_DATA:
    4649           0 :                 if (tx->owns_data)
    4650           0 :                         return JS_NewArrayBuffer(c, (u8 *) tx->data, tx->data_size, NULL, NULL, GF_TRUE);
    4651           0 :                 return JS_NULL;
    4652             :         }
    4653           0 :         return JS_UNDEFINED;
    4654             : }
    4655         251 : static JSValue texture_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    4656             : {
    4657             :         u32 v;
    4658         251 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4659         251 :         if (!tx || !tx->stencil) return JS_EXCEPTION;
    4660             : 
    4661         251 :         switch (magic) {
    4662           4 :         case TX_FILTER:
    4663           4 :                 if (JS_ToInt32(c, &v, value)) return JS_EXCEPTION;
    4664           4 :                 gf_evg_stencil_set_filter(tx->stencil, v);
    4665           4 :                 return JS_UNDEFINED;
    4666           1 :         case TX_CMX:
    4667           1 :                 if (JS_IsNull(value)) {
    4668           0 :                         gf_evg_stencil_set_color_matrix(tx->stencil, NULL);
    4669             :                 } else {
    4670           1 :                         GF_ColorMatrix *cmx = JS_GetOpaque(value, colmx_class_id);
    4671           1 :                         gf_evg_stencil_set_color_matrix(tx->stencil, cmx);
    4672             :                 }
    4673           1 :                 return JS_UNDEFINED;
    4674          16 :         case TX_REPEAT_S:
    4675          16 :                 if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_REPEAT_S;
    4676           0 :                 else tx->flags &= ~GF_TEXTURE_REPEAT_S;
    4677          16 :                 gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
    4678          16 :                 return JS_UNDEFINED;
    4679          16 :         case TX_REPEAT_T:
    4680          16 :                 if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_REPEAT_T;
    4681           0 :                 else tx->flags &= ~GF_TEXTURE_REPEAT_T;
    4682          16 :                 gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
    4683          16 :                 return JS_UNDEFINED;
    4684           0 :         case TX_FLIP_X:
    4685           0 :                 if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_FLIP_X;
    4686           0 :                 else tx->flags &= ~GF_TEXTURE_FLIP_X;
    4687           0 :                 gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
    4688           0 :                 return JS_UNDEFINED;
    4689           3 :         case TX_FLIP_Y:
    4690           3 :                 if (JS_ToBool(c, value)) tx->flags |= GF_TEXTURE_FLIP_Y;
    4691           0 :                 else tx->flags &= ~GF_TEXTURE_FLIP_Y;
    4692           3 :                 gf_evg_stencil_set_mapping(tx->stencil, tx->flags);
    4693           3 :                 return JS_UNDEFINED;
    4694         211 :         case TX_MAT:
    4695         211 :                 if (JS_IsNull(value)) {
    4696           0 :                         gf_evg_stencil_set_matrix(tx->stencil, NULL);
    4697             :                 } else {
    4698         211 :                         GF_Matrix2D *mx = JS_GetOpaque(value, mx2d_class_id);
    4699         211 :                         gf_evg_stencil_set_matrix(tx->stencil, mx);
    4700             :                 }
    4701         211 :                 return JS_UNDEFINED;
    4702             :         }
    4703           0 :         return JS_UNDEFINED;
    4704             : }
    4705             : 
    4706             : 
    4707             : #define min_f(a, b, c)  (fminf(a, fminf(b, c)))
    4708             : #define max_f(a, b, c)  (fmaxf(a, fmaxf(b, c)))
    4709             : 
    4710       16384 : void rgb2hsv(const u8 src_r, const u8 src_g, const u8 src_b, u8 *dst_h, u8 *dst_s, u8 *dst_v)
    4711             : {
    4712             :     float h, s, v; // h:0-360.0, s:0.0-1.0, v:0.0-1.0
    4713       16384 :     float r = src_r / 255.0f;
    4714       16384 :     float g = src_g / 255.0f;
    4715       16384 :     float b = src_b / 255.0f;
    4716             : 
    4717       16384 :     float max = max_f(r, g, b);
    4718       16384 :     float min = min_f(r, g, b);
    4719             : 
    4720             :     v = max;
    4721       16384 :     if (max == 0.0f) {
    4722             :         s = 0;
    4723             :         h = 0;
    4724       16020 :     } else if (max - min == 0.0f) {
    4725             :         s = 0;
    4726             :         h = 0;
    4727             :     } else {
    4728        3667 :         s = (max - min) / max;
    4729             : 
    4730        3667 :         if (max == r) {
    4731        3340 :             h = 60 * ((g - b) / (max - min)) + 0;
    4732         327 :         } else if (max == g) {
    4733         323 :             h = 60 * ((b - r) / (max - min)) + 120;
    4734             :         } else {
    4735           4 :             h = 60 * ((r - g) / (max - min)) + 240;
    4736             :         }
    4737             :     }
    4738       16384 :     if (h < 0) h += 360.0f;
    4739             : 
    4740       16384 :     *dst_h = (u8)(h / 2);   // dst_h : 0-180
    4741       16384 :     *dst_s = (u8)(s * 255); // dst_s : 0-255
    4742       16384 :     *dst_v = (u8)(v * 255); // dst_v : 0-255
    4743       16384 : }
    4744             : 
    4745       16384 : void hsv2rgb(u8 src_h, u8 src_s, u8 src_v, u8 *dst_r, u8 *dst_g, u8 *dst_b)
    4746             : {
    4747             :         float r, g, b; // 0.0-1.0
    4748       16384 :         float h = src_h *   2.0f; // 0-360
    4749       16384 :         float s = src_s / 255.0f; // 0.0-1.0
    4750       16384 :         float v = src_v / 255.0f; // 0.0-1.0
    4751             : 
    4752       16384 :         int   hi = (int)(h / 60.0f) % 6;
    4753       16384 :         float f  = (h / 60.0f) - hi;
    4754       16384 :         float p  = v * (1.0f - s);
    4755       16384 :         float q  = v * (1.0f - s * f);
    4756       16384 :         float t  = v * (1.0f - s * (1.0f - f));
    4757             : 
    4758       16384 :         switch(hi) {
    4759             :         case 0: r = v; g = t; b = p; break;
    4760           3 :         case 1: r = q; g = v; b = p; break;
    4761          47 :         case 2: r = p; g = v; b = t; break;
    4762         277 :         case 3: r = p; g = q; b = v; break;
    4763           3 :         case 4: r = t; g = p; b = v; break;
    4764         173 :         case 5:
    4765             :         default:
    4766         173 :                 r = v; g = p; b = q; break;
    4767             :         }
    4768             : 
    4769       16384 :         *dst_r = (u8)(r * 255); // dst_r : 0-255
    4770       16384 :         *dst_g = (u8)(g * 255); // dst_r : 0-255
    4771       16384 :         *dst_b = (u8)(b * 255); // dst_r : 0-255
    4772       16384 : }
    4773             : 
    4774             : enum
    4775             : {
    4776             :         EVG_CONV_RGB_TO_HSV=0,
    4777             :         EVG_CONV_HSV_TO_RGB,
    4778             :         EVG_CONV_YUV_TO_RGB,
    4779             :         EVG_CONV_RGB_TO_YUV,
    4780             : };
    4781             : 
    4782           4 : static JSValue texture_convert(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, u32 conv_type)
    4783             : {
    4784             :         JSValue nobj;
    4785             :         u32 i, j, dst_pf, nb_comp;
    4786             :         GF_JSCanvas *canvas=NULL;
    4787             :         GF_JSTexture *tx_hsv;
    4788           4 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4789           4 :         if (!tx || !tx->stencil) return JS_EXCEPTION;
    4790           4 :         if (argc) {
    4791           2 :                 canvas = JS_GetOpaque(argv[0], canvas_class_id);
    4792           2 :                 if (!canvas)
    4793           1 :                         canvas = JS_GetOpaque(argv[0], canvas3d_class_id);
    4794             :         }
    4795           4 :         if ((conv_type==EVG_CONV_YUV_TO_RGB) || (conv_type==EVG_CONV_RGB_TO_YUV)) {
    4796           2 :                 if (!canvas) return js_throw_err_msg(c, GF_BAD_PARAM, "Missing canvas parameter for RBG/YUV conversion");
    4797             :         }
    4798             : 
    4799           4 :         switch (tx->pf) {
    4800             :         case GF_PIXEL_ARGB:
    4801             :         case GF_PIXEL_RGBA:
    4802             :         case GF_PIXEL_BGRA:
    4803             :         case GF_PIXEL_ABGR:
    4804             :         case GF_PIXEL_ALPHAGREY:
    4805             :         case GF_PIXEL_GREYALPHA:
    4806             :                 dst_pf = GF_PIXEL_RGBA;
    4807             :                 nb_comp = 4;
    4808             :                 break;
    4809           0 :         default:
    4810             :                 dst_pf = GF_PIXEL_RGB;
    4811             :                 nb_comp = 3;
    4812           0 :                 break;
    4813             :         }
    4814             : 
    4815           4 :         GF_SAFEALLOC(tx_hsv, GF_JSTexture);
    4816           4 :         if (!tx_hsv)
    4817           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    4818           4 :         tx_hsv->width = tx->width;
    4819           4 :         tx_hsv->height = tx->height;
    4820           4 :         tx_hsv->pf = dst_pf;
    4821           4 :         tx_hsv->nb_comp = nb_comp;
    4822           4 :         gf_pixel_get_size_info(tx_hsv->pf, tx_hsv->width, tx_hsv->height, &tx_hsv->data_size, &tx_hsv->stride, &tx_hsv->stride_uv, NULL, NULL);
    4823           4 :         tx_hsv->data = gf_malloc(sizeof(char)*tx_hsv->data_size);
    4824           4 :         tx_hsv->owns_data = GF_TRUE;
    4825             : 
    4826         516 :         for (j=0; j<tx_hsv->height; j++) {
    4827         512 :                 u8 *dst = tx_hsv->data + j*tx_hsv->stride;
    4828       66048 :                 for (i=0; i<tx_hsv->width; i++) {
    4829             :                         u8 a, r, g, b;
    4830       65536 :                         u32 col = gf_evg_stencil_get_pixel(tx->stencil, i, j);
    4831             : 
    4832       65536 :                         if (conv_type == EVG_CONV_RGB_TO_HSV ) {
    4833       16384 :                                 a = GF_COL_A(col);
    4834       16384 :                                 r = GF_COL_R(col);
    4835       16384 :                                 g = GF_COL_G(col);
    4836       16384 :                                 b = GF_COL_B(col);
    4837       16384 :                                 rgb2hsv(r, g, b, &r, &g, &b);
    4838       49152 :                         } else if (conv_type == EVG_CONV_HSV_TO_RGB ) {
    4839       16384 :                                 a = GF_COL_A(col);
    4840       16384 :                                 r = GF_COL_R(col);
    4841       16384 :                                 g = GF_COL_G(col);
    4842       16384 :                                 b = GF_COL_B(col);
    4843       16384 :                                 hsv2rgb(r, g, b, &r, &g, &b);
    4844       32768 :                         } else if (conv_type == EVG_CONV_RGB_TO_YUV ) {
    4845       16384 :                                 col = gf_evg_argb_to_ayuv(canvas->surface, col);
    4846       16384 :                                 a = GF_COL_A(col);
    4847       16384 :                                 r = GF_COL_R(col);
    4848       16384 :                                 g = GF_COL_G(col);
    4849       16384 :                                 b = GF_COL_B(col);
    4850             :                         } else {
    4851       16384 :                                 col = gf_evg_ayuv_to_argb(canvas->surface, col);
    4852       16384 :                                 a = GF_COL_A(col);
    4853       16384 :                                 r = GF_COL_R(col);
    4854       16384 :                                 g = GF_COL_G(col);
    4855       16384 :                                 b = GF_COL_B(col);
    4856             :                         }
    4857       65536 :                         dst[0] = r;
    4858       65536 :                         dst[1] = g;
    4859       65536 :                         dst[2] = b;
    4860       65536 :                         if (nb_comp==4) {
    4861       65536 :                                 dst[3] = a;
    4862       65536 :                                 dst += 4;
    4863             :                         } else {
    4864           0 :                                 dst += 3;
    4865             :                         }
    4866             :                 }
    4867             :         }
    4868           4 :         tx_hsv->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
    4869           4 :         gf_evg_stencil_set_texture(tx_hsv->stencil, tx_hsv->data, tx_hsv->width, tx_hsv->height, tx_hsv->stride, tx_hsv->pf);
    4870           4 :         nobj = JS_NewObjectClass(c, texture_class_id);
    4871           4 :         JS_SetOpaque(nobj, tx_hsv);
    4872           4 :         return nobj;
    4873             : }
    4874           1 : static JSValue texture_rgb2hsv(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4875             : {
    4876           1 :         return texture_convert(c, obj, argc, argv, EVG_CONV_RGB_TO_HSV);
    4877             : }
    4878           1 : static JSValue texture_hsv2rgb(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4879             : {
    4880           1 :         return texture_convert(c, obj, argc, argv, EVG_CONV_HSV_TO_RGB);
    4881             : }
    4882           1 : static JSValue texture_rgb2yuv(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4883             : {
    4884           1 :         return texture_convert(c, obj, argc, argv, EVG_CONV_RGB_TO_YUV);
    4885             : }
    4886           1 : static JSValue texture_yuv2rgb(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4887             : {
    4888           1 :         return texture_convert(c, obj, argc, argv, EVG_CONV_YUV_TO_RGB);
    4889             : }
    4890             : 
    4891           1 : static JSValue texture_split(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4892             : {
    4893             :         JSValue nobj;
    4894             :         u32 i, j, idx, pix_shift=0;
    4895             :         GF_IRect src;
    4896             :         GF_JSTexture *tx_split;
    4897           1 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4898           1 :         if (!tx || !tx->stencil || !argc) return JS_EXCEPTION;
    4899             : 
    4900           1 :         if (JS_ToInt32(c, &idx, argv[0])) return JS_EXCEPTION;
    4901           1 :         if (idx>=tx->nb_comp) return JS_EXCEPTION;
    4902             : 
    4903           1 :         src.x = src.y = 0;
    4904           1 :         src.width = tx->width;
    4905           1 :         src.height = tx->height;
    4906           1 :         if (argc>1) {
    4907             :                 JSValue v;
    4908             :                 int res;
    4909           0 :                 if (!JS_IsObject(argv[1])) return JS_EXCEPTION;
    4910             : 
    4911             : #define GETIT(_name, _var) \
    4912             :                 v = JS_GetPropertyStr(c, argv[1], _name);\
    4913             :                 res = JS_ToInt32(c, &(src._var), v);\
    4914             :                 JS_FreeValue(c, v);\
    4915             :                 if (res) return JS_EXCEPTION;\
    4916             :                 if (src._var<0) return JS_EXCEPTION;\
    4917             : 
    4918           0 :                 GETIT("x", x)
    4919           0 :                 GETIT("y", y)
    4920           0 :                 GETIT("w", width)
    4921           0 :                 GETIT("h", height)
    4922             : #undef GETIT
    4923             :         }
    4924             : 
    4925           1 :         GF_SAFEALLOC(tx_split, GF_JSTexture);
    4926           1 :         if (!tx_split)
    4927           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    4928           1 :         tx_split->width = src.width;
    4929           1 :         tx_split->height = src.height;
    4930           1 :         tx_split->pf = GF_PIXEL_GREYSCALE;
    4931           1 :         tx_split->nb_comp = 1;
    4932           1 :         tx_split->stride = tx_split->width;
    4933           1 :         tx_split->data_size = tx_split->width * tx_split->height;
    4934           1 :         tx_split->data = gf_malloc(sizeof(char) * tx_split->data_size);
    4935           1 :         tx_split->owns_data = GF_TRUE;
    4936             : 
    4937             :         pix_shift = 0;
    4938           1 :         if (idx==0) {
    4939             :                 pix_shift = 16; //R component
    4940           0 :         } else if (idx==1) {
    4941           0 :                 if ((tx->pf == GF_PIXEL_ALPHAGREY) || (tx->pf == GF_PIXEL_GREYALPHA)) pix_shift = 24; //alpha
    4942             :                 else pix_shift = 8; //green
    4943           0 :         } else if (idx==2) {
    4944             :                 pix_shift = 0; //blue
    4945           0 :         } else if (idx==3) {
    4946             :                 pix_shift = 24; //alpha
    4947             :         }
    4948         129 :         for (j=0; j<tx_split->height; j++) {
    4949         128 :                 u8 *dst = tx_split->data + j*tx_split->stride;
    4950       16512 :                 for (i=0; i<tx_split->width; i++) {
    4951       16384 :                         u32 col = gf_evg_stencil_get_pixel(tx->stencil, src.x + i, src.y + j);
    4952       16384 :                         *dst++ = (col >> pix_shift) & 0xFF;
    4953             :                 }
    4954             :         }
    4955           1 :         tx_split->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
    4956           1 :         gf_evg_stencil_set_texture(tx_split->stencil, tx_split->data, tx_split->width, tx_split->height, tx_split->stride, tx_split->pf);
    4957           1 :         nobj = JS_NewObjectClass(c, texture_class_id);
    4958           1 :         JS_SetOpaque(nobj, tx_split);
    4959           1 :         return nobj;
    4960             : }
    4961           1 : static JSValue texture_convolution(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    4962             : {
    4963             :         JSValue v, kernv, nobj;
    4964           1 :         u32 i, j, kw=0, kh=0, kl=0, hkh, hkw;
    4965             :         s32 *kdata;
    4966           1 :         s32 knorm=0;
    4967             :         GF_JSTexture *tx_conv;
    4968           1 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    4969           1 :         if (!tx || !tx->stencil || !argc) return JS_EXCEPTION;
    4970             : 
    4971           2 :         if (!JS_IsObject(argv[0]))
    4972           0 :                 return JS_EXCEPTION;
    4973             : 
    4974           1 :         v = JS_GetPropertyStr(c, argv[0], "w");
    4975           1 :         JS_ToInt32(c, &kw, v);
    4976             :         JS_FreeValue(c, v);
    4977           1 :         v = JS_GetPropertyStr(c, argv[0], "h");
    4978           1 :         JS_ToInt32(c, &kh, v);
    4979             :         JS_FreeValue(c, v);
    4980           1 :         v = JS_GetPropertyStr(c, argv[0], "norm");
    4981           1 :         if (!JS_IsUndefined(v))
    4982           0 :                 JS_ToInt32(c, &knorm, v);
    4983             :         JS_FreeValue(c, v);
    4984           1 :         if (!kh || !kw)
    4985           0 :                 return JS_EXCEPTION;
    4986           1 :         if (!(kh%2) || !(kw%2))
    4987           0 :                 return JS_EXCEPTION;
    4988           1 :         kernv = JS_GetPropertyStr(c, argv[0], "k");
    4989           1 :         if (JS_IsUndefined(kernv))
    4990           0 :                 return JS_EXCEPTION;
    4991             : 
    4992           1 :         v = JS_GetPropertyStr(c, kernv, "length");
    4993           1 :         JS_ToInt32(c, &kl, v);
    4994             :         JS_FreeValue(c, v);
    4995           1 :         if (kl < kw * kh) {
    4996             :                 JS_FreeValue(c, kernv);
    4997           0 :                 return JS_EXCEPTION;
    4998             :         }
    4999           1 :         kl = kw*kh;
    5000           1 :         kdata = gf_malloc(sizeof(s32)*kl);
    5001           2 :         for (j=0; j<kh; j++) {
    5002           3 :                 for (i=0; i<kw; i++) {
    5003           3 :                         u32 idx = j*kw + i;
    5004           3 :                         v = JS_GetPropertyUint32(c, kernv, idx);
    5005           3 :                         JS_ToInt32(c, &kdata[idx] , v);
    5006             :                         JS_FreeValue(c, v);
    5007             :                 }
    5008             :         }
    5009             :         JS_FreeValue(c, kernv);
    5010             : 
    5011           1 :         GF_SAFEALLOC(tx_conv, GF_JSTexture);
    5012           1 :         if (!tx_conv)
    5013           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    5014           1 :         tx_conv->width = tx->width;
    5015           1 :         tx_conv->height = tx->height;
    5016           1 :         tx_conv->pf = GF_PIXEL_RGB;
    5017           1 :         tx_conv->nb_comp = 3;
    5018           1 :         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);
    5019           1 :         tx_conv->data = gf_malloc(sizeof(char)*tx_conv->data_size);
    5020           1 :         tx_conv->owns_data = GF_TRUE;
    5021             : 
    5022           1 :         hkh = kh/2;
    5023           1 :         hkw = kw/2;
    5024         129 :         for (j=0; j<tx_conv->height; j++) {
    5025         128 :                 u8 *dst = tx_conv->data + j*tx_conv->stride;
    5026       16512 :                 for (i=0; i<tx_conv->width; i++) {
    5027             :                         u32 k, l, nb_pix=0;
    5028             :                         s32 kr = 0;
    5029             :                         s32 kg = 0;
    5030             :                         s32 kb = 0;
    5031             : 
    5032       16384 :                         for (k=0; k<kh; k++) {
    5033       16384 :                                 if (j+k < hkh) continue;
    5034       16384 :                                 if (j+k >= tx_conv->height + hkh) continue;
    5035             : 
    5036       49152 :                                 for (l=0; l<kw; l++) {
    5037             :                                         s32 kv;
    5038       49152 :                                         if (i+l < hkw) continue;
    5039       49024 :                                         else if (i+l >= tx_conv->width + hkw) continue;
    5040             : 
    5041       48896 :                                         u32 col = gf_evg_stencil_get_pixel(tx->stencil, i+l-hkw, j+k-hkh);
    5042       48896 :                                         kv = kdata[k*kw + l];
    5043       48896 :                                         kr += kv * (s32) GF_COL_R(col);
    5044       48896 :                                         kg += kv * (s32) GF_COL_G(col);
    5045       48896 :                                         kb += kv * (s32) GF_COL_B(col);
    5046       48896 :                                         nb_pix++;
    5047             :                                 }
    5048             :                         }
    5049             : 
    5050       16384 :                         if (nb_pix!=kl) {
    5051         256 :                                 u32 n = knorm ? knorm : 1;
    5052         256 :                                 if (nb_pix) n *= nb_pix;
    5053         256 :                                 kr = (kr * kl / n);
    5054         256 :                                 kg = (kg * kl / n);
    5055         256 :                                 kb = (kb * kl / n);
    5056       16128 :                         } else if (knorm) {
    5057           0 :                                 kr /= knorm;
    5058           0 :                                 kg /= knorm;
    5059           0 :                                 kb /= knorm;
    5060             :                         }
    5061             : #define SET_CLAMP(_d, _s)\
    5062             :                         if (_s<0) _d = 0;\
    5063             :                         else if (_s>255) _d = 255;\
    5064             :                         else _d = (u8) _s;
    5065             : 
    5066       16384 :                         SET_CLAMP(dst[0], kr)
    5067       16384 :                         SET_CLAMP(dst[1], kg)
    5068       16384 :                         SET_CLAMP(dst[2], kb)
    5069             : 
    5070       16384 :                         dst += 3;
    5071             :                 }
    5072             :         }
    5073           1 :         gf_free(kdata);
    5074             : 
    5075           1 :         tx_conv->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
    5076           1 :         gf_evg_stencil_set_texture(tx_conv->stencil, tx_conv->data, tx_conv->width, tx_conv->height, tx_conv->stride, tx_conv->pf);
    5077           1 :         nobj = JS_NewObjectClass(c, texture_class_id);
    5078           1 :         JS_SetOpaque(nobj, tx_conv);
    5079           1 :         return nobj;
    5080             : }
    5081             : 
    5082             : 
    5083         100 : static JSValue texture_update(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    5084             : {
    5085             :         GF_Err e;
    5086         100 :         u32 width=0, height=0, pf=0, stride=0, stride_uv=0;
    5087         100 :         u8 *data=NULL;
    5088         100 :         u8 *p_u=NULL;
    5089         100 :         u8 *p_v=NULL;
    5090         100 :         u8 *p_a=NULL;
    5091             : 
    5092         100 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    5093         100 :         if (!tx || !tx->stencil || !argc) return JS_EXCEPTION;
    5094             : 
    5095         200 :         if (JS_IsObject(argv[0])) {
    5096             :                 //create from canvas object
    5097         100 :                 GF_JSCanvas *canvas = JS_GetOpaque(argv[0], canvas_class_id);
    5098         100 :                 if (canvas) {
    5099           0 :                         width = canvas->width;
    5100           0 :                         height = canvas->height;
    5101           0 :                         stride = canvas->stride;
    5102           0 :                         stride_uv = canvas->stride_uv;
    5103           0 :                         data = canvas->data;
    5104           0 :                         pf = canvas->pf;
    5105             :                 }
    5106             :                 //create from filter packet
    5107         100 :                 else if (jsf_is_packet(c, argv[0])) {
    5108         100 :                         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);
    5109         100 :                         if (e) return js_throw_err(c, e);
    5110             :                 } else {
    5111           0 :                         return js_throw_err(c, GF_BAD_PARAM);
    5112             :                 }
    5113             :         } else {
    5114           0 :                 return js_throw_err(c, GF_BAD_PARAM);
    5115             :         }
    5116             : 
    5117         100 :         tx->owns_data = GF_FALSE;
    5118         100 :         tx->width = width;
    5119         100 :         tx->height = height;
    5120         100 :         tx->pf = pf;
    5121         100 :         tx->stride = stride;
    5122         100 :         tx->stride_uv = stride_uv;
    5123         100 :         tx->data = data;
    5124         100 :         if (p_u || p_v) {
    5125           0 :                 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);
    5126           0 :                  if (e) return js_throw_err(c, e);
    5127             :         } else {
    5128             :                 assert(data);
    5129         100 :                 e = gf_evg_stencil_set_texture(tx->stencil, tx->data, tx->width, tx->height, tx->stride, tx->pf);
    5130         100 :                 if (e) return js_throw_err(c, e);
    5131             :         }
    5132             : 
    5133         100 :         if (tx->pf) {
    5134         100 :                 tx->nb_comp = gf_pixel_get_nb_comp(tx->pf);
    5135         100 :                 gf_pixel_get_size_info(tx->pf, tx->width, tx->height, NULL, NULL, NULL, NULL, NULL);
    5136             :         }
    5137         100 :         return JS_UNDEFINED;
    5138             : }
    5139             : 
    5140           3 : static JSValue texture_get_pixel_internal(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv, Bool is_float)
    5141             : {
    5142             :         Bool as_array=GF_FALSE;
    5143             :         JSValue ret;
    5144             :         Double x, y;
    5145             :         Float r, g, b, a;
    5146             : 
    5147           3 :         GF_JSTexture *tx = JS_GetOpaque(obj, texture_class_id);
    5148           3 :         if (!tx || !tx->stencil || (argc<2) ) return JS_EXCEPTION;
    5149             : 
    5150           3 :         if (is_float) {
    5151           2 :                 if (JS_ToFloat64(c, &x, argv[0])) return JS_EXCEPTION;
    5152           2 :                 if (JS_ToFloat64(c, &y, argv[1])) return JS_EXCEPTION;
    5153             :         } else {
    5154             :                 s32 _x, _y;
    5155           1 :                 if (JS_ToInt32(c, &_x, argv[0])) return JS_EXCEPTION;
    5156           1 :                 if (JS_ToInt32(c, &_y, argv[1])) return JS_EXCEPTION;
    5157           1 :                 x = ((Float)_x) / tx->width;
    5158           1 :                 y = ((Float)_y) / tx->height;
    5159             :         }
    5160           3 :         if ((argc>2) && JS_ToBool(c, argv[2]))
    5161             :                 as_array = GF_TRUE;
    5162             : 
    5163           3 :         gf_evg_stencil_get_pixel_f(tx->stencil, (Float) x, (Float) y, &r, &g, &b, &a);
    5164             : 
    5165           3 :         if (as_array) {
    5166           0 :                 ret = JS_NewArray(c);
    5167           0 :                 JS_SetPropertyStr(c, ret, "length", JS_NewInt32(c, 4) );
    5168           0 :                 JS_SetPropertyUint32(c, ret, 0, JS_NewFloat64(c, r) );
    5169           0 :                 JS_SetPropertyUint32(c, ret, 1, JS_NewFloat64(c, g) );
    5170           0 :                 JS_SetPropertyUint32(c, ret, 2, JS_NewFloat64(c, b) );
    5171           0 :                 JS_SetPropertyUint32(c, ret, 3, JS_NewFloat64(c, a) );
    5172             :         } else {
    5173           3 :                 ret = JS_NewObject(c);
    5174           6 :                 JS_SetPropertyStr(c, ret, "r", JS_NewFloat64(c, r) );
    5175           6 :                 JS_SetPropertyStr(c, ret, "g", JS_NewFloat64(c, g) );
    5176           6 :                 JS_SetPropertyStr(c, ret, "b", JS_NewFloat64(c, b) );
    5177           6 :                 JS_SetPropertyStr(c, ret, "a", JS_NewFloat64(c, a) );
    5178             :         }
    5179           3 :         return ret;
    5180             : }
    5181           1 : static JSValue texture_get_pixel(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    5182             : {
    5183           1 :         return texture_get_pixel_internal(c, obj, argc, argv, GF_FALSE);
    5184             : }
    5185             : 
    5186           2 : static JSValue texture_get_pixelf(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    5187             : {
    5188           2 :         return texture_get_pixel_internal(c, obj, argc, argv, GF_TRUE);
    5189             : }
    5190             : 
    5191          16 : static GF_Err texture_load_data(JSContext *c, GF_JSTexture *tx, u8 *data, u32 size)
    5192             : {
    5193             :         GF_Err e;
    5194             :         GF_BitStream *bs;
    5195          16 :         u8 *dsi=NULL;
    5196             :         u32 codecid, width, height, pf, dsi_len, osize;
    5197             : 
    5198          16 :         bs = gf_bs_new(data, size, GF_BITSTREAM_READ);
    5199          16 :         gf_img_parse(bs, &codecid, &width, &height, &dsi, &dsi_len);
    5200          16 :         gf_bs_del(bs);
    5201             : 
    5202             :         e = GF_NOT_SUPPORTED;
    5203          16 :         if (codecid==GF_CODECID_PNG) {
    5204          15 :                 e = gf_img_png_dec(data, size, &width, &height, &pf, NULL, &osize);
    5205          15 :                 if (e ==GF_BUFFER_TOO_SMALL) {
    5206          15 :                         tx->owns_data = GF_TRUE;
    5207          15 :                         tx->data_size = osize;
    5208          15 :                         tx->data = gf_malloc(sizeof(char) * osize);
    5209          15 :                         e = gf_img_png_dec(data, size, &width, &height, &pf, tx->data, &osize);
    5210          15 :                         if (e==GF_OK) {
    5211          15 :                                 tx->width = width;
    5212          15 :                                 tx->height = height;
    5213          15 :                                 tx->pf = pf;
    5214             :                         }
    5215             :                 }
    5216             :         }
    5217          16 :         if (codecid==GF_CODECID_JPEG) {
    5218           1 :                 e = gf_img_jpeg_dec(data, size, &width, &height, &pf, NULL, &osize, 0);
    5219           1 :                 if (e ==GF_BUFFER_TOO_SMALL) {
    5220           1 :                         tx->owns_data = GF_TRUE;
    5221           1 :                         tx->data_size = osize;
    5222           1 :                         tx->data = gf_malloc(sizeof(char) * osize);
    5223           1 :                         e = gf_img_jpeg_dec(data, size, &width, &height, &pf, tx->data, &osize, 0);
    5224           1 :                         if (e==GF_OK) {
    5225           1 :                                 tx->width = width;
    5226           1 :                                 tx->height = height;
    5227           1 :                                 tx->pf = pf;
    5228             :                         }
    5229             :                 }
    5230             :         }
    5231          16 :         if (dsi) gf_free(dsi);
    5232             : 
    5233          16 :         if (e != GF_OK) {
    5234             :                 return e;
    5235             :         }
    5236          16 :         tx->nb_comp = gf_pixel_get_nb_comp(tx->pf);
    5237          16 :         gf_pixel_get_size_info(tx->pf, tx->width, tx->height, NULL, &tx->stride, NULL, NULL, NULL);
    5238          16 :         e = gf_evg_stencil_set_texture(tx->stencil, tx->data, tx->width, tx->height, tx->stride, tx->pf);
    5239          16 :         if (e != GF_OK) {
    5240             :                 return e;
    5241             :         }
    5242             :         return GF_OK;
    5243             : }
    5244          16 : static GF_Err texture_load_file(JSContext *c, GF_JSTexture *tx, const char *fileName, Bool rel_to_script)
    5245             : {
    5246             :         char szPath[GF_MAX_PATH];
    5247             :         u8 *data;
    5248             :         u32 size;
    5249             :         GF_Err e;
    5250             :         char *full_url = NULL;
    5251             : 
    5252          16 :         if (rel_to_script) {
    5253          11 :                 const char *par_url = jsf_get_script_filename(c);
    5254          11 :                 full_url = gf_url_concatenate(par_url, fileName);
    5255             :                 fileName = full_url;
    5256             :         }
    5257          16 :         if (!strncmp(fileName, "$GSHARE/", 8)) {
    5258           5 :                 gf_opts_default_shared_directory(szPath);
    5259           5 :                 strcat(szPath, fileName + 7);
    5260             :                 fileName = szPath;
    5261             :         }
    5262          16 :         if (!gf_file_exists(fileName) || (gf_file_load_data(fileName, &data, &size) != GF_OK)) {
    5263           0 :                 if (full_url) gf_free(full_url);
    5264             :                 return GF_URL_ERROR;
    5265             :         }
    5266          16 :         if (full_url) gf_free(full_url);
    5267          16 :         e = texture_load_data(c, tx, data, size);
    5268          16 :         gf_free(data);
    5269          16 :         return e;
    5270             : }
    5271             : 
    5272             : static const JSCFunctionListEntry texture_funcs[] =
    5273             : {
    5274             :         JS_CGETSET_MAGIC_DEF("filtering", NULL, texture_setProperty, TX_FILTER),
    5275             :         JS_CGETSET_MAGIC_DEF("cmx", NULL, texture_setProperty, TX_CMX),
    5276             :         JS_CGETSET_MAGIC_DEF("mx", NULL, texture_setProperty, TX_MAT),
    5277             :         JS_CGETSET_MAGIC_DEF("repeat_s", texture_getProperty, texture_setProperty, TX_REPEAT_S),
    5278             :         JS_CGETSET_MAGIC_DEF("repeat_t", texture_getProperty, texture_setProperty, TX_REPEAT_T),
    5279             :         JS_CGETSET_MAGIC_DEF("flip_h", texture_getProperty, texture_setProperty, TX_FLIP_X),
    5280             :         JS_CGETSET_MAGIC_DEF("flip_v", texture_getProperty, texture_setProperty, TX_FLIP_Y),
    5281             :         JS_CGETSET_MAGIC_DEF("width", texture_getProperty, NULL, TX_WIDTH),
    5282             :         JS_CGETSET_MAGIC_DEF("height", texture_getProperty, NULL, TX_HEIGHT),
    5283             :         JS_CGETSET_MAGIC_DEF("pixfmt", texture_getProperty, NULL, TX_PIXFMT),
    5284             :         JS_CGETSET_MAGIC_DEF("comp", texture_getProperty, NULL, TX_NB_COMP),
    5285             :         JS_CGETSET_MAGIC_DEF("data", texture_getProperty, NULL, TX_DATA),
    5286             : 
    5287             :         JS_CFUNC_DEF("set_alpha", 0, stencil_set_alpha),
    5288             :         JS_CFUNC_DEF("set_alphaf", 0, stencil_set_alphaf),
    5289             :         JS_CFUNC_DEF("rgb2hsv", 0, texture_rgb2hsv),
    5290             :         JS_CFUNC_DEF("hsv2rgb", 0, texture_hsv2rgb),
    5291             :         JS_CFUNC_DEF("rgb2yuv", 0, texture_rgb2yuv),
    5292             :         JS_CFUNC_DEF("yuv2rgb", 0, texture_yuv2rgb),
    5293             :         JS_CFUNC_DEF("convolution", 0, texture_convolution),
    5294             :         JS_CFUNC_DEF("split", 0, texture_split),
    5295             :         JS_CFUNC_DEF("update", 0, texture_update),
    5296             :         JS_CFUNC_DEF("get_pixelf", 0, texture_get_pixelf),
    5297             :         JS_CFUNC_DEF("get_pixel", 0, texture_get_pixel),
    5298             : 
    5299             : };
    5300             : 
    5301             : 
    5302       62102 : static void evg_param_tex_callback(void *cbk, u32 x, u32 y, Float *r, Float *g, Float *b, Float *a)
    5303             : {
    5304             :         Double compv;
    5305             :         JSValue ret, v, argv[2];
    5306             :         GF_JSTexture *tx = (GF_JSTexture *)cbk;
    5307      124204 :         argv[0] = JS_NewInt32(tx->ctx, x);
    5308      124204 :         argv[1] = JS_NewInt32(tx->ctx, y);
    5309       62102 :         ret = JS_Call(tx->ctx, tx->param_fun, tx->obj, 2, argv);
    5310       62102 :         JS_FreeValue(tx->ctx, argv[0]);
    5311       62102 :         JS_FreeValue(tx->ctx, argv[1]);
    5312             : 
    5313       62102 :         *r = *g = *b = 0.0;
    5314       62102 :         *a = 1.0;
    5315             : #define GETCOMP(_comp)\
    5316             :         v = JS_GetPropertyStr(tx->ctx, ret, #_comp);\
    5317             :         if (!JS_IsUndefined(v)) {\
    5318             :                 JS_ToFloat64(tx->ctx, &compv, v);\
    5319             :                 JS_FreeValue(tx->ctx, v);\
    5320             :                 *_comp = (Float) compv;\
    5321             :         }\
    5322             : 
    5323      186306 :         GETCOMP(r)
    5324      124204 :         GETCOMP(g)
    5325      124204 :         GETCOMP(b)
    5326      186306 :         GETCOMP(a)
    5327             : 
    5328             : #undef GETCOMP
    5329       62102 :         JS_FreeValue(tx->ctx, ret);
    5330       62102 : }
    5331             : 
    5332         127 : static JSValue texture_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    5333             : {
    5334             :         Bool use_screen_coords=GF_FALSE;
    5335             :         JSValue obj;
    5336         127 :         JSValue tx_fun=JS_UNDEFINED;
    5337         127 :         u32 width=0, height=0, pf=0, stride=0, stride_uv=0;
    5338         127 :         size_t data_size=0;
    5339         127 :         u8 *data=NULL;
    5340         127 :         u8 *p_u=NULL;
    5341         127 :         u8 *p_v=NULL;
    5342         127 :         u8 *p_a=NULL;
    5343             :         GF_JSTexture *tx;
    5344         127 :         GF_SAFEALLOC(tx, GF_JSTexture);
    5345         127 :         if (!tx)
    5346           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    5347         127 :         tx->stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
    5348         127 :         if (!tx->stencil) {
    5349           0 :                 gf_free(tx);
    5350           0 :                 return JS_EXCEPTION;
    5351             :         }
    5352         127 :         if (!argc) goto done;
    5353             : 
    5354         254 :         if (JS_IsString(argv[0])) {
    5355             :                 GF_Err e;
    5356             :                 Bool rel_to_script = GF_FALSE;
    5357             :                 const char *str = JS_ToCString(c, argv[0]);
    5358          16 :                 if (argc>1) rel_to_script = JS_ToBool(c, argv[1]);
    5359          16 :                 e = texture_load_file(c, tx, str, rel_to_script);
    5360          16 :                 JS_FreeCString(c, str);
    5361          16 :                 if (e) {
    5362           0 :                         if (tx->stencil) gf_evg_stencil_delete(tx->stencil);
    5363           0 :                         gf_free(tx);
    5364           0 :                         return js_throw_err_msg(c, e, "Failed to load texture: %s", gf_error_to_string(e));
    5365             :                 }
    5366             :                 goto done;
    5367             :         }
    5368         111 :         if (JS_IsObject(argv[0])) {
    5369             : 
    5370             :                 //create from canvas object
    5371         102 :                 GF_JSCanvas *canvas = JS_GetOpaque(argv[0], canvas_class_id);
    5372         102 :                 if (canvas) {
    5373           0 :                         width = canvas->width;
    5374           0 :                         height = canvas->height;
    5375           0 :                         stride = canvas->stride;
    5376           0 :                         stride_uv = canvas->stride_uv;
    5377           0 :                         data = canvas->data;
    5378           0 :                         pf = canvas->pf;
    5379             :                 }
    5380             :                 //create from filter packet
    5381         102 :                 else if (jsf_is_packet(c, argv[0])) {
    5382         102 :                         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);
    5383         102 :                         if (e) goto error;
    5384             : 
    5385         102 :                         if (!stride) {
    5386           0 :                                 gf_pixel_get_size_info(pf, width, height, NULL, &stride, &stride_uv, NULL, NULL);
    5387             :                         }
    5388             :                 } else {
    5389           0 :                         data = JS_GetArrayBuffer(c, &data_size, argv[0]);
    5390           0 :                         if (data) {
    5391           0 :                                 GF_Err e = texture_load_data(c, tx, data, (u32) data_size);
    5392           0 :                                 if (e) {
    5393           0 :                                         if (tx->stencil) gf_evg_stencil_delete(tx->stencil);
    5394           0 :                                         gf_free(tx);
    5395           0 :                                         return js_throw_err_msg(c, e, "Failed to load texture: %s", gf_error_to_string(e));
    5396             :                                 }
    5397             :                                 goto done;
    5398             :                         }
    5399             :                         goto error;
    5400             :                 }
    5401             :         }
    5402             :         //arraybuffer
    5403             :         else {
    5404           9 :                 if (argc<4) goto error;
    5405           9 :                 if (JS_ToInt32(c, &width, argv[0])) goto error;
    5406           9 :                 if (JS_ToInt32(c, &height, argv[1])) goto error;
    5407          18 :                 if (JS_IsString(argv[2])) {
    5408             :                         const char *str = JS_ToCString(c, argv[2]);
    5409           9 :                         pf = gf_pixel_fmt_parse(str);
    5410           9 :                         JS_FreeCString(c, str);
    5411           0 :                 } else if (JS_ToInt32(c, &pf, argv[2])) {
    5412             :                         goto error;
    5413             :                 }
    5414           9 :                 if (JS_IsFunction(c, argv[3])) {
    5415           2 :                         tx_fun = argv[3];
    5416             :                 }
    5417          14 :                 else if (JS_IsObject(argv[3])) {
    5418           7 :                         data = JS_GetArrayBuffer(c, &data_size, argv[3]);
    5419             :                 }
    5420          38 :                 if (!width || !height || !pf || (!data && JS_IsUndefined(tx_fun)))
    5421             :                         goto error;
    5422             : 
    5423           9 :                 if (argc>4) {
    5424           9 :                         if (JS_ToInt32(c, &stride, argv[4]))
    5425             :                                 goto error;
    5426           9 :                         if (argc>5) {
    5427           0 :                                 if (JS_ToInt32(c, &stride_uv, argv[5]))
    5428             :                                         goto error;
    5429             :                         }
    5430             :                 }
    5431             :         }
    5432             : 
    5433         111 :         tx->owns_data = GF_FALSE;
    5434         111 :         tx->width = width;
    5435         111 :         tx->height = height;
    5436         111 :         tx->pf = pf;
    5437         111 :         tx->stride = stride;
    5438         111 :         tx->stride_uv = stride_uv;
    5439         111 :         tx->data = data;
    5440         111 :         if (p_u || p_v) {
    5441           0 :                 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)
    5442             :                         goto error;
    5443         111 :         } else if (data) {
    5444         109 :                 if (gf_evg_stencil_set_texture(tx->stencil, tx->data, tx->width, tx->height, tx->stride, tx->pf) != GF_OK)
    5445             :                         goto error;
    5446             :         } else {
    5447           2 :                 use_screen_coords = stride ? GF_TRUE : GF_FALSE;
    5448           2 :                 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)
    5449             :                         goto error;
    5450           2 :                 tx->param_fun = JS_DupValue(c, tx_fun);
    5451           2 :                 tx->ctx = c;
    5452             :         }
    5453             : 
    5454         127 : done:
    5455         127 :         if (tx->pf) {
    5456         127 :                 tx->nb_comp = gf_pixel_get_nb_comp(tx->pf);
    5457         127 :                 gf_pixel_get_size_info(tx->pf, tx->width, tx->height, NULL, NULL, NULL, NULL, NULL);
    5458             :         }
    5459         127 :         obj = JS_NewObjectClass(c, texture_class_id);
    5460         127 :         if (JS_IsException(obj)) return obj;
    5461         127 :         JS_SetOpaque(obj, tx);
    5462         127 :         tx->obj = obj;
    5463         127 :         return obj;
    5464             : 
    5465           0 : error:
    5466           0 :         if (tx->stencil) gf_evg_stencil_delete(tx->stencil);
    5467           0 :         gf_free(tx);
    5468           0 :         return js_throw_err_msg(c, GF_BAD_PARAM, "Failed to create texture");
    5469             : }
    5470             : 
    5471           2 : Bool js_evg_is_texture(JSContext *ctx, JSValue this_obj)
    5472             : {
    5473           2 :         GF_JSTexture *tx = JS_GetOpaque(this_obj, texture_class_id);
    5474           2 :         if (!tx) return GF_FALSE;
    5475           2 :         return GF_TRUE;
    5476             : }
    5477             : 
    5478           6 : 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)
    5479             : {
    5480           6 :         GF_JSTexture *tx = JS_GetOpaque(this_obj, texture_class_id);
    5481           6 :         if (!tx) return GF_FALSE;
    5482           6 :         if (width) *width = tx->width;
    5483           6 :         if (height) *height = tx->height;
    5484           6 :         if (pixfmt) *pixfmt = tx->pf;
    5485           6 :         if (stride) *stride = tx->stride;
    5486           6 :         if (stride_uv) *stride_uv = tx->stride_uv;
    5487           6 :         if (!tx->data) return GF_TRUE;
    5488           6 :         if (p_data) *p_data = tx->data;
    5489           6 :         if (p_u) *p_u = NULL;
    5490           6 :         if (p_v) *p_v = NULL;
    5491           6 :         if (p_a) *p_a = NULL;
    5492             :         return GF_TRUE;
    5493             : }
    5494             : 
    5495          46 : static void text_reset(GF_JSText *txt)
    5496             : {
    5497          46 :         if (txt->path) gf_path_del(txt->path);
    5498          46 :         txt->path = NULL;
    5499         128 :         while (gf_list_count(txt->spans)) {
    5500          36 :                 GF_TextSpan *s = gf_list_pop_back(txt->spans);
    5501          36 :                 gf_font_manager_delete_span(txt->fm, s);
    5502             :         }
    5503          46 :         txt->min_x = txt->min_y = txt->max_x = txt->max_y = txt->max_w = txt->max_h = 0;
    5504          46 : }
    5505          37 : static void text_finalize(JSRuntime *rt, JSValue obj)
    5506             : {
    5507          37 :         GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
    5508          37 :         if (!txt) return;
    5509          37 :         text_reset(txt);
    5510          37 :         if (txt->fontname) gf_free(txt->fontname);
    5511          37 :         gf_list_del(txt->spans);
    5512          37 :         gf_free(txt);
    5513             : }
    5514             : 
    5515             : enum{
    5516             :         TXT_FONT=0,
    5517             :         TXT_FONTSIZE,
    5518             :         TXT_ALIGN,
    5519             :         TXT_BASELINE,
    5520             :         TXT_HORIZ,
    5521             :         TXT_FLIP,
    5522             :         TXT_UNDERLINED,
    5523             :         TXT_BOLD,
    5524             :         TXT_ITALIC,
    5525             :         TXT_SMALLCAP,
    5526             :         TXT_STRIKEOUT,
    5527             :         TXT_MAX_WIDTH,
    5528             :         TXT_LINESPACING,
    5529             : };
    5530             : 
    5531             : enum
    5532             : {
    5533             :         TXT_BL_TOP=0,
    5534             :         TXT_BL_HANGING,
    5535             :         TXT_BL_MIDDLE,
    5536             :         TXT_BL_ALPHABETIC,
    5537             :         TXT_BL_IDEOGRAPHIC,
    5538             :         TXT_BL_BOTTOM,
    5539             : };
    5540             : enum
    5541             : {
    5542             :         TXT_AL_START=0,
    5543             :         TXT_AL_END,
    5544             :         TXT_AL_LEFT,
    5545             :         TXT_AL_RIGHT,
    5546             :         TXT_AL_CENTER
    5547             : };
    5548             : 
    5549           1 : static JSValue text_getProperty(JSContext *c, JSValueConst obj, int magic)
    5550             : {
    5551           1 :         GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
    5552           1 :         if (!txt) return JS_EXCEPTION;
    5553           1 :         switch (magic) {
    5554           1 :         case TXT_FONT: return JS_NewString(c, txt->fontname);
    5555           0 :         case TXT_BASELINE: return JS_NewInt32(c, txt->baseline);
    5556           0 :         case TXT_ALIGN: return JS_NewInt32(c, txt->align);
    5557           0 :         case TXT_FONTSIZE: return JS_NewFloat64(c, txt->font_size);
    5558           0 :         case TXT_HORIZ: return JS_NewBool(c, txt->horizontal);
    5559           0 :         case TXT_FLIP: return JS_NewBool(c, txt->flip);
    5560           0 :         case TXT_MAX_WIDTH: return JS_NewFloat64(c, txt->maxWidth);
    5561           0 :         case TXT_LINESPACING: return JS_NewFloat64(c, txt->lineSpacing);
    5562           0 :         case TXT_BOLD: return JS_NewBool(c, txt->styles & GF_FONT_WEIGHT_100);
    5563           0 :         case TXT_ITALIC: return JS_NewBool(c, txt->styles & GF_FONT_ITALIC);
    5564           0 :         case TXT_UNDERLINED: return JS_NewBool(c, txt->styles & GF_FONT_UNDERLINED);
    5565           0 :         case TXT_SMALLCAP: return JS_NewBool(c, txt->styles & GF_FONT_SMALLCAPS);
    5566           0 :         case TXT_STRIKEOUT: return JS_NewBool(c, txt->styles & GF_FONT_STRIKEOUT);
    5567             :         }
    5568           0 :         return JS_UNDEFINED;
    5569             : }
    5570             : 
    5571         310 : static void text_update_path(GF_JSText *txt, Bool for_centered)
    5572             : {
    5573             :         Fixed cy, ascent, descent, scale_x, ls;
    5574             :         u32 i, nb_lines;
    5575             : 
    5576         310 :         if ((txt->path_for_centered == for_centered) && txt->path) {
    5577             :                 return;
    5578             :         }
    5579           8 :         if (txt->path) gf_path_del(txt->path);
    5580           8 :         txt->path = NULL;
    5581           8 :         if (!txt->font)
    5582             :                 return;
    5583             : 
    5584           8 :         txt->path_for_centered = for_centered;
    5585             : 
    5586           8 :         cy = FLT2FIX((txt->font_size * txt->font->baseline) / txt->font->em_size);
    5587             : 
    5588           8 :         ascent = FLT2FIX((txt->font_size*txt->font->ascent) / txt->font->em_size);
    5589           8 :         if (txt->lineSpacing)
    5590           0 :                 ls = FLT2FIX((txt->lineSpacing*txt->font->line_spacing) / txt->font->em_size);
    5591             :         else
    5592           8 :                 ls = FLT2FIX((txt->font_size*txt->font->line_spacing) / txt->font->em_size);
    5593           8 :         descent = -FLT2FIX((txt->font_size*txt->font->descent) / txt->font->em_size);
    5594             :         scale_x = 0;
    5595           8 :         if (txt->maxWidth && txt->max_w && (txt->max_w > txt->maxWidth)) {
    5596           0 :                 scale_x = gf_divfix(FLT2FIX(txt->maxWidth), INT2FIX(txt->max_w) );
    5597             :         }
    5598             : 
    5599           8 :         if (txt->baseline==TXT_BL_TOP) {
    5600             :                 cy = ascent;
    5601           8 :         } else if (txt->baseline==TXT_BL_BOTTOM) {
    5602             :                 cy = -descent;
    5603           8 :         } else if (txt->baseline==TXT_BL_MIDDLE) {
    5604           0 :                 Fixed mid = (ascent + descent)/2;
    5605             :                 cy = mid;
    5606             :         }
    5607             : 
    5608           8 :         txt->path = gf_path_new();
    5609           8 :         nb_lines = gf_list_count(txt->spans);
    5610          43 :         for (i=0; i<nb_lines; i++) {
    5611             :                 Fixed cx=0;
    5612             :                 u32 flags;
    5613             :                 GF_Path *path;
    5614             :                 GF_Matrix2D mx;
    5615          35 :                 GF_TextSpan *span = gf_list_get(txt->spans, i);
    5616          35 :                 if ((txt->align == TXT_AL_LEFT)
    5617          35 :                 || ((txt->align == TXT_AL_START) && !(span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
    5618          35 :                 || ((txt->align == TXT_AL_END) && (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
    5619             :                 ) {
    5620             :                         cx = 0;
    5621             :                 }
    5622          35 :                 else if ((txt->align == TXT_AL_RIGHT)
    5623          34 :                 || ((txt->align == TXT_AL_END) && !(span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
    5624          34 :                 || ((txt->align == TXT_AL_START) && (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT))
    5625             :                 ) {
    5626           1 :                         cx = txt->max_w - span->bounds.width;
    5627          34 :                 } else if (txt->align == TXT_AL_CENTER) {
    5628          34 :                         cx = (txt->max_w - span->bounds.width) / 2;
    5629             :                 }
    5630          35 :                 gf_mx2d_init(mx);
    5631             : 
    5632          35 :                 gf_mx2d_add_translation(&mx, cx, cy);
    5633          35 :                 if (scale_x)
    5634           0 :                         gf_mx2d_add_scale(&mx, scale_x, FIX_ONE);
    5635             : 
    5636          35 :                 flags = span->flags;
    5637          35 :                 if (txt->path_for_centered) {
    5638          35 :                         if (txt->flip)
    5639           0 :                                 span->flags |= GF_TEXT_SPAN_FLIP;
    5640             :                 } else {
    5641           0 :                         if (!txt->flip)
    5642           0 :                                 span->flags |= GF_TEXT_SPAN_FLIP;
    5643             :                 }
    5644             : 
    5645          35 :                 path = gf_font_span_create_path(span);
    5646          35 :                 gf_path_add_subpath(txt->path, path, &mx);
    5647          35 :                 gf_path_del(path);
    5648          35 :                 span->flags = flags;
    5649             : 
    5650          35 :                 if (txt->path_for_centered)
    5651          35 :                         cy -= ls;
    5652             :                 else
    5653           0 :                         cy += ls;
    5654             :         }
    5655             : }
    5656             : static void text_set_path(GF_JSCanvas *canvas, GF_JSText *txt)
    5657             : {
    5658         209 :         text_update_path(txt, canvas->center_coords);
    5659         209 :         gf_evg_surface_set_path(canvas->surface, txt->path);
    5660             : }
    5661             : 
    5662          36 : static void text_set_text_from_value(GF_JSText *txt, GF_Font *font, JSContext *c, JSValueConst value)
    5663             : {
    5664             :         const char *str = JS_ToCString(c, value);
    5665             :         char *start = (char *) str;
    5666          72 :         while (start) {
    5667             :                 GF_TextSpan *span;
    5668          36 :                 char *nline = strchr(str, '\n');
    5669          36 :                 if (nline) nline[0] = 0;
    5670          36 :                 span = gf_font_manager_create_span(txt->fm, font, (char *) str, FLT2FIX(txt->font_size), GF_FALSE, GF_FALSE, GF_FALSE, NULL, GF_FALSE, 0, NULL);
    5671          36 :                 if (span) {
    5672          36 :                         if (txt->horizontal)
    5673          36 :                                 span->flags |= GF_TEXT_SPAN_HORIZONTAL;
    5674          36 :                         gf_list_add(txt->spans, span);
    5675             :                 }
    5676             : 
    5677          36 :                 if (!nline) break;
    5678           0 :                 nline[0] = '\n';
    5679           0 :                 start = nline + 1;
    5680             :         }
    5681          36 :         JS_FreeCString(c, str);
    5682          36 : }
    5683             : 
    5684           1 : static JSValue text_get_path(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    5685             : {
    5686             :         JSValue nobj;
    5687             :         Bool is_center = GF_TRUE;
    5688           1 :         GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
    5689           1 :         if (!txt) return JS_EXCEPTION;
    5690           1 :         if (argc) is_center = JS_ToBool(c, argv[0]);
    5691             : 
    5692           1 :         text_update_path(txt, is_center);
    5693           1 :         if (!txt->path) return JS_NULL;
    5694           1 :         nobj = JS_NewObjectClass(c, path_class_id);
    5695           1 :         JS_SetOpaque(nobj, gf_path_clone(txt->path));
    5696           1 :         return nobj;
    5697             : }
    5698             : 
    5699           9 : static JSValue text_set_text(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    5700             : {
    5701             :         s32 i, j, nb_lines;
    5702           9 :         GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
    5703           9 :         if (!txt) return JS_EXCEPTION;
    5704           9 :         text_reset(txt);
    5705           9 :         if (!argc) return JS_UNDEFINED;
    5706             : 
    5707           9 :         txt->font = gf_font_manager_set_font(txt->fm, &txt->fontname, 1, txt->styles);
    5708           9 :         if (!txt->font)
    5709           0 :                 return js_throw_err_msg(c, GF_NOT_FOUND, "Font %s not found and no default font available!\n"
    5710             :                         "Check your GPAC configuration, or use `-rescan-fonts` to refresh font directory.\n", txt->fontname);
    5711             : 
    5712           9 :         for (i=0; i<argc; i++) {
    5713           9 :                 if (JS_IsArray(c, argv[i])) {
    5714           9 :                         JSValue v = JS_GetPropertyStr(c, argv[i], "length");
    5715           9 :                         JS_ToInt32(c, &nb_lines, v);
    5716             :                         JS_FreeValue(c, v);
    5717             : 
    5718          36 :                         for (j=0; j<nb_lines; j++) {
    5719          36 :                                 v = JS_GetPropertyUint32(c, argv[i], j);
    5720          36 :                                 text_set_text_from_value(txt, txt->font, c, v);
    5721             :                                 JS_FreeValue(c, v);
    5722             :                         }
    5723             :                 } else {
    5724           0 :                         text_set_text_from_value(txt, txt->font, c, argv[i]);
    5725             :                 }
    5726             :         }
    5727             : 
    5728           9 :         nb_lines = gf_list_count(txt->spans);
    5729          45 :         for (i=0; i<nb_lines; i++) {
    5730          36 :                 GF_TextSpan *span = gf_list_get(txt->spans, i);
    5731          36 :                 gf_font_manager_refresh_span_bounds(span);
    5732          36 :                 span->bounds.y += FLT2FIX( i*txt->lineSpacing );
    5733             : 
    5734          36 :                 if (!txt->max_h && !txt->max_w) {
    5735           9 :                         txt->max_w = span->bounds.width;
    5736           9 :                         txt->max_h = span->bounds.height;
    5737           9 :                         txt->min_x = span->bounds.x;
    5738           9 :                         txt->min_y = span->bounds.y;
    5739           9 :                         txt->max_x = txt->min_x + span->bounds.width;
    5740           9 :                         txt->max_y = txt->min_y + span->bounds.x;
    5741             :                 } else {
    5742          27 :                         if (txt->min_x > span->bounds.x)
    5743           0 :                                 txt->min_x = span->bounds.x;
    5744          27 :                         if (txt->min_y > span->bounds.y)
    5745           0 :                                 txt->min_y = span->bounds.y;
    5746          27 :                         if (txt->max_w < span->bounds.width)
    5747           7 :                                 txt->max_w = span->bounds.width;
    5748          27 :                         if (txt->max_h < span->bounds.height)
    5749           0 :                                 txt->max_h = span->bounds.height;
    5750          27 :                         if (txt->max_x < span->bounds.x + span->bounds.width)
    5751           7 :                                 txt->max_x = span->bounds.x + span->bounds.width;
    5752          27 :                         if (txt->max_y < span->bounds.y + span->bounds.height)
    5753           7 :                                 txt->max_y = span->bounds.y + span->bounds.height;
    5754             :                 }
    5755             :         }
    5756             : 
    5757           9 :         return JS_UNDEFINED;
    5758             : }
    5759             : 
    5760         202 : static JSValue text_setProperty(JSContext *c, JSValueConst obj, JSValueConst value, int magic)
    5761             : {
    5762             :         const char *str;
    5763         202 :         GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
    5764         202 :         if (!txt) return JS_EXCEPTION;
    5765             : 
    5766         202 :         switch (magic) {
    5767          37 :         case TXT_FONT:
    5768             :                 str = JS_ToCString(c, value);
    5769          37 :                 if (txt->fontname) gf_free(txt->fontname);
    5770          37 :                 txt->fontname = str ? gf_strdup(str) : NULL;
    5771          37 :                 JS_FreeCString(c, str);
    5772          37 :                 break;
    5773          37 :         case TXT_BASELINE:
    5774          37 :                 if (JS_ToInt32(c, &txt->baseline, value)) return JS_EXCEPTION;
    5775          37 :                 return JS_UNDEFINED;
    5776          47 :         case TXT_ALIGN:
    5777          47 :                 if (JS_ToInt32(c, &txt->align, value)) return JS_EXCEPTION;
    5778          47 :                 return JS_UNDEFINED;
    5779          42 :         case TXT_FONTSIZE:
    5780          42 :                 if (JS_ToFloat64(c, &txt->font_size, value)) return JS_EXCEPTION;
    5781             :                 break;
    5782           0 :         case TXT_HORIZ:
    5783           0 :                 txt->horizontal = JS_ToBool(c, value);
    5784           0 :                 break;
    5785           0 :         case TXT_FLIP:
    5786           0 :                 txt->flip = JS_ToBool(c, value);
    5787           0 :                 break;
    5788             : #define TOG_FLAG(_val) \
    5789             :                 if (JS_ToBool(c, value)) \
    5790             :                         txt->styles |= _val;\
    5791             :                 else\
    5792             :                         txt->styles &= ~_val;\
    5793             : 
    5794             : 
    5795           0 :         case TXT_UNDERLINED:
    5796           0 :                 TOG_FLAG(GF_FONT_UNDERLINED);
    5797             :                 break;
    5798           2 :         case TXT_ITALIC:
    5799           2 :                 TOG_FLAG(GF_FONT_ITALIC);
    5800             :                 break;
    5801           0 :         case TXT_BOLD:
    5802           0 :                 TOG_FLAG(GF_FONT_WEIGHT_100);
    5803             :                 break;
    5804           0 :         case TXT_STRIKEOUT:
    5805           0 :                 TOG_FLAG(GF_FONT_STRIKEOUT);
    5806             :                 break;
    5807           0 :         case TXT_SMALLCAP:
    5808           0 :                 TOG_FLAG(GF_FONT_SMALLCAPS);
    5809             :                 break;
    5810             : #undef TOG_FLAG
    5811             : 
    5812           0 :         case TXT_MAX_WIDTH:
    5813           0 :                 JS_ToFloat64(c, &txt->maxWidth, value);
    5814           0 :                 break;
    5815          37 :         case TXT_LINESPACING:
    5816          37 :                 JS_ToFloat64(c, &txt->lineSpacing, value);
    5817          37 :                 break;
    5818             :         }
    5819         118 :         return JS_UNDEFINED;
    5820             : }
    5821             : 
    5822           1 : static JSValue text_measure(JSContext *c, JSValueConst obj, int argc, JSValueConst *argv)
    5823             : {
    5824             :         JSValue res;
    5825           1 :         GF_JSText *txt = JS_GetOpaque(obj, text_class_id);
    5826           1 :         if (!txt) return JS_EXCEPTION;
    5827           1 :         res = JS_NewObject(c);
    5828           2 :         JS_SetPropertyStr(c, res, "width", JS_NewFloat64(c, txt->max_w) );
    5829           2 :         JS_SetPropertyStr(c, res, "height", JS_NewFloat64(c, txt->max_y-txt->min_y) );
    5830           1 :         if (txt->font) {
    5831           2 :                 JS_SetPropertyStr(c, res, "em_size", JS_NewInt32(c, txt->font->em_size) );
    5832           2 :                 JS_SetPropertyStr(c, res, "ascent", JS_NewInt32(c, txt->font->ascent) );
    5833           2 :                 JS_SetPropertyStr(c, res, "descent", JS_NewInt32(c, txt->font->descent) );
    5834           2 :                 JS_SetPropertyStr(c, res, "line_spacing", JS_NewInt32(c, txt->font->line_spacing) );
    5835           2 :                 JS_SetPropertyStr(c, res, "underlined", JS_NewInt32(c, txt->font->underline) );
    5836           2 :                 JS_SetPropertyStr(c, res, "baseline", JS_NewInt32(c, txt->font->baseline) );
    5837           2 :                 JS_SetPropertyStr(c, res, "max_advance_h", JS_NewInt32(c, txt->font->max_advance_h) );
    5838           2 :                 JS_SetPropertyStr(c, res, "max_advance_v", JS_NewInt32(c, txt->font->max_advance_v) );
    5839             :         }
    5840           1 :         return res;
    5841             : }
    5842             : 
    5843             : static const JSCFunctionListEntry text_funcs[] =
    5844             : {
    5845             :         JS_CGETSET_MAGIC_DEF("font", text_getProperty, text_setProperty, TXT_FONT),
    5846             :         JS_CGETSET_MAGIC_DEF("fontsize", text_getProperty, text_setProperty, TXT_FONTSIZE),
    5847             :         JS_CGETSET_MAGIC_DEF("align", text_getProperty, text_setProperty, TXT_ALIGN),
    5848             :         JS_CGETSET_MAGIC_DEF("baseline", text_getProperty, text_setProperty, TXT_BASELINE),
    5849             :         JS_CGETSET_MAGIC_DEF("horizontal", text_getProperty, text_setProperty, TXT_HORIZ),
    5850             :         JS_CGETSET_MAGIC_DEF("flip", text_getProperty, text_setProperty, TXT_FLIP),
    5851             :         JS_CGETSET_MAGIC_DEF("underline", text_getProperty, text_setProperty, TXT_UNDERLINED),
    5852             :         JS_CGETSET_MAGIC_DEF("bold", text_getProperty, text_setProperty, TXT_BOLD),
    5853             :         JS_CGETSET_MAGIC_DEF("italic", text_getProperty, text_setProperty, TXT_ITALIC),
    5854             :         JS_CGETSET_MAGIC_DEF("maxWidth", text_getProperty, text_setProperty, TXT_MAX_WIDTH),
    5855             :         JS_CGETSET_MAGIC_DEF("lineSpacing", text_getProperty, text_setProperty, TXT_LINESPACING),
    5856             :         JS_CFUNC_DEF("set_text", 0, text_set_text),
    5857             :         JS_CFUNC_DEF("measure", 0, text_measure),
    5858             :         JS_CFUNC_DEF("get_path", 0, text_get_path),
    5859             : };
    5860             : 
    5861             : JSClassDef text_class = {
    5862             :         "Text",
    5863             :         .finalizer = text_finalize
    5864             : };
    5865          37 : static JSValue text_constructor(JSContext *c, JSValueConst new_target, int argc, JSValueConst *argv)
    5866             : {
    5867             :         JSValue obj;
    5868             :         GF_JSText *txt;
    5869          37 :         GF_SAFEALLOC(txt, GF_JSText);
    5870          37 :         if (!txt)
    5871           0 :                 return js_throw_err(c, GF_OUT_OF_MEM);
    5872          37 :         txt->fm = jsf_get_font_manager(c);
    5873             : 
    5874          37 :         if (!txt->fm) {
    5875           0 :                 gf_free(txt);
    5876           0 :                 return js_throw_err_msg(c, GF_IO_ERR, "Failed to load font manager\n");
    5877             :         }
    5878          37 :         txt->spans = gf_list_new();
    5879          37 :         if (!txt->spans) {
    5880           0 :                 gf_free(txt);
    5881           0 :                 return JS_EXCEPTION;
    5882             :         }
    5883          37 :         if (argc) {
    5884             :                 const char *str = JS_ToCString(c, argv[0]);
    5885           0 :                 if (str) txt->fontname = gf_strdup(str);
    5886           0 :                 JS_FreeCString(c, str);
    5887             :         }
    5888          37 :         txt->font_size = 12.0;
    5889          37 :         txt->horizontal = GF_TRUE;
    5890          37 :         txt->align = TXT_AL_START;
    5891          37 :         txt->baseline = TXT_BL_ALPHABETIC;
    5892             : 
    5893          37 :         obj = JS_NewObjectClass(c, text_class_id);
    5894          37 :         if (JS_IsException(obj)) return obj;
    5895          37 :         JS_SetOpaque(obj, txt);
    5896          37 :         return obj;
    5897             : }
    5898             : 
    5899             : 
    5900             : 
    5901             : enum
    5902             : {
    5903             :         MX_PROP_IDENTITY=0,
    5904             :         MX_PROP_YAW,
    5905             :         MX_PROP_PITCH,
    5906             :         MX_PROP_ROLL,
    5907             :         MX_PROP_TRANLATE,
    5908             :         MX_PROP_SCALE,
    5909             :         MX_PROP_ROTATE,
    5910             :         MX_PROP_SHEAR,
    5911             :         MX_PROP_M,
    5912             : };
    5913             : 
    5914        1702 : static void mx_finalize(JSRuntime *rt, JSValue obj)
    5915             : {
    5916        1702 :         GF_Matrix *mx = JS_GetOpaque(obj, matrix_class_id);
    5917        1702 :         if (mx) gf_free(mx);
    5918        1702 : }
    5919             : 
    5920             : JSClassDef matrix_class = {
    5921             :         .class_name = "Matrix",
    5922             :         .finalizer = mx_finalize
    5923             : };
    5924             : 
    5925             : #define MAKEVEC(_v) \
    5926             :                 res = JS_NewObject(ctx);\
    5927             :                 JS_SetPropertyStr(ctx, res, "x", JS_NewFloat64(ctx, FIX2FLT(_v.x) ));\
    5928             :                 JS_SetPropertyStr(ctx, res, "y", JS_NewFloat64(ctx, FIX2FLT(_v.y) ));\
    5929             :                 JS_SetPropertyStr(ctx, res, "z", JS_NewFloat64(ctx, FIX2FLT(_v.z) ));\
    5930             :                 return res;\
    5931             : 
    5932             : #define MAKEVEC4(_v) \
    5933             :                 res = JS_NewObject(ctx);\
    5934             :                 JS_SetPropertyStr(ctx, res, "x", JS_NewFloat64(ctx, FIX2FLT(_v.x) ));\
    5935             :                 JS_SetPropertyStr(ctx, res, "y", JS_NewFloat64(ctx, FIX2FLT(_v.y) ));\
    5936             :                 JS_SetPropertyStr(ctx, res, "z", JS_NewFloat64(ctx, FIX2FLT(_v.z) ));\
    5937             :                 JS_SetPropertyStr(ctx, res, "w", JS_NewFloat64(ctx, FIX2FLT(_v.q) ));\
    5938             :                 return res;\
    5939             : 
    5940             : #define MAKERECT(_v) \
    5941             :                 res = JS_NewObject(ctx);\
    5942             :                 JS_SetPropertyStr(ctx, res, "x", JS_NewFloat64(ctx, FIX2FLT(_v.x) ));\
    5943             :                 JS_SetPropertyStr(ctx, res, "y", JS_NewFloat64(ctx, FIX2FLT(_v.y) ));\
    5944             :                 JS_SetPropertyStr(ctx, res, "width", JS_NewFloat64(ctx, FIX2FLT(_v.width) ));\
    5945             :                 JS_SetPropertyStr(ctx, res, "height", JS_NewFloat64(ctx, FIX2FLT(_v.height) ));\
    5946             :                 return res;\
    5947             : 
    5948        1737 : static JSValue mx_getProperty(JSContext *ctx, JSValueConst this_val, int magic)
    5949             : {
    5950             :         JSValue res;
    5951             :         Fixed yaw, pitch, roll;
    5952             :         GF_Vec tr, sc, sh;
    5953             :         GF_Vec4 ro;
    5954             :         u32 i;
    5955        1737 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    5956        1737 :         if (!mx) return JS_EXCEPTION;
    5957        1737 :         switch (magic) {
    5958           0 :         case MX_PROP_IDENTITY: return JS_NewBool(ctx, gf_mx2d_is_identity(*mx));
    5959           0 :         case MX_PROP_YAW:
    5960           0 :                 gf_mx_get_yaw_pitch_roll(mx, &yaw, &pitch, &roll);
    5961           0 :                 return JS_NewFloat64(ctx, FIX2FLT(yaw));
    5962           0 :         case MX_PROP_PITCH:
    5963           0 :                 gf_mx_get_yaw_pitch_roll(mx, &yaw, &pitch, &roll);
    5964           0 :                 return JS_NewFloat64(ctx, FIX2FLT(pitch));
    5965           0 :         case MX_PROP_ROLL:
    5966           0 :                 gf_mx_get_yaw_pitch_roll(mx, &yaw, &pitch, &roll);
    5967           0 :                 return JS_NewFloat64(ctx, FIX2FLT(roll));
    5968             : 
    5969           0 :         case MX_PROP_TRANLATE:
    5970           0 :                 gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
    5971           0 :                 MAKEVEC(tr)
    5972             : 
    5973           0 :         case MX_PROP_SCALE:
    5974           0 :                 gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
    5975           0 :                 MAKEVEC(sc)
    5976             : 
    5977           0 :         case MX_PROP_ROTATE:
    5978           0 :                 gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
    5979           0 :                 MAKEVEC4(ro)
    5980             : 
    5981           0 :         case MX_PROP_SHEAR:
    5982           0 :                 gf_mx_decompose(mx, &tr, &sc, &ro, &sh);
    5983           0 :                 MAKEVEC(sh)
    5984             : 
    5985        1737 :         case MX_PROP_M:
    5986        1737 :                 res = JS_NewArray(ctx);
    5987       29529 :                 for (i=0; i<16; i++)
    5988       55584 :                         JS_SetPropertyUint32(ctx, res, i, JS_NewFloat64(ctx, mx->m[i]));
    5989        1737 :                 return res;
    5990             :         }
    5991           0 :         return JS_UNDEFINED;
    5992             : }
    5993             : 
    5994         225 : static JSValue mx_setProperty(JSContext *ctx, JSValueConst this_val, JSValueConst value, int magic)
    5995             : {
    5996             :         JSValue v;
    5997             :         u32 i, len;
    5998         225 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    5999         225 :         if (!mx) return JS_EXCEPTION;
    6000             : 
    6001             : 
    6002         225 :         switch (magic) {
    6003         100 :         case MX_PROP_IDENTITY:
    6004         200 :                 gf_mx_init(*mx);
    6005         100 :                 break;
    6006         125 :         case MX_PROP_M:
    6007         125 :                 if (!JS_IsArray(ctx, value)) return JS_EXCEPTION;
    6008         125 :                 v = JS_GetPropertyStr(ctx, value, "length");
    6009         125 :                 len=0;
    6010         125 :                 JS_ToInt32(ctx, &len, v);
    6011             :                 JS_FreeValue(ctx, v);
    6012         125 :                 if (len != 16) return JS_EXCEPTION;
    6013        2000 :                 for (i=0; i<len; i++) {
    6014             :                         Double _d;
    6015        2000 :                         v = JS_GetPropertyUint32(ctx, value, i);
    6016        2000 :                         JS_ToFloat64(ctx, &_d, v);
    6017             :                         JS_FreeValue(ctx, v);
    6018        2000 :                         mx->m[i] = FLT2FIX(_d);
    6019             :                 }
    6020             :                 break;
    6021             :         }
    6022         225 :         return JS_UNDEFINED;
    6023             : }
    6024             : 
    6025             : 
    6026         250 : static JSValue mx_copy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6027             : {
    6028         250 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6029         250 :         if (!mx || !argc) return JS_EXCEPTION;
    6030         250 :         GF_Matrix *mx2 = JS_GetOpaque(argv[0], matrix_class_id);
    6031         250 :         if (!mx2) return JS_EXCEPTION;
    6032             :         gf_mx_copy(*mx, *mx2);
    6033             :         return JS_DupValue(ctx, this_val);
    6034             : }
    6035           1 : static JSValue mx_equal(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6036             : {
    6037           1 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6038           1 :         if (!mx || !argc) return JS_EXCEPTION;
    6039           1 :         GF_Matrix *mx2 = JS_GetOpaque(argv[0], matrix_class_id);
    6040           1 :         if (!mx2) return JS_EXCEPTION;
    6041           1 :         if (gf_mx_equal(mx, mx2)) return JS_TRUE;
    6042           1 :         return JS_FALSE;
    6043             : }
    6044             : 
    6045             : #define WGL_GET_VEC3(_x, _y, _z, _arg)\
    6046             : {\
    6047             :         JSValue v;\
    6048             :         v = JS_GetPropertyStr(ctx, _arg, "x");\
    6049             :         EVG_GET_FLOAT(_x, v);\
    6050             :         JS_FreeValue(ctx, v);\
    6051             :         v = JS_GetPropertyStr(ctx, _arg, "y");\
    6052             :         EVG_GET_FLOAT(_y, v);\
    6053             :         JS_FreeValue(ctx, v);\
    6054             :         v = JS_GetPropertyStr(ctx, _arg, "z");\
    6055             :         EVG_GET_FLOAT(_z, v);\
    6056             :         JS_FreeValue(ctx, v);\
    6057             : }
    6058             : 
    6059             : #define WGL_GET_VEC3F(_v, _arg)\
    6060             : {\
    6061             :         JSValue v;\
    6062             :         Double _f;\
    6063             :         v = JS_GetPropertyStr(ctx, _arg, "x");\
    6064             :         EVG_GET_FLOAT(_f, v);\
    6065             :         _v.x = FLT2FIX(_f);\
    6066             :         JS_FreeValue(ctx, v);\
    6067             :         v = JS_GetPropertyStr(ctx, _arg, "y");\
    6068             :         EVG_GET_FLOAT(_f, v);\
    6069             :         _v.y = FLT2FIX(_f);\
    6070             :         JS_FreeValue(ctx, v);\
    6071             :         v = JS_GetPropertyStr(ctx, _arg, "z");\
    6072             :         EVG_GET_FLOAT(_f, v);\
    6073             :         _v.z = FLT2FIX(_f);\
    6074             :         JS_FreeValue(ctx, v);\
    6075             : }
    6076             : 
    6077             : #define WGL_GET_VEC4(_x, _y, _z, _q, _arg)\
    6078             : {\
    6079             :         JSValue v;\
    6080             :         v = JS_GetPropertyStr(ctx, _arg, "x");\
    6081             :         EVG_GET_FLOAT(_x, v);\
    6082             :         JS_FreeValue(ctx, v);\
    6083             :         v = JS_GetPropertyStr(ctx, _arg, "y");\
    6084             :         EVG_GET_FLOAT(_y, v);\
    6085             :         JS_FreeValue(ctx, v);\
    6086             :         v = JS_GetPropertyStr(ctx, _arg, "z");\
    6087             :         EVG_GET_FLOAT(_z, v);\
    6088             :         JS_FreeValue(ctx, v);\
    6089             :         v = JS_GetPropertyStr(ctx, _arg, "w");\
    6090             :         if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, _arg, "q");\
    6091             :         if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, _arg, "angle");\
    6092             :         EVG_GET_FLOAT(_q, v);\
    6093             :         JS_FreeValue(ctx, v);\
    6094             : }
    6095             : 
    6096             : 
    6097         931 : static JSValue mx_translate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6098             : {
    6099             :         Double vx, vy, vz;
    6100         931 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6101         931 :         if (!mx || !argc) return JS_EXCEPTION;
    6102        1862 :         if (!JS_IsObject(argv[0])) {
    6103         931 :                 if (argc<3) return JS_EXCEPTION;
    6104         931 :                 EVG_GET_FLOAT(vx, argv[0])
    6105         931 :                 EVG_GET_FLOAT(vy, argv[1])
    6106         931 :                 EVG_GET_FLOAT(vz, argv[2])
    6107             :         } else {
    6108           0 :                 WGL_GET_VEC3(vx, vy, vz, argv[0])
    6109             :         }
    6110         931 :         gf_mx_add_translation(mx, FLT2FIX(vx), FLT2FIX(vy), FLT2FIX(vz));
    6111             :         return JS_DupValue(ctx, this_val);
    6112             : }
    6113           1 : static JSValue mx_scale(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6114             : {
    6115             :         Double vx, vy, vz;
    6116           1 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6117           1 :         if (!mx || !argc) return JS_EXCEPTION;
    6118           2 :         if (!JS_IsObject(argv[0])) {
    6119           1 :                 if (argc<3) return JS_EXCEPTION;
    6120           1 :                 EVG_GET_FLOAT(vx, argv[0])
    6121           1 :                 EVG_GET_FLOAT(vy, argv[1])
    6122           1 :                 EVG_GET_FLOAT(vz, argv[2])
    6123             :         } else {
    6124           0 :                 WGL_GET_VEC3(vx, vy, vz, argv[0])
    6125             :         }
    6126           1 :         gf_mx_add_scale(mx, FLT2FIX(vx), FLT2FIX(vy), FLT2FIX(vz));
    6127             :         return JS_DupValue(ctx, this_val);
    6128             : }
    6129         931 : static JSValue mx_rotate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6130             : {
    6131             :         Double vx, vy, vz, angle;
    6132         931 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6133         931 :         if (!mx || !argc) return JS_EXCEPTION;
    6134        1862 :         if (!JS_IsObject(argv[0])) {
    6135         931 :                 if (argc<4) return JS_EXCEPTION;
    6136         931 :                 EVG_GET_FLOAT(vx, argv[0])
    6137         931 :                 EVG_GET_FLOAT(vy, argv[1])
    6138         931 :                 EVG_GET_FLOAT(vz, argv[2])
    6139         931 :                 EVG_GET_FLOAT(angle, argv[3])
    6140             :         } else {
    6141           0 :                 WGL_GET_VEC4(vx, vy, vz, angle, argv[0])
    6142             :         }
    6143         931 :         gf_mx_add_rotation(mx, FLT2FIX(angle), FLT2FIX(vx), FLT2FIX(vy), FLT2FIX(vz));
    6144             :         return JS_DupValue(ctx, this_val);
    6145             : }
    6146         126 : static JSValue mx_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6147             : {
    6148         126 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6149         126 :         if (!mx || !argc) return JS_EXCEPTION;
    6150         126 :         GF_Matrix *mx2 = JS_GetOpaque(argv[0], matrix_class_id);
    6151         126 :         if (!mx2) return JS_EXCEPTION;
    6152         126 :         if ((argc>1) && JS_ToBool(ctx, argv[1])) {
    6153         125 :                 gf_mx_add_matrix_4x4(mx, mx2);
    6154             :         } else {
    6155           1 :                 gf_mx_add_matrix(mx, mx2);
    6156             :         }
    6157             :         return JS_DupValue(ctx, this_val);
    6158             : }
    6159             : 
    6160         125 : static JSValue mx_inverse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6161             : {
    6162         125 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6163         125 :         if (!mx) return JS_EXCEPTION;
    6164         125 :         if (argc && JS_ToBool(ctx, argv[0])) {
    6165           0 :                 gf_mx_inverse_4x4(mx);
    6166             :         } else {
    6167         125 :                 gf_mx_inverse(mx);
    6168             :         }
    6169             :         return JS_DupValue(ctx, this_val);
    6170             : }
    6171             : 
    6172         125 : static JSValue mx_transpose(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6173             : {
    6174         125 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6175         125 :         if (!mx) return JS_EXCEPTION;
    6176         125 :         gf_mx_transpose(mx);
    6177             :         return JS_DupValue(ctx, this_val);
    6178             : }
    6179             : 
    6180           1 : static JSValue mx_apply(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6181             : {
    6182             :         Fixed width, height, x, y;
    6183             :         GF_Vec pt;
    6184             :         GF_Vec4 pt4;
    6185             :         JSValue v, res;
    6186           1 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6187           1 :         if (!mx || !argc) return JS_EXCEPTION;
    6188           2 :         if (!JS_IsObject(argv[0])) return JS_EXCEPTION;
    6189             : 
    6190             :         /*try rect*/
    6191           1 :         v = JS_GetPropertyStr(ctx, argv[0], "width");
    6192           1 :         if (!JS_IsUndefined(v)) {
    6193             :                 GF_Rect rc;
    6194           0 :                 EVG_GET_FLOAT(width, v);
    6195             :                 JS_FreeValue(ctx, v);
    6196           0 :                 v = JS_GetPropertyStr(ctx, argv[0], "height");
    6197           0 :                 EVG_GET_FLOAT(height, v);
    6198             :                 JS_FreeValue(ctx, v);
    6199           0 :                 v = JS_GetPropertyStr(ctx, argv[0], "x");
    6200           0 :                 EVG_GET_FLOAT(x, v);
    6201             :                 JS_FreeValue(ctx, v);
    6202           0 :                 v = JS_GetPropertyStr(ctx, argv[0], "y");
    6203           0 :                 EVG_GET_FLOAT(y, v);
    6204             :                 JS_FreeValue(ctx, v);
    6205           0 :                 rc.x = FLT2FIX(x);
    6206           0 :                 rc.y = FLT2FIX(y);
    6207           0 :                 rc.width = FLT2FIX(width);
    6208           0 :                 rc.height = FLT2FIX(height);
    6209           0 :                 gf_mx_apply_rect(mx, &rc);
    6210           0 :                 MAKERECT(rc)
    6211             :         }
    6212             :         JS_FreeValue(ctx, v);
    6213             : 
    6214           1 :         v = JS_GetPropertyStr(ctx, argv[0], "q");
    6215           1 :         if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, argv[0], "w");
    6216           1 :         if (JS_IsUndefined(v)) v = JS_GetPropertyStr(ctx, argv[0], "angle");
    6217           1 :         if (!JS_IsUndefined(v)) {
    6218             :                 Double _v;
    6219           0 :                 if (JS_ToFloat64(ctx, &_v, v)) return JS_EXCEPTION;
    6220           0 :                 pt4.q = FLT2FIX(_v);
    6221             : 
    6222           0 :                 WGL_GET_VEC3F(pt4, argv[0])
    6223           0 :                 gf_mx_apply_vec_4x4(mx, &pt4);
    6224           0 :                 MAKEVEC4(pt4)
    6225             :         }
    6226             :         JS_FreeValue(ctx, v);
    6227             : 
    6228             :         /*try bbox ?*/
    6229             : 
    6230             :         /*try vec*/
    6231           3 :         WGL_GET_VEC3F(pt, argv[0])
    6232           1 :         gf_mx_apply_vec(mx, &pt);
    6233           4 :         MAKEVEC(pt)
    6234             : 
    6235             : /*
    6236             : 
    6237             : void gf_mx_apply_vec(GF_Matrix *mx, GF_Vec *pt);
    6238             : void gf_mx_apply_rect(GF_Matrix *_this, GF_Rect *rc);
    6239             : void gf_mx_apply_bbox(GF_Matrix *mx, GF_BBox *b);
    6240             : void gf_mx_apply_bbox_sphere(GF_Matrix *mx, GF_BBox *box);
    6241             : void gf_mx_apply_vec_4x4(GF_Matrix *mx, GF_Vec4 *vec);
    6242             : */
    6243             :         return JS_UNDEFINED;
    6244             : }
    6245             : 
    6246           1 : static JSValue mx_ortho(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6247             : {
    6248             :         Double left, right, bottom, top, z_near, z_far;
    6249           1 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6250           1 :         if (!mx || (argc<6)) return JS_EXCEPTION;
    6251           1 :         EVG_GET_FLOAT(left, argv[0])
    6252           1 :         EVG_GET_FLOAT(right, argv[1])
    6253           1 :         EVG_GET_FLOAT(bottom, argv[2])
    6254           1 :         EVG_GET_FLOAT(top, argv[3])
    6255           1 :         EVG_GET_FLOAT(z_near, argv[4])
    6256           1 :         EVG_GET_FLOAT(z_far, argv[5])
    6257           1 :         if ((argc>6) && JS_ToBool(ctx, argv[6])) {
    6258           0 :                 gf_mx_ortho_reverse_z(mx, FLT2FIX(left), FLT2FIX(right), FLT2FIX(bottom), FLT2FIX(top), FLT2FIX(z_near), FLT2FIX(z_far));
    6259             :         } else {
    6260           1 :                 gf_mx_ortho(mx, FLT2FIX(left), FLT2FIX(right), FLT2FIX(bottom), FLT2FIX(top), FLT2FIX(z_near), FLT2FIX(z_far));
    6261             :         }
    6262             :         return JS_DupValue(ctx, this_val);
    6263             : }
    6264         832 : static JSValue mx_perspective(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6265             : {
    6266             :         Double fov, ar, z_near, z_far;
    6267         832 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6268         832 :         if (!mx || (argc<4)) return JS_EXCEPTION;
    6269         832 :         EVG_GET_FLOAT(fov, argv[0])
    6270         832 :         EVG_GET_FLOAT(ar, argv[1])
    6271         832 :         EVG_GET_FLOAT(z_near, argv[2])
    6272         832 :         EVG_GET_FLOAT(z_far, argv[3])
    6273         832 :         if ((argc>4) && JS_ToBool(ctx, argv[4])) {
    6274           0 :                 gf_mx_perspective_reverse_z(mx, FLT2FIX(fov), FLT2FIX(ar), FLT2FIX(z_near), FLT2FIX(z_far));
    6275             :         } else {
    6276         832 :                 gf_mx_perspective(mx, FLT2FIX(fov), FLT2FIX(ar), FLT2FIX(z_near), FLT2FIX(z_far));
    6277             :         }
    6278             :         return JS_DupValue(ctx, this_val);
    6279             : }
    6280           2 : static JSValue mx_lookat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6281             : {
    6282             :         GF_Vec pos, target, up;
    6283           2 :         GF_Matrix *mx = JS_GetOpaque(this_val, matrix_class_id);
    6284           2 :         if (!mx || (argc!=3) ) return JS_EXCEPTION;
    6285             : 
    6286           6 :         WGL_GET_VEC3F(pos, argv[0]);
    6287           6 :         WGL_GET_VEC3F(target, argv[1]);
    6288           6 :         WGL_GET_VEC3F(up, argv[2]);
    6289             : 
    6290           2 :         gf_mx_lookat(mx, pos, target, up);
    6291             :         return JS_DupValue(ctx, this_val);
    6292             : }
    6293             : 
    6294             : static const JSCFunctionListEntry mx_funcs[] =
    6295             : {
    6296             :         JS_CGETSET_MAGIC_DEF("identity", mx_getProperty, mx_setProperty, MX_PROP_IDENTITY),
    6297             :         JS_CGETSET_MAGIC_DEF("m", mx_getProperty, mx_setProperty, MX_PROP_M),
    6298             :         JS_CGETSET_MAGIC_DEF("yaw", mx_getProperty, NULL, MX_PROP_YAW),
    6299             :         JS_CGETSET_MAGIC_DEF("pitch", mx_getProperty, NULL, MX_PROP_PITCH),
    6300             :         JS_CGETSET_MAGIC_DEF("roll", mx_getProperty, NULL, MX_PROP_ROLL),
    6301             :         JS_CGETSET_MAGIC_DEF("dec_translate", mx_getProperty, NULL, MX_PROP_TRANLATE),
    6302             :         JS_CGETSET_MAGIC_DEF("dec_scale", mx_getProperty, NULL, MX_PROP_SCALE),
    6303             :         JS_CGETSET_MAGIC_DEF("dec_rotate", mx_getProperty, NULL, MX_PROP_ROTATE),
    6304             :         JS_CGETSET_MAGIC_DEF("dec_shear", mx_getProperty, NULL, MX_PROP_SHEAR),
    6305             :         JS_CFUNC_DEF("copy", 0, mx_copy),
    6306             :         JS_CFUNC_DEF("equal", 0, mx_equal),
    6307             :         JS_CFUNC_DEF("translate", 0, mx_translate),
    6308             :         JS_CFUNC_DEF("scale", 0, mx_scale),
    6309             :         JS_CFUNC_DEF("rotate", 0, mx_rotate),
    6310             :         JS_CFUNC_DEF("add", 0, mx_add),
    6311             :         JS_CFUNC_DEF("inverse", 0, mx_inverse),
    6312             :         JS_CFUNC_DEF("transpose", 0, mx_transpose),
    6313             :         JS_CFUNC_DEF("apply", 0, mx_apply),
    6314             :         JS_CFUNC_DEF("ortho", 0, mx_ortho),
    6315             :         JS_CFUNC_DEF("perspective", 0, mx_perspective),
    6316             :         JS_CFUNC_DEF("lookat", 0, mx_lookat),
    6317             : };
    6318             : /*
    6319             : void gf_mx_rotate_vector(GF_Matrix *mx, GF_Vec *pt);
    6320             : */
    6321             : 
    6322        1702 : static JSValue mx_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
    6323             : {
    6324             :         JSValue res;
    6325             :         GF_Matrix *mx;
    6326        1702 :         GF_SAFEALLOC(mx, GF_Matrix);
    6327        1702 :         if (!mx)
    6328           0 :                 return js_throw_err(ctx, GF_OUT_OF_MEM);
    6329        3404 :         gf_mx_init(*mx);
    6330        1702 :         res = JS_NewObjectClass(ctx, matrix_class_id);
    6331        1702 :         JS_SetOpaque(res, mx);
    6332        1702 :         if (argc) {
    6333           0 :                 GF_Matrix *from = JS_GetOpaque(argv[0], matrix_class_id);
    6334           0 :                 if (from) {
    6335             :                         gf_mx_copy(*mx, *from);
    6336           0 :                 } else if (argc>=3) {
    6337             :                         GF_Vec x_axis, y_axis, z_axis;
    6338           0 :                         WGL_GET_VEC3F(x_axis, argv[0])
    6339           0 :                         WGL_GET_VEC3F(y_axis, argv[1])
    6340           0 :                         WGL_GET_VEC3F(z_axis, argv[2])
    6341           0 :                         gf_mx_rotation_matrix_from_vectors(mx, x_axis, y_axis,z_axis);
    6342             :                 }
    6343             :         }
    6344        1702 :         return res;
    6345             : }
    6346             : 
    6347             : 
    6348          15 : static JSValue evg_pixel_size(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
    6349             : {
    6350          15 :         u32 pfmt=0;
    6351          15 :         if (!argc) return js_throw_err_msg(ctx, GF_BAD_PARAM, "missing pixel format parameter");
    6352          30 :         if (JS_IsString(argv[0])) {
    6353             :                 const char *s = JS_ToCString(ctx, argv[0]);
    6354          15 :                 if (s) {
    6355          15 :                         pfmt = gf_pixel_fmt_parse(s);
    6356          15 :                         JS_FreeCString(ctx, s);
    6357             :                 }
    6358           0 :         } else if (JS_IsNumber(argv[0])) {
    6359           0 :                 JS_ToInt32(ctx, &pfmt, argv[0]);
    6360             :         }
    6361          15 :         if (!pfmt) return js_throw_err_msg(ctx, GF_BAD_PARAM, "missing pixel format parameter");
    6362          15 :         return JS_NewInt32(ctx, gf_pixel_get_bytes_per_pixel(pfmt));
    6363             : }
    6364             : 
    6365             : 
    6366          58 : static int js_evg_load_module(JSContext *c, JSModuleDef *m)
    6367             : {
    6368             :         JSValue ctor;
    6369             :         JSValue proto;
    6370             :         JSValue global;
    6371             : 
    6372          58 :         if (!canvas_class_id) {
    6373          58 :                 JSRuntime *rt = JS_GetRuntime(c);
    6374             : 
    6375          58 :                 JS_NewClassID(&canvas_class_id);
    6376          58 :                 JS_NewClass(rt, canvas_class_id, &canvas_class);
    6377             : 
    6378          58 :                 JS_NewClassID(&path_class_id);
    6379          58 :                 JS_NewClass(rt, path_class_id, &path_class);
    6380             : 
    6381          58 :                 JS_NewClassID(&mx2d_class_id);
    6382          58 :                 JS_NewClass(rt, mx2d_class_id, &mx2d_class);
    6383             : 
    6384          58 :                 JS_NewClassID(&colmx_class_id);
    6385          58 :                 JS_NewClass(rt, colmx_class_id, &colmx_class);
    6386             : 
    6387          58 :                 JS_NewClassID(&stencil_class_id);
    6388          58 :                 JS_NewClass(rt, stencil_class_id, &stencil_class);
    6389             : 
    6390          58 :                 JS_NewClassID(&texture_class_id);
    6391          58 :                 JS_NewClass(rt, texture_class_id, &texture_class);
    6392             : 
    6393          58 :                 JS_NewClassID(&text_class_id);
    6394          58 :                 JS_NewClass(rt, text_class_id, &text_class);
    6395             : 
    6396          58 :                 JS_NewClassID(&matrix_class_id);
    6397          58 :                 JS_NewClass(rt, matrix_class_id, &matrix_class);
    6398             : 
    6399          58 :                 JS_NewClassID(&canvas3d_class_id);
    6400          58 :                 JS_NewClass(rt, canvas3d_class_id, &canvas3d_class);
    6401             : 
    6402          58 :                 JS_NewClassID(&shader_class_id);
    6403          58 :                 JS_NewClass(rt, shader_class_id, &shader_class);
    6404             : 
    6405          58 :                 JS_NewClassID(&vai_class_id);
    6406          58 :                 JS_NewClass(rt, vai_class_id, &vai_class);
    6407             : 
    6408          58 :                 JS_NewClassID(&va_class_id);
    6409          58 :                 JS_NewClass(rt, va_class_id, &va_class);
    6410             : 
    6411             : #ifdef EVG_USE_JS_SHADER
    6412             :                 JS_NewClassID(&fragment_class_id);
    6413             :                 JS_NewClass(rt, fragment_class_id, &fragment_class);
    6414             : 
    6415             :                 JS_NewClassID(&vertex_class_id);
    6416             :                 JS_NewClass(rt, vertex_class_id, &vertex_class);
    6417             : 
    6418             :                 JS_NewClassID(&vaires_class_id);
    6419             :                 JS_NewClass(rt, vaires_class_id, &vaires_class);
    6420             : #endif// EVG_USE_JS_SHADER
    6421             : 
    6422             :         }
    6423          58 :         proto = JS_NewObject(c);
    6424          58 :     JS_SetPropertyFunctionList(c, proto, canvas_funcs, countof(canvas_funcs));
    6425          58 :     JS_SetClassProto(c, canvas_class_id, proto);
    6426             : 
    6427          58 :         proto = JS_NewObject(c);
    6428          58 :     JS_SetPropertyFunctionList(c, proto, path_funcs, countof(path_funcs));
    6429          58 :     JS_SetClassProto(c, path_class_id, proto);
    6430             : 
    6431          58 :         proto = JS_NewObject(c);
    6432          58 :     JS_SetPropertyFunctionList(c, proto, mx2d_funcs, countof(mx2d_funcs));
    6433          58 :     JS_SetClassProto(c, mx2d_class_id, proto);
    6434             : 
    6435          58 :         proto = JS_NewObject(c);
    6436          58 :     JS_SetPropertyFunctionList(c, proto, colmx_funcs, countof(colmx_funcs));
    6437          58 :     JS_SetClassProto(c, colmx_class_id, proto);
    6438             : 
    6439          58 :         proto = JS_NewObject(c);
    6440          58 :     JS_SetPropertyFunctionList(c, proto, stencil_funcs, countof(stencil_funcs));
    6441          58 :     JS_SetClassProto(c, stencil_class_id, proto);
    6442             : 
    6443          58 :         proto = JS_NewObject(c);
    6444          58 :     JS_SetPropertyFunctionList(c, proto, texture_funcs, countof(texture_funcs));
    6445          58 :     JS_SetClassProto(c, texture_class_id, proto);
    6446             : 
    6447          58 :         proto = JS_NewObject(c);
    6448          58 :     JS_SetPropertyFunctionList(c, proto, text_funcs, countof(text_funcs));
    6449          58 :     JS_SetClassProto(c, text_class_id, proto);
    6450             : 
    6451          58 :         proto = JS_NewObject(c);
    6452          58 :     JS_SetPropertyFunctionList(c, proto, mx_funcs, countof(mx_funcs));
    6453          58 :     JS_SetClassProto(c, matrix_class_id, proto);
    6454             : 
    6455          58 :         proto = JS_NewObject(c);
    6456          58 :     JS_SetPropertyFunctionList(c, proto, canvas3d_funcs, countof(canvas3d_funcs));
    6457          58 :     JS_SetClassProto(c, canvas3d_class_id, proto);
    6458             : 
    6459             : #ifdef EVG_USE_JS_SHADER
    6460             :         proto = JS_NewObject(c);
    6461             :     JS_SetPropertyFunctionList(c, proto, fragment_funcs, countof(fragment_funcs));
    6462             :     JS_SetClassProto(c, fragment_class_id, proto);
    6463             : 
    6464             :         proto = JS_NewObject(c);
    6465             :     JS_SetPropertyFunctionList(c, proto, vertex_funcs, countof(vertex_funcs));
    6466             :     JS_SetClassProto(c, vertex_class_id, proto);
    6467             : 
    6468             :         proto = JS_NewObject(c);
    6469             :         JS_SetPropertyFunctionList(c, proto, vaires_funcs, countof(vaires_funcs));
    6470             :         JS_SetClassProto(c, vaires_class_id, proto);
    6471             : #endif
    6472             : 
    6473          58 :         proto = JS_NewObject(c);
    6474          58 :     JS_SetPropertyFunctionList(c, proto, shader_funcs, countof(shader_funcs));
    6475          58 :     JS_SetClassProto(c, shader_class_id, proto);
    6476             : 
    6477          58 :         proto = JS_NewObject(c);
    6478          58 :     JS_SetPropertyFunctionList(c, proto, vai_funcs, countof(vai_funcs));
    6479          58 :     JS_SetClassProto(c, vai_class_id, proto);
    6480             : 
    6481          58 :         proto = JS_NewObject(c);
    6482          58 :     JS_SetPropertyFunctionList(c, proto, va_funcs, countof(va_funcs));
    6483          58 :     JS_SetClassProto(c, va_class_id, proto);
    6484             : 
    6485             : 
    6486          58 :         global = JS_GetGlobalObject(c);
    6487          58 :         JS_SetPropertyStr(c, global, "GF_GRADIENT_MODE_PAD", JS_NewInt32(c, GF_GRADIENT_MODE_PAD));
    6488          58 :         JS_SetPropertyStr(c, global, "GF_GRADIENT_MODE_STREAD", JS_NewInt32(c, GF_GRADIENT_MODE_SPREAD));
    6489          58 :         JS_SetPropertyStr(c, global, "GF_GRADIENT_MODE_REPEAT", JS_NewInt32(c, GF_GRADIENT_MODE_REPEAT));
    6490             : 
    6491          58 :         JS_SetPropertyStr(c, global, "GF_TEXTURE_FILTER_HIGH_SPEED", JS_NewInt32(c, GF_TEXTURE_FILTER_HIGH_SPEED));
    6492          58 :         JS_SetPropertyStr(c, global, "GF_TEXTURE_FILTER_MID", JS_NewInt32(c, GF_TEXTURE_FILTER_MID));
    6493          58 :         JS_SetPropertyStr(c, global, "GF_TEXTURE_FILTER_HIGH_QUALITY", JS_NewInt32(c, GF_TEXTURE_FILTER_HIGH_QUALITY));
    6494             : 
    6495          58 :         JS_SetPropertyStr(c, global, "GF_PATH2D_ARC_OPEN", JS_NewInt32(c, GF_PATH2D_ARC_OPEN));
    6496          58 :         JS_SetPropertyStr(c, global, "GF_PATH2D_ARC_OPEN", JS_NewInt32(c, GF_PATH2D_ARC_OPEN));
    6497          58 :         JS_SetPropertyStr(c, global, "GF_PATH2D_ARC_PIE", JS_NewInt32(c, GF_PATH2D_ARC_PIE));
    6498             : 
    6499          58 :         JS_SetPropertyStr(c, global, "GF_PATH_LINE_CENTER", JS_NewInt32(c, GF_PATH_LINE_CENTER));
    6500          58 :         JS_SetPropertyStr(c, global, "GF_PATH_LINE_INSIDE", JS_NewInt32(c, GF_PATH_LINE_INSIDE));
    6501          58 :         JS_SetPropertyStr(c, global, "GF_PATH_LINE_OUTSIDE", JS_NewInt32(c, GF_PATH_LINE_OUTSIDE));
    6502          58 :         JS_SetPropertyStr(c, global, "GF_LINE_CAP_FLAT", JS_NewInt32(c, GF_LINE_CAP_FLAT));
    6503          58 :         JS_SetPropertyStr(c, global, "GF_LINE_CAP_ROUND", JS_NewInt32(c, GF_LINE_CAP_ROUND));
    6504          58 :         JS_SetPropertyStr(c, global, "GF_LINE_CAP_SQUARE", JS_NewInt32(c, GF_LINE_CAP_SQUARE));
    6505          58 :         JS_SetPropertyStr(c, global, "GF_LINE_CAP_TRIANGLE", JS_NewInt32(c, GF_LINE_CAP_TRIANGLE));
    6506          58 :         JS_SetPropertyStr(c, global, "GF_LINE_JOIN_MITER", JS_NewInt32(c, GF_LINE_JOIN_MITER));
    6507          58 :         JS_SetPropertyStr(c, global, "GF_LINE_JOIN_ROUND", JS_NewInt32(c, GF_LINE_JOIN_ROUND));
    6508          58 :         JS_SetPropertyStr(c, global, "GF_LINE_JOIN_BEVEL", JS_NewInt32(c, GF_LINE_JOIN_BEVEL));
    6509          58 :         JS_SetPropertyStr(c, global, "GF_LINE_JOIN_MITER_SVG", JS_NewInt32(c, GF_LINE_JOIN_MITER_SVG));
    6510          58 :         JS_SetPropertyStr(c, global, "GF_DASH_STYLE_PLAIN", JS_NewInt32(c, GF_DASH_STYLE_PLAIN));
    6511          58 :         JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH", JS_NewInt32(c, GF_DASH_STYLE_DASH));
    6512          58 :         JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DOT", JS_NewInt32(c, GF_DASH_STYLE_DOT));
    6513          58 :         JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH_DOT", JS_NewInt32(c, GF_DASH_STYLE_DASH_DOT));
    6514          58 :         JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH_DASH_DOT", JS_NewInt32(c, GF_DASH_STYLE_DASH_DASH_DOT));
    6515          58 :         JS_SetPropertyStr(c, global, "GF_DASH_STYLE_DASH_DOT_DOT", JS_NewInt32(c, GF_DASH_STYLE_DASH_DOT_DOT));
    6516          58 :         JS_SetPropertyStr(c, global, "GF_DASH_STYLE_SVG", JS_NewInt32(c, GF_DASH_STYLE_SVG));
    6517             : 
    6518          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_TOP", JS_NewInt32(c, TXT_BL_TOP));
    6519          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_HANGING", JS_NewInt32(c, TXT_BL_HANGING));
    6520          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_MIDDLE", JS_NewInt32(c, TXT_BL_MIDDLE));
    6521          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_ALPHABETIC", JS_NewInt32(c, TXT_BL_ALPHABETIC));
    6522          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_IDEOGRAPHIC", JS_NewInt32(c, TXT_BL_IDEOGRAPHIC));
    6523          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_BASELINE_BOTTOM", JS_NewInt32(c, TXT_BL_BOTTOM));
    6524             : 
    6525          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_START", JS_NewInt32(c, TXT_AL_START));
    6526          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_END", JS_NewInt32(c, TXT_AL_END));
    6527          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_LEFT", JS_NewInt32(c, TXT_AL_LEFT));
    6528          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_RIGHT", JS_NewInt32(c, TXT_AL_RIGHT));
    6529          58 :         JS_SetPropertyStr(c, global, "GF_TEXT_ALIGN_CENTER", JS_NewInt32(c, TXT_AL_CENTER));
    6530             : 
    6531          58 :         JS_SetPropertyStr(c, global, "GF_EVG_SRC_ATOP", JS_NewInt32(c, GF_EVG_SRC_ATOP));
    6532          58 :         JS_SetPropertyStr(c, global, "GF_EVG_SRC_IN", JS_NewInt32(c, GF_EVG_SRC_IN));
    6533          58 :         JS_SetPropertyStr(c, global, "GF_EVG_SRC_OUT", JS_NewInt32(c, GF_EVG_SRC_OUT));
    6534          58 :         JS_SetPropertyStr(c, global, "GF_EVG_SRC_OVER", JS_NewInt32(c, GF_EVG_SRC_OVER));
    6535          58 :         JS_SetPropertyStr(c, global, "GF_EVG_DST_ATOP", JS_NewInt32(c, GF_EVG_DST_ATOP));
    6536          58 :         JS_SetPropertyStr(c, global, "GF_EVG_DST_IN", JS_NewInt32(c, GF_EVG_DST_IN));
    6537          58 :         JS_SetPropertyStr(c, global, "GF_EVG_DST_OUT", JS_NewInt32(c, GF_EVG_DST_OUT));
    6538          58 :         JS_SetPropertyStr(c, global, "GF_EVG_DST_OVER", JS_NewInt32(c, GF_EVG_DST_OVER));
    6539          58 :         JS_SetPropertyStr(c, global, "GF_EVG_LIGHTER", JS_NewInt32(c, GF_EVG_LIGHTER));
    6540          58 :         JS_SetPropertyStr(c, global, "GF_EVG_COPY", JS_NewInt32(c, GF_EVG_COPY));
    6541          58 :         JS_SetPropertyStr(c, global, "GF_EVG_XOR", JS_NewInt32(c, GF_EVG_XOR));
    6542             : 
    6543          58 :         JS_SetPropertyStr(c, global, "GF_EVG_POINTS", JS_NewInt32(c, GF_EVG_POINTS));
    6544          58 :         JS_SetPropertyStr(c, global, "GF_EVG_POLYGON", JS_NewInt32(c, GF_EVG_POLYGON));
    6545          58 :         JS_SetPropertyStr(c, global, "GF_EVG_LINES", JS_NewInt32(c, GF_EVG_LINES));
    6546          58 :         JS_SetPropertyStr(c, global, "GF_EVG_TRIANGLES", JS_NewInt32(c, GF_EVG_TRIANGLES));
    6547          58 :         JS_SetPropertyStr(c, global, "GF_EVG_QUADS", JS_NewInt32(c, GF_EVG_QUADS));
    6548          58 :         JS_SetPropertyStr(c, global, "GF_EVG_LINE_STRIP", JS_NewInt32(c, GF_EVG_LINE_STRIP));
    6549          58 :         JS_SetPropertyStr(c, global, "GF_EVG_TRIANGLE_STRIP", JS_NewInt32(c, GF_EVG_TRIANGLE_STRIP));
    6550          58 :         JS_SetPropertyStr(c, global, "GF_EVG_TRIANGLE_FAN", JS_NewInt32(c, GF_EVG_TRIANGLE_FAN));
    6551             : 
    6552          58 :         JS_SetPropertyStr(c, global, "GF_EVG_SHADER_FRAGMENT", JS_NewInt32(c, GF_EVG_SHADER_FRAGMENT));
    6553          58 :         JS_SetPropertyStr(c, global, "GF_EVG_SHADER_VERTEX", JS_NewInt32(c, GF_EVG_SHADER_VERTEX));
    6554             : 
    6555          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEVER", JS_NewInt32(c, GF_EVGDEPTH_NEVER));
    6556          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_ALWAYS", JS_NewInt32(c, GF_EVGDEPTH_ALWAYS));
    6557          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_ALWAYS", JS_NewInt32(c, GF_EVGDEPTH_ALWAYS));
    6558          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_EQUAL));
    6559          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEQUAL", JS_NewInt32(c, GF_EVGDEPTH_NEQUAL));
    6560          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS", JS_NewInt32(c, GF_EVGDEPTH_LESS));
    6561          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_LESS_EQUAL));
    6562          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER", JS_NewInt32(c, GF_EVGDEPTH_GREATER));
    6563          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_GREATER_EQUAL));
    6564             : 
    6565          58 :         JS_SetPropertyStr(c, global, "GF_EVG_VAI_VERTEX_INDEX", JS_NewInt32(c, GF_EVG_VAI_VERTEX_INDEX));
    6566          58 :         JS_SetPropertyStr(c, global, "GF_EVG_VAI_VERTEX", JS_NewInt32(c, GF_EVG_VAI_VERTEX));
    6567          58 :         JS_SetPropertyStr(c, global, "GF_EVG_VAI_PRIMITIVE", JS_NewInt32(c, GF_EVG_VAI_PRIMITIVE));
    6568             : 
    6569          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_DISABLE", JS_NewInt32(c, GF_EVGDEPTH_DISABLE));
    6570          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEVER", JS_NewInt32(c, GF_EVGDEPTH_NEVER));
    6571          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_ALWAYS", JS_NewInt32(c, GF_EVGDEPTH_ALWAYS));
    6572          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_EQUAL));
    6573          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_NEQUAL", JS_NewInt32(c, GF_EVGDEPTH_NEQUAL));
    6574          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS", JS_NewInt32(c, GF_EVGDEPTH_LESS));
    6575          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_LESS_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_LESS_EQUAL));
    6576          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER", JS_NewInt32(c, GF_EVGDEPTH_GREATER));
    6577          58 :         JS_SetPropertyStr(c, global, "GF_EVGDEPTH_GREATER_EQUAL", JS_NewInt32(c, GF_EVGDEPTH_GREATER_EQUAL));
    6578             : 
    6579             :         JS_FreeValue(c, global);
    6580             : 
    6581             : 
    6582             :         /*export constructors*/
    6583          58 :         ctor = JS_NewCFunction2(c, canvas_constructor, "Canvas", 1, JS_CFUNC_constructor, 0);
    6584          58 :     JS_SetModuleExport(c, m, "Canvas", ctor);
    6585          58 :         ctor = JS_NewCFunction2(c, path_constructor, "Path", 1, JS_CFUNC_constructor, 0);
    6586          58 :     JS_SetModuleExport(c, m, "Path", ctor);
    6587          58 :         ctor = JS_NewCFunction2(c, mx2d_constructor, "Matrix2D", 1, JS_CFUNC_constructor, 0);
    6588          58 :     JS_SetModuleExport(c, m, "Matrix2D", ctor);
    6589          58 :         ctor = JS_NewCFunction2(c, colmx_constructor, "ColorMatrix", 1, JS_CFUNC_constructor, 0);
    6590          58 :     JS_SetModuleExport(c, m, "ColorMatrix", ctor);
    6591          58 :         ctor = JS_NewCFunction2(c, solid_brush_constructor, "SolidBrush", 1, JS_CFUNC_constructor, 0);
    6592          58 :     JS_SetModuleExport(c, m, "SolidBrush", ctor);
    6593          58 :         ctor = JS_NewCFunction2(c, linear_gradient_constructor, "LinearGradient", 1, JS_CFUNC_constructor, 0);
    6594          58 :     JS_SetModuleExport(c, m, "LinearGradient", ctor);
    6595          58 :         ctor = JS_NewCFunction2(c, radial_gradient_constructor, "RadialGradient", 1, JS_CFUNC_constructor, 0);
    6596          58 :     JS_SetModuleExport(c, m, "RadialGradient", ctor);
    6597          58 :         ctor = JS_NewCFunction2(c, texture_constructor, "Texture", 1, JS_CFUNC_constructor, 0);
    6598          58 :     JS_SetModuleExport(c, m, "Texture", ctor);
    6599          58 :         ctor = JS_NewCFunction2(c, canvas3d_constructor, "Canvas3D", 1, JS_CFUNC_constructor, 0);
    6600          58 :     JS_SetModuleExport(c, m, "Canvas3D", ctor);
    6601          58 :         ctor = JS_NewCFunction2(c, text_constructor, "Text", 1, JS_CFUNC_constructor, 0);
    6602          58 :     JS_SetModuleExport(c, m, "Text", ctor);
    6603          58 :         ctor = JS_NewCFunction2(c, mx_constructor, "Matrix", 1, JS_CFUNC_constructor, 0);
    6604          58 :     JS_SetModuleExport(c, m, "Matrix", ctor);
    6605          58 :         ctor = JS_NewCFunction2(c, vai_constructor, "VertexAttribInterpolator", 1, JS_CFUNC_constructor, 0);
    6606          58 :     JS_SetModuleExport(c, m, "VertexAttribInterpolator", ctor);
    6607          58 :         ctor = JS_NewCFunction2(c, va_constructor, "VertexAttrib", 1, JS_CFUNC_constructor, 0);
    6608          58 :     JS_SetModuleExport(c, m, "VertexAttrib", ctor);
    6609             : 
    6610          58 :         ctor = JS_NewCFunction2(c, evg_pixel_size, "PixelSize", 1, JS_CFUNC_generic, 0);
    6611          58 :     JS_SetModuleExport(c, m, "PixelSize", ctor);
    6612             : 
    6613          58 :         return 0;
    6614             : }
    6615             : 
    6616          64 : void qjs_module_init_evg(JSContext *ctx)
    6617             : {
    6618             :         JSModuleDef *m;
    6619          64 :         m = JS_NewCModule(ctx, "evg", js_evg_load_module);
    6620          64 :         if (!m) return;
    6621             : 
    6622          64 :         JS_AddModuleExport(ctx, m, "Canvas");
    6623          64 :         JS_AddModuleExport(ctx, m, "Path");
    6624          64 :         JS_AddModuleExport(ctx, m, "Matrix2D");
    6625          64 :         JS_AddModuleExport(ctx, m, "ColorMatrix");
    6626          64 :         JS_AddModuleExport(ctx, m, "SolidBrush");
    6627          64 :         JS_AddModuleExport(ctx, m, "LinearGradient");
    6628          64 :         JS_AddModuleExport(ctx, m, "RadialGradient");
    6629          64 :         JS_AddModuleExport(ctx, m, "Texture");
    6630          64 :         JS_AddModuleExport(ctx, m, "Text");
    6631          64 :         JS_AddModuleExport(ctx, m, "Matrix");
    6632          64 :         JS_AddModuleExport(ctx, m, "Canvas3D");
    6633          64 :     JS_AddModuleExport(ctx, m, "VertexAttribInterpolator");
    6634          64 :     JS_AddModuleExport(ctx, m, "VertexAttrib");
    6635          64 :     JS_AddModuleExport(ctx, m, "PixelSize");
    6636          64 :     return;
    6637             : }
    6638             : 
    6639             : #else
    6640             : void qjs_module_init_evg(void *ctx)
    6641             : {
    6642             : 
    6643             : }
    6644             : #endif
    6645             : 

Generated by: LCOV version 1.13