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 "visual_manager.h"
28 : #include "mpeg4_grouping.h"
29 : #include "texturing.h"
30 : #include <gpac/utf.h>
31 : #include <gpac/options.h>
32 :
33 : #ifndef GPAC_DISABLE_VRML
34 :
35 : /*default value when no fontStyle*/
36 : #define FSFAMILY (fs && fs->family.count) ? (const char *)fs->family.vals[0] : ""
37 :
38 : /*here it's tricky since it depends on our metric system...*/
39 : #define FSSIZE (fs ? fs->size : -1)
40 : #define FSSTYLE (fs && fs->style.buffer) ? (const char *)fs->style.buffer : ""
41 : #define FSMAJOR ( (fs && fs->justify.count && fs->justify.vals[0]) ? (const char *)fs->justify.vals[0] : "FIRST")
42 : #define FSMINOR ( (fs && (fs->justify.count>1) && fs->justify.vals[1]) ? (const char *)fs->justify.vals[1] : "FIRST")
43 : #define FSHORIZ (fs ? fs->horizontal : 1)
44 : #define FSLTR (fs ? fs->leftToRight : 1)
45 : #define FSTTB (fs ? fs->topToBottom : 1)
46 : #define FSLANG (fs ? fs->language : "")
47 : #define FSSPACE (fs ? fs->spacing : 1)
48 :
49 :
50 : /*exported to access the strike list ...*/
51 : typedef struct
52 : {
53 : struct _drawable s_graph;
54 : Fixed ascent, descent;
55 : GF_List *spans;
56 : GF_Rect bounds;
57 : u32 texture_text_flag;
58 : Bool is_dirty;
59 : GF_Compositor *compositor;
60 : } TextStack;
61 :
62 3316 : void text_clean_paths(GF_Compositor *compositor, TextStack *stack)
63 : {
64 : /*delete all path objects*/
65 12807 : while (gf_list_count(stack->spans)) {
66 6175 : GF_TextSpan *span = (GF_TextSpan*) gf_list_get(stack->spans, 0);
67 6175 : gf_list_rem(stack->spans, 0);
68 6175 : gf_font_manager_delete_span(compositor->font_manager, span);
69 : }
70 3316 : stack->bounds.width = stack->bounds.height = 0;
71 3316 : drawable_reset_path(&stack->s_graph);
72 3316 : }
73 :
74 :
75 507 : static void build_text_split(TextStack *st, M_Text *txt, GF_TraverseState *tr_state)
76 : {
77 : u32 i, j, k, len, styles, idx, first_char;
78 : Bool split_words = GF_FALSE;
79 : GF_Font *font;
80 : GF_TextSpan *tspan;
81 507 : GF_FontManager *ft_mgr = tr_state->visual->compositor->font_manager;
82 : Fixed fontSize, start_y;
83 507 : M_FontStyle *fs = (M_FontStyle *)txt->fontStyle;
84 :
85 507 : fontSize = FSSIZE;
86 507 : if (fontSize <= 0) {
87 : fontSize = INT2FIX(12);
88 0 : if (!tr_state->pixel_metrics) fontSize = gf_divfix(fontSize, tr_state->visual->compositor->output_width);
89 : }
90 :
91 : styles = 0;
92 507 : if (fs && fs->style.buffer) {
93 507 : if (strstr(fs->style.buffer, "BOLD") || strstr(fs->style.buffer, "bold")) styles |= GF_FONT_WEIGHT_BOLD;
94 507 : if (strstr(fs->style.buffer, "ITALIC") || strstr(fs->style.buffer, "italic")) styles |= GF_FONT_ITALIC;
95 507 : if (strstr(fs->style.buffer, "UNDERLINED") || strstr(fs->style.buffer, "underlined")) styles |= GF_FONT_UNDERLINED;
96 507 : if (strstr(fs->style.buffer, "STRIKETHROUGH") || strstr(fs->style.buffer, "strikethrough")) styles |= GF_FONT_STRIKEOUT;
97 : }
98 :
99 507 : font = gf_font_manager_set_font(ft_mgr, fs ? fs->family.vals : NULL, fs ? fs->family.count : 0, styles);
100 507 : if (!font) return;
101 :
102 507 : st->ascent = (fontSize*font->ascent) / font->em_size;
103 507 : st->descent = -(fontSize*font->descent) / font->em_size;
104 :
105 507 : if (!strcmp(FSMINOR, "MIDDLE")) {
106 0 : start_y = (st->descent + st->ascent)/2;
107 : }
108 507 : else if (!strcmp(FSMINOR, "BEGIN")) {
109 : start_y = st->descent;
110 : }
111 507 : else if (!strcmp(FSMINOR, "END")) {
112 0 : start_y = st->descent + st->ascent;
113 : }
114 : else {
115 : start_y = st->ascent;
116 : }
117 :
118 507 : st->bounds.width = st->bounds.x = st->bounds.height = 0;
119 : idx = 0;
120 507 : split_words = (tr_state->text_split_mode==1) ? GF_TRUE : GF_FALSE;
121 :
122 1014 : for (i=0; i < txt->string.count; i++) {
123 :
124 507 : char *str = txt->string.vals[i];
125 507 : if (!str || !strlen(str)) continue;
126 :
127 507 : tspan = gf_font_manager_create_span(ft_mgr, font, str, fontSize, GF_FALSE, GF_FALSE, GF_FALSE, NULL, GF_FALSE, styles, (GF_Node*)txt);
128 507 : if (!tspan) continue;
129 :
130 507 : len = tspan->nb_glyphs;
131 507 : tspan->flags |= GF_TEXT_SPAN_HORIZONTAL;
132 :
133 : first_char = 0;
134 5421 : for (j=0; j<len; j++) {
135 : u32 is_space = 0;
136 : GF_TextSpan *span;
137 :
138 4914 : if (!tspan->glyphs[j]) continue;
139 :
140 : /*we currently only split sentences at spaces*/
141 4914 : if (tspan->glyphs[j]->utf_name == (unsigned short) ' ') is_space = 1;
142 4148 : else if (tspan->glyphs[j]->utf_name == (unsigned short) '\n')
143 : is_space = 2;
144 4914 : if (split_words && (j+1!=len) && !is_space)
145 1166 : continue;
146 :
147 3748 : span = (GF_TextSpan*) gf_malloc(sizeof(GF_TextSpan));
148 : memcpy(span, tspan, sizeof(GF_TextSpan));
149 :
150 3748 : span->nb_glyphs = split_words ? (j - first_char) : 1;
151 3748 : if (split_words && !is_space) span->nb_glyphs++;
152 3748 : span->glyphs = (GF_Glyph**)gf_malloc(sizeof(void *)*span->nb_glyphs);
153 :
154 3748 : span->bounds.height = st->ascent + st->descent;
155 : span->bounds.y = start_y;
156 3748 : span->bounds.x = 0;
157 3748 : span->bounds.width = 0;
158 3748 : span->bounds.y += 2;
159 3748 : span->bounds.height += 4;
160 :
161 3748 : if (split_words) {
162 1343 : for (k=0; k<span->nb_glyphs; k++) {
163 1343 : span->glyphs[k] = tspan->glyphs[FSLTR ? (first_char+k) : (len - first_char - k - 1)];
164 1343 : span->bounds.width += tspan->font_scale * (span->glyphs[k] ? span->glyphs[k]->horiz_advance : tspan->font->max_advance_h);
165 : }
166 : } else {
167 : //span->glyphs[0] = tspan->glyphs[FSLTR ? j : (len - j - 1) ];
168 3465 : span->glyphs[0] = tspan->glyphs[j];
169 3465 : span->bounds.width = tspan->font_scale * (span->glyphs[0] ? span->glyphs[0]->horiz_advance : tspan->font->max_advance_h);
170 : }
171 :
172 3748 : gf_list_add(st->spans, span);
173 :
174 : /*request a context (first one is always valid when entering sort phase)*/
175 3748 : if (idx) parent_node_start_group(tr_state->parent, NULL, GF_FALSE);
176 :
177 3748 : idx++;
178 3748 : parent_node_end_text_group(tr_state->parent, &span->bounds, st->ascent, st->descent, idx);
179 :
180 3748 : if (is_space && split_words) {
181 106 : span = (GF_TextSpan*) gf_malloc(sizeof(GF_TextSpan));
182 : memcpy(span, tspan, sizeof(GF_TextSpan));
183 106 : span->nb_glyphs = 1;
184 106 : span->glyphs = (GF_Glyph**)gf_malloc(sizeof(void *));
185 :
186 106 : gf_list_add(st->spans, span);
187 106 : span->bounds.height = st->ascent + st->descent;
188 : span->bounds.y = start_y;
189 106 : span->bounds.x = 0;
190 106 : span->bounds.y += 2;
191 106 : span->bounds.height += 4;
192 106 : k = (j - first_char);
193 106 : span->glyphs[0] = tspan->glyphs[FSLTR ? (first_char+k) : (len - first_char - k - 1)];
194 106 : span->bounds.width = tspan->font_scale * (span->glyphs[0] ? span->glyphs[0]->horiz_advance : tspan->font->max_advance_h);
195 106 : parent_node_start_group(tr_state->parent, NULL, is_space);
196 106 : idx++;
197 106 : parent_node_end_text_group(tr_state->parent, &span->bounds, st->ascent, st->descent, idx);
198 : }
199 3748 : first_char = j+1;
200 : }
201 507 : gf_font_manager_delete_span(ft_mgr, tspan);
202 : }
203 : }
204 :
205 :
206 1804 : static void build_text(TextStack *st, M_Text *txt, GF_TraverseState *tr_state)
207 : {
208 : u32 i, j, int_major, k, styles, count;
209 : Fixed fontSize, start_x, start_y, line_spacing, tot_width, tot_height, max_scale, maxExtent;
210 : u32 size, trim_size;
211 : GF_Font *font;
212 : Bool horizontal;
213 : GF_TextSpan *trim_tspan = NULL;
214 1804 : GF_FontManager *ft_mgr = tr_state->visual->compositor->font_manager;
215 1804 : M_FontStyle *fs = (M_FontStyle *)txt->fontStyle;
216 :
217 1804 : fontSize = FSSIZE;
218 1804 : if (fontSize <= 0) {
219 : fontSize = INT2FIX(12);
220 0 : if (!tr_state->pixel_metrics) fontSize = gf_divfix(fontSize, tr_state->visual->compositor->output_width);
221 : }
222 1804 : horizontal = FSHORIZ;
223 : start_x = start_y = 0;
224 :
225 : styles = 0;
226 1804 : if (fs && fs->style.buffer) {
227 1804 : if (strstr(fs->style.buffer, "BOLD") || strstr(fs->style.buffer, "bold")) styles |= GF_FONT_WEIGHT_BOLD;
228 1804 : if (strstr(fs->style.buffer, "ITALIC") || strstr(fs->style.buffer, "italic")) styles |= GF_FONT_ITALIC;
229 1804 : if (strstr(fs->style.buffer, "UNDERLINED") || strstr(fs->style.buffer, "underlined")) styles |= GF_FONT_UNDERLINED;
230 1804 : if (strstr(fs->style.buffer, "STRIKETHROUGH") || strstr(fs->style.buffer, "strikethrough")) styles |= GF_FONT_STRIKEOUT;
231 : }
232 :
233 1804 : font = gf_font_manager_set_font(ft_mgr, fs ? fs->family.vals : NULL, fs ? fs->family.count : 0, styles);
234 1804 : if (!font) return;
235 :
236 : /*NOTA: we could use integer maths here but we have a risk of overflow with large fonts, so use fixed maths*/
237 1804 : st->ascent = gf_muldiv(fontSize, INT2FIX(font->ascent), INT2FIX(font->em_size));
238 1804 : st->descent = -gf_muldiv(fontSize, INT2FIX(font->descent), INT2FIX(font->em_size));
239 1804 : line_spacing = gf_mulfix(FSSPACE, fontSize);
240 :
241 1804 : maxExtent = txt->maxExtent;
242 : trim_size = 0;
243 :
244 1804 : if (maxExtent<0) {
245 112 : trim_tspan = gf_font_manager_create_span(ft_mgr, font, "...", fontSize, GF_FALSE, GF_FALSE, GF_FALSE, NULL, GF_FALSE, styles, (GF_Node*)txt);
246 336 : for (i=0; i<trim_tspan->nb_glyphs; i++) {
247 336 : if (horizontal) {
248 336 : trim_size += trim_tspan->glyphs[i] ? trim_tspan->glyphs[i]->horiz_advance : trim_tspan->font->max_advance_h;
249 : } else {
250 0 : trim_size += trim_tspan->glyphs[i] ? trim_tspan->glyphs[i]->vert_advance : trim_tspan->font->max_advance_v;
251 : }
252 : }
253 : }
254 :
255 : tot_width = tot_height = 0;
256 2379 : for (i=0; i < txt->string.count; i++) {
257 : GF_TextSpan *tspan;
258 2379 : char *str = txt->string.vals[i];
259 2379 : if (!str) continue;
260 :
261 2337 : tspan = gf_font_manager_create_span(ft_mgr, font, txt->string.vals[i], fontSize, GF_FALSE, GF_FALSE, GF_FALSE, NULL, GF_FALSE, styles, (GF_Node*)txt);
262 2337 : if (!tspan) continue;
263 :
264 2321 : if (horizontal) tspan->flags |= GF_TEXT_SPAN_HORIZONTAL;
265 :
266 : size = 0;
267 2321 : if (trim_size) {
268 1155 : for (j=0; j<tspan->nb_glyphs; j++) {
269 1155 : if (horizontal) {
270 1155 : size += tspan->glyphs[j] ? tspan->glyphs[j]->horiz_advance : tspan->font->max_advance_h;
271 : } else {
272 0 : size += tspan->glyphs[j] ? tspan->glyphs[j]->vert_advance : tspan->font->max_advance_v;
273 : }
274 : /*word is bigger than allowed extent, rewrite 3 previous chars*/
275 1155 : if ((s32)size*tspan->font_scale >= -maxExtent) {
276 0 : u32 nb_chars = (j<2) ? j : 3;
277 :
278 0 : for (k=0; k<nb_chars; k++) {
279 0 : u32 idx = nb_chars-k-1;
280 0 : if (horizontal) {
281 0 : size -= tspan->glyphs[j-k] ? tspan->glyphs[j-k]->horiz_advance : tspan->font->max_advance_h;
282 0 : size += trim_tspan->glyphs[idx] ? trim_tspan->glyphs[idx]->horiz_advance : tspan->font->max_advance_h;
283 : } else {
284 0 : size -= tspan->glyphs[j-k] ? tspan->glyphs[j-k]->vert_advance : tspan->font->max_advance_v;
285 0 : size += trim_tspan->glyphs[idx] ? trim_tspan->glyphs[idx]->vert_advance : tspan->font->max_advance_v;
286 : }
287 0 : tspan->glyphs[j-k] = trim_tspan->glyphs[idx];
288 : }
289 0 : tspan->nb_glyphs = j+1;
290 : break;
291 : }
292 : }
293 : }
294 :
295 2321 : if ((horizontal && !FSLTR) || (!horizontal && !FSTTB)) {
296 288 : for (k=0; k<tspan->nb_glyphs/2; k++) {
297 288 : GF_Glyph *g = tspan->glyphs[k];
298 288 : tspan->glyphs[k] = tspan->glyphs[tspan->nb_glyphs-1-k];
299 288 : tspan->glyphs[tspan->nb_glyphs-k-1] = g;
300 : }
301 : }
302 :
303 2321 : if (!size) {
304 27752 : for (j=0; j<tspan->nb_glyphs; j++) {
305 27752 : if (horizontal) {
306 27234 : size += tspan->glyphs[j] ? tspan->glyphs[j]->horiz_advance : tspan->font->max_advance_h;
307 : } else {
308 518 : size += tspan->glyphs[j] ? tspan->glyphs[j]->vert_advance : tspan->font->max_advance_v;
309 : }
310 : }
311 : }
312 2321 : gf_list_add(st->spans, tspan);
313 :
314 2321 : if (horizontal) {
315 2227 : tspan->bounds.width = tspan->font_scale * size;
316 : /*apply length*/
317 2227 : if ((txt->length.count>i) && (txt->length.vals[i]>0)) {
318 9 : tspan->x_scale = gf_divfix(txt->length.vals[i], tspan->bounds.width);
319 9 : tspan->bounds.width = txt->length.vals[i];
320 : }
321 2227 : if (tot_width < tspan->bounds.width ) tot_width = tspan->bounds.width;
322 : } else {
323 94 : tspan->bounds.height = tspan->font_scale * size;
324 :
325 : /*apply length*/
326 94 : if ((txt->length.count>i) && (txt->length.vals[i]>0)) {
327 9 : tspan->y_scale = gf_divfix(txt->length.vals[i], tspan->bounds.height);
328 9 : tspan->bounds.height = txt->length.vals[i];
329 : }
330 94 : if (tot_height < tspan->bounds.height) tot_height = tspan->bounds.height;
331 : }
332 : }
333 1804 : if (trim_tspan) gf_font_manager_delete_span(ft_mgr, trim_tspan);
334 :
335 :
336 : max_scale = FIX_ONE;
337 1804 : if (horizontal) {
338 1757 : if ((maxExtent > 0) && (tot_width>maxExtent)) {
339 1 : max_scale = gf_divfix(maxExtent, tot_width);
340 : }
341 1757 : tot_height = (txt->string.count-1) * line_spacing + (st->ascent + st->descent);
342 1757 : st->bounds.height = tot_height;
343 :
344 1757 : if (!strcmp(FSMINOR, "MIDDLE")) {
345 1029 : if (FSTTB) {
346 1021 : start_y = tot_height/2;
347 1021 : st->bounds.y = start_y;
348 : } else {
349 8 : start_y = st->descent + st->ascent - tot_height/2;
350 8 : st->bounds.y = tot_height/2;
351 : }
352 : }
353 728 : else if (!strcmp(FSMINOR, "BEGIN")) {
354 43 : if (FSTTB) {
355 : start_y = 0;
356 35 : st->bounds.y = start_y;
357 : } else {
358 8 : st->bounds.y = st->bounds.height;
359 : start_y = st->descent + st->ascent;
360 : }
361 : }
362 685 : else if (!strcmp(FSMINOR, "END")) {
363 18 : if (FSTTB) {
364 : start_y = tot_height;
365 10 : st->bounds.y = start_y;
366 : } else {
367 8 : start_y = -tot_height + 2*st->descent + st->ascent;
368 8 : st->bounds.y = start_y - (st->descent + st->ascent) + tot_height;
369 : }
370 : }
371 : else {
372 : start_y = st->ascent;
373 667 : st->bounds.y = FSTTB ? start_y : (tot_height - st->descent);
374 : }
375 : } else {
376 47 : if ((maxExtent > 0) && (tot_height>maxExtent) ) {
377 1 : max_scale = gf_divfix(maxExtent, tot_height);
378 : }
379 47 : tot_width = txt->string.count * line_spacing;
380 47 : st->bounds.width = tot_width;
381 :
382 47 : if (!strcmp(FSMINOR, "MIDDLE")) {
383 23 : if (FSLTR) {
384 17 : start_x = -tot_width/2;
385 17 : st->bounds.x = start_x;
386 : } else {
387 6 : start_x = tot_width/2 - line_spacing;
388 6 : st->bounds.x = - tot_width + line_spacing;
389 : }
390 : }
391 24 : else if (!strcmp(FSMINOR, "END")) {
392 12 : if (FSLTR) {
393 6 : start_x = -tot_width;
394 6 : st->bounds.x = start_x;
395 : } else {
396 6 : start_x = tot_width-line_spacing;
397 6 : st->bounds.x = 0;
398 : }
399 : }
400 : else {
401 12 : if (FSLTR) {
402 : start_x = 0;
403 6 : st->bounds.x = start_x;
404 : } else {
405 6 : start_x = -line_spacing;
406 6 : st->bounds.x = -tot_width;
407 : }
408 : }
409 : }
410 :
411 :
412 : /*major-justification*/
413 1804 : if (!strcmp(FSMAJOR, "MIDDLE") ) {
414 : int_major = 0;
415 242 : } else if (!strcmp(FSMAJOR, "END") ) {
416 : int_major = 1;
417 : } else {
418 : int_major = 2;
419 : }
420 :
421 1804 : st->bounds.width = st->bounds.height = 0;
422 :
423 1804 : count = gf_list_count(st->spans);
424 2321 : for (i=0; i < count; i++) {
425 2321 : GF_TextSpan *span = (GF_TextSpan*)gf_list_get(st->spans, i);
426 2321 : switch (int_major) {
427 : /*major-justification MIDDLE*/
428 1955 : case 0:
429 1955 : if (horizontal) {
430 1909 : start_x = -span->bounds.width/2;
431 : } else {
432 : //start_y = FSTTB ? span->bounds.height/2 : (-span->bounds.height/2 + space);
433 46 : start_y = span->bounds.height/2;
434 : }
435 : break;
436 : /*major-justification END*/
437 103 : case 1:
438 103 : if (horizontal) {
439 79 : start_x = (FSLTR) ? -span->bounds.width : 0;
440 : } else {
441 : //start_y = FSTTB ? span->bounds.height : (-span->bounds.height + space);
442 24 : start_y = FSTTB ? span->bounds.height : 0;
443 : }
444 : break;
445 : /*BEGIN, FIRST or default*/
446 263 : default:
447 263 : if (horizontal) {
448 239 : start_x = (FSLTR) ? 0 : -span->bounds.width;
449 : } else {
450 : //start_y = FSTTB ? 0 : space;
451 24 : start_y = FSTTB ? 0 : span->bounds.height;
452 : }
453 : break;
454 : }
455 2321 : span->off_x = start_x;
456 2321 : span->bounds.x = start_x;
457 2321 : if (horizontal) {
458 2227 : span->off_y = start_y - st->ascent;
459 2227 : span->x_scale = gf_mulfix(span->x_scale, max_scale);
460 2227 : span->bounds.y = start_y;
461 : } else {
462 94 : span->y_scale = gf_mulfix(span->y_scale, max_scale);
463 94 : span->off_y = start_y - gf_mulfix(st->ascent, span->y_scale);
464 94 : span->bounds.y = start_y;
465 : }
466 2321 : span->off_x = gf_mulfix(span->off_x, max_scale);
467 2321 : span->off_y = gf_mulfix(span->off_y, max_scale);
468 :
469 2321 : if (horizontal) {
470 2227 : start_y += FSTTB ? -line_spacing : line_spacing;
471 2227 : span->bounds.height = st->descent + st->ascent;
472 : } else {
473 94 : start_x += FSLTR ? line_spacing : -line_spacing;
474 94 : span->bounds.width = line_spacing;
475 : }
476 2321 : gf_rect_union(&st->bounds, &span->bounds);
477 : }
478 : }
479 :
480 26200 : static void text_get_draw_opt(GF_Node *node, TextStack *st, Bool *force_texture, u32 *hl_color, DrawAspect2D *asp)
481 : {
482 : const char *fs_style;
483 : char *hlight;
484 26200 : M_FontStyle *fs = (M_FontStyle *) ((M_Text *) node)->fontStyle;
485 :
486 26200 : *hl_color = 0;
487 :
488 26200 : fs_style = FSSTYLE;
489 26200 : hlight = strstr(fs_style, "HIGHLIGHT");
490 26200 : if (hlight) hlight = strchr(hlight, '#');
491 26200 : if (hlight) {
492 0 : hlight += 1;
493 0 : if (!strnicmp(hlight, "RV", 2)) *hl_color = 0x00FFFFFF;
494 : else {
495 0 : sscanf(hlight, "%x", hl_color);
496 0 : if (strlen(hlight)!=8) *hl_color |= 0xFF000000;
497 : }
498 : }
499 26200 : *force_texture = st->texture_text_flag;
500 26200 : if (strstr(fs_style, "TEXTURED")) *force_texture = GF_TRUE;
501 26200 : if (strstr(fs_style, "OUTLINED")) {
502 0 : if (asp && !asp->pen_props.width) {
503 0 : asp->pen_props.width = FIX_ONE/2;
504 0 : asp->pen_props.align = GF_PATH_LINE_OUTSIDE;
505 0 : asp->line_scale=FIX_ONE;
506 0 : asp->line_color = 0xFF000000;
507 : }
508 : }
509 26200 : }
510 :
511 :
512 : #ifndef GPAC_DISABLE_3D
513 :
514 :
515 : static Bool text_is_3d_material(GF_TraverseState *tr_state)
516 : {
517 : GF_Node *__mat;
518 2535 : if (!tr_state->appear) return GF_FALSE;
519 2399 : __mat = ((M_Appearance *)tr_state->appear)->material;
520 2399 : if (!__mat) return GF_FALSE;
521 2399 : if (gf_node_get_tag(__mat)==TAG_MPEG4_Material2D) return GF_FALSE;
522 : return GF_TRUE;
523 : }
524 :
525 2535 : static void text_draw_3d(GF_TraverseState *tr_state, GF_Node *node, TextStack *st)
526 : {
527 : DrawAspect2D the_asp, *asp;
528 : Bool is_3d, force_texture;
529 : u32 hl_color;
530 :
531 2535 : is_3d = text_is_3d_material(tr_state);
532 : asp = NULL;
533 : if (!is_3d) {
534 : memset(&the_asp, 0, sizeof(DrawAspect2D));
535 : asp = &the_asp;
536 2496 : drawable_get_aspect_2d_mpeg4(node, asp, tr_state);
537 : }
538 2535 : text_get_draw_opt(node, st, &force_texture, &hl_color, asp);
539 2535 : gf_font_spans_draw_3d(st->spans, tr_state, asp, hl_color, force_texture);
540 2535 : }
541 :
542 : #endif
543 :
544 :
545 :
546 :
547 23665 : void text_draw_2d(GF_Node *node, GF_TraverseState *tr_state)
548 : {
549 : Bool force_texture;
550 : u32 hl_color;
551 23665 : TextStack *st = (TextStack *) gf_node_get_private((GF_Node *) node);
552 :
553 23665 : if (!GF_COL_A(tr_state->ctx->aspect.fill_color) && !tr_state->ctx->aspect.pen_props.width) return;
554 :
555 23665 : text_get_draw_opt(node, st, &force_texture, &hl_color, &tr_state->ctx->aspect);
556 :
557 23665 : tr_state->text_parent = node;
558 23665 : gf_font_spans_draw_2d(st->spans, tr_state, hl_color, force_texture, &st->bounds);
559 23665 : tr_state->text_parent = NULL;
560 : }
561 :
562 :
563 :
564 61803 : static void text_check_changes(GF_Node *node, TextStack *stack, GF_TraverseState *tr_state)
565 : {
566 61803 : if (gf_node_dirty_get(node) || tr_state->visual->compositor->reset_fonts) {
567 1804 : text_clean_paths(tr_state->visual->compositor, stack);
568 1804 : build_text(stack, (M_Text*)node, tr_state);
569 1804 : gf_node_dirty_clear(node, 0);
570 1804 : drawable_mark_modified(&stack->s_graph, tr_state);
571 : }
572 :
573 61803 : if (tr_state->visual->compositor->edited_text && (tr_state->visual->compositor->focus_node==node)) {
574 0 : drawable_mark_modified(&stack->s_graph, tr_state);
575 0 : tr_state->visual->has_text_edit = GF_TRUE;
576 0 : if (!stack->bounds.width) stack->bounds.width = INT2FIX(1)/100;
577 0 : if (!stack->bounds.height) stack->bounds.height = INT2FIX(1)/100;
578 : }
579 61803 : }
580 :
581 :
582 63400 : static void Text_Traverse(GF_Node *n, void *rs, Bool is_destroy)
583 : {
584 : DrawableContext *ctx;
585 : M_Text *txt = (M_Text *) n;
586 63400 : TextStack *st = (TextStack *) gf_node_get_private(n);
587 : GF_TraverseState *tr_state = (GF_TraverseState *)rs;
588 :
589 63400 : if (is_destroy) {
590 1005 : GF_Compositor *compositor = gf_sc_get_compositor(n);
591 1005 : text_clean_paths(compositor, st);
592 1005 : drawable_del_ex(&st->s_graph, compositor, GF_TRUE);
593 1005 : gf_list_del(st->spans);
594 1005 : gf_free(st);
595 1005 : return;
596 : }
597 :
598 62395 : if (!txt->string.count) return;
599 :
600 62310 : if (tr_state->text_split_mode) {
601 507 : st->is_dirty = gf_node_dirty_get(n) ? GF_TRUE : GF_FALSE;
602 507 : gf_node_dirty_clear(n, 0);
603 507 : text_clean_paths(tr_state->visual->compositor, st);
604 507 : build_text_split(st, txt, tr_state);
605 507 : return;
606 : }
607 :
608 61803 : text_check_changes(n, st, tr_state);
609 :
610 61803 : switch (tr_state->traversing_mode) {
611 23665 : case TRAVERSE_DRAW_2D:
612 23665 : text_draw_2d(n, tr_state);
613 23665 : return;
614 : #ifndef GPAC_DISABLE_3D
615 2535 : case TRAVERSE_DRAW_3D:
616 2535 : text_draw_3d(tr_state, n, st);
617 2535 : return;
618 : #endif
619 7494 : case TRAVERSE_PICK:
620 7494 : tr_state->text_parent = n;
621 7494 : gf_font_spans_pick(n, st->spans, tr_state, &st->bounds, GF_FALSE, NULL);
622 7494 : tr_state->text_parent = NULL;
623 7494 : return;
624 315 : case TRAVERSE_GET_BOUNDS:
625 315 : tr_state->bounds = st->bounds;
626 315 : return;
627 0 : case TRAVERSE_GET_TEXT:
628 0 : tr_state->text_parent = n;
629 0 : gf_font_spans_get_selection(n, st->spans, tr_state);
630 0 : tr_state->text_parent = NULL;
631 0 : return;
632 : case TRAVERSE_SORT:
633 : break;
634 : default:
635 : return;
636 : }
637 :
638 : #ifndef GPAC_DISABLE_3D
639 27794 : if (tr_state->visual->type_3d) return;
640 : #endif
641 :
642 27794 : ctx = drawable_init_context_mpeg4(&st->s_graph, tr_state);
643 27794 : if (!ctx) return;
644 27772 : ctx->sub_path_index = tr_state->text_split_idx;
645 :
646 27772 : ctx->flags |= CTX_IS_TEXT;
647 27772 : if (!GF_COL_A(ctx->aspect.fill_color)) {
648 : /*override line join*/
649 302 : ctx->aspect.pen_props.join = GF_LINE_JOIN_MITER;
650 302 : ctx->aspect.pen_props.cap = GF_LINE_CAP_FLAT;
651 : }
652 :
653 : /*if text selection mode, we must force redraw of the entire text span because we don't
654 : if glyphs have been (un)selected*/
655 54288 : if (!tr_state->immediate_draw &&
656 : /*text selection on*/
657 26516 : (tr_state->visual->compositor->text_selection
658 : /*text sel release*/
659 26157 : || (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED))
660 750 : ) {
661 : GF_TextSpan *span;
662 750 : u32 i = 0;
663 750 : Bool unselect = (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED) ? GF_TRUE : GF_FALSE;
664 2583 : while ((span = (GF_TextSpan*)gf_list_enum(st->spans, &i))) {
665 1083 : if (span->flags & GF_TEXT_SPAN_SELECTED) {
666 392 : if (unselect) span->flags &= ~GF_TEXT_SPAN_SELECTED;
667 392 : ctx->flags |= CTX_APP_DIRTY;
668 : }
669 : }
670 27022 : } else if (st->is_dirty) {
671 93 : ctx->flags |= CTX_APP_DIRTY;
672 : }
673 :
674 27772 : if (ctx->sub_path_index) {
675 3299 : GF_TextSpan *span = (GF_TextSpan *)gf_list_get(st->spans, ctx->sub_path_index-1);
676 3299 : if (span) drawable_finalize_sort(ctx, tr_state, &span->bounds);
677 : } else {
678 24473 : drawable_finalize_sort(ctx, tr_state, &st->bounds);
679 : }
680 : }
681 :
682 :
683 1005 : void compositor_init_text(GF_Compositor *compositor, GF_Node *node)
684 : {
685 : TextStack *stack;
686 1005 : GF_SAFEALLOC(stack, TextStack);
687 1005 : if (!stack) {
688 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate text stack\n"));
689 : return;
690 : }
691 1005 : drawable_init_ex(&stack->s_graph);
692 1005 : stack->s_graph.node = node;
693 1005 : stack->s_graph.flags = DRAWABLE_USE_TRAVERSE_DRAW;
694 1005 : stack->ascent = stack->descent = 0;
695 1005 : stack->spans = gf_list_new();
696 1005 : stack->texture_text_flag = 0;
697 :
698 1005 : stack->compositor = compositor;
699 1005 : gf_node_set_private(node, stack);
700 1005 : gf_node_set_callback_function(node, Text_Traverse);
701 : }
702 :
703 : #ifndef GPAC_DISABLE_3D
704 1 : void compositor_extrude_text(GF_Node *node, GF_TraverseState *tr_state, GF_Mesh *mesh, MFVec3f *thespine, Fixed creaseAngle, Bool begin_cap, Bool end_cap, MFRotation *spine_ori, MFVec2f *spine_scale, Bool txAlongSpine)
705 : {
706 : u32 i, count;
707 : Fixed min_cx, min_cy, width_cx, width_cy;
708 1 : TextStack *st = (TextStack *) gf_node_get_private(node);
709 :
710 : /*rebuild text node*/
711 1 : if (gf_node_dirty_get(node)) {
712 0 : ParentNode2D *parent = tr_state->parent;
713 0 : tr_state->parent = NULL;
714 0 : text_clean_paths(tr_state->visual->compositor, st);
715 0 : drawable_reset_path(&st->s_graph);
716 0 : gf_node_dirty_clear(node, 0);
717 0 : build_text(st, (M_Text *)node, tr_state);
718 0 : tr_state->parent = parent;
719 : }
720 :
721 1 : min_cx = st->bounds.x;
722 1 : min_cy = st->bounds.y - st->bounds.height;
723 1 : width_cx = st->bounds.width;
724 : width_cy = st->bounds.height;
725 :
726 1 : mesh_reset(mesh);
727 1 : count = gf_list_count(st->spans);
728 2 : for (i=0; i<count; i++) {
729 1 : GF_TextSpan *span = (GF_TextSpan *)gf_list_get(st->spans, i);
730 1 : GF_Path *span_path = gf_font_span_create_path(span);
731 1 : mesh_extrude_path_ext(mesh, span_path, thespine, creaseAngle, min_cx, min_cy, width_cx, width_cy, begin_cap, end_cap, spine_ori, spine_scale, txAlongSpine);
732 1 : gf_path_del(span_path);
733 : }
734 1 : mesh_update_bounds(mesh);
735 1 : gf_mesh_build_aabbtree(mesh);
736 1 : }
737 :
738 : #endif /*GPAC_DISABLE_3D*/
739 :
740 :
741 : #endif /*GPAC_DISABLE_VRML*/
742 :
743 :
|