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 = &dash;
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