/src/gpac/src/compositor/mpeg4_geometry_2d.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2023 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / Scene Compositor sub-project |
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 | | #include "nodes_stacks.h" |
29 | | #include "visual_manager.h" |
30 | | |
31 | | #if !defined(GPAC_DISABLE_COMPOSITOR) |
32 | | Bool compositor_get_2d_plane_intersection(GF_Ray *ray, SFVec3f *res) |
33 | 0 | { |
34 | 0 | GF_Plane p; |
35 | 0 | Fixed t, t2; |
36 | 0 | if (!ray->dir.x && !ray->dir.y) { |
37 | 0 | res->x = ray->orig.x; |
38 | 0 | res->y = ray->orig.y; |
39 | 0 | res->z = 0; |
40 | 0 | return 1; |
41 | 0 | } |
42 | 0 | p.normal.x = p.normal.y = 0; |
43 | 0 | p.normal.z = FIX_ONE; |
44 | 0 | p.d = 0; |
45 | 0 | t2 = gf_vec_dot(p.normal, ray->dir); |
46 | 0 | if (t2 == 0) return 0; |
47 | 0 | t = - gf_divfix(gf_vec_dot(p.normal, ray->orig) + p.d, t2); |
48 | 0 | if (t<0) return 0; |
49 | 0 | *res = gf_vec_scale(ray->dir, t); |
50 | 0 | gf_vec_add(*res, ray->orig, *res); |
51 | 0 | return 1; |
52 | 0 | } |
53 | | |
54 | | #ifndef GPAC_DISABLE_VRML |
55 | | |
56 | | /* |
57 | | Shape |
58 | | */ |
59 | | static void TraverseShape(GF_Node *node, void *rs, Bool is_destroy) |
60 | 0 | { |
61 | 0 | GF_TraverseState *tr_state; |
62 | 0 | M_Shape *shape; |
63 | 0 | if (is_destroy ) return; |
64 | | |
65 | 0 | tr_state = (GF_TraverseState *)rs; |
66 | | #ifndef GPAC_DISABLE_3D |
67 | | if (tr_state->traversing_mode==TRAVERSE_LIGHTING) return; |
68 | | #endif |
69 | |
|
70 | 0 | shape = (M_Shape *) node; |
71 | 0 | if (!shape->geometry) return; |
72 | | |
73 | | /*reset this node dirty flag (because bitmap may trigger bounds invalidation on the fly)*/ |
74 | 0 | gf_node_dirty_clear(node, 0); |
75 | | |
76 | | |
77 | | /*check traverse mode, and take care of switch-off flag*/ |
78 | 0 | if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { |
79 | 0 | tr_state->appear = (GF_Node *) shape->appearance; |
80 | | |
81 | | /*this is done regardless of switch flag*/ |
82 | 0 | gf_node_traverse((GF_Node *) shape->geometry, tr_state); |
83 | |
|
84 | 0 | if (tr_state->appear) { |
85 | | /*apply line width*/ |
86 | 0 | GF_Node *m = ((M_Appearance *)tr_state->appear)->material; |
87 | 0 | if (m && (gf_node_get_tag(m)==TAG_MPEG4_Material2D) ) { |
88 | 0 | DrawAspect2D asp; |
89 | 0 | Fixed width = 0; |
90 | 0 | asp.line_scale = FIX_ONE; |
91 | 0 | m = ((M_Material2D *)m)->lineProps; |
92 | 0 | if (m) { |
93 | 0 | switch (gf_node_get_tag(m)) { |
94 | 0 | case TAG_MPEG4_LineProperties: |
95 | 0 | width = ((M_LineProperties *) m)->width; |
96 | 0 | drawable_compute_line_scale(tr_state, &asp); |
97 | 0 | break; |
98 | 0 | case TAG_MPEG4_XLineProperties: |
99 | 0 | if ( ((M_XLineProperties *) m)->isCenterAligned) |
100 | 0 | width = ((M_XLineProperties *) m)->width; |
101 | 0 | if ( ((M_XLineProperties *) m)->isScalable) |
102 | 0 | drawable_compute_line_scale(tr_state, &asp); |
103 | 0 | break; |
104 | 0 | } |
105 | 0 | width = gf_mulfix(width, asp.line_scale); |
106 | 0 | tr_state->bounds.width += width; |
107 | 0 | tr_state->bounds.height += width; |
108 | 0 | tr_state->bounds.y += width/2; |
109 | 0 | tr_state->bounds.x -= width/2; |
110 | 0 | } |
111 | 0 | } |
112 | 0 | tr_state->appear = NULL; |
113 | 0 | } |
114 | 0 | } else { |
115 | 0 | if (tr_state->switched_off) return; |
116 | | |
117 | 0 | tr_state->appear = (GF_Node *) shape->appearance; |
118 | |
|
119 | 0 | switch (tr_state->traversing_mode) { |
120 | 0 | case TRAVERSE_SORT: |
121 | | #ifndef GPAC_DISABLE_3D |
122 | | if (tr_state->visual->type_3d) |
123 | | visual_3d_register_context(tr_state, shape->geometry); |
124 | | else |
125 | | #endif |
126 | 0 | gf_node_traverse((GF_Node *) shape->geometry, tr_state); |
127 | 0 | break; |
128 | 0 | case TRAVERSE_PICK: |
129 | 0 | gf_node_traverse((GF_Node *) shape->geometry, tr_state); |
130 | 0 | break; |
131 | | #ifndef GPAC_DISABLE_3D |
132 | | /*if we're here we passed culler already*/ |
133 | | case TRAVERSE_DRAW_3D: |
134 | | if (!tr_state->visual->type_3d && tr_state->visual->compositor->hybrid_opengl) { |
135 | | tr_state->visual->compositor->root_visual_setup=0; |
136 | | tr_state->visual->compositor->force_type_3d=1; |
137 | | } |
138 | | gf_node_traverse((GF_Node *) shape->geometry, tr_state); |
139 | | break; |
140 | | case TRAVERSE_COLLIDE: |
141 | | visual_3d_vrml_drawable_collide(shape->geometry, tr_state); |
142 | | break; |
143 | | #endif |
144 | 0 | } |
145 | | |
146 | 0 | tr_state->appear = NULL; |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | void compositor_init_shape(GF_Compositor *compositor, GF_Node *node) |
151 | 0 | { |
152 | 0 | gf_node_set_callback_function(node, TraverseShape); |
153 | 0 | } |
154 | | |
155 | | static void circle_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) |
156 | 0 | { |
157 | 0 | if (gf_node_dirty_get(node)) { |
158 | 0 | Fixed a = ((M_Circle *) node)->radius * 2; |
159 | 0 | drawable_reset_path(stack); |
160 | 0 | gf_path_add_ellipse(stack->path, 0, 0, a, a); |
161 | 0 | gf_node_dirty_clear(node, 0); |
162 | 0 | drawable_mark_modified(stack, tr_state); |
163 | 0 | } |
164 | 0 | } |
165 | | static void TraverseCircle(GF_Node *node, void *rs, Bool is_destroy) |
166 | 0 | { |
167 | 0 | DrawableContext *ctx; |
168 | 0 | Drawable *stack = (Drawable *)gf_node_get_private(node); |
169 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
170 | |
|
171 | 0 | if (is_destroy) { |
172 | 0 | drawable_node_del(node); |
173 | 0 | return; |
174 | 0 | } |
175 | | |
176 | 0 | circle_check_changes(node, stack, tr_state); |
177 | |
|
178 | 0 | switch (tr_state->traversing_mode) { |
179 | | #ifndef GPAC_DISABLE_3D |
180 | | case TRAVERSE_DRAW_3D: |
181 | | if (!stack->mesh) { |
182 | | Fixed a = ((M_Circle *) node)->radius * 2; |
183 | | stack->mesh = new_mesh(); |
184 | | mesh_new_ellipse(stack->mesh, a, a, tr_state->visual->compositor->fast); |
185 | | } |
186 | | visual_3d_draw_2d(stack, tr_state); |
187 | | return; |
188 | | #endif |
189 | 0 | case TRAVERSE_GET_BOUNDS: |
190 | 0 | gf_path_get_bounds(stack->path, &tr_state->bounds); |
191 | 0 | return; |
192 | 0 | case TRAVERSE_PICK: |
193 | 0 | vrml_drawable_pick(stack, tr_state); |
194 | 0 | return; |
195 | 0 | case TRAVERSE_SORT: |
196 | | #ifndef GPAC_DISABLE_3D |
197 | | if (tr_state->visual->type_3d) return; |
198 | | #endif |
199 | 0 | ctx = drawable_init_context_mpeg4(stack, tr_state); |
200 | 0 | if (!ctx) return; |
201 | 0 | drawable_finalize_sort(ctx, tr_state, NULL); |
202 | 0 | return; |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | void compositor_init_circle(GF_Compositor *compositor, GF_Node *node) |
207 | 0 | { |
208 | 0 | drawable_stack_new(compositor, node); |
209 | 0 | gf_node_set_callback_function(node, TraverseCircle); |
210 | 0 | } |
211 | | |
212 | | static void ellipse_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) |
213 | 0 | { |
214 | 0 | if (gf_node_dirty_get(node)) { |
215 | 0 | drawable_reset_path(stack); |
216 | 0 | gf_path_add_ellipse(stack->path, 0, 0, ((M_Ellipse *) node)->radius.x*2, ((M_Ellipse *) node)->radius.y*2); |
217 | 0 | gf_node_dirty_clear(node, 0); |
218 | 0 | drawable_mark_modified(stack, tr_state); |
219 | 0 | } |
220 | 0 | } |
221 | | static void TraverseEllipse(GF_Node *node, void *rs, Bool is_destroy) |
222 | 0 | { |
223 | 0 | DrawableContext *ctx; |
224 | 0 | Drawable *stack = (Drawable *)gf_node_get_private(node); |
225 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
226 | |
|
227 | 0 | if (is_destroy) { |
228 | 0 | drawable_node_del(node); |
229 | 0 | return; |
230 | 0 | } |
231 | | |
232 | 0 | ellipse_check_changes(node, stack, tr_state); |
233 | |
|
234 | 0 | switch (tr_state->traversing_mode) { |
235 | | #ifndef GPAC_DISABLE_3D |
236 | | case TRAVERSE_DRAW_3D: |
237 | | if (!stack->mesh) { |
238 | | stack->mesh = new_mesh(); |
239 | | mesh_new_ellipse(stack->mesh, ((M_Ellipse *) node)->radius.x * 2, ((M_Ellipse *) node)->radius.y * 2, tr_state->visual->compositor->fast); |
240 | | } |
241 | | visual_3d_draw_2d(stack, tr_state); |
242 | | return; |
243 | | #endif |
244 | 0 | case TRAVERSE_PICK: |
245 | 0 | vrml_drawable_pick(stack, tr_state); |
246 | 0 | return; |
247 | 0 | case TRAVERSE_GET_BOUNDS: |
248 | 0 | gf_path_get_bounds(stack->path, &tr_state->bounds); |
249 | 0 | return; |
250 | 0 | case TRAVERSE_SORT: |
251 | | #ifndef GPAC_DISABLE_3D |
252 | | if (tr_state->visual->type_3d) return; |
253 | | #endif |
254 | 0 | ctx = drawable_init_context_mpeg4(stack, tr_state); |
255 | 0 | if (!ctx) return; |
256 | 0 | drawable_finalize_sort(ctx, tr_state, NULL); |
257 | 0 | break; |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | void compositor_init_ellipse(GF_Compositor *compositor, GF_Node *node) |
262 | 0 | { |
263 | 0 | drawable_stack_new(compositor, node); |
264 | 0 | gf_node_set_callback_function(node, TraverseEllipse); |
265 | 0 | } |
266 | | |
267 | | static void compositor_2d_draw_rectangle(GF_TraverseState *tr_state) |
268 | 0 | { |
269 | 0 | DrawableContext *ctx = tr_state->ctx; |
270 | |
|
271 | 0 | if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->data |
272 | | #ifndef GPAC_DISABLE_3D |
273 | | && !tr_state->visual->compositor->hybrid_opengl |
274 | | #endif |
275 | 0 | ) { |
276 | 0 | Bool res; |
277 | | |
278 | | /*get image size WITHOUT line size or antialias margin*/ |
279 | 0 | if ( !(ctx->flags & CTX_NO_ANTIALIAS) ) { |
280 | 0 | GF_Rect orig_unclip; |
281 | 0 | GF_IRect orig_clip; |
282 | 0 | orig_unclip = ctx->bi->unclip; |
283 | 0 | orig_clip = ctx->bi->clip; |
284 | |
|
285 | 0 | gf_path_get_bounds(ctx->drawable->path, &ctx->bi->unclip); |
286 | 0 | gf_mx2d_apply_rect(&ctx->transform, &ctx->bi->unclip); |
287 | 0 | ctx->bi->clip = gf_rect_pixelize(&ctx->bi->unclip); |
288 | 0 | gf_irect_intersect(&ctx->bi->clip, &orig_clip); |
289 | |
|
290 | 0 | res = tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx); |
291 | | |
292 | | /*strike path*/ |
293 | 0 | ctx->bi->unclip = orig_unclip; |
294 | 0 | ctx->bi->clip = orig_clip; |
295 | 0 | if (res) { |
296 | 0 | ctx->flags |= CTX_PATH_FILLED; |
297 | 0 | visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); |
298 | 0 | } |
299 | 0 | } else { |
300 | 0 | res = tr_state->visual->DrawBitmap(tr_state->visual, tr_state, ctx); |
301 | 0 | } |
302 | | /*if failure retry with raster*/ |
303 | 0 | if (res) return; |
304 | 0 | } |
305 | | |
306 | 0 | visual_2d_texture_path(tr_state->visual, ctx->drawable->path, ctx, tr_state); |
307 | 0 | visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); |
308 | 0 | } |
309 | | |
310 | | static void rectangle_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) |
311 | 0 | { |
312 | | /*if modified update node - we don't update for other traversing mode in order not to mess up the dirty |
313 | | rect tracking (otherwise we would miss geometry changes with same bounds)*/ |
314 | 0 | if (gf_node_dirty_get(node)) { |
315 | 0 | drawable_reset_path(stack); |
316 | 0 | gf_path_add_rect_center(stack->path, 0, 0, ((M_Rectangle *) node)->size.x, ((M_Rectangle *) node)->size.y); |
317 | 0 | gf_node_dirty_clear(node, 0); |
318 | 0 | drawable_mark_modified(stack, tr_state); |
319 | 0 | } |
320 | 0 | } |
321 | | |
322 | | Bool rectangle_check_adaptation(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) |
323 | 0 | { |
324 | 0 | GF_TextureHandler *txh; |
325 | 0 | GF_MediaObjectVRInfo vrinfo; |
326 | 0 | s32 gaze_x, gaze_y; |
327 | 0 | Bool is_visible = GF_FALSE; |
328 | 0 | if (! tr_state->visual->compositor->gazer_enabled) |
329 | 0 | return GF_TRUE; |
330 | | |
331 | 0 | if (!tr_state->appear || ! ((M_Appearance *)tr_state->appear)->texture) |
332 | 0 | return GF_TRUE; |
333 | | |
334 | 0 | txh = gf_sc_texture_get_handler( ((M_Appearance *) tr_state->appear)->texture ); |
335 | 0 | if (!txh->stream) return GF_TRUE; |
336 | | |
337 | 0 | if (! gf_mo_get_srd_info(txh->stream, &vrinfo)) |
338 | 0 | return GF_TRUE; |
339 | | |
340 | 0 | if (!vrinfo.srd_w && !vrinfo.srd_h && vrinfo.is_tiled_srd) { |
341 | 0 | if (txh->stream->srd_full_w && txh->stream->srd_full_h) { |
342 | 0 | gaze_x = tr_state->visual->compositor->gaze_x; |
343 | 0 | gaze_x *= txh->stream->srd_full_w; |
344 | 0 | gaze_x /= tr_state->visual->width; |
345 | 0 | gaze_x += txh->stream->srd_map_ox; |
346 | |
|
347 | 0 | gaze_y = tr_state->visual->compositor->gaze_y; |
348 | 0 | gaze_y *= txh->stream->srd_full_h; |
349 | 0 | gaze_y /= tr_state->visual->height; |
350 | 0 | gaze_y += txh->stream->srd_map_oy; |
351 | |
|
352 | 0 | gf_mo_hint_gaze(txh->stream, gaze_x, gaze_y); |
353 | 0 | } |
354 | |
|
355 | 0 | return GF_TRUE; |
356 | 0 | } |
357 | | |
358 | 0 | gaze_x = tr_state->visual->compositor->gaze_x; |
359 | 0 | gaze_x *= vrinfo.srd_max_x; |
360 | 0 | gaze_x /= tr_state->visual->width; |
361 | |
|
362 | 0 | gaze_y = tr_state->visual->compositor->gaze_y; |
363 | 0 | gaze_y *= vrinfo.srd_max_y; |
364 | 0 | gaze_y /= tr_state->visual->height; |
365 | | |
366 | | //simple test condition: only keep the first row |
367 | 0 | if ((gaze_x>=vrinfo.srd_x) && (gaze_x<=vrinfo.srd_x+vrinfo.srd_w) && (gaze_y>=vrinfo.srd_y) && (gaze_y<=vrinfo.srd_y+vrinfo.srd_h)) { |
368 | |
|
369 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %d Partial plane is under gaze coord %d %d\n", txh->stream->OD_ID, tr_state->visual->compositor->gaze_x, tr_state->visual->compositor->gaze_y)); |
370 | 0 | is_visible = GF_TRUE; |
371 | 0 | } |
372 | |
|
373 | 0 | if (vrinfo.has_full_coverage) { |
374 | 0 | if (is_visible) { |
375 | 0 | if (!txh->is_open) { |
376 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texture %d stopped on visible partial plane - starting it\n", txh->stream->OD_ID)); |
377 | 0 | if (txh->stream->odm) |
378 | 0 | txh->stream->odm->disable_buffer_at_next_play = GF_TRUE; |
379 | |
|
380 | 0 | gf_sc_texture_play(txh, NULL); |
381 | 0 | } |
382 | 0 | if (! txh->data) return GF_FALSE; |
383 | 0 | return GF_TRUE; |
384 | 0 | } else { |
385 | 0 | if (txh->is_open) { |
386 | 0 | GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Texure %d playing on hidden partial plane - stopping it\n", txh->stream->OD_ID)); |
387 | 0 | gf_sc_texture_stop_no_unregister(txh); |
388 | 0 | } |
389 | 0 | return GF_FALSE; |
390 | 0 | } |
391 | 0 | } else { |
392 | 0 | if (is_visible) { |
393 | 0 | gf_mo_hint_quality_degradation(txh->stream, 0); |
394 | 0 | if (! txh->data) return GF_FALSE; |
395 | 0 | } else { |
396 | 0 | gf_mo_hint_quality_degradation(txh->stream, 100); |
397 | 0 | } |
398 | 0 | } |
399 | 0 | return GF_TRUE; |
400 | 0 | } |
401 | | |
402 | | static void TraverseRectangle(GF_Node *node, void *rs, Bool is_destroy) |
403 | 0 | { |
404 | 0 | DrawableContext *ctx; |
405 | 0 | Drawable *stack = (Drawable *)gf_node_get_private(node); |
406 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
407 | |
|
408 | 0 | if (is_destroy) { |
409 | 0 | drawable_node_del(node); |
410 | 0 | return; |
411 | 0 | } |
412 | | |
413 | 0 | rectangle_check_changes(node, stack, tr_state); |
414 | |
|
415 | 0 | if (! rectangle_check_adaptation(node, stack, tr_state)) |
416 | 0 | return; |
417 | | |
418 | 0 | switch (tr_state->traversing_mode) { |
419 | 0 | case TRAVERSE_DRAW_2D: |
420 | 0 | compositor_2d_draw_rectangle(tr_state); |
421 | 0 | return; |
422 | | #ifndef GPAC_DISABLE_3D |
423 | | case TRAVERSE_DRAW_3D: |
424 | | if (!stack->mesh) { |
425 | | stack->mesh = new_mesh(); |
426 | | mesh_new_rectangle(stack->mesh, ((M_Rectangle *) node)->size, NULL, 0); |
427 | | } |
428 | | visual_3d_draw_2d(stack, tr_state); |
429 | | return; |
430 | | #endif |
431 | 0 | case TRAVERSE_PICK: |
432 | 0 | vrml_drawable_pick(stack, tr_state); |
433 | 0 | return; |
434 | 0 | case TRAVERSE_GET_BOUNDS: |
435 | 0 | gf_path_get_bounds(stack->path, &tr_state->bounds); |
436 | 0 | return; |
437 | 0 | case TRAVERSE_SORT: |
438 | | #ifndef GPAC_DISABLE_3D |
439 | | if (tr_state->visual->type_3d) return; |
440 | | #endif |
441 | 0 | break; |
442 | 0 | default: |
443 | 0 | return; |
444 | 0 | } |
445 | | |
446 | 0 | ctx = drawable_init_context_mpeg4(stack, tr_state); |
447 | 0 | if (!ctx) return; |
448 | | |
449 | | /*if rotated, object is transparent (doesn't fill bounds) and antialias must be used*/ |
450 | 0 | if (tr_state->transform.m[1] || tr_state->transform.m[3]) { |
451 | 0 | } |
452 | 0 | else { |
453 | | |
454 | | /*if alpha or not filled, transparent*/ |
455 | 0 | if (ctx->aspect.fill_color && (GF_COL_A(ctx->aspect.fill_color) != 0xFF)) { |
456 | 0 | } |
457 | | /*if texture transparent, transparent*/ |
458 | 0 | else if (ctx->aspect.fill_texture && ctx->aspect.fill_texture->transparent) { |
459 | 0 | } |
460 | | /*TODO check matrix for alpha*/ |
461 | 0 | else if (!tr_state->color_mat.identity) { |
462 | 0 | } |
463 | | /*otherwise, not transparent*/ |
464 | 0 | else { |
465 | 0 | ctx->flags &= ~CTX_IS_TRANSPARENT; |
466 | 0 | } |
467 | | /*if no line width, we skip antialiasing*/ |
468 | 0 | if (!ctx->aspect.pen_props.width) ctx->flags |= CTX_NO_ANTIALIAS; |
469 | 0 | } |
470 | 0 | drawable_finalize_sort(ctx, tr_state, NULL); |
471 | 0 | } |
472 | | |
473 | | void compositor_init_rectangle(GF_Compositor *compositor, GF_Node *node) |
474 | 0 | { |
475 | 0 | Drawable *stack = drawable_stack_new(compositor, node); |
476 | 0 | stack->flags = DRAWABLE_USE_TRAVERSE_DRAW; |
477 | 0 | gf_node_set_callback_function(node, TraverseRectangle); |
478 | 0 | } |
479 | | |
480 | | |
481 | 0 | #define CHECK_VALID_C2D(nbPts) if (!idx && cur_index+nbPts>=pt_count) { gf_path_reset(stack->path); return; } |
482 | | //#define CHECK_VALID_C2D(nbPts) |
483 | 0 | #define GET_IDX(_i) ((idx && (idx->count>_i) && (idx->vals[_i]>=0) ) ? (u32) idx->vals[_i] : _i) |
484 | | |
485 | | void curve2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state, MFInt32 *idx) |
486 | 0 | { |
487 | 0 | M_Curve2D *c2D; |
488 | 0 | SFVec2f orig, ct_orig, ct_end, end; |
489 | 0 | u32 cur_index, i, remain, type_count, pt_count; |
490 | 0 | SFVec2f *pts; |
491 | 0 | M_Coordinate2D *coord; |
492 | |
|
493 | 0 | c2D = (M_Curve2D *)node; |
494 | 0 | coord = (M_Coordinate2D *)c2D->point; |
495 | 0 | drawable_reset_path(stack); |
496 | 0 | if (!coord) return; |
497 | | |
498 | 0 | stack->path->fineness = c2D->fineness; |
499 | 0 | if (tr_state->visual->compositor->fast) stack->path->fineness /= 2; |
500 | | |
501 | |
|
502 | 0 | pts = coord->point.vals; |
503 | 0 | if (!pts) |
504 | 0 | return; |
505 | | |
506 | 0 | cur_index = c2D->type.count ? 1 : 0; |
507 | | /*if the first type is a moveTo skip initial moveTo*/ |
508 | 0 | i=0; |
509 | 0 | if (cur_index) { |
510 | 0 | while (c2D->type.vals[i]==0) i++; |
511 | 0 | } |
512 | 0 | ct_orig = orig = pts[ GET_IDX(i) ]; |
513 | |
|
514 | 0 | gf_path_add_move_to(stack->path, orig.x, orig.y); |
515 | |
|
516 | 0 | pt_count = coord->point.count; |
517 | 0 | type_count = c2D->type.count; |
518 | 0 | for (; i<type_count; i++) { |
519 | |
|
520 | 0 | switch (c2D->type.vals[i]) { |
521 | | /*moveTo, 1 point*/ |
522 | 0 | case 0: |
523 | 0 | CHECK_VALID_C2D(0); |
524 | 0 | orig = pts[ GET_IDX(cur_index) ]; |
525 | 0 | if (i) gf_path_add_move_to(stack->path, orig.x, orig.y); |
526 | 0 | cur_index += 1; |
527 | 0 | break; |
528 | | /*lineTo, 1 point*/ |
529 | 0 | case 1: |
530 | 0 | CHECK_VALID_C2D(0); |
531 | 0 | end = pts[ GET_IDX(cur_index) ]; |
532 | 0 | gf_path_add_line_to(stack->path, end.x, end.y); |
533 | 0 | orig = end; |
534 | 0 | cur_index += 1; |
535 | 0 | break; |
536 | | /*curveTo, 3 points*/ |
537 | 0 | case 2: |
538 | 0 | CHECK_VALID_C2D(2); |
539 | 0 | ct_orig = pts[ GET_IDX(cur_index) ]; |
540 | 0 | ct_end = pts[ GET_IDX(cur_index+1) ]; |
541 | 0 | end = pts[ GET_IDX(cur_index+2) ]; |
542 | 0 | gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); |
543 | 0 | cur_index += 3; |
544 | 0 | ct_orig = ct_end; |
545 | 0 | orig = end; |
546 | 0 | break; |
547 | | /*nextCurveTo, 2 points (cf spec)*/ |
548 | 0 | case 3: |
549 | 0 | CHECK_VALID_C2D(1); |
550 | 0 | ct_orig.x = 2*orig.x - ct_orig.x; |
551 | 0 | ct_orig.y = 2*orig.y - ct_orig.y; |
552 | 0 | ct_end = pts[ GET_IDX(cur_index) ]; |
553 | 0 | end = pts[ GET_IDX(cur_index+1) ]; |
554 | 0 | gf_path_add_cubic_to(stack->path, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, end.x, end.y); |
555 | 0 | cur_index += 2; |
556 | 0 | ct_orig = ct_end; |
557 | 0 | orig = end; |
558 | 0 | break; |
559 | | |
560 | | /*all XCurve2D specific*/ |
561 | | |
562 | | /*CW and CCW ArcTo*/ |
563 | 0 | case 4: |
564 | 0 | case 5: |
565 | 0 | CHECK_VALID_C2D(2); |
566 | 0 | ct_orig = pts[ GET_IDX(cur_index) ]; |
567 | 0 | ct_end = pts[ GET_IDX(cur_index+1) ]; |
568 | 0 | end = pts[ GET_IDX(cur_index+2) ]; |
569 | 0 | gf_path_add_arc_to(stack->path, end.x, end.y, ct_orig.x, ct_orig.y, ct_end.x, ct_end.y, (c2D->type.vals[i]==5) ? 1 : 0); |
570 | 0 | cur_index += 3; |
571 | 0 | ct_orig = ct_end; |
572 | 0 | orig = end; |
573 | 0 | break; |
574 | | /*ClosePath*/ |
575 | 0 | case 6: |
576 | 0 | gf_path_close(stack->path); |
577 | 0 | break; |
578 | | /*quadratic CurveTo, 2 points*/ |
579 | 0 | case 7: |
580 | 0 | CHECK_VALID_C2D(1); |
581 | 0 | ct_end = pts[ GET_IDX(cur_index) ]; |
582 | 0 | end = pts[ GET_IDX(cur_index+1) ]; |
583 | 0 | gf_path_add_quadratic_to(stack->path, ct_end.x, ct_end.y, end.x, end.y); |
584 | 0 | cur_index += 2; |
585 | 0 | ct_orig = ct_end; |
586 | 0 | orig = end; |
587 | 0 | break; |
588 | 0 | } |
589 | 0 | } |
590 | | |
591 | | /*what's left is an N-bezier spline*/ |
592 | 0 | if (!idx && (pt_count > cur_index) ) { |
593 | | /*first moveto*/ |
594 | 0 | if (!cur_index) cur_index++; |
595 | |
|
596 | 0 | remain = pt_count - cur_index; |
597 | |
|
598 | 0 | if (remain>1) |
599 | 0 | gf_path_add_bezier(stack->path, &pts[cur_index], remain); |
600 | 0 | } |
601 | |
|
602 | 0 | gf_node_dirty_clear(node, 0); |
603 | 0 | drawable_mark_modified(stack, tr_state); |
604 | 0 | } |
605 | | |
606 | | static void TraverseCurve2D(GF_Node *node, void *rs, Bool is_destroy) |
607 | 0 | { |
608 | 0 | DrawableContext *ctx; |
609 | 0 | Drawable *stack = (Drawable *)gf_node_get_private(node); |
610 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
611 | |
|
612 | 0 | if (is_destroy) { |
613 | 0 | drawable_node_del(node); |
614 | 0 | return; |
615 | 0 | } |
616 | 0 | if (gf_node_dirty_get(node)) { |
617 | 0 | curve2d_check_changes(node, stack, tr_state, NULL); |
618 | 0 | } |
619 | |
|
620 | 0 | switch (tr_state->traversing_mode) { |
621 | | #ifndef GPAC_DISABLE_3D |
622 | | case TRAVERSE_DRAW_3D: |
623 | | if (!stack->mesh) { |
624 | | stack->mesh = new_mesh(); |
625 | | mesh_from_path(stack->mesh, stack->path); |
626 | | } |
627 | | visual_3d_draw_2d(stack, tr_state); |
628 | | return; |
629 | | #endif |
630 | 0 | case TRAVERSE_PICK: |
631 | 0 | vrml_drawable_pick(stack, tr_state); |
632 | 0 | return; |
633 | 0 | case TRAVERSE_GET_BOUNDS: |
634 | 0 | gf_path_get_bounds(stack->path, &tr_state->bounds); |
635 | 0 | return; |
636 | 0 | case TRAVERSE_SORT: |
637 | | #ifndef GPAC_DISABLE_3D |
638 | | if (tr_state->visual->type_3d) return; |
639 | | #endif |
640 | 0 | ctx = drawable_init_context_mpeg4(stack, tr_state); |
641 | 0 | if (!ctx) return; |
642 | 0 | drawable_finalize_sort(ctx, tr_state, NULL); |
643 | 0 | return; |
644 | 0 | } |
645 | 0 | } |
646 | | |
647 | | void compositor_init_curve2d(GF_Compositor *compositor, GF_Node *node) |
648 | 0 | { |
649 | 0 | drawable_stack_new(compositor, node); |
650 | 0 | gf_node_set_callback_function(node, TraverseCurve2D); |
651 | 0 | } |
652 | | |
653 | | |
654 | | |
655 | | /* |
656 | | Note on point set 2D: this is a very bad node and should be avoided in DEF/USE, since the size |
657 | | of the rectangle representing the pixel shall always be 1 pixel w/h, therefore |
658 | | the path object is likely not the same depending on transformation context... |
659 | | |
660 | | */ |
661 | | |
662 | | static void get_point_size(GF_Matrix2D *mat, Fixed *w, Fixed *h) |
663 | 0 | { |
664 | 0 | GF_Point2D pt; |
665 | 0 | pt.x = mat->m[0] + mat->m[1]; |
666 | 0 | pt.y = mat->m[3] + mat->m[4]; |
667 | 0 | *w = *h = gf_divfix(FLT2FIX(1.41421356f) , gf_v2d_len(&pt)); |
668 | 0 | } |
669 | | |
670 | | static void pointset2d_check_changes(GF_Node *node, Drawable *stack, GF_TraverseState *tr_state) |
671 | 0 | { |
672 | 0 | u32 i; |
673 | 0 | Fixed w, h; |
674 | 0 | M_Coordinate2D *coord; |
675 | |
|
676 | 0 | if (!gf_node_dirty_get(node)) return; |
677 | 0 | coord = (M_Coordinate2D *) ((M_PointSet2D *)node)->coord; |
678 | |
|
679 | 0 | drawable_reset_path(stack); |
680 | |
|
681 | 0 | get_point_size(&tr_state->transform, &w, &h); |
682 | | /*for PS2D don't add to avoid too much antialiasing, just try to fill the given pixel*/ |
683 | 0 | for (i=0; i < coord->point.count; i++) |
684 | 0 | gf_path_add_rect(stack->path, coord->point.vals[i].x, coord->point.vals[i].y, w, h); |
685 | |
|
686 | 0 | stack->path->flags |= GF_PATH_FILL_ZERO_NONZERO; |
687 | |
|
688 | 0 | gf_node_dirty_clear(node, 0); |
689 | 0 | drawable_mark_modified(stack, tr_state); |
690 | 0 | } |
691 | | |
692 | | static void PointSet2D_Draw(GF_Node *node, GF_TraverseState *tr_state) |
693 | 0 | { |
694 | 0 | GF_Path *path; |
695 | 0 | Fixed alpha, w, h; |
696 | 0 | u32 i; |
697 | 0 | SFColor col; |
698 | 0 | DrawableContext *ctx = tr_state->ctx; |
699 | 0 | M_PointSet2D *ps2D = (M_PointSet2D *)node; |
700 | 0 | M_Coordinate2D *coord = (M_Coordinate2D*) ps2D->coord; |
701 | 0 | M_Color *color = (M_Color *) ps2D->color; |
702 | | |
703 | | /*never outline PS2D*/ |
704 | 0 | ctx->flags |= CTX_PATH_STROKE; |
705 | 0 | if (!color || color->color.count<coord->point.count) { |
706 | | /*no texturing*/ |
707 | 0 | visual_2d_draw_path(tr_state->visual, ctx->drawable->path, ctx, NULL, NULL, tr_state); |
708 | 0 | return; |
709 | 0 | } |
710 | | |
711 | 0 | get_point_size(&ctx->transform, &w, &h); |
712 | |
|
713 | 0 | path = gf_path_new(); |
714 | 0 | alpha = INT2FIX(GF_COL_A(ctx->aspect.line_color)) / 255; |
715 | 0 | for (i = 0; i < coord->point.count; i++) { |
716 | 0 | col = color->color.vals[i]; |
717 | 0 | ctx->aspect.line_color = GF_COL_ARGB_FIXED(alpha, col.red, col.green, col.blue); |
718 | 0 | gf_path_add_rect_center(path, coord->point.vals[i].x, coord->point.vals[i].y, w, h); |
719 | 0 | visual_2d_draw_path(tr_state->visual, path, ctx, NULL, NULL, tr_state); |
720 | 0 | gf_path_reset(path); |
721 | 0 | ctx->flags &= ~CTX_PATH_FILLED; |
722 | 0 | } |
723 | 0 | gf_path_del(path); |
724 | 0 | } |
725 | | |
726 | | static void TraversePointSet2D(GF_Node *node, void *rs, Bool is_destroy) |
727 | 0 | { |
728 | 0 | DrawableContext *ctx; |
729 | 0 | M_PointSet2D *ps2D = (M_PointSet2D *)node; |
730 | 0 | Drawable *stack = (Drawable *)gf_node_get_private(node); |
731 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
732 | |
|
733 | 0 | if (is_destroy) { |
734 | 0 | drawable_node_del(node); |
735 | 0 | return; |
736 | 0 | } |
737 | | |
738 | 0 | if (!ps2D->coord) return; |
739 | | |
740 | 0 | pointset2d_check_changes(node, stack, tr_state); |
741 | |
|
742 | 0 | switch (tr_state->traversing_mode) { |
743 | 0 | case TRAVERSE_DRAW_2D: |
744 | 0 | PointSet2D_Draw(node, tr_state); |
745 | 0 | return; |
746 | | #ifndef GPAC_DISABLE_3D |
747 | | case TRAVERSE_DRAW_3D: |
748 | | { |
749 | | DrawAspect2D asp; |
750 | | if (!stack->mesh) { |
751 | | stack->mesh = new_mesh(); |
752 | | mesh_new_ps(stack->mesh, ps2D->coord, ps2D->color); |
753 | | } |
754 | | memset(&asp, 0, sizeof(DrawAspect2D)); |
755 | | drawable_get_aspect_2d_mpeg4(node, &asp, tr_state); |
756 | | visual_3d_set_material_2d_argb(tr_state->visual, asp.fill_color); |
757 | | visual_3d_mesh_paint(tr_state, stack->mesh); |
758 | | return; |
759 | | } |
760 | | #endif |
761 | 0 | case TRAVERSE_GET_BOUNDS: |
762 | 0 | gf_path_get_bounds(stack->path, &tr_state->bounds); |
763 | 0 | return; |
764 | 0 | case TRAVERSE_PICK: |
765 | 0 | return; |
766 | 0 | case TRAVERSE_SORT: |
767 | | #ifndef GPAC_DISABLE_3D |
768 | | if (tr_state->visual->type_3d) return; |
769 | | #endif |
770 | 0 | ctx = drawable_init_context_mpeg4(stack, tr_state); |
771 | 0 | if (!ctx) return; |
772 | 0 | drawable_finalize_sort(ctx, tr_state, NULL); |
773 | 0 | break; |
774 | 0 | default: |
775 | 0 | return; |
776 | 0 | } |
777 | 0 | } |
778 | | |
779 | | |
780 | | void compositor_init_pointset2d(GF_Compositor *compositor, GF_Node *node) |
781 | 0 | { |
782 | 0 | Drawable *stack = drawable_stack_new(compositor, node); |
783 | 0 | stack->flags = DRAWABLE_USE_TRAVERSE_DRAW; |
784 | 0 | gf_node_set_callback_function(node, TraversePointSet2D); |
785 | 0 | } |
786 | | |
787 | | static void TraverseBitWrapper(GF_Node *node, void *rs, Bool is_destroy) |
788 | 0 | { |
789 | 0 | GF_TraverseState *tr_state; |
790 | 0 | M_BitWrapper *bitWrap; |
791 | |
|
792 | 0 | if (is_destroy) { |
793 | 0 | gf_node_set_private(node, NULL); |
794 | 0 | return; |
795 | 0 | } |
796 | | #ifdef GPAC_ENABLE_COVERAGE |
797 | | if (!rs) return; |
798 | | #endif |
799 | | |
800 | 0 | tr_state = (GF_TraverseState *)rs; |
801 | | // Traverse the node here |
802 | 0 | bitWrap = (M_BitWrapper *)node; |
803 | 0 | gf_node_traverse(bitWrap->node, tr_state); |
804 | 0 | } |
805 | | |
806 | | void compositor_init_bitwrapper(GF_Compositor *compositor, GF_Node *node) |
807 | 0 | { |
808 | 0 | M_BitWrapper *bit; |
809 | 0 | bit = (M_BitWrapper *)node; |
810 | 0 | if (!bit->node) return; |
811 | 0 | gf_node_set_private(node, gf_node_get_private(bit->node)); |
812 | 0 | gf_node_set_callback_function(node, TraverseBitWrapper); |
813 | |
|
814 | | #ifdef GPAC_ENABLE_COVERAGE |
815 | | if (gf_sys_is_cov_mode()) { |
816 | | TraverseBitWrapper(node, NULL, GF_FALSE); |
817 | | } |
818 | | #endif |
819 | 0 | } |
820 | | |
821 | | |
822 | | #endif /*GPAC_DISABLE_VRML*/ |
823 | | #endif //!defined(GPAC_DISABLE_COMPOSITOR) |