/src/gpac/src/compositor/mpeg4_grouping_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 | | #include "nodes_stacks.h" |
27 | | #include "mpeg4_grouping.h" |
28 | | #include "visual_manager.h" |
29 | | |
30 | | #if !defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR) |
31 | | |
32 | | typedef struct |
33 | | { |
34 | | s32 last_switch; |
35 | | } SwitchStack; |
36 | | |
37 | | static void TraverseSwitch(GF_Node *node, void *rs, Bool is_destroy) |
38 | 0 | { |
39 | 0 | GF_ChildNodeItem *l; |
40 | 0 | u32 i; |
41 | 0 | Bool prev_switch; |
42 | 0 | GF_ChildNodeItem *children; |
43 | 0 | s32 whichChoice; |
44 | 0 | GF_Node *child; |
45 | 0 | SwitchStack *st = (SwitchStack *)gf_node_get_private(node); |
46 | 0 | GF_TraverseState *tr_state; |
47 | 0 | tr_state = (GF_TraverseState *)rs; |
48 | 0 | children = NULL; |
49 | |
|
50 | 0 | whichChoice = -1; |
51 | 0 | if (is_destroy) { |
52 | 0 | gf_sc_check_focus_upon_destroy(node); |
53 | 0 | gf_free(st); |
54 | 0 | return; |
55 | 0 | } |
56 | | /*WARNING: X3D/MPEG4 NOT COMPATIBLE*/ |
57 | 0 | switch (gf_node_get_tag(node)) { |
58 | 0 | case TAG_MPEG4_Switch: |
59 | 0 | children = ((M_Switch *)node)->choice; |
60 | 0 | whichChoice = ((M_Switch *)node)->whichChoice; |
61 | 0 | break; |
62 | 0 | #ifndef GPAC_DISABLE_X3D |
63 | 0 | case TAG_X3D_Switch: |
64 | 0 | children = ((X_Switch *)node)->children; |
65 | 0 | whichChoice = ((X_Switch *)node)->whichChoice; |
66 | 0 | break; |
67 | 0 | #endif |
68 | 0 | } |
69 | | |
70 | 0 | if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) { |
71 | 0 | prev_switch = tr_state->switched_off; |
72 | | /*check changes in choice field*/ |
73 | 0 | if ((gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) || (st->last_switch != whichChoice) ) { |
74 | 0 | tr_state->switched_off = 1; |
75 | 0 | i=0; |
76 | 0 | l = children; |
77 | 0 | while (l) { |
78 | | // if ((s32) i!=whichChoice) gf_node_traverse(l->node, tr_state); |
79 | 0 | if ((s32) i == st->last_switch) gf_node_traverse(l->node, tr_state); |
80 | 0 | l = l->next; |
81 | 0 | i++; |
82 | 0 | } |
83 | 0 | tr_state->switched_off = 0; |
84 | 0 | st->last_switch = whichChoice; |
85 | 0 | } |
86 | |
|
87 | 0 | gf_node_dirty_clear(node, 0); |
88 | | |
89 | | /*no need to check for sensors since a sensor is active for the whole parent group, that is for switch itself |
90 | | CSQ: switch cannot be used to switch sensors, too bad...*/ |
91 | 0 | tr_state->switched_off = prev_switch; |
92 | 0 | } |
93 | |
|
94 | 0 | if (!children) return; |
95 | 0 | if (whichChoice==-2) { |
96 | | #ifndef GPAC_DISABLE_3D |
97 | | if (tr_state->visual->autostereo_type) { |
98 | | u32 idx; |
99 | | u32 count = gf_node_list_get_count(children); |
100 | | /*this should be a bit more subtle (reusing views if missing, ...)...*/ |
101 | | idx = tr_state->visual->current_view % count; |
102 | | |
103 | | child = (GF_Node*)gf_node_list_get_child(children, idx); |
104 | | gf_node_traverse(child, tr_state); |
105 | | return; |
106 | | } else |
107 | | #endif //GPAC_DISABLE_3D |
108 | 0 | { |
109 | | /*fallback to first view*/ |
110 | 0 | whichChoice=0; |
111 | 0 | } |
112 | 0 | } |
113 | 0 | if (whichChoice>=0) { |
114 | 0 | child = (GF_Node*)gf_node_list_get_child(children, whichChoice); |
115 | 0 | gf_node_traverse(child, tr_state); |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | | void compositor_init_switch(GF_Compositor *compositor, GF_Node *node) |
120 | 0 | { |
121 | 0 | SwitchStack *st = (SwitchStack *)gf_malloc(sizeof(SwitchStack)); |
122 | 0 | st->last_switch = -1; |
123 | 0 | gf_node_set_private(node, st); |
124 | 0 | gf_node_set_callback_function(node, TraverseSwitch); |
125 | 0 | } |
126 | | |
127 | | |
128 | | /*transform2D*/ |
129 | | typedef struct |
130 | | { |
131 | | GROUPING_MPEG4_STACK_2D |
132 | | GF_Matrix2D mat; |
133 | | u8 is_identity; |
134 | | u8 is_null; |
135 | | u8 is_untransform; |
136 | | } Transform2DStack; |
137 | | |
138 | | static void traverse_transform(GF_Node *node, Transform2DStack *stack, GF_TraverseState *tr_state) |
139 | 0 | { |
140 | 0 | if (stack->is_null) return; |
141 | | |
142 | | /*note we don't clear dirty flag, this is done in traversing*/ |
143 | 0 | if (stack->is_identity) { |
144 | 0 | group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); |
145 | 0 | } |
146 | | #ifndef GPAC_DISABLE_3D |
147 | | else if (tr_state->visual->type_3d) { |
148 | | GF_Matrix mx_bckup; |
149 | | gf_mx_copy(mx_bckup, tr_state->model_matrix); |
150 | | |
151 | | gf_mx_add_matrix_2d(&tr_state->model_matrix, &stack->mat); |
152 | | group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); |
153 | | gf_mx_copy(tr_state->model_matrix, mx_bckup); |
154 | | } |
155 | | #endif |
156 | 0 | else { |
157 | 0 | GF_Matrix2D bckup; |
158 | 0 | gf_mx2d_copy(bckup, tr_state->transform); |
159 | 0 | gf_mx2d_pre_multiply(&tr_state->transform, &stack->mat); |
160 | |
|
161 | 0 | group_2d_traverse(node, (GroupingNode2D *)stack, tr_state); |
162 | |
|
163 | 0 | gf_mx2d_copy(tr_state->transform, bckup); |
164 | 0 | } |
165 | |
|
166 | 0 | if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) { |
167 | 0 | gf_mx2d_apply_rect(&stack->mat, &tr_state->bounds); |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | | void TraverseUntransformEx(GF_Node *node, void *rs, GroupingNode2D *grp); |
172 | | |
173 | | static void TraverseTransform2D(GF_Node *node, void *rs, Bool is_destroy) |
174 | 0 | { |
175 | 0 | M_Transform2D *tr = (M_Transform2D *)node; |
176 | 0 | Transform2DStack *ptr = (Transform2DStack *)gf_node_get_private(node); |
177 | 0 | GF_TraverseState *tr_state; |
178 | |
|
179 | 0 | if (is_destroy) { |
180 | 0 | gf_sc_check_focus_upon_destroy(node); |
181 | 0 | group_2d_destroy(node, (GroupingNode2D*)ptr); |
182 | 0 | gf_free(ptr); |
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | 0 | tr_state = (GF_TraverseState *) rs; |
187 | |
|
188 | 0 | if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { |
189 | 0 | gf_mx2d_init(ptr->mat); |
190 | 0 | ptr->is_identity = 1; |
191 | 0 | if ((tr->scale.x != FIX_ONE) || (tr->scale.y != FIX_ONE)) { |
192 | 0 | gf_mx2d_add_scale_at(&ptr->mat, tr->scale.x, tr->scale.y, 0, 0, tr->scaleOrientation); |
193 | 0 | ptr->is_identity = 0; |
194 | 0 | } |
195 | 0 | if (tr->rotationAngle) { |
196 | 0 | gf_mx2d_add_rotation(&ptr->mat, tr->center.x, tr->center.y, tr->rotationAngle); |
197 | 0 | ptr->is_identity = 0; |
198 | 0 | } |
199 | 0 | if (tr->translation.x || tr->translation.y) { |
200 | 0 | ptr->is_identity = 0; |
201 | 0 | gf_mx2d_add_translation(&ptr->mat, tr->translation.x, tr->translation.y); |
202 | 0 | } |
203 | 0 | gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); |
204 | 0 | ptr->is_null = (!tr->scale.x || !tr->scale.y) ? 1 : 0; |
205 | 0 | } |
206 | |
|
207 | 0 | if (ptr->is_untransform) { |
208 | 0 | TraverseUntransformEx(node, tr_state, (GroupingNode2D *)ptr); |
209 | 0 | } else { |
210 | 0 | traverse_transform(node, ptr, tr_state); |
211 | 0 | } |
212 | 0 | } |
213 | | void transform2d_set_untransform(GF_Node *node) |
214 | 0 | { |
215 | 0 | Transform2DStack *stack; |
216 | 0 | if (gf_node_get_tag(node)!=TAG_MPEG4_Transform2D) return; |
217 | 0 | stack = (Transform2DStack *)gf_node_get_private(node); |
218 | 0 | stack->is_untransform = 1; |
219 | 0 | } |
220 | | |
221 | | void compositor_init_transform2d(GF_Compositor *compositor, GF_Node *node) |
222 | 0 | { |
223 | 0 | Transform2DStack *stack; |
224 | 0 | GF_SAFEALLOC(stack, Transform2DStack); |
225 | 0 | if (!stack) { |
226 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate transform2d stack\n")); |
227 | 0 | return; |
228 | 0 | } |
229 | | |
230 | 0 | gf_mx2d_init(stack->mat); |
231 | 0 | stack->is_identity = 1; |
232 | 0 | gf_node_set_private(node, stack); |
233 | 0 | gf_node_set_callback_function(node, TraverseTransform2D); |
234 | 0 | } |
235 | | |
236 | | void tr_mx2d_get_matrix(GF_Node *n, GF_Matrix2D *mat) |
237 | 0 | { |
238 | 0 | M_TransformMatrix2D *tr = (M_TransformMatrix2D*)n; |
239 | 0 | gf_mx2d_init(*mat); |
240 | 0 | mat->m[0] = tr->mxx; |
241 | 0 | mat->m[1] = tr->mxy; |
242 | 0 | mat->m[2] = tr->tx; |
243 | 0 | mat->m[3] = tr->myx; |
244 | 0 | mat->m[4] = tr->myy; |
245 | 0 | mat->m[5] = tr->ty; |
246 | 0 | } |
247 | | |
248 | | |
249 | | /*TransformMatrix2D*/ |
250 | | static void TraverseTransformMatrix2D(GF_Node *node, void *rs, Bool is_destroy) |
251 | 0 | { |
252 | 0 | Transform2DStack *ptr = (Transform2DStack *) gf_node_get_private(node); |
253 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
254 | |
|
255 | 0 | if (is_destroy) { |
256 | 0 | gf_sc_check_focus_upon_destroy(node); |
257 | 0 | group_2d_destroy(node, (GroupingNode2D*)ptr); |
258 | 0 | gf_free(ptr); |
259 | 0 | return; |
260 | 0 | } |
261 | | |
262 | 0 | if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { |
263 | 0 | M_TransformMatrix2D *tr = (M_TransformMatrix2D*)node; |
264 | 0 | tr_mx2d_get_matrix(node, &ptr->mat); |
265 | 0 | if ((tr->mxx==FIX_ONE) && (tr->mxy==0) && (tr->tx==0) |
266 | 0 | && (tr->myx==0) && (tr->myy==FIX_ONE) && (tr->ty==0) ) |
267 | 0 | ptr->is_identity = 1; |
268 | 0 | else |
269 | 0 | ptr->is_identity = 0; |
270 | |
|
271 | 0 | ptr->is_null = ( (!ptr->mat.m[0] && !ptr->mat.m[1]) || (!ptr->mat.m[3] && !ptr->mat.m[4]) ) ? 1 : 0; |
272 | 0 | gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); |
273 | 0 | } |
274 | 0 | traverse_transform(node, ptr, tr_state); |
275 | 0 | } |
276 | | |
277 | | |
278 | | void compositor_init_transformmatrix2d(GF_Compositor *compositor, GF_Node *node) |
279 | 0 | { |
280 | 0 | Transform2DStack *stack; |
281 | 0 | GF_SAFEALLOC(stack, Transform2DStack); |
282 | 0 | if (!stack) return; |
283 | | |
284 | 0 | gf_mx2d_init(stack->mat); |
285 | 0 | gf_node_set_private(node, stack); |
286 | 0 | gf_node_set_callback_function(node, TraverseTransformMatrix2D); |
287 | 0 | } |
288 | | |
289 | | |
290 | | typedef struct |
291 | | { |
292 | | GROUPING_MPEG4_STACK_2D |
293 | | GF_ColorMatrix cmat; |
294 | | } ColorTransformStack; |
295 | | |
296 | | /*ColorTransform*/ |
297 | | static void TraverseColorTransform(GF_Node *node, void *rs, Bool is_destroy) |
298 | 0 | { |
299 | 0 | Bool c_changed; |
300 | 0 | M_ColorTransform *tr = (M_ColorTransform *)node; |
301 | 0 | ColorTransformStack *ptr = (ColorTransformStack *)gf_node_get_private(node); |
302 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *) rs; |
303 | 0 | Bool prev_inv; |
304 | |
|
305 | 0 | if (is_destroy) { |
306 | 0 | gf_sc_check_focus_upon_destroy(node); |
307 | 0 | group_2d_destroy(node, (GroupingNode2D*)ptr); |
308 | 0 | gf_free(ptr); |
309 | 0 | return; |
310 | 0 | } |
311 | 0 | if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { |
312 | 0 | group_2d_traverse(node, (GroupingNode2D *) ptr, tr_state); |
313 | 0 | return; |
314 | 0 | } |
315 | | |
316 | 0 | prev_inv = tr_state->invalidate_all; |
317 | 0 | c_changed = 0; |
318 | 0 | if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { |
319 | 0 | gf_cmx_set(&ptr->cmat, |
320 | 0 | tr->mrr , tr->mrg, tr->mrb, tr->mra, tr->tr, |
321 | 0 | tr->mgr , tr->mgg, tr->mgb, tr->mga, tr->tg, |
322 | 0 | tr->mbr, tr->mbg, tr->mbb, tr->mba, tr->tb, |
323 | 0 | tr->mar, tr->mag, tr->mab, tr->maa, tr->ta); |
324 | 0 | c_changed = 1; |
325 | 0 | gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); |
326 | 0 | } |
327 | |
|
328 | 0 | if ((tr_state->traversing_mode==TRAVERSE_SORT) |
329 | 0 | && !tr->maa && !tr->mar && !tr->mag && !tr->mab && !tr->ta) |
330 | 0 | return; |
331 | | |
332 | | /*if modified redraw all nodes*/ |
333 | 0 | if (c_changed) tr_state->invalidate_all = 1; |
334 | | |
335 | | /*note we don't clear dirty flag, this is done in traversing*/ |
336 | 0 | if (ptr->cmat.identity) { |
337 | 0 | group_2d_traverse(node, (GroupingNode2D *) ptr, tr_state); |
338 | 0 | } else { |
339 | 0 | GF_ColorMatrix gf_cmx_bck; |
340 | 0 | gf_cmx_copy(&gf_cmx_bck, &tr_state->color_mat); |
341 | 0 | gf_cmx_multiply(&tr_state->color_mat, &ptr->cmat); |
342 | | |
343 | |
|
344 | 0 | group_2d_traverse(node, (GroupingNode2D *) ptr, tr_state); |
345 | | /*restore traversing state*/ |
346 | 0 | gf_cmx_copy(&tr_state->color_mat, &gf_cmx_bck); |
347 | 0 | } |
348 | 0 | tr_state->invalidate_all = prev_inv; |
349 | 0 | } |
350 | | |
351 | | void compositor_init_colortransform(GF_Compositor *compositor, GF_Node *node) |
352 | 0 | { |
353 | 0 | ColorTransformStack *stack; |
354 | 0 | GF_SAFEALLOC(stack, ColorTransformStack); |
355 | 0 | if (!stack) return; |
356 | | |
357 | 0 | gf_cmx_init(&stack->cmat); |
358 | 0 | gf_node_set_private(node, stack); |
359 | 0 | gf_node_set_callback_function(node, TraverseColorTransform); |
360 | 0 | } |
361 | | |
362 | | |
363 | | struct og_pos |
364 | | { |
365 | | Fixed priority; |
366 | | u32 position; |
367 | | }; |
368 | | typedef struct |
369 | | { |
370 | | GROUPING_MPEG4_STACK_2D |
371 | | u32 *positions; |
372 | | } OrderedGroupStack; |
373 | | |
374 | | |
375 | | static s32 compare_priority(const void* elem1, const void* elem2) |
376 | 0 | { |
377 | 0 | struct og_pos *p1, *p2; |
378 | 0 | p1 = (struct og_pos *)elem1; |
379 | 0 | p2 = (struct og_pos *)elem2; |
380 | 0 | if (p1->priority < p2->priority) return -1; |
381 | 0 | if (p1->priority > p2->priority) return 1; |
382 | 0 | return 0; |
383 | 0 | } |
384 | | |
385 | | |
386 | | static void TraverseOrderedGroup(GF_Node *node, void *rs, Bool is_destroy) |
387 | 0 | { |
388 | 0 | u32 i, count; |
389 | 0 | struct og_pos *priorities; |
390 | 0 | Bool invalidate_backup; |
391 | 0 | OrderedGroupStack *stack = (OrderedGroupStack *) gf_node_get_private(node); |
392 | 0 | M_OrderedGroup *og = (M_OrderedGroup *) node; |
393 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
394 | |
|
395 | 0 | if (is_destroy) { |
396 | 0 | gf_sc_check_focus_upon_destroy(node); |
397 | 0 | group_2d_destroy(node, (GroupingNode2D*)stack); |
398 | 0 | if (stack->positions) gf_free(stack->positions); |
399 | 0 | gf_free(stack); |
400 | 0 | return; |
401 | 0 | } |
402 | | |
403 | 0 | if (!og->order.count || (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) ) { |
404 | 0 | gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); |
405 | 0 | group_2d_traverse(node, (GroupingNode2D*)stack, tr_state); |
406 | 0 | return; |
407 | 0 | } |
408 | | |
409 | 0 | invalidate_backup = tr_state->invalidate_all; |
410 | | /*check whether the OrderedGroup node has changed*/ |
411 | 0 | if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { |
412 | 0 | if (stack->positions) gf_free(stack->positions); |
413 | 0 | count = gf_node_list_get_count(og->children); |
414 | 0 | priorities = (struct og_pos*)gf_malloc(sizeof(struct og_pos)*count); |
415 | 0 | for (i=0; i<count; i++) { |
416 | 0 | priorities[i].position = i; |
417 | 0 | priorities[i].priority = (i<og->order.count) ? og->order.vals[i] : 0; |
418 | 0 | } |
419 | 0 | qsort(priorities, count, sizeof(struct og_pos), compare_priority); |
420 | |
|
421 | 0 | stack->positions = (u32*)gf_malloc(sizeof(u32) * count); |
422 | 0 | for (i=0; i<count; i++) stack->positions[i] = priorities[i].position; |
423 | 0 | gf_free(priorities); |
424 | |
|
425 | 0 | tr_state->invalidate_all = 1; |
426 | 0 | gf_node_dirty_clear(node, GF_SG_NODE_DIRTY); |
427 | 0 | } |
428 | 0 | group_2d_traverse_with_order(node, (GroupingNode2D*)stack, tr_state, stack->positions); |
429 | 0 | tr_state->invalidate_all = invalidate_backup; |
430 | 0 | } |
431 | | |
432 | | void compositor_init_orderedgroup(GF_Compositor *compositor, GF_Node *node) |
433 | 0 | { |
434 | 0 | OrderedGroupStack *ptr; |
435 | 0 | GF_SAFEALLOC(ptr, OrderedGroupStack); |
436 | 0 | if (!ptr) return; |
437 | | |
438 | 0 | gf_node_set_private(node, ptr); |
439 | 0 | gf_node_set_callback_function(node, TraverseOrderedGroup); |
440 | 0 | } |
441 | | |
442 | | #endif //!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR) |
443 | | |