/src/gpac/src/compositor/mpeg4_layout.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 | | PARENT_MPEG4_STACK_2D |
35 | | |
36 | | Bool is_scrolling; |
37 | | u32 start_scroll_type; |
38 | | Double start_time, pause_time; |
39 | | GF_List *lines; |
40 | | GF_Rect clip; |
41 | | Fixed last_scroll, prev_rate, scroll_rate, scale_scroll, scroll_len, scroll_min, scroll_max; |
42 | | |
43 | | /*for keyboard navigation*/ |
44 | | GF_SensorHandler hdl; |
45 | | s32 key_scroll; |
46 | | Bool keys_active; |
47 | | } LayoutStack; |
48 | | |
49 | | typedef struct |
50 | | { |
51 | | Fixed width, height, ascent, descent; |
52 | | u32 first_child, nb_children; |
53 | | Bool line_break; |
54 | | } LineInfo; |
55 | | |
56 | | static void layout_reset_lines(LayoutStack *st) |
57 | 0 | { |
58 | 0 | while (gf_list_count(st->lines)) { |
59 | 0 | LineInfo *li = (LineInfo *)gf_list_get(st->lines, 0); |
60 | 0 | gf_list_rem(st->lines, 0); |
61 | 0 | gf_free(li); |
62 | 0 | } |
63 | 0 | } |
64 | | |
65 | | static LineInfo *new_line_info(LayoutStack *st) |
66 | 0 | { |
67 | 0 | LineInfo *li; |
68 | 0 | GF_SAFEALLOC(li, LineInfo); |
69 | 0 | if (li) |
70 | 0 | gf_list_add(st->lines, li); |
71 | 0 | return li; |
72 | 0 | } |
73 | | |
74 | | |
75 | | enum |
76 | | { |
77 | | L_FIRST, |
78 | | L_BEGIN, |
79 | | L_MIDDLE, |
80 | | L_END, |
81 | | L_JUSTIFY, |
82 | | }; |
83 | | |
84 | | static u32 get_justify(M_Layout *l, u32 i) |
85 | 0 | { |
86 | 0 | if (l->justify.count <= i) return L_BEGIN; |
87 | 0 | if (!strcmp(l->justify.vals[i], "END")) return L_END; |
88 | 0 | if (!strcmp(l->justify.vals[i], "MIDDLE")) return L_MIDDLE; |
89 | 0 | if (!strcmp(l->justify.vals[i], "FIRST")) return L_FIRST; |
90 | 0 | if (!strcmp(l->justify.vals[i], "SPREAD")) return L_JUSTIFY; |
91 | 0 | if (!strcmp(l->justify.vals[i], "JUSTIFY")) return L_JUSTIFY; |
92 | 0 | return L_BEGIN; |
93 | 0 | } |
94 | | |
95 | | static void get_lines_info(LayoutStack *st, M_Layout *l) |
96 | 0 | { |
97 | 0 | Fixed prev_discard_width; |
98 | 0 | u32 i, count; |
99 | 0 | LineInfo *li; |
100 | 0 | Fixed max_w, max_h; |
101 | |
|
102 | 0 | max_w = st->clip.width; |
103 | 0 | max_h = st->clip.height; |
104 | 0 | layout_reset_lines(st); |
105 | |
|
106 | 0 | count = gf_list_count(st->groups); |
107 | 0 | if (!count) return; |
108 | | |
109 | 0 | li = new_line_info(st); |
110 | 0 | li->first_child = 0; |
111 | 0 | prev_discard_width = 0; |
112 | |
|
113 | 0 | for (i=0; i<count; i++) { |
114 | 0 | ChildGroup *cg = (ChildGroup *)gf_list_get(st->groups, i); |
115 | 0 | if (!l->horizontal) { |
116 | | /*check if exceed column size or not - if so, move to next column or clip given wrap mode*/ |
117 | 0 | if (cg->final.height + li->height > max_h) { |
118 | 0 | if (l->wrap) { |
119 | 0 | li = new_line_info(st); |
120 | 0 | li->first_child = i; |
121 | 0 | } |
122 | 0 | } |
123 | 0 | if (cg->final.width > li->width) li->width = cg->final.width; |
124 | 0 | li->height += cg->final.height; |
125 | 0 | li->nb_children ++; |
126 | 0 | } else { |
127 | 0 | if ((cg->text_type==2) || (i && (cg->final.width + li->width> max_w))) { |
128 | 0 | if (cg->text_type==2) li->line_break = 1; |
129 | 0 | if (l->wrap) { |
130 | 0 | if (!li->ascent) { |
131 | 0 | li->ascent = li->height; |
132 | 0 | li->descent = 0; |
133 | 0 | } |
134 | | /*previous word is discardable (' ')*/ |
135 | 0 | if (prev_discard_width) { |
136 | 0 | li->width -= prev_discard_width; |
137 | 0 | li->nb_children--; |
138 | 0 | } |
139 | 0 | if ((cg->text_type==1) && (i+1==count)) break; |
140 | | |
141 | 0 | li = new_line_info(st); |
142 | 0 | li->first_child = i; |
143 | 0 | if (cg->text_type) { |
144 | 0 | li->first_child++; |
145 | 0 | continue; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | | /*get ascent/descent for text or height for non-text*/ |
151 | 0 | if (cg->ascent) { |
152 | 0 | if (li->ascent < cg->ascent) li->ascent = cg->ascent; |
153 | 0 | if (li->descent < cg->descent) li->descent = cg->descent; |
154 | 0 | if (li->height < li->ascent + li->descent) li->height = li->ascent + li->descent; |
155 | 0 | } else if (cg->final.height > li->height) { |
156 | 0 | li->height = cg->final.height; |
157 | 0 | } |
158 | 0 | li->width += cg->final.width; |
159 | 0 | li->nb_children ++; |
160 | |
|
161 | 0 | prev_discard_width = (cg->text_type==1) ? cg->final.width : 0; |
162 | |
|
163 | 0 | } |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | |
168 | | static void layout_justify(LayoutStack *st, M_Layout *l) |
169 | 0 | { |
170 | 0 | u32 first, minor, major, i, k, nbLines; |
171 | 0 | Fixed current_top, current_left, h; |
172 | 0 | LineInfo *li; |
173 | 0 | ChildGroup *cg, *prev; |
174 | 0 | get_lines_info(st, l); |
175 | 0 | major = get_justify(l, 0); |
176 | 0 | minor = get_justify(l, 1); |
177 | |
|
178 | 0 | st->scroll_len = 0; |
179 | 0 | nbLines = gf_list_count(st->lines); |
180 | 0 | if (l->horizontal) { |
181 | 0 | if (l->wrap && !l->topToBottom) { |
182 | 0 | li = (LineInfo*)gf_list_get(st->lines, 0); |
183 | 0 | current_top = st->clip.y - st->clip.height; |
184 | 0 | if (li) current_top += li->height; |
185 | 0 | } else { |
186 | 0 | current_top = st->clip.y; |
187 | 0 | } |
188 | | |
189 | | /*for each line perform adjustment*/ |
190 | 0 | for (k=0; k<nbLines; k++) { |
191 | 0 | Fixed spacing = 0; |
192 | 0 | li = (LineInfo*)gf_list_get(st->lines, k); |
193 | 0 | first = li->first_child; |
194 | 0 | if (!l->leftToRight) first += li->nb_children - 1; |
195 | |
|
196 | 0 | if (!l->topToBottom && k) current_top += li->height; |
197 | | |
198 | | /*set major alignment (X) */ |
199 | 0 | cg = (ChildGroup *)gf_list_get(st->groups, first); |
200 | 0 | if (!cg) continue; |
201 | 0 | switch (major) { |
202 | 0 | case L_END: |
203 | 0 | cg->final.x = st->clip.x + st->clip.width - li->width; |
204 | 0 | break; |
205 | 0 | case L_MIDDLE: |
206 | 0 | cg->final.x = st->clip.x + (st->clip.width - li->width)/2; |
207 | 0 | break; |
208 | 0 | case L_FIRST: |
209 | 0 | case L_BEGIN: |
210 | 0 | cg->final.x = st->clip.x; |
211 | 0 | break; |
212 | 0 | case L_JUSTIFY: |
213 | 0 | cg->final.x = st->clip.x; |
214 | 0 | if (li->nb_children>1) { |
215 | 0 | cg = (ChildGroup *)gf_list_get(st->groups, li->nb_children-1); |
216 | 0 | spacing = (st->clip.width - li->width) / (li->nb_children-1) ; |
217 | 0 | if (spacing<0) spacing = 0; |
218 | 0 | else if (cg->ascent) { |
219 | | /*disable spacing for last text line and line breaks*/ |
220 | 0 | if ( (k+1==nbLines) || li->line_break) { |
221 | 0 | spacing = 0; |
222 | 0 | } |
223 | 0 | } |
224 | 0 | } |
225 | 0 | break; |
226 | 0 | } |
227 | | |
228 | | |
229 | | /*for each in the run */ |
230 | 0 | i = first; |
231 | 0 | while (1) { |
232 | 0 | cg = (ChildGroup *)gf_list_get(st->groups, i); |
233 | 0 | if (!cg) break; |
234 | 0 | h = MAX(li->ascent, li->height); |
235 | 0 | switch (minor) { |
236 | 0 | case L_FIRST: |
237 | 0 | cg->final.y = current_top - h; |
238 | 0 | if (cg->ascent) { |
239 | 0 | cg->final.y += cg->ascent; |
240 | 0 | } else { |
241 | 0 | cg->final.y += cg->final.height; |
242 | 0 | } |
243 | 0 | break; |
244 | 0 | case L_MIDDLE: |
245 | 0 | cg->final.y = current_top - (h - cg->final.height)/2; |
246 | 0 | break; |
247 | 0 | case L_END: |
248 | 0 | cg->final.y = current_top; |
249 | 0 | break; |
250 | 0 | case L_BEGIN: |
251 | 0 | default: |
252 | 0 | cg->final.y = current_top - h + cg->final.height; |
253 | 0 | break; |
254 | 0 | } |
255 | | /*update left for non-first children in line*/ |
256 | 0 | if (i != first) { |
257 | 0 | if (l->leftToRight) { |
258 | 0 | prev = (ChildGroup *)gf_list_get(st->groups, i-1); |
259 | 0 | } else { |
260 | 0 | prev = (ChildGroup *)gf_list_get(st->groups, i+1); |
261 | 0 | } |
262 | 0 | cg->final.x = prev->final.x + prev->final.width + spacing; |
263 | 0 | } |
264 | 0 | i += l->leftToRight ? +1 : -1; |
265 | 0 | if (l->leftToRight && (i==li->first_child + li->nb_children)) |
266 | 0 | break; |
267 | 0 | else if (!l->leftToRight && (i==li->first_child - 1)) |
268 | 0 | break; |
269 | 0 | } |
270 | 0 | if (l->topToBottom) { |
271 | 0 | current_top -= gf_mulfix(l->spacing, li->height); |
272 | 0 | } else { |
273 | 0 | current_top += gf_mulfix(l->spacing - FIX_ONE, li->height); |
274 | 0 | } |
275 | 0 | if (l->scrollVertical) { |
276 | 0 | st->scroll_len += li->height; |
277 | 0 | } else { |
278 | 0 | if (st->scroll_len < li->width) st->scroll_len = li->width; |
279 | 0 | } |
280 | 0 | } |
281 | 0 | return; |
282 | 0 | } |
283 | | |
284 | | /*Vertical aligment*/ |
285 | 0 | li = (LineInfo*)gf_list_get(st->lines, 0); |
286 | 0 | if (l->wrap && !l->leftToRight) { |
287 | 0 | current_left = st->clip.x + st->clip.width; |
288 | 0 | if (li) current_left -= li->width; |
289 | 0 | } else { |
290 | 0 | current_left = st->clip.x; |
291 | 0 | } |
292 | | |
293 | | /*for all columns in run*/ |
294 | 0 | for (k=0; k<nbLines; k++) { |
295 | 0 | Fixed spacing = 0; |
296 | 0 | li = (LineInfo*)gf_list_get(st->lines, k); |
297 | |
|
298 | 0 | first = li->first_child; |
299 | 0 | if (!l->topToBottom) first += li->nb_children - 1; |
300 | | |
301 | | /*set major alignment (Y) */ |
302 | 0 | cg = (ChildGroup *)gf_list_get(st->groups, first); |
303 | 0 | switch (major) { |
304 | 0 | case L_END: |
305 | 0 | cg->final.y = st->clip.y - st->clip.height + li->height; |
306 | 0 | break; |
307 | 0 | case L_MIDDLE: |
308 | 0 | cg->final.y = st->clip.y - st->clip.height/2 + li->height/2; |
309 | 0 | break; |
310 | 0 | case L_FIRST: |
311 | 0 | case L_BEGIN: |
312 | 0 | cg->final.y = st->clip.y; |
313 | 0 | break; |
314 | 0 | case L_JUSTIFY: |
315 | 0 | cg->final.y = st->clip.y; |
316 | 0 | if (li->nb_children>1) { |
317 | 0 | spacing = (st->clip.height - li->height) / (li->nb_children-1) ; |
318 | 0 | if (spacing<0) spacing = 0; |
319 | 0 | } |
320 | 0 | break; |
321 | 0 | } |
322 | | |
323 | | /*for each in the run */ |
324 | 0 | i = first; |
325 | 0 | while (1) { |
326 | 0 | cg = (ChildGroup *)gf_list_get(st->groups, i); |
327 | 0 | switch (minor) { |
328 | 0 | case L_MIDDLE: |
329 | 0 | cg->final.x = current_left + li->width/2 - cg->final.width/2; |
330 | 0 | break; |
331 | 0 | case L_END: |
332 | 0 | cg->final.x = current_left + li->width - cg->final.width; |
333 | 0 | break; |
334 | 0 | case L_BEGIN: |
335 | 0 | case L_FIRST: |
336 | 0 | default: |
337 | 0 | cg->final.x = current_left; |
338 | 0 | break; |
339 | 0 | } |
340 | | /*update top for non-first children in line*/ |
341 | 0 | if (i != first) { |
342 | 0 | if (l->topToBottom) { |
343 | 0 | prev = (ChildGroup *)gf_list_get(st->groups, i-1); |
344 | 0 | } else { |
345 | 0 | prev = (ChildGroup *)gf_list_get(st->groups, i+1); |
346 | 0 | } |
347 | 0 | cg->final.y = prev->final.y - prev->final.height + spacing; |
348 | 0 | } |
349 | 0 | i += l->topToBottom ? +1 : -1; |
350 | 0 | if (l->topToBottom && (i==li->first_child + li->nb_children)) |
351 | 0 | break; |
352 | 0 | else if (!l->topToBottom && (i==li->first_child - 1)) |
353 | 0 | break; |
354 | 0 | } |
355 | 0 | if (l->leftToRight) { |
356 | 0 | current_left += gf_mulfix(l->spacing, li->width); |
357 | 0 | } else if (k < nbLines - 1) { |
358 | 0 | li = (LineInfo*)gf_list_get(st->lines, k+1); |
359 | 0 | current_left -= gf_mulfix(l->spacing, li->width); |
360 | 0 | } |
361 | 0 | if (l->scrollVertical) { |
362 | 0 | if (st->scroll_len < li->height) st->scroll_len = li->height; |
363 | 0 | } else { |
364 | 0 | st->scroll_len += li->width; |
365 | 0 | } |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | | static void layout_setup_scroll_bounds(LayoutStack *st, M_Layout *l) |
370 | 0 | { |
371 | 0 | u32 minor_justify = 0; |
372 | |
|
373 | 0 | st->scroll_min = st->scroll_max = 0; |
374 | |
|
375 | 0 | if (l->horizontal) minor_justify = l->scrollVertical ? 1 : 0; |
376 | 0 | else minor_justify = l->scrollVertical ? 0 : 1; |
377 | | |
378 | | /*update scroll-out max limit*/ |
379 | 0 | if (l->scrollMode != -1) { |
380 | | /*set max limit*/ |
381 | 0 | switch( get_justify(l, minor_justify)) { |
382 | 0 | case L_END: |
383 | 0 | if (l->scrollVertical) { |
384 | 0 | if (st->scale_scroll<0) st->scroll_max = - st->scroll_len; |
385 | 0 | else st->scroll_max = st->clip.height; |
386 | 0 | } else { |
387 | 0 | if (st->scale_scroll<0) st->scroll_max = - st->clip.width; |
388 | 0 | else st->scroll_max = st->scroll_len; |
389 | 0 | } |
390 | 0 | break; |
391 | 0 | case L_MIDDLE: |
392 | 0 | if (l->scrollVertical) { |
393 | 0 | if (st->scale_scroll<0) st->scroll_max = - (st->clip.height + st->scroll_len)/2; |
394 | 0 | else st->scroll_max = (st->clip.height + st->scroll_len)/2; |
395 | 0 | } else { |
396 | 0 | if (st->scale_scroll<0) st->scroll_max = - (st->clip.width + st->scroll_len)/2; |
397 | 0 | else st->scroll_max = (st->clip.width + st->scroll_len)/2; |
398 | 0 | } |
399 | 0 | break; |
400 | 0 | default: |
401 | 0 | if (l->scrollVertical) { |
402 | 0 | if (st->scale_scroll<0) st->scroll_max = - st->clip.height; |
403 | 0 | else st->scroll_max = st->scroll_len; |
404 | 0 | } else { |
405 | 0 | if (st->scale_scroll<0) st->scroll_max = - st->scroll_len; |
406 | 0 | else st->scroll_max = st->clip.width; |
407 | 0 | } |
408 | 0 | break; |
409 | 0 | } |
410 | 0 | } |
411 | | /*scroll-in only*/ |
412 | 0 | else { |
413 | 0 | st->scroll_max = 0; |
414 | 0 | } |
415 | | |
416 | | /*scroll-out only*/ |
417 | 0 | if (l->scrollMode==1) { |
418 | 0 | st->scroll_min = 0; |
419 | 0 | return; |
420 | 0 | } |
421 | | |
422 | | /*when vertically scrolling an horizontal layout, don't use vertical justification, only justify top/bottom lines*/ |
423 | 0 | if (l->horizontal && l->scrollVertical) { |
424 | 0 | if (st->scale_scroll<0) { |
425 | 0 | st->scroll_min = st->scroll_len; |
426 | 0 | } else { |
427 | 0 | st->scroll_min = - st->clip.height; |
428 | 0 | } |
429 | 0 | return; |
430 | 0 | } |
431 | | |
432 | | /*update scroll-in offset*/ |
433 | 0 | switch( get_justify(l, minor_justify)) { |
434 | 0 | case L_END: |
435 | 0 | if (l->scrollVertical) { |
436 | 0 | if (st->scale_scroll<0) st->scroll_min = st->clip.height; |
437 | 0 | else st->scroll_min = - st->scroll_len; |
438 | 0 | } else { |
439 | 0 | if (st->scale_scroll<0) st->scroll_min = st->scroll_len; |
440 | 0 | else st->scroll_min = -st->clip.width; |
441 | 0 | } |
442 | 0 | break; |
443 | 0 | case L_MIDDLE: |
444 | 0 | if (l->scrollVertical) { |
445 | 0 | if (st->scale_scroll<0) st->scroll_min = (st->clip.height + st->scroll_len)/2; |
446 | 0 | else st->scroll_min = - (st->clip.height + st->scroll_len)/2; |
447 | 0 | } else { |
448 | 0 | if (st->scale_scroll<0) st->scroll_min = (st->clip.width + st->scroll_len)/2; |
449 | 0 | else st->scroll_min = - (st->clip.width + st->scroll_len)/2; |
450 | 0 | } |
451 | 0 | break; |
452 | 0 | default: |
453 | 0 | if (l->scrollVertical) { |
454 | 0 | if (st->scale_scroll<0) st->scroll_min = st->scroll_len; |
455 | 0 | else st->scroll_min = - st->clip.height; |
456 | 0 | } else { |
457 | 0 | if (st->scale_scroll<0) st->scroll_min = st->clip.width; |
458 | 0 | else st->scroll_min = - st->scroll_len; |
459 | 0 | } |
460 | 0 | break; |
461 | 0 | } |
462 | 0 | } |
463 | | |
464 | | |
465 | | static void layout_scroll(GF_TraverseState *tr_state, LayoutStack *st, M_Layout *l) |
466 | 0 | { |
467 | 0 | u32 i, nb_lines; |
468 | 0 | Fixed scrolled, rate, elapsed, scroll_diff; |
469 | 0 | Bool smooth, do_scroll, stop_anim; |
470 | 0 | Double time = 0; |
471 | 0 | ChildGroup *cg; |
472 | | |
473 | | /*not scrolling*/ |
474 | 0 | if (!st->scale_scroll && !st->is_scrolling && !st->key_scroll) return; |
475 | | |
476 | 0 | if (st->key_scroll) { |
477 | 0 | if (!st->is_scrolling) { |
478 | 0 | layout_setup_scroll_bounds(st, l); |
479 | 0 | st->is_scrolling = 1; |
480 | 0 | } |
481 | |
|
482 | 0 | scrolled = st->last_scroll + INT2FIX(st->key_scroll); |
483 | 0 | } else { |
484 | |
|
485 | 0 | time = gf_node_get_scene_time((GF_Node *)l); |
486 | | |
487 | | // if (st->scale_scroll && (st->prev_rate!=st->scale_scroll)) st->start_scroll_type = 1; |
488 | | |
489 | | /*if scroll rate changed to previous non-zero value, this is a |
490 | | scroll restart, don't re-update bounds*/ |
491 | 0 | if ((st->start_scroll_type==2) && (st->prev_rate==st->scale_scroll)) st->start_scroll_type = 0; |
492 | |
|
493 | 0 | if (st->start_scroll_type) { |
494 | 0 | st->start_time = time; |
495 | 0 | st->is_scrolling = 1; |
496 | 0 | st->prev_rate = st->scale_scroll; |
497 | | |
498 | | /*continuous restart: use last scroll to update the start time. We must recompute scroll bounds |
499 | | since switching from scroll_rate >0 to <0 changes the bounds !*/ |
500 | 0 | if ((st->start_scroll_type==2) && st->scale_scroll) { |
501 | 0 | Fixed cur_pos = st->scroll_min + st->last_scroll; |
502 | 0 | layout_setup_scroll_bounds(st, l); |
503 | 0 | cur_pos -= st->scroll_min; |
504 | 0 | st->start_time = time - FIX2FLT(gf_divfix(cur_pos, st->scale_scroll)); |
505 | 0 | } else { |
506 | 0 | layout_setup_scroll_bounds(st, l); |
507 | 0 | } |
508 | 0 | st->last_scroll = 0; |
509 | 0 | st->start_scroll_type = 0; |
510 | 0 | } |
511 | | |
512 | | /*handle pause/resume*/ |
513 | 0 | rate = st->scale_scroll; |
514 | 0 | if (!rate) { |
515 | 0 | if (!st->pause_time) { |
516 | 0 | st->pause_time = time; |
517 | 0 | } else { |
518 | 0 | time = st->pause_time; |
519 | 0 | } |
520 | 0 | rate = st->prev_rate; |
521 | 0 | } else if (st->pause_time) { |
522 | 0 | st->start_time += (time - st->pause_time); |
523 | 0 | st->pause_time = 0; |
524 | 0 | } |
525 | | |
526 | | /*compute advance in pixels for smooth scroll*/ |
527 | 0 | elapsed = FLT2FIX((Float) (time - st->start_time)); |
528 | 0 | scrolled = gf_mulfix(elapsed, rate); |
529 | 0 | } |
530 | |
|
531 | 0 | smooth = l->smoothScroll; |
532 | | /*if the scroll is in the same direction as the layout, there is no notion of line or column to scroll |
533 | | so move to smooth mode*/ |
534 | 0 | if (!l->horizontal && l->scrollVertical) smooth = 1; |
535 | 0 | else if (l->horizontal && !l->scrollVertical) smooth = 1; |
536 | | |
537 | |
|
538 | 0 | stop_anim = 0; |
539 | | /*compute scroll diff for non-smooth mode*/ |
540 | 0 | if (smooth) { |
541 | 0 | do_scroll = 1; |
542 | 0 | } else { |
543 | 0 | Fixed dim; |
544 | 0 | scroll_diff = scrolled - st->last_scroll; |
545 | 0 | do_scroll = 0; |
546 | |
|
547 | 0 | nb_lines = gf_list_count(st->lines); |
548 | 0 | for (i=0; i < nb_lines; i++) { |
549 | 0 | LineInfo *li = (LineInfo*)gf_list_get(st->lines, i); |
550 | 0 | dim = l->scrollVertical ? li->height : li->width; |
551 | 0 | if (st->key_scroll) { |
552 | 0 | if (st->key_scroll<0) dim = -dim; |
553 | 0 | scrolled = dim + st->last_scroll; |
554 | | /*in key mode we must handle the min ourselves since we can go below the scroll limit*/ |
555 | 0 | if (st->scroll_min > st->scroll_len + scrolled) { |
556 | 0 | scrolled = st->scroll_min - st->scroll_len; |
557 | 0 | } else { |
558 | 0 | do_scroll = 1; |
559 | 0 | } |
560 | 0 | break; |
561 | 0 | } else if (ABS(scroll_diff) >= dim) { |
562 | | // if (scroll_diff<0) scroll_diff = -dim; |
563 | | // else scroll_diff = dim; |
564 | 0 | do_scroll = 1; |
565 | 0 | break; |
566 | 0 | } |
567 | 0 | } |
568 | 0 | } |
569 | |
|
570 | 0 | scroll_diff = st->scroll_max - st->scroll_min; |
571 | 0 | if ((scroll_diff<0) && (scrolled<=scroll_diff)) { |
572 | 0 | stop_anim = 1; |
573 | 0 | scrolled = scroll_diff; |
574 | 0 | } |
575 | 0 | else if ((scroll_diff>0) && (scrolled>=scroll_diff)) { |
576 | 0 | stop_anim = 1; |
577 | 0 | scrolled = scroll_diff; |
578 | 0 | } |
579 | |
|
580 | 0 | if (do_scroll) |
581 | 0 | st->last_scroll = scrolled; |
582 | 0 | else |
583 | 0 | scrolled = st->last_scroll; |
584 | |
|
585 | 0 | i=0; |
586 | 0 | while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) { |
587 | 0 | if (l->scrollVertical) { |
588 | 0 | cg->scroll_y = st->scroll_min + scrolled; |
589 | 0 | cg->scroll_x = 0; |
590 | 0 | } else { |
591 | 0 | cg->scroll_x = st->scroll_min + scrolled; |
592 | 0 | cg->scroll_y = 0; |
593 | 0 | } |
594 | 0 | } |
595 | |
|
596 | 0 | if (st->key_scroll) { |
597 | 0 | st->key_scroll = 0; |
598 | 0 | return; |
599 | 0 | } |
600 | | /*draw next frame*/ |
601 | 0 | if (!stop_anim) { |
602 | 0 | tr_state->visual->compositor->force_next_frame_redraw = GF_TRUE; |
603 | 0 | return; |
604 | 0 | } |
605 | | |
606 | | /*done*/ |
607 | 0 | if (!l->loop) return; |
608 | | |
609 | | /*restart*/ |
610 | 0 | st->start_time = time; |
611 | 0 | gf_sc_invalidate(tr_state->visual->compositor, NULL); |
612 | 0 | } |
613 | | |
614 | | |
615 | | static void TraverseLayout(GF_Node *node, void *rs, Bool is_destroy) |
616 | 0 | { |
617 | 0 | Bool recompute_layout; |
618 | 0 | u32 i; |
619 | 0 | ChildGroup *cg; |
620 | 0 | GF_IRect prev_clip; |
621 | 0 | Bool mode_bckup, had_clip=GF_FALSE; |
622 | 0 | ParentNode2D *parent_bck; |
623 | 0 | GF_Rect prev_clipper; |
624 | 0 | M_Layout *l = (M_Layout *)node; |
625 | 0 | LayoutStack *st = (LayoutStack *) gf_node_get_private(node); |
626 | 0 | GF_TraverseState *tr_state = (GF_TraverseState *)rs; |
627 | |
|
628 | 0 | if (is_destroy) { |
629 | 0 | layout_reset_lines(st); |
630 | 0 | parent_node_predestroy((ParentNode2D *)st); |
631 | 0 | gf_list_del(st->lines); |
632 | 0 | gf_free(st); |
633 | 0 | return; |
634 | 0 | } |
635 | | |
636 | | /*note we don't clear dirty flag, this is done in traversing*/ |
637 | 0 | if (gf_node_dirty_get(node) & GF_SG_NODE_DIRTY) { |
638 | | |
639 | | /*TO CHANGE IN BIFS - scroll_rate is quite unusable*/ |
640 | 0 | st->scale_scroll = st->scroll_rate = l->scrollRate; |
641 | | /*move to pixel metrics*/ |
642 | 0 | if (visual_get_size_info(tr_state, &st->clip.width, &st->clip.height)) { |
643 | 0 | st->scale_scroll = gf_mulfix(st->scale_scroll, l->scrollVertical ? st->clip.height : st->clip.width); |
644 | 0 | } |
645 | | /*setup bounds in local coord system*/ |
646 | 0 | if (l->size.x>=0) st->clip.width = l->size.x; |
647 | 0 | if (l->size.y>=0) st->clip.height = l->size.y; |
648 | 0 | st->bounds = st->clip = gf_rect_center(st->clip.width, st->clip.height); |
649 | |
|
650 | 0 | if (st->scale_scroll && !st->start_scroll_type) st->start_scroll_type = 1; |
651 | |
|
652 | 0 | drawable_reset_group_highlight(tr_state, node); |
653 | 0 | } |
654 | | |
655 | | /*don't waste time traversing is pick ray not in clipper*/ |
656 | 0 | if ((tr_state->traversing_mode==TRAVERSE_PICK) && !gf_sc_pick_in_clipper(tr_state, &st->clip)) |
657 | 0 | goto layout_exit; |
658 | | |
659 | 0 | if ((tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) && !tr_state->for_node) { |
660 | 0 | tr_state->bounds = st->clip; |
661 | | #ifndef GPAC_DISABLE_3D |
662 | | gf_bbox_from_rect(&tr_state->bbox, &st->clip); |
663 | | #endif |
664 | 0 | goto layout_exit; |
665 | 0 | } |
666 | | |
667 | 0 | recompute_layout = 0; |
668 | 0 | if (gf_node_dirty_get(node)) |
669 | 0 | recompute_layout = 1; |
670 | | |
671 | | /*setup clipping*/ |
672 | 0 | prev_clip = tr_state->visual->top_clipper; |
673 | 0 | if (tr_state->traversing_mode==TRAVERSE_SORT) { |
674 | 0 | compositor_2d_update_clipper(tr_state, st->clip, &had_clip, &prev_clipper, 0); |
675 | 0 | if (tr_state->has_clip) { |
676 | 0 | tr_state->visual->top_clipper = gf_rect_pixelize(&tr_state->clipper); |
677 | 0 | gf_irect_intersect(&tr_state->visual->top_clipper, &prev_clip); |
678 | 0 | } |
679 | 0 | } |
680 | 0 | if (recompute_layout) { |
681 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_COMPOSE, ("[Layout] recomputing positions\n")); |
682 | |
|
683 | 0 | parent_node_reset((ParentNode2D*)st); |
684 | | |
685 | | /*setup traversing state*/ |
686 | 0 | parent_bck = tr_state->parent; |
687 | 0 | mode_bckup = tr_state->traversing_mode; |
688 | 0 | tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; |
689 | 0 | tr_state->parent = (ParentNode2D *) st; |
690 | |
|
691 | 0 | if (l->wrap) tr_state->text_split_mode = 1; |
692 | 0 | parent_node_traverse(node, (ParentNode2D *)st, tr_state); |
693 | | /*restore traversing state*/ |
694 | 0 | tr_state->parent = parent_bck; |
695 | 0 | tr_state->traversing_mode = mode_bckup; |
696 | 0 | if (l->wrap) tr_state->text_split_mode = 0; |
697 | | |
698 | | /*center all nodes*/ |
699 | 0 | i=0; |
700 | 0 | while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) { |
701 | 0 | cg->final.x = - cg->final.width/2; |
702 | 0 | cg->final.y = cg->final.height/2; |
703 | 0 | } |
704 | | |
705 | | /*apply justification*/ |
706 | 0 | layout_justify(st, l); |
707 | | |
708 | | /*if scrolling, update bounds*/ |
709 | 0 | if (l->scrollRate && st->is_scrolling) { |
710 | 0 | layout_setup_scroll_bounds(st, l); |
711 | 0 | } |
712 | 0 | } |
713 | | |
714 | |
|
715 | 0 | if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) { |
716 | 0 | tr_state->bounds = st->bounds; |
717 | 0 | if (l->scrollVertical) { |
718 | 0 | tr_state->bounds.height = st->scroll_len; |
719 | 0 | } else { |
720 | 0 | tr_state->bounds.width = st->scroll_len; |
721 | 0 | } |
722 | | #ifndef GPAC_DISABLE_3D |
723 | | gf_bbox_from_rect(&tr_state->bbox, &tr_state->bounds); |
724 | | #endif |
725 | 0 | goto layout_exit; |
726 | 0 | } |
727 | | |
728 | | |
729 | | /*scroll*/ |
730 | 0 | layout_scroll(tr_state, st, l); |
731 | |
|
732 | 0 | i=0; |
733 | 0 | while ((cg = (ChildGroup *)gf_list_enum(st->groups, &i))) { |
734 | 0 | parent_node_child_traverse(cg, tr_state); |
735 | 0 | } |
736 | 0 | tr_state->visual->top_clipper = prev_clip; |
737 | 0 | if (tr_state->traversing_mode==TRAVERSE_SORT) { |
738 | 0 | if (had_clip) tr_state->clipper = prev_clipper; |
739 | 0 | tr_state->has_clip = had_clip; |
740 | |
|
741 | 0 | drawable_check_focus_highlight(node, tr_state, &st->clip); |
742 | 0 | } |
743 | |
|
744 | 0 | layout_exit: |
745 | 0 | tr_state->text_split_mode = 0; |
746 | 0 | } |
747 | | |
748 | | static Bool OnLayout(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor) |
749 | 0 | { |
750 | 0 | Bool vertical; |
751 | 0 | LayoutStack *st; |
752 | 0 | if (!sh || !ev) return GF_FALSE; |
753 | | |
754 | 0 | st = (LayoutStack *) gf_node_get_private(sh->sensor); |
755 | 0 | vertical = ((M_Layout *)sh->sensor)->scrollVertical; |
756 | |
|
757 | 0 | if (!is_over) { |
758 | 0 | st->is_scrolling = 0; |
759 | 0 | st->key_scroll = 0; |
760 | 0 | return 0; |
761 | 0 | } |
762 | 0 | if (ev->type!=GF_EVENT_KEYDOWN) { |
763 | 0 | st->is_scrolling = 0; |
764 | 0 | st->key_scroll = 0; |
765 | 0 | return 0; |
766 | 0 | } |
767 | | |
768 | 0 | switch (ev->key.key_code) { |
769 | 0 | case GF_KEY_LEFT: |
770 | 0 | if (!st->keys_active) return 0; |
771 | | |
772 | 0 | if (vertical) return 0; |
773 | 0 | st->key_scroll = -1; |
774 | 0 | break; |
775 | 0 | case GF_KEY_RIGHT: |
776 | 0 | if (!st->keys_active) return 0; |
777 | | |
778 | 0 | if (vertical) return 0; |
779 | 0 | st->key_scroll = +1; |
780 | 0 | break; |
781 | 0 | case GF_KEY_UP: |
782 | 0 | if (!st->keys_active) return 0; |
783 | | |
784 | 0 | if (!vertical) return 0; |
785 | 0 | st->key_scroll = +1; |
786 | 0 | break; |
787 | 0 | case GF_KEY_DOWN: |
788 | 0 | if (!st->keys_active) return 0; |
789 | | |
790 | 0 | if (!vertical) return 0; |
791 | 0 | st->key_scroll = -1; |
792 | 0 | break; |
793 | 0 | case GF_KEY_ENTER: |
794 | 0 | st->keys_active = !st->keys_active; |
795 | 0 | break; |
796 | 0 | default: |
797 | 0 | st->key_scroll = 0; |
798 | 0 | return 0; |
799 | 0 | } |
800 | 0 | gf_sc_invalidate(compositor, NULL); |
801 | 0 | return 1; |
802 | 0 | } |
803 | | |
804 | | static Bool layout_is_enabled(GF_Node *node) |
805 | 0 | { |
806 | 0 | M_Layout *l = (M_Layout *)node; |
807 | 0 | if (node && (l->scrollRate != 0)) return 0; |
808 | 0 | return 1; |
809 | 0 | } |
810 | | |
811 | | void compositor_init_layout(GF_Compositor *compositor, GF_Node *node) |
812 | 0 | { |
813 | 0 | LayoutStack *stack; |
814 | 0 | GF_SAFEALLOC(stack, LayoutStack); |
815 | 0 | if (!stack) { |
816 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate layout stack\n")); |
817 | 0 | return; |
818 | 0 | } |
819 | | |
820 | 0 | parent_node_setup((ParentNode2D*)stack); |
821 | 0 | stack->lines = gf_list_new(); |
822 | 0 | gf_node_set_private(node, stack); |
823 | 0 | gf_node_set_callback_function(node, TraverseLayout); |
824 | 0 | stack->hdl.sensor = node; |
825 | 0 | stack->hdl.IsEnabled = layout_is_enabled; |
826 | 0 | stack->hdl.OnUserEvent = OnLayout; |
827 | | #ifdef GPAC_ENABLE_COVERAGE |
828 | | if (gf_sys_is_cov_mode()) { |
829 | | OnLayout(NULL, GF_FALSE, GF_FALSE, NULL, compositor); |
830 | | layout_is_enabled(node); |
831 | | compositor_mpeg4_layout_get_sensor_handler(node); |
832 | | } |
833 | | #endif |
834 | |
|
835 | 0 | } |
836 | | |
837 | | void compositor_layout_modified(GF_Compositor *compositor, GF_Node *node) |
838 | 0 | { |
839 | 0 | LayoutStack *st = (LayoutStack *) gf_node_get_private(node); |
840 | | /*if modif other than scrollrate restart scroll*/ |
841 | 0 | if (st->scroll_rate == ((M_Layout*)node)->scrollRate) { |
842 | 0 | st->start_scroll_type = 1; |
843 | 0 | } |
844 | | /*if modif on scroll rate only, indicate continous restart*/ |
845 | 0 | else if (((M_Layout*)node)->scrollRate) { |
846 | 0 | st->start_scroll_type = 2; |
847 | 0 | } |
848 | 0 | gf_node_dirty_set(node, GF_SG_NODE_DIRTY, 0); |
849 | 0 | gf_sc_invalidate(compositor, NULL); |
850 | 0 | } |
851 | | |
852 | | GF_SensorHandler *compositor_mpeg4_layout_get_sensor_handler(GF_Node *node) |
853 | 0 | { |
854 | 0 | LayoutStack *st; |
855 | 0 | if (!node) return NULL; |
856 | 0 | st = (LayoutStack *) gf_node_get_private(node); |
857 | 0 | return &st->hdl; |
858 | 0 | } |
859 | | |
860 | | |
861 | | #endif //!defined(GPAC_DISABLE_VRML) && !defined(GPAC_DISABLE_COMPOSITOR) |