Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
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 : #ifndef GPAC_DISABLE_VRML
31 :
32 : typedef struct
33 : {
34 : PARENT_MPEG4_STACK_2D
35 :
36 : GF_Node *last_geom;
37 : GF_PathIterator *iter;
38 : } PathLayoutStack;
39 :
40 :
41 :
42 :
43 489 : static void TraversePathLayout(GF_Node *node, void *rs, Bool is_destroy)
44 : {
45 : u32 i, count, minor, major, int_bck;
46 : Fixed length, offset, length_after_point;
47 : Bool res;
48 : u32 mode_bckup;
49 : ChildGroup *cg;
50 : #ifndef GPAC_DISABLE_3D
51 : GF_Matrix mat;
52 : #endif
53 : GF_Matrix2D mx2d;
54 : ParentNode2D *parent_bck;
55 489 : PathLayoutStack *gr = (PathLayoutStack*) gf_node_get_private(node);
56 : M_PathLayout *pl = (M_PathLayout *)node;
57 : GF_TraverseState *tr_state = (GF_TraverseState *) rs;
58 :
59 489 : if (is_destroy) {
60 8 : parent_node_predestroy((ParentNode2D *)gr);
61 8 : if (gr->iter) gf_path_iterator_del(gr->iter);
62 8 : gf_free(gr);
63 8 : return;
64 : }
65 481 : if (!pl->geometry) return;
66 :
67 : /*only low-level primitives allowed*/
68 339 : switch (gf_node_get_tag((GF_Node *) pl->geometry)) {
69 : case TAG_MPEG4_Rectangle:
70 : return;
71 : case TAG_MPEG4_Circle:
72 : return;
73 : case TAG_MPEG4_Ellipse:
74 : return;
75 : }
76 :
77 : /*store traversing state*/
78 : #ifndef GPAC_DISABLE_3D
79 339 : gf_mx_copy(mat, tr_state->model_matrix);
80 678 : gf_mx_init(tr_state->model_matrix);
81 : #endif
82 :
83 339 : gf_mx2d_copy(mx2d, tr_state->transform);
84 339 : gf_mx2d_init(tr_state->transform);
85 :
86 339 : parent_bck = tr_state->parent;
87 339 : tr_state->parent = NULL;
88 :
89 : /*check geom changes*/
90 339 : if ((pl->geometry != gr->last_geom) || gf_node_dirty_get(pl->geometry)) {
91 339 : if (gr->iter) gf_path_iterator_del(gr->iter);
92 339 : gr->iter = NULL;
93 :
94 339 : int_bck = tr_state->switched_off;
95 339 : mode_bckup = tr_state->traversing_mode;
96 339 : tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
97 339 : tr_state->switched_off = 1;
98 339 : gf_node_traverse((GF_Node *)pl->geometry, tr_state);
99 339 : tr_state->traversing_mode = mode_bckup;
100 339 : tr_state->switched_off = int_bck;
101 : }
102 :
103 339 : if (!gr->iter) {
104 : Drawable *dr = NULL;
105 : /*get the drawable */
106 339 : switch (gf_node_get_tag(pl->geometry) ) {
107 339 : case TAG_MPEG4_Circle:
108 : case TAG_MPEG4_Curve2D:
109 : case TAG_MPEG4_XCurve2D:
110 : case TAG_MPEG4_Ellipse:
111 : case TAG_MPEG4_IndexedLineSet2D:
112 : case TAG_MPEG4_IndexedFaceSet2D:
113 : case TAG_MPEG4_Rectangle:
114 : #ifndef GPAC_DISABLE_X3D
115 : case TAG_X3D_Disk2D:
116 : case TAG_X3D_Arc2D:
117 : case TAG_X3D_Polyline2D:
118 : case TAG_X3D_TriangleSet2D:
119 : #endif
120 339 : dr = (Drawable *) gf_node_get_private( (GF_Node *) pl->geometry);
121 : break;
122 : default:
123 : break;
124 : }
125 : /*init iteration*/
126 339 : if (!dr || !dr->path) return;
127 339 : gr->iter = gf_path_iterator_new(dr->path);
128 339 : if (!gr->iter) return;
129 : }
130 :
131 339 : tr_state->parent = (ParentNode2D *) gr;
132 339 : int_bck = tr_state->text_split_mode;
133 339 : tr_state->text_split_mode = 2;
134 339 : mode_bckup = tr_state->traversing_mode;
135 339 : tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
136 339 : parent_node_traverse(node, (ParentNode2D *) gr, tr_state);
137 339 : tr_state->text_split_mode = int_bck;
138 339 : tr_state->traversing_mode = mode_bckup;
139 :
140 : /*restore traversing state*/
141 : #ifndef GPAC_DISABLE_3D
142 : gf_mx_copy(tr_state->model_matrix, mat);
143 : #endif
144 339 : tr_state->parent = parent_bck;
145 : gf_mx2d_copy(tr_state->transform, mx2d);
146 :
147 339 : count = gf_list_count(gr->groups);
148 :
149 339 : length = gf_path_iterator_get_length(gr->iter);
150 : /*place all children*/
151 339 : offset = gf_mulfix(length, pl->pathOffset);
152 :
153 339 : major = pl->alignment.count ? pl->alignment.vals[0] : 0;
154 339 : minor = (pl->alignment.count==2) ? pl->alignment.vals[1] : 0;
155 :
156 339 : if (pl->wrapMode==1) {
157 48 : while (offset<0) offset += length;
158 : }
159 :
160 4548 : for (i=0; i<count; i++) {
161 4548 : cg = (ChildGroup*)gf_list_get(gr->groups, i);
162 4548 : if (cg->original.width>length) break;
163 :
164 : /*first set our center and baseline*/
165 4548 : gf_mx2d_init(mx2d);
166 :
167 : /*major align*/
168 4548 : switch (major) {
169 0 : case 2:
170 0 : if (cg->ascent) gf_mx2d_add_translation(&mx2d, -cg->original.x - cg->original.width, 0);
171 0 : else gf_mx2d_add_translation(&mx2d, -cg->original.width/2, 0);
172 : length_after_point = 0;
173 : break;
174 0 : case 1:
175 0 : length_after_point = cg->original.width/2;
176 0 : if (cg->ascent) gf_mx2d_add_translation(&mx2d, -cg->original.x - cg->original.width / 2, 0);
177 : break;
178 4548 : default:
179 : case 0:
180 4548 : if (cg->ascent) gf_mx2d_add_translation(&mx2d, cg->original.x, 0);
181 1044 : else gf_mx2d_add_translation(&mx2d, cg->original.width/2, 0);
182 4548 : length_after_point = cg->original.width;
183 4548 : break;
184 : }
185 :
186 : /*if wrapping and out of path, restart*/
187 4548 : if ((pl->wrapMode==1) && (offset+length_after_point>=length)) {
188 : offset += length_after_point;
189 48 : offset -= length;
190 48 : i--;
191 48 : continue;
192 : }
193 : /*if not wrapping and not yet in path skip */
194 4500 : if (!pl->wrapMode && (offset+length_after_point < 0)) {
195 1094 : parent_node_child_traverse_matrix(cg, (GF_TraverseState *)rs, NULL);
196 1094 : goto next;
197 : }
198 :
199 : /*minor justify*/
200 3406 : switch (minor) {
201 : /*top alignment*/
202 0 : case 3:
203 0 : if (cg->ascent)
204 0 : gf_mx2d_add_translation(&mx2d, 0, -1 * cg->ascent);
205 : else
206 0 : gf_mx2d_add_translation(&mx2d, 0, -1 * cg->original.height / 2);
207 :
208 : break;
209 : /*baseline*/
210 3406 : case 1:
211 : /*move to bottom align if not text*/
212 3406 : if (!cg->ascent)
213 727 : gf_mx2d_add_translation(&mx2d, 0, cg->original.height / 2);
214 : break;
215 : /*middle*/
216 0 : case 2:
217 : /*if text use (asc+desc) /2 as line height since glyph height differ*/
218 0 : if (cg->ascent)
219 0 : gf_mx2d_add_translation(&mx2d, 0, cg->descent - (cg->ascent + cg->descent) / 2);
220 : break;
221 : /*bottomline alignment*/
222 0 : case 0:
223 : default:
224 0 : if (cg->ascent)
225 0 : gf_mx2d_add_translation(&mx2d, 0, cg->descent);
226 : else
227 0 : gf_mx2d_add_translation(&mx2d, 0, cg->original.height / 2);
228 :
229 : break;
230 : }
231 3406 : res = gf_path_iterator_get_transform(gr->iter, offset, (Bool) (pl->wrapMode==2), &mx2d, 1, length_after_point);
232 3406 : if (!res) break;
233 :
234 3406 : parent_node_child_traverse_matrix(cg, (GF_TraverseState *)rs, &mx2d);
235 :
236 4500 : next:
237 4500 : if (i+1<count) {
238 4161 : ChildGroup *cg_next = (ChildGroup*)gf_list_get(gr->groups, i+1);
239 :
240 : /*update offset according to major alignment */
241 4161 : switch (major) {
242 0 : case 2:
243 0 : if (cg_next->ascent) offset += gf_mulfix(pl->spacing , cg_next->original.x);
244 0 : offset += gf_mulfix(pl->spacing , cg_next->original.width);
245 0 : break;
246 0 : case 1:
247 0 : if (cg->ascent) offset += gf_mulfix(pl->spacing, cg->original.x) / 2;
248 0 : offset += gf_mulfix(pl->spacing, cg->original.width) / 2;
249 0 : offset += cg_next->original.width / 2;
250 0 : break;
251 4161 : default:
252 : case 0:
253 4161 : if (cg->ascent) offset += gf_mulfix(pl->spacing, cg->original.x);
254 4161 : offset += gf_mulfix(pl->spacing , cg->original.width);
255 4161 : break;
256 : }
257 339 : }
258 : /*wrap*/
259 4500 : if ((pl->wrapMode==1) && (offset>=length)) offset-=length;
260 : }
261 :
262 : /*undrawn nodes*/
263 0 : for (; i<count; i++) {
264 0 : cg = (ChildGroup*)gf_list_get(gr->groups, i);
265 0 : parent_node_child_traverse_matrix(cg, (GF_TraverseState *)rs, NULL);
266 : }
267 :
268 339 : parent_node_reset((ParentNode2D *) gr);
269 : }
270 :
271 8 : void compositor_init_path_layout(GF_Compositor *compositor, GF_Node *node)
272 : {
273 : PathLayoutStack *stack;
274 8 : GF_SAFEALLOC(stack, PathLayoutStack);
275 8 : if (!stack) return;
276 :
277 8 : parent_node_setup((ParentNode2D*)stack);
278 8 : gf_node_set_private(node, stack);
279 8 : gf_node_set_callback_function(node, TraversePathLayout);
280 : }
281 :
282 : #endif /*GPAC_DISABLE_VRML*/
|