Coverage Report

Created: 2026-05-30 07:38

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