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