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 Rendering 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 <gpac/internal/compositor_dev.h>
27 : #include <gpac/modules/font.h>
28 : #include <gpac/options.h>
29 : #include "visual_manager.h"
30 : #include "nodes_stacks.h"
31 : #include "texturing.h"
32 :
33 : struct _gf_ft_mgr
34 : {
35 : GF_FontReader *reader;
36 :
37 : GF_Font *font, *default_font;
38 : GF_Path *line_path;
39 :
40 : u32 *id_buffer;
41 : u32 id_buffer_size;
42 :
43 : Bool wait_font_load;
44 : };
45 :
46 : GF_EXPORT
47 641 : GF_FontManager *gf_font_manager_new()
48 : {
49 641 : char *def_font = "SERIF";
50 : GF_FontManager *font_mgr;
51 : GF_FontReader *ifce;
52 641 : Bool wait_for_fonts = gf_opts_get_bool("core", "wait-fonts");
53 :
54 641 : ifce = (GF_FontReader *) gf_module_load(GF_FONT_READER_INTERFACE, NULL);
55 641 : if (ifce) ifce->init_font_engine(ifce);
56 :
57 :
58 641 : GF_SAFEALLOC(font_mgr, GF_FontManager);
59 641 : if (!font_mgr) {
60 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate font manager\n"));
61 : return NULL;
62 : }
63 641 : font_mgr->reader = ifce;
64 641 : font_mgr->id_buffer_size = 20;
65 641 : font_mgr->id_buffer = gf_malloc(sizeof(u32)*font_mgr->id_buffer_size);
66 : gf_font_manager_set_font(font_mgr, &def_font, 1, 0);
67 641 : font_mgr->default_font = font_mgr->font;
68 :
69 641 : font_mgr->line_path= gf_path_new();
70 641 : gf_path_add_move_to(font_mgr->line_path, -FIX_ONE/2, FIX_ONE/2);
71 641 : gf_path_add_line_to(font_mgr->line_path, FIX_ONE/2, FIX_ONE/2);
72 641 : gf_path_add_line_to(font_mgr->line_path, FIX_ONE/2, -FIX_ONE/2);
73 641 : gf_path_add_line_to(font_mgr->line_path, -FIX_ONE/2, -FIX_ONE/2);
74 641 : gf_path_close(font_mgr->line_path);
75 :
76 :
77 641 : font_mgr->wait_font_load = wait_for_fonts;
78 :
79 641 : return font_mgr;
80 : }
81 :
82 692 : void gf_font_predestroy(GF_Font *font)
83 : {
84 692 : if (font->spans) {
85 2 : while (gf_list_count(font->spans)) {
86 1 : GF_TextSpan *ts = gf_list_get(font->spans, 0);
87 1 : gf_list_rem(font->spans, 0);
88 : #ifndef GPAC_DISABLE_PLAYER
89 1 : gf_node_dirty_set(ts->user, 0, 0);
90 : #endif
91 1 : ts->user=NULL;
92 : }
93 1 : gf_list_del(font->spans);
94 1 : font->spans = NULL;
95 : }
96 692 : }
97 :
98 691 : void gf_font_del(GF_Font *font)
99 : {
100 691 : gf_font_predestroy(font);
101 691 : if (!font->get_glyphs) {
102 : GF_Glyph *glyph;
103 691 : glyph = font->glyph;
104 6372 : while (glyph) {
105 4990 : GF_Glyph *next = glyph->next;
106 4990 : gf_path_del(glyph->path);
107 4990 : gf_free(glyph);
108 : glyph = next;
109 : }
110 : }
111 691 : gf_free(font->name);
112 691 : gf_free(font);
113 691 : }
114 :
115 :
116 640 : void gf_font_manager_del(GF_FontManager *fm)
117 : {
118 : GF_Font *font;
119 640 : if (fm->reader) {
120 640 : fm->reader->shutdown_font_engine(fm->reader);
121 640 : gf_modules_close_interface((GF_BaseInterface *)fm->reader);
122 : }
123 :
124 640 : font = fm->font;
125 1971 : while (font) {
126 691 : GF_Font *next = font->next;
127 691 : gf_font_del(font);
128 : font = next;
129 : }
130 640 : gf_free(fm->id_buffer);
131 640 : gf_path_del(fm->line_path);
132 640 : gf_free(fm);
133 640 : }
134 :
135 1 : GF_Err gf_font_manager_register_font(GF_FontManager *fm, GF_Font *font)
136 : {
137 1 : if (fm->font) {
138 : GF_Font *a_font = fm->font;
139 1 : while (a_font->next) a_font = a_font->next;
140 1 : a_font->next = font;
141 : } else {
142 0 : fm->font = font;
143 : }
144 1 : font->ft_mgr = fm;
145 1 : if (!font->spans) font->spans = gf_list_new();
146 1 : return GF_OK;
147 : }
148 :
149 1 : GF_Err gf_font_manager_unregister_font(GF_FontManager *fm, GF_Font *font)
150 : {
151 : GF_Font *prev_font, *a_font;
152 :
153 : prev_font = NULL;
154 1 : a_font = fm->font;
155 3 : while (a_font) {
156 2 : if (a_font==font) break;
157 : prev_font = a_font;
158 1 : a_font = a_font->next;
159 : }
160 1 : if (prev_font) {
161 1 : prev_font->next = font->next;
162 : } else {
163 0 : fm->font = font->next;
164 : }
165 1 : gf_font_predestroy(font);
166 1 : return GF_OK;
167 : }
168 :
169 :
170 : GF_EXPORT
171 4246 : GF_Font *gf_font_manager_set_font_ex(GF_FontManager *fm, char **alt_fonts, u32 nb_fonts, u32 styles, Bool check_only)
172 : {
173 : u32 i;
174 : GF_Err e;
175 4246 : Bool has_italic = (styles & GF_FONT_ITALIC) ? 1 : 0;
176 4246 : Bool has_smallcaps = (styles & GF_FONT_SMALLCAPS) ? 1 : 0;
177 : GF_Font *the_font = NULL;
178 :
179 5427 : for (i=0; i<nb_fonts; i++) {
180 : char *font_name;
181 : const char *opt;
182 : u32 weight_diff = 0xFFFFFFFF;
183 : GF_Font *best_font = NULL;
184 5086 : GF_Font *font = fm->font;
185 5086 : font_name = alt_fonts[i];
186 5086 : if (!font_name) font_name = "SANS";
187 :
188 5086 : if (!stricmp(font_name, "SERIF")) {
189 2777 : opt = gf_opts_get_key("FontCache", "FontSerif");
190 2777 : if (opt) font_name = (char*)opt;
191 : }
192 2309 : else if (!stricmp(font_name, "SANS") || !stricmp(font_name, "sans-serif")) {
193 1126 : opt = gf_opts_get_key("FontCache", "FontSans");
194 1126 : if (opt) font_name = (char*)opt;
195 : }
196 1183 : else if (!stricmp(font_name, "TYPEWRITER") || !stricmp(font_name, "monospace")) {
197 0 : opt = gf_opts_get_key("FontCache", "FontFixed");
198 0 : if (opt) font_name = (char*)opt;
199 : }
200 :
201 8224 : while (font) {
202 6349 : if (fm->wait_font_load && font->not_loaded && !check_only && !stricmp(font->name, font_name)) {
203 : GF_Font *a_font = NULL;
204 0 : if (font->get_alias) a_font = font->get_alias(font->udta);
205 0 : if (!a_font || a_font->not_loaded)
206 : return font;
207 : }
208 6349 : if ((check_only || !font->not_loaded) && font->name && !stricmp(font->name, font_name)) {
209 : s32 fw;
210 : s32 w;
211 : u32 diff;
212 : Bool ft_has_smallcaps;
213 : Bool ft_has_weight;
214 :
215 4250 : if (check_only) return font;
216 :
217 4250 : ft_has_weight = (font->styles & GF_FONT_WEIGHT_MASK) ? 1 : 0;
218 4250 : if (font->styles == styles) {
219 : the_font = font;
220 : break;
221 : }
222 : /*check we have the same font variant*/
223 1039 : ft_has_smallcaps = (font->styles & GF_FONT_SMALLCAPS) ? 1 : 0;
224 1039 : if (ft_has_smallcaps != has_smallcaps) {
225 0 : font = font->next;
226 0 : continue;
227 : }
228 : /*check if we have an oblique/italic match*/
229 1039 : if (has_italic) {
230 62 : if (! (font->styles & (GF_FONT_OBLIQUE|GF_FONT_ITALIC))) {
231 49 : font = font->next;
232 49 : continue;
233 : }
234 : /*if italic force it*/
235 13 : if (font->styles & GF_FONT_ITALIC) best_font = font;
236 : /*if oblic use it if no italic found*/
237 0 : else if (!best_font) best_font = font;
238 : else {
239 0 : font = font->next;
240 0 : continue;
241 : }
242 : }
243 :
244 : /*compute min weight diff*/
245 990 : fw = font->styles>>10;
246 990 : w = styles>>10;
247 990 : diff = ABS(fw - w);
248 990 : if (ft_has_weight) {
249 15 : if (diff<weight_diff) {
250 : weight_diff = diff;
251 : best_font = font;
252 : }
253 : }
254 : /*no weight means "all"*/
255 : else {
256 975 : if ((font->styles & GF_FONT_STYLE_MASK) == (styles & GF_FONT_STYLE_MASK) ) {
257 : weight_diff = diff;
258 : best_font = font;
259 966 : font = font->next;
260 966 : continue;
261 : }
262 : }
263 : }
264 2123 : font = font->next;
265 : }
266 5086 : if (the_font) break;
267 1875 : if (fm->reader) {
268 1875 : e = fm->reader->set_font(fm->reader, font_name, styles);
269 1875 : if (!e) {
270 692 : GF_SAFEALLOC(the_font, GF_Font);
271 692 : if (!the_font) {
272 : return NULL;
273 : }
274 692 : fm->reader->get_font_info(fm->reader, &the_font->name, &the_font->em_size, &the_font->ascent, &the_font->descent, &the_font->underline, &the_font->line_spacing, &the_font->max_advance_h, &the_font->max_advance_v);
275 692 : the_font->styles = styles;
276 692 : if (!the_font->name) the_font->name = gf_strdup(font_name);
277 :
278 692 : if (fm->font) {
279 : font = fm->font;
280 90 : while (font->next) font = font->next;
281 51 : font->next = the_font;
282 : } else {
283 641 : fm->font = the_font;
284 : }
285 692 : the_font->ft_mgr = fm;
286 692 : return the_font;
287 : }
288 : }
289 1183 : if (best_font) {
290 : the_font = best_font;
291 : break;
292 : }
293 : }
294 :
295 : /*check for font alias*/
296 3554 : if (the_font && the_font->get_alias) {
297 0 : return the_font->get_alias(the_font->udta);
298 :
299 : }
300 :
301 3554 : if (!the_font) {
302 341 : if (check_only) return NULL;
303 191 : the_font = fm->default_font;
304 : }
305 : /*embeded font*/
306 3404 : if (fm->reader && the_font && !the_font->get_glyphs)
307 3402 : fm->reader->set_font(fm->reader, the_font->name, the_font->styles);
308 :
309 : return the_font;
310 : }
311 2320 : GF_Font *gf_font_manager_set_font(GF_FontManager *fm, char **alt_fonts, u32 nb_fonts, u32 styles)
312 : {
313 2961 : return gf_font_manager_set_font_ex(fm, alt_fonts, nb_fonts, styles, 0);
314 : }
315 :
316 272288 : static GF_Glyph *gf_font_get_glyph(GF_FontManager *fm, GF_Font *font, u32 name)
317 : {
318 272288 : GF_Glyph *glyph = font->glyph;
319 3411303 : while (glyph) {
320 3406252 : if (glyph->ID==name) return glyph;
321 3139015 : glyph = glyph->next;
322 : }
323 :
324 5051 : if (name==GF_CARET_CHAR) {
325 0 : GF_SAFEALLOC(glyph, GF_Glyph);
326 0 : if (!glyph) return NULL;
327 0 : glyph->height = font->ascent;
328 0 : glyph->horiz_advance = 0;
329 0 : glyph->width = 0;
330 0 : glyph->ID = GF_CARET_CHAR;
331 0 : glyph->path = gf_path_new();
332 0 : gf_path_add_move_to(glyph->path, 0, INT2FIX(font->descent));
333 0 : gf_path_add_line_to(glyph->path, 0, INT2FIX(font->ascent));
334 0 : gf_path_add_line_to(glyph->path, 1, INT2FIX(font->ascent));
335 0 : gf_path_add_line_to(glyph->path, 1, INT2FIX(font->descent));
336 0 : gf_path_close(glyph->path);
337 0 : glyph->utf_name=0;
338 5051 : } else if (name==(u32) '\n') {
339 0 : GF_SAFEALLOC(glyph, GF_Glyph);
340 0 : if (!glyph) return NULL;
341 0 : glyph->height = font->ascent;
342 0 : glyph->horiz_advance = 0;
343 0 : glyph->width = 0;
344 0 : glyph->ID = name;
345 0 : glyph->utf_name=name;
346 5051 : } else if (name==(u32) '\t') {
347 : return NULL;
348 : } else {
349 : /*load glyph*/
350 5047 : if (font->load_glyph) {
351 0 : glyph = font->load_glyph(font->udta, name);
352 : } else {
353 5047 : if (!fm->reader) return NULL;
354 : // fm->reader->set_font(fm->reader, font->name, font->styles);
355 5047 : glyph = fm->reader->load_glyph(fm->reader, name);
356 : }
357 : }
358 5047 : if (!glyph) return NULL;
359 :
360 5047 : if (!font->glyph) font->glyph = glyph;
361 : else {
362 : GF_Glyph *a_glyph = font->glyph;
363 60682 : while (a_glyph->next) a_glyph = a_glyph->next;
364 4769 : a_glyph->next = glyph;
365 : }
366 : /*space character - this may need adjustment for other empty glyphs*/
367 5047 : if (glyph->path && !glyph->path->n_points) {
368 209 : glyph->path->bbox.x = 0;
369 209 : glyph->path->bbox.width = INT2FIX(font->max_advance_h);
370 209 : glyph->path->bbox.y = INT2FIX(font->ascent);
371 209 : glyph->path->bbox.height = INT2FIX(font->ascent - font->descent);
372 : }
373 : return glyph;
374 : }
375 :
376 : GF_EXPORT
377 4127 : GF_TextSpan *gf_font_manager_create_span(GF_FontManager *fm, GF_Font *font, char *text, Fixed font_size, Bool needs_x_offset, Bool needs_y_offset, Bool needs_rotate, const char *xml_lang, Bool fliped_text, u32 styles, GF_Node *user)
378 : {
379 : GF_Err e;
380 : Bool is_rtl;
381 : u32 len, i;
382 : GF_TextSpan *span;
383 :
384 4127 : if (!strlen(text)) return NULL;
385 :
386 3832 : len = fm->id_buffer_size;
387 3832 : if (font->get_glyphs)
388 2 : e = font->get_glyphs(font->udta, text, fm->id_buffer, &len, xml_lang, &is_rtl);
389 : else
390 3830 : e = fm->reader->get_glyphs(fm->reader, text, fm->id_buffer, &len, xml_lang, &is_rtl);
391 :
392 3832 : if (e==GF_BUFFER_TOO_SMALL) {
393 169 : fm->id_buffer_size = len;
394 169 : fm->id_buffer = gf_realloc(fm->id_buffer, sizeof(u32) * len);
395 169 : if (!fm->id_buffer) return NULL;
396 :
397 169 : if (font->get_glyphs)
398 0 : e = font->get_glyphs(font->udta, text, fm->id_buffer, &len, xml_lang, &is_rtl);
399 : else
400 169 : e = fm->reader->get_glyphs(fm->reader, text, fm->id_buffer, &len, xml_lang, &is_rtl);
401 : }
402 3832 : if (e) return NULL;
403 :
404 3831 : GF_SAFEALLOC(span, GF_TextSpan);
405 3831 : if (!span) return NULL;
406 3831 : span->font = font;
407 3831 : span->font_size = font_size;
408 3831 : if (font->em_size)
409 3831 : span->font_scale = font_size / font->em_size;
410 3831 : span->x_scale = span->y_scale = FIX_ONE;
411 : // span->lang = xml_lang;
412 3831 : if (fliped_text) span->flags |= GF_TEXT_SPAN_FLIP;
413 3831 : if (styles & GF_FONT_UNDERLINED) span->flags |= GF_TEXT_SPAN_UNDERLINE;
414 3831 : if (styles & GF_FONT_STRIKEOUT) span->flags |= GF_TEXT_SPAN_STRIKEOUT;
415 3831 : span->nb_glyphs = len;
416 3831 : span->glyphs = gf_malloc(sizeof(void *)*len);
417 3831 : if (needs_x_offset) {
418 495 : span->dx = gf_malloc(sizeof(Fixed)*len);
419 495 : memset(span->dx, 0, sizeof(Fixed)*len);
420 : }
421 3831 : if (needs_y_offset) {
422 495 : span->dy = gf_malloc(sizeof(Fixed)*len);
423 495 : memset(span->dy, 0, sizeof(Fixed)*len);
424 : }
425 3831 : if (needs_rotate) {
426 0 : span->rot = gf_malloc(sizeof(Fixed)*len);
427 0 : memset(span->rot, 0, sizeof(Fixed)*len);
428 : }
429 :
430 272288 : for (i=0; i<len; i++) {
431 272288 : span->glyphs[i] = gf_font_get_glyph(fm, font, fm->id_buffer[i]);
432 : }
433 3831 : span->user = user;
434 3831 : if (span->font->spans)
435 2 : gf_list_add(font->spans, span);
436 3831 : if (is_rtl) span->flags |= GF_TEXT_SPAN_RIGHT_TO_LEFT;
437 : return span;
438 : }
439 :
440 :
441 : #ifndef GPAC_DISABLE_PLAYER
442 :
443 : typedef struct _span_internal
444 : {
445 : /*zoom when texture was computed*/
446 : Fixed last_zoom;
447 : /*texture handler for bitmap text*/
448 : GF_TextureHandler *txh;
449 : /*texture path (rectangle)*/
450 : GF_Path *path;
451 :
452 : #ifndef GPAC_DISABLE_3D
453 : /*span mesh (built out of the # glyphs)*/
454 : GF_Mesh *mesh;
455 : /*span texture mesh (rectangle)*/
456 : GF_Mesh *tx_mesh;
457 : /*span outline*/
458 : GF_Mesh *outline;
459 : #endif
460 : } GF_SpanExtensions;
461 :
462 : #endif
463 :
464 :
465 7685 : void gf_font_manager_delete_span(GF_FontManager *fm, GF_TextSpan *span)
466 : {
467 7685 : if (span->user && span->font->spans) gf_list_del_item(span->font->spans, span);
468 :
469 7685 : gf_free(span->glyphs);
470 7685 : if (span->dx) gf_free(span->dx);
471 7685 : if (span->dy) gf_free(span->dy);
472 7685 : if (span->rot) gf_free(span->rot);
473 :
474 : #ifndef GPAC_DISABLE_PLAYER
475 7685 : if (span->ext) {
476 302 : if (span->ext->path) gf_path_del(span->ext->path);
477 : #ifndef GPAC_DISABLE_3D
478 302 : if (span->ext->mesh) mesh_free(span->ext->mesh);
479 302 : if (span->ext->tx_mesh) mesh_free(span->ext->tx_mesh);
480 302 : if (span->ext->outline) mesh_free(span->ext->outline);
481 : #endif
482 302 : if (span->ext->txh) {
483 272 : gf_sc_texture_destroy(span->ext->txh);
484 272 : if (span->ext->txh->data) gf_free(span->ext->txh->data);
485 272 : gf_free(span->ext->txh);
486 : }
487 302 : gf_free(span->ext);
488 : }
489 : #endif
490 :
491 7685 : gf_free(span);
492 7685 : }
493 :
494 : GF_EXPORT
495 763 : void gf_font_manager_refresh_span_bounds(GF_TextSpan *span)
496 : {
497 : u32 i;
498 : Fixed descent, ascent, bline;
499 : Fixed min_x, min_y, max_y;
500 :
501 763 : if (!span->nb_glyphs) {
502 302 : span->bounds.width = span->bounds.height = 0;
503 302 : return;
504 : }
505 : descent = 0;
506 461 : if (span->font->descent<0) descent = -span->font_scale * span->font->descent;
507 461 : ascent = span->font->ascent * span->font_scale;
508 :
509 : /*if fliped text (SVG), the min_y is at the ascent side*/
510 461 : if (span->flags & GF_TEXT_SPAN_FLIP) {
511 : Fixed tmp = ascent;
512 : ascent = descent;
513 : descent = tmp;
514 : }
515 :
516 461 : bline = span->font->baseline * span->font_scale;
517 :
518 461 : min_x = span->dx ? FIX_MAX : span->off_x;
519 461 : min_y = span->dy ? FIX_MAX : span->off_y - descent;
520 461 : max_y = span->dy ? -FIX_MAX : span->off_y + ascent;
521 :
522 : /*adjust start_x for first glyph*/
523 461 : if (span->glyphs[0] && span->glyphs[0]->path) {
524 461 : min_x += gf_mulfix(span->glyphs[0]->path->bbox.x, span->font_scale);
525 : }
526 461 : span->bounds = gf_rect_center(0, 0);
527 :
528 70748 : for (i=0; i<span->nb_glyphs; i++) {
529 : Fixed g_width;
530 : GF_Rect rc;
531 :
532 : /*compute glyph size*/
533 70287 : if (!span->glyphs[i]) g_width = span->font->max_advance_h * span->font_scale;
534 : /*if last glyph of the span, increase by width only*/
535 : // else if (i+1==span->nb_glyphs) g_width = span->glyphs[i]->width * span->font_scale;
536 : /*otherwise increase by the horizontal advance*/
537 70287 : else g_width = span->glyphs[i]->horiz_advance * span->font_scale;
538 :
539 70287 : if (span->dy) {
540 66508 : if (span->dy[i] - descent < min_y) min_y = span->dy[i] - descent;
541 66508 : if (span->dy[i] + ascent > max_y) max_y = span->dy[i] + ascent;
542 : }
543 3779 : else if (span->glyphs[i]) {
544 3779 : Fixed size = span->glyphs[i]->height * span->font_scale;
545 3779 : if (size > max_y-min_y) max_y = size + min_y;
546 : }
547 :
548 70287 : if (span->dx) {
549 66508 : rc.x = span->dx[i];
550 : } else {
551 3779 : rc.x = min_x;
552 3779 : if (span->flags & GF_TEXT_SPAN_HORIZONTAL)
553 3779 : min_x += g_width;
554 : }
555 70287 : rc.y = span->dy ? span->dy[i] + ascent : max_y;
556 70287 : rc.width = g_width;
557 70287 : rc.height = max_y - min_y;
558 :
559 70287 : if (span->rot) {
560 : GF_Matrix2D tr;
561 0 : gf_mx2d_init(tr);
562 0 : gf_mx2d_add_rotation(&tr, rc.x, rc.y - ascent - bline, span->rot[i]);
563 0 : gf_mx2d_apply_rect(&tr, &rc);
564 : }
565 70287 : gf_rect_union(&span->bounds, &rc);
566 : }
567 : }
568 :
569 :
570 :
571 :
572 338 : GF_Path *gf_font_span_create_path(GF_TextSpan *span)
573 : {
574 : u32 i;
575 : GF_Matrix2D mat;
576 : Fixed dx, dy;
577 338 : GF_Path *path = gf_path_new();
578 :
579 : gf_mx2d_init(mat);
580 338 : mat.m[0] = gf_mulfix(span->font_scale, span->x_scale);
581 338 : mat.m[4] = gf_mulfix(span->font_scale, span->y_scale);
582 338 : if (span->flags & GF_TEXT_SPAN_FLIP) gf_mx2d_add_scale(&mat, FIX_ONE, -FIX_ONE);
583 :
584 338 : dx = gf_divfix(span->off_x, mat.m[0]);
585 338 : dy = gf_divfix(span->off_y, mat.m[4]);
586 :
587 5927 : for (i=0; i<span->nb_glyphs; i++) {
588 5927 : if (!span->glyphs[i]) {
589 0 : if (span->flags & GF_TEXT_SPAN_HORIZONTAL) {
590 0 : dx += INT2FIX(span->font->max_advance_h);
591 : } else {
592 0 : dy -= INT2FIX(span->font->max_advance_v);
593 : }
594 : } else {
595 5927 : if (span->dx) dx = gf_divfix(span->dx[i], mat.m[0]);
596 5927 : if (span->dy) dy = gf_divfix(span->dy[i], mat.m[4]);
597 :
598 :
599 5927 : if (span->glyphs[i]->utf_name != ' ') {
600 : GF_Matrix2D mx;
601 5284 : gf_mx2d_init(mx);
602 5284 : if (span->rot) {
603 0 : gf_mx2d_add_rotation(&mx, 0, 0, -span->rot[i]);
604 : }
605 5284 : if (span->glyphs[i]->ID==GF_CARET_CHAR) {
606 0 : gf_mx2d_add_scale(&mx, mat.m[0], FIX_ONE);
607 : }
608 5284 : gf_mx2d_add_translation(&mx, dx, dy);
609 5284 : gf_path_add_subpath(path, span->glyphs[i]->path, &mx);
610 : }
611 :
612 5927 : if (span->flags & GF_TEXT_SPAN_HORIZONTAL) {
613 5867 : dx += INT2FIX(span->glyphs[i]->horiz_advance);
614 : } else {
615 60 : dy -= INT2FIX(span->glyphs[i]->vert_advance);
616 : }
617 : }
618 : }
619 :
620 258149 : for (i=0; i<path->n_points; i++) {
621 258149 : gf_mx2d_apply_point(&mat, &path->points[i]);
622 : }
623 338 : return path;
624 : }
625 :
626 : #ifndef GPAC_DISABLE_PLAYER
627 :
628 3334 : static void span_alloc_extensions(GF_TextSpan *span)
629 : {
630 3334 : if (span->ext) return;
631 302 : GF_SAFEALLOC(span->ext, GF_SpanExtensions);
632 : }
633 :
634 : #ifndef GPAC_DISABLE_3D
635 252 : static void span_build_mesh(GF_TextSpan *span)
636 : {
637 252 : span_alloc_extensions(span);
638 252 : span->ext->tx_mesh = new_mesh();
639 252 : mesh_set_vertex(span->ext->tx_mesh, span->bounds.x, span->bounds.y-span->bounds.height, 0, 0, 0, FIX_ONE, 0, FIX_ONE);
640 252 : mesh_set_vertex(span->ext->tx_mesh, span->bounds.x+span->bounds.width, span->bounds.y-span->bounds.height, 0, 0, 0, FIX_ONE, FIX_ONE, FIX_ONE);
641 252 : mesh_set_vertex(span->ext->tx_mesh, span->bounds.x+span->bounds.width, span->bounds.y, 0, 0, 0, FIX_ONE, FIX_ONE, 0);
642 252 : mesh_set_vertex(span->ext->tx_mesh, span->bounds.x, span->bounds.y, 0, 0, 0, FIX_ONE, 0, 0);
643 252 : mesh_set_triangle(span->ext->tx_mesh, 0, 1, 2);
644 252 : mesh_set_triangle(span->ext->tx_mesh, 0, 2, 3);
645 252 : span->ext->tx_mesh->flags |= MESH_IS_2D;
646 252 : span->ext->tx_mesh->mesh_type = MESH_TRIANGLES;
647 252 : mesh_update_bounds(span->ext->tx_mesh);
648 252 : }
649 : #endif
650 :
651 : /*don't build too large textures*/
652 : #define MAX_TX_SIZE 512
653 : /*and don't build too small ones otherwise result is as crap as non-textured*/
654 : #define MIN_TX_SIZE 32
655 :
656 2814 : static Bool span_setup_texture(GF_Compositor *compositor, GF_TextSpan *span, Bool for_3d, GF_TraverseState *tr_state)
657 : {
658 : GF_Path *span_path;
659 : GF_Rect bounds;
660 : Fixed cx, cy, sx, sy, max, min;
661 : u32 tw, th;
662 : GF_Matrix2D mx;
663 : GF_EVGStencil *stencil, *brush;
664 : GF_EVGSurface *surface;
665 : u32 width, height;
666 : Fixed scale;
667 2814 : span_alloc_extensions(span);
668 :
669 : /*something failed*/
670 2814 : if (span->ext->txh && !span->ext->txh->data) return 0;
671 :
672 2814 : if (span->ext->txh && span->ext->txh->data) {
673 2542 : if (span->ext->last_zoom == compositor->zoom) {
674 :
675 : #ifndef GPAC_DISABLE_3D
676 2542 : if (for_3d && !span->ext->tx_mesh) span_build_mesh(span);
677 : #endif
678 : return 1;
679 : }
680 : }
681 272 : span->ext->last_zoom = compositor->zoom;
682 :
683 272 : bounds = span->bounds;
684 :
685 : /*check not too big, but also not really small (meter metrics)*/
686 : max = INT2FIX(MAX_TX_SIZE);
687 : min = INT2FIX(MIN_TX_SIZE);
688 272 : scale = compositor->zoom;
689 272 : if (!tr_state->pixel_metrics) {
690 150 : scale = gf_mulfix(scale, tr_state->min_hsize);
691 : }
692 272 : if ((gf_mulfix(scale, bounds.width)>max) || (gf_mulfix(scale, bounds.height)>max)) {
693 145 : scale = MIN(gf_divfix(max, bounds.width), gf_divfix(max, bounds.height));
694 : }
695 127 : else if ((gf_mulfix(scale, bounds.width)<min) || (gf_mulfix(scale, bounds.height)<min)) {
696 121 : scale = MAX(gf_divfix(min, bounds.width), gf_divfix(min, bounds.height));
697 : }
698 272 : if (scale<FIX_ONE) scale = FIX_ONE;
699 :
700 : /*get closest pow2 sizes*/
701 272 : tw = FIX2INT( gf_ceil(gf_mulfix(scale, bounds.width)) );
702 : width = MIN_TX_SIZE;
703 1490 : while (width <tw) {
704 948 : if (width >=MAX_TX_SIZE) break;
705 946 : width *=2;
706 : }
707 272 : th = FIX2INT( gf_ceil(gf_mulfix(scale, bounds.height)) );
708 : height = MIN_TX_SIZE;
709 757 : while (height<th) {
710 213 : height*=2;
711 213 : if (height>=MAX_TX_SIZE) break;
712 : }
713 : /*and get scaling*/
714 272 : sx = gf_divfix( INT2FIX(width), bounds.width);
715 272 : sy = gf_divfix( INT2FIX(height), bounds.height);
716 :
717 272 : if (span->ext->txh && (width == span->ext->txh->width) && (height==span->ext->txh->height)) return 1;
718 :
719 272 : if (span->ext->path) gf_path_del(span->ext->path);
720 272 : span->ext->path = NULL;
721 : #ifndef GPAC_DISABLE_3D
722 272 : if (span->ext->tx_mesh) mesh_free(span->ext->tx_mesh);
723 272 : span->ext->tx_mesh = NULL;
724 : #endif
725 :
726 272 : if (span->ext->txh) {
727 0 : gf_sc_texture_destroy(span->ext->txh);
728 0 : if (span->ext->txh->data) gf_free(span->ext->txh->data);
729 0 : gf_free(span->ext->txh);
730 : }
731 272 : GF_SAFEALLOC(span->ext->txh, GF_TextureHandler);
732 272 : if (!span->ext->txh) return 0;
733 272 : gf_sc_texture_setup(span->ext->txh, compositor, NULL);
734 272 : gf_sc_texture_allocate(span->ext->txh);
735 272 : stencil = gf_sc_texture_get_stencil(span->ext->txh);
736 272 : if (!stencil) stencil = gf_evg_stencil_new(GF_STENCIL_TEXTURE);
737 :
738 : /*FIXME - make it work with alphagrey...*/
739 272 : span->ext->txh->width = width;
740 272 : span->ext->txh->height = height;
741 272 : span->ext->txh->stride = 4*width;
742 272 : span->ext->txh->pixelformat = GF_PIXEL_RGBA;
743 272 : span->ext->txh->transparent = 1;
744 272 : span->ext->txh->flags |= GF_SR_TEXTURE_NO_GL_FLIP;
745 :
746 272 : surface = gf_evg_surface_new(1);
747 272 : if (!surface) {
748 0 : gf_sc_texture_release(span->ext->txh);
749 0 : return 0;
750 : }
751 272 : span->ext->txh->data = (char *) gf_malloc(sizeof(char)*span->ext->txh->stride*span->ext->txh->height);
752 272 : memset(span->ext->txh->data, 0, sizeof(char)*span->ext->txh->stride*span->ext->txh->height);
753 :
754 272 : gf_evg_stencil_set_texture(stencil, span->ext->txh->data, span->ext->txh->width, span->ext->txh->height, span->ext->txh->stride, span->ext->txh->pixelformat);
755 272 : gf_evg_surface_attach_to_texture(surface, stencil);
756 :
757 272 : brush = gf_evg_stencil_new(GF_STENCIL_SOLID);
758 272 : gf_evg_stencil_set_brush_color(brush, for_3d ? 0xFFFFFFFF : tr_state->ctx->aspect.fill_color);
759 :
760 272 : cx = bounds.x + bounds.width/2;
761 272 : cy = bounds.y - bounds.height/2;
762 :
763 272 : gf_mx2d_init(mx);
764 272 : gf_mx2d_add_translation(&mx, -cx, -cy);
765 272 : gf_mx2d_add_scale(&mx, sx, sy);
766 : // gf_mx2d_add_scale(&mx, 99*FIX_ONE/100, 99*FIX_ONE/100);
767 :
768 272 : gf_evg_surface_set_matrix(surface, &mx);
769 272 : gf_evg_surface_set_raster_level(surface, GF_RASTER_HIGH_QUALITY);
770 272 : span_path = gf_font_span_create_path(span);
771 272 : gf_evg_surface_set_path(surface, span_path);
772 :
773 272 : gf_evg_surface_fill(surface, brush);
774 272 : gf_evg_stencil_delete(brush);
775 272 : gf_evg_surface_delete(surface);
776 272 : gf_path_del(span_path);
777 :
778 272 : if (span->font->baseline) {
779 0 : Fixed dy = gf_mulfix(span->font->baseline, span->font_scale);
780 0 : bounds.y += dy;
781 0 : span->bounds.y += dy;
782 : }
783 272 : span->ext->path = gf_path_new();
784 272 : gf_path_add_move_to(span->ext->path, bounds.x, bounds.y-bounds.height);
785 272 : gf_path_add_line_to(span->ext->path, bounds.x+bounds.width, bounds.y-bounds.height);
786 272 : gf_path_add_line_to(span->ext->path, bounds.x+bounds.width, bounds.y);
787 272 : gf_path_add_line_to(span->ext->path, bounds.x, bounds.y);
788 272 : gf_path_close(span->ext->path);
789 :
790 272 : gf_sc_texture_set_stencil(span->ext->txh, stencil);
791 272 : gf_sc_texture_set_data(span->ext->txh);
792 :
793 : #ifndef GPAC_DISABLE_3D
794 272 : gf_sc_texture_set_blend_mode(span->ext->txh, TX_BLEND);
795 272 : if (for_3d) span_build_mesh(span);
796 : #endif
797 : return 1;
798 : }
799 :
800 :
801 : #ifndef GPAC_DISABLE_3D
802 :
803 40 : static void span_fill_3d(GF_TextSpan *span, GF_TraverseState *tr_state)
804 : {
805 40 : span_alloc_extensions(span);
806 40 : if (!span->ext->mesh) {
807 20 : GF_Path *path = gf_font_span_create_path(span);
808 20 : span->ext->mesh = new_mesh();
809 20 : mesh_from_path(span->ext->mesh, path);
810 20 : gf_path_del(path);
811 : }
812 :
813 40 : visual_3d_mesh_paint(tr_state, span->ext->mesh);
814 40 : }
815 :
816 228 : static void span_strike_3d(GF_TextSpan *span, GF_TraverseState *tr_state, DrawAspect2D *asp, Bool vect_outline)
817 : {
818 228 : span_alloc_extensions(span);
819 228 : if (!span->ext->outline) {
820 10 : GF_Path *path = gf_font_span_create_path(span);
821 10 : span->ext->outline = new_mesh();
822 : #ifdef GPAC_HAS_GLU
823 10 : if (vect_outline) {
824 10 : GF_Path *outline = gf_path_get_outline(path, asp->pen_props);
825 10 : gf_mesh_tesselate_path(span->ext->outline, outline, asp->line_texture ? 2 : 1);
826 10 : gf_path_del(outline);
827 : } else {
828 0 : mesh_get_outline(span->ext->outline, path);
829 : }
830 : #else
831 : vect_outline = 0;
832 : mesh_get_outline(span->ext->outline, path);
833 : #endif
834 10 : gf_path_del(path);
835 : }
836 228 : if (vect_outline) {
837 228 : visual_3d_mesh_paint(tr_state, span->ext->outline);
838 : } else {
839 0 : visual_3d_mesh_strike(tr_state, span->ext->outline, asp->pen_props.width, asp->line_scale, asp->pen_props.dash);
840 : }
841 228 : }
842 :
843 :
844 2537 : void gf_font_spans_draw_3d(GF_List *spans, GF_TraverseState *tr_state, DrawAspect2D *asp, u32 text_hl, Bool force_texturing)
845 : {
846 : u32 i;
847 : SFColorRGBA hl_color;
848 : GF_TextSpan *span;
849 : Bool fill_2d, vect_outline, can_texture_text;
850 2537 : GF_Compositor *compositor = (GF_Compositor*)tr_state->visual->compositor;
851 :
852 2537 : vect_outline = !compositor->linegl;
853 :
854 2537 : visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0);
855 :
856 : fill_2d = 0;
857 2537 : if (!asp) {
858 39 : if (!visual_3d_setup_appearance(tr_state)) return;
859 : } else {
860 2498 : fill_2d = (asp->fill_color) ? 1 : 0;
861 : }
862 : memset(&hl_color, 0, sizeof(SFColorRGBA));
863 :
864 2537 : if (text_hl && (fill_2d || !asp) ) {
865 : /*reverse video: highlighting uses the text color, and text color is inverted (except alpha channel)
866 : the ideal impl would be to use the background color for the text, but since the text may be
867 : displayed over anything non uniform this would require clipping the highlight rect with the text
868 : which is too onerous (and not supported anyway) */
869 0 : if (text_hl == 0x00FFFFFF) {
870 0 : if (!asp) {
871 : #ifndef GPAC_DISABLE_VRML
872 0 : if (tr_state->appear) {
873 : SFColor c, rc;
874 0 : c = ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->diffuseColor;
875 0 : hl_color = gf_sg_sfcolor_to_rgba(c);
876 0 : hl_color.alpha = ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->transparency;
877 : /*invert diffuse color and resetup*/
878 0 : rc.red = FIX_ONE - c.red;
879 0 : rc.green = FIX_ONE - c.green;
880 0 : rc.blue = FIX_ONE - c.blue;
881 0 : ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->diffuseColor = rc;
882 0 : visual_3d_setup_appearance(tr_state);
883 0 : ((M_Material *) ((M_Appearance *) tr_state->appear)->material)->diffuseColor = c;
884 : } else
885 : #endif /*GPAC_DISABLE_VRML*/
886 : {
887 : hl_color.red = hl_color.green = hl_color.blue = 0;
888 0 : hl_color.alpha = FIX_ONE;
889 : }
890 : } else {
891 0 : hl_color.alpha = FIX_ONE;
892 0 : hl_color.red = INT2FIX( GF_COL_R(asp->fill_color) ) / 255;
893 0 : hl_color.green = INT2FIX( GF_COL_G(asp->fill_color) ) / 255;
894 0 : hl_color.blue = INT2FIX( GF_COL_B(asp->fill_color) ) / 255;
895 0 : if (GF_COL_A(asp->fill_color) ) {
896 : u8 r = GF_COL_R(asp->fill_color);
897 : u8 g = GF_COL_G(asp->fill_color);
898 : u8 b = GF_COL_B(asp->fill_color);
899 0 : asp->fill_color = GF_COL_ARGB(GF_COL_A(asp->fill_color), 255-r, 255-g, 255-b);
900 : }
901 : }
902 : } else {
903 0 : hl_color.red = INT2FIX(GF_COL_R(text_hl)) / 255;
904 0 : hl_color.green = INT2FIX(GF_COL_G(text_hl)) / 255;
905 0 : hl_color.blue = INT2FIX(GF_COL_B(text_hl)) / 255;
906 0 : hl_color.alpha = INT2FIX(GF_COL_A(text_hl)) / 255;
907 : }
908 0 : if (asp && !asp->fill_color) text_hl = 0;
909 : }
910 :
911 : /*setup texture*/
912 2537 : visual_3d_setup_texture(tr_state, FIX_ONE);
913 : can_texture_text = 0;
914 2537 : if (fill_2d || !asp) {
915 : /*check if we can use text texturing*/
916 2401 : if (force_texturing || (compositor->textxt != GF_TEXTURE_TEXT_NEVER) ) {
917 2381 : if (fill_2d && asp->pen_props.width) {
918 : can_texture_text = 0;
919 : } else {
920 2381 : can_texture_text = tr_state->mesh_num_textures ? 0 : 1;
921 : }
922 : }
923 : }
924 :
925 2537 : visual_3d_enable_antialias(tr_state->visual, compositor->aa);
926 2537 : if (fill_2d || !asp || tr_state->mesh_num_textures) {
927 2401 : if (fill_2d && asp) visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color);
928 :
929 2401 : i = tr_state->text_split_idx ? tr_state->text_split_idx-1 : 0;
930 7616 : while ((span = (GF_TextSpan *)gf_list_enum(spans, &i))) {
931 2814 : if (text_hl) {
932 0 : visual_3d_fill_rect(tr_state->visual, span->bounds, hl_color);
933 :
934 0 : if (fill_2d) {
935 0 : if (asp)
936 0 : visual_3d_set_material_2d_argb(tr_state->visual, asp->fill_color);
937 : } else
938 0 : visual_3d_setup_appearance(tr_state);
939 : }
940 :
941 2814 : if (can_texture_text && span_setup_texture(tr_state->visual->compositor, span, 1, tr_state)) {
942 2774 : tr_state->mesh_num_textures = gf_sc_texture_enable(span->ext->txh, NULL);
943 2774 : if (tr_state->mesh_num_textures) {
944 2774 : Bool has_mat_2d = tr_state->visual->compositor->visual->has_material_2d;
945 2774 : visual_3d_mesh_paint(tr_state, span->ext->tx_mesh);
946 2774 : gf_sc_texture_disable(span->ext->txh);
947 2774 : tr_state->mesh_num_textures = 0;
948 2774 : tr_state->visual->has_material_2d = has_mat_2d;
949 : }
950 : } else {
951 40 : span_fill_3d(span, tr_state);
952 : }
953 :
954 2814 : if (tr_state->text_split_idx) break;
955 : }
956 2401 : tr_state->visual->has_material_2d = GF_FALSE;
957 :
958 : /*reset texturing in case of line texture*/
959 2401 : if (!asp) visual_3d_disable_texture(tr_state);
960 : }
961 2537 : visual_3d_set_state(tr_state->visual, V3D_STATE_BLEND, 0);
962 :
963 2537 : if (asp && asp->pen_props.width) {
964 136 : if (!asp->line_scale) {
965 136 : drawable_compute_line_scale(tr_state, asp);
966 : }
967 136 : asp->pen_props.width = gf_divfix(asp->pen_props.width, asp->line_scale);
968 136 : visual_3d_set_2d_strike(tr_state, asp);
969 :
970 136 : if (tr_state->text_split_idx) {
971 0 : span = (GF_TextSpan *)gf_list_get(spans, tr_state->text_split_idx-1);
972 0 : span_strike_3d(span, tr_state, asp, vect_outline);
973 : } else {
974 136 : i=0;
975 500 : while ((span = (GF_TextSpan *)gf_list_enum(spans, &i))) {
976 228 : span_strike_3d(span, tr_state, asp, vect_outline);
977 : }
978 : }
979 : }
980 : }
981 :
982 :
983 : #endif
984 :
985 40565 : static void gf_font_span_draw_2d(GF_TraverseState *tr_state, GF_TextSpan *span, DrawableContext *ctx, GF_Rect *bounds)
986 : {
987 : u32 flags, i;
988 : Bool flip_text;
989 : Fixed dx, dy, sx, sy, lscale, bline;
990 40565 : Bool needs_texture = (ctx->aspect.fill_texture || ctx->aspect.line_texture) ? 1 : 0;
991 : GF_Matrix2D mx, tx;
992 :
993 40565 : gf_mx2d_copy(mx, ctx->transform);
994 :
995 40565 : flags = ctx->flags;
996 40565 : dx = span->off_x;
997 40565 : dy = span->off_y;
998 40565 : sx = gf_mulfix(span->font_scale, span->x_scale);
999 40565 : sy = gf_mulfix(span->font_scale, span->y_scale);
1000 :
1001 40565 : flip_text = (ctx->flags & CTX_FLIPED_COORDS) ? tr_state->visual->center_coords : !tr_state->visual->center_coords;
1002 :
1003 40565 : bline = span->font->baseline*span->font_scale;
1004 40565 : lscale = ctx->aspect.line_scale;
1005 40565 : ctx->aspect.line_scale = gf_divfix(ctx->aspect.line_scale, span->font_scale);
1006 :
1007 634694 : for (i=0; i<span->nb_glyphs; i++) {
1008 594129 : if (!span->glyphs[i]) {
1009 8 : if (span->flags & GF_TEXT_SPAN_HORIZONTAL) {
1010 8 : dx += sx * span->font->max_advance_h;
1011 : } else {
1012 0 : dy -= sy * span->font->max_advance_v;
1013 : }
1014 8 : continue;
1015 : }
1016 594121 : if (span->glyphs[i]->ID==GF_CARET_CHAR) {
1017 0 : if (tr_state->visual->compositor->show_caret) {
1018 0 : ctx->transform.m[0] = FIX_ONE;
1019 : } else {
1020 0 : continue;
1021 : }
1022 : } else {
1023 594121 : ctx->transform.m[0] = sx;
1024 : }
1025 594121 : ctx->transform.m[4] = flip_text ? -sy : sy;
1026 594121 : ctx->transform.m[1] = ctx->transform.m[3] = 0;
1027 594121 : ctx->transform.m[2] = span->dx ? span->dx[i] : dx;
1028 594121 : ctx->transform.m[5] = span->dy ? span->dy[i] : dy;
1029 594121 : ctx->transform.m[5] += bline;
1030 594121 : if (span->rot) {
1031 0 : gf_mx2d_add_rotation(&ctx->transform, ctx->transform.m[2], ctx->transform.m[5], span->rot[i]);
1032 : }
1033 :
1034 594121 : gf_mx2d_add_matrix(&ctx->transform, &mx);
1035 :
1036 594121 : if (needs_texture) {
1037 5134 : tx.m[0] = sx;
1038 5134 : tx.m[4] = sy;
1039 5134 : tx.m[1] = tx.m[3] = 0;
1040 5134 : tx.m[2] = span->dx ? span->dx[i] : dx;
1041 5134 : tx.m[5] = span->dy ? span->dy[i] : dy;
1042 5134 : tx.m[5] += bline;
1043 5134 : if (span->rot) {
1044 0 : gf_mx2d_add_rotation(&tx, tx.m[2], tx.m[5], span->rot[i]);
1045 : }
1046 5134 : gf_mx2d_inverse(&tx);
1047 :
1048 5134 : visual_2d_texture_path_extended(tr_state->visual, span->glyphs[i]->path, ctx->aspect.fill_texture, ctx, bounds ? bounds : &span->bounds, &tx, tr_state);
1049 5134 : visual_2d_draw_path_extended(tr_state->visual, span->glyphs[i]->path, ctx, NULL, NULL, tr_state, bounds ? bounds : &span->bounds, &tx, GF_FALSE);
1050 : } else {
1051 588987 : visual_2d_draw_path(tr_state->visual, span->glyphs[i]->path, ctx, NULL, NULL, tr_state);
1052 : }
1053 594121 : ctx->flags = flags;
1054 :
1055 594121 : if (span->flags & GF_TEXT_SPAN_HORIZONTAL) {
1056 593205 : dx += sx * span->glyphs[i]->horiz_advance;
1057 : } else {
1058 916 : dy -= sy * span->glyphs[i]->vert_advance;
1059 : }
1060 : }
1061 : gf_mx2d_copy(ctx->transform, mx);
1062 40565 : ctx->aspect.line_scale = lscale;
1063 40565 : }
1064 :
1065 10 : void gf_font_underline_span(GF_TraverseState *tr_state, GF_TextSpan *span, DrawableContext *ctx, Bool is_strikeout)
1066 : {
1067 : GF_Matrix2D mx, m;
1068 : u32 col;
1069 : Fixed sx, width, diff;
1070 10 : if (span->dx || span->dy) return;
1071 :
1072 10 : gf_mx2d_copy(mx, ctx->transform);
1073 10 : sx = gf_mulfix(span->font_scale, span->x_scale);
1074 :
1075 10 : if (span->flags & GF_TEXT_SPAN_FLIP)
1076 0 : diff = sx * (span->font->descent - span->font->underline);
1077 : else
1078 10 : diff = sx * (- span->font->ascent + span->font->underline);
1079 :
1080 10 : if (is_strikeout)
1081 0 : diff = 2*diff/3;
1082 :
1083 10 : gf_mx2d_init(m);
1084 10 : gf_mx2d_add_scale(&m, span->bounds.width, FIX_ONE);
1085 10 : gf_mx2d_add_translation(&m, span->bounds.x + span->bounds.width / 2, span->bounds.y+diff);
1086 10 : gf_mx2d_pre_multiply(&ctx->transform, &m);
1087 :
1088 10 : col = ctx->aspect.fill_color;
1089 10 : width = ctx->aspect.pen_props.width;
1090 10 : ctx->aspect.pen_props.width = 0;
1091 10 : ctx->flags &= ~CTX_PATH_FILLED;
1092 10 : if (span->anchor) ctx->aspect.fill_color = 0xFF0000FF;
1093 :
1094 10 : visual_2d_draw_path(tr_state->visual, span->font->ft_mgr->line_path, ctx, NULL, NULL, tr_state);
1095 10 : ctx->aspect.fill_color = col;
1096 :
1097 : gf_mx2d_copy(ctx->transform, mx);
1098 10 : ctx->aspect.pen_props.width = width;
1099 : }
1100 :
1101 : #if 0
1102 : static u32 col_reverse_video(u32 col)
1103 : {
1104 : u32 a, r, g, b;
1105 : a = GF_COL_A(col);
1106 : r = GF_COL_R(col);
1107 : g = GF_COL_G(col);
1108 : b = GF_COL_B(col);
1109 : return GF_COL_ARGB(a, 255-r, 255-g, 255-b);
1110 : }
1111 : #endif
1112 :
1113 216 : static GF_Rect font_get_sel_rect(GF_TraverseState *tr_state)
1114 : {
1115 : GF_Vec s, e;
1116 : GF_Rect rc;
1117 216 : GF_Compositor *compositor = tr_state->visual->compositor;
1118 :
1119 216 : e.x = compositor->end_sel.x;
1120 216 : e.y = compositor->end_sel.y;
1121 216 : e.z = 0;
1122 216 : gf_mx_apply_vec(&compositor->hit_local_to_world, &e);
1123 :
1124 216 : s.x = compositor->start_sel.x;
1125 216 : s.y = compositor->start_sel.y;
1126 216 : s.z = 0;
1127 216 : gf_mx_apply_vec(&compositor->hit_local_to_world, &s);
1128 :
1129 216 : if (s.x > e.x) {
1130 : rc.x = e.x;
1131 0 : rc.width = s.x - e.x;
1132 : } else {
1133 : rc.x = s.x;
1134 216 : rc.width = e.x - s.x;
1135 : }
1136 216 : if (s.y > e.y) {
1137 : rc.y = s.y;
1138 0 : rc.height = s.y - e.y;
1139 : } else {
1140 : rc.y = e.y;
1141 216 : rc.height = e.y - s.y;
1142 : }
1143 216 : if (!rc.height) rc.height = FIX_ONE;
1144 216 : return rc;
1145 : }
1146 :
1147 641 : static void gf_font_spans_select(GF_TextSpan *span, GF_TraverseState *tr_state, DrawableContext *ctx, Bool has_more_spans, Bool first_span, GF_Rect *rc)
1148 : {
1149 : GF_Matrix2D mx;
1150 : u32 flags, i, color;
1151 : Bool has_selection = 0;
1152 : Fixed dx, dy, sx, sy, width, ascent, descent;
1153 641 : GF_Compositor *compositor = tr_state->visual->compositor;
1154 :
1155 641 : if (first_span) rc->width = 0;
1156 :
1157 890 : if (!(span->flags & GF_TEXT_SPAN_SELECTED) ) return;
1158 :
1159 :
1160 392 : dx = gf_mulfix(span->off_x, span->x_scale);
1161 392 : dy = gf_mulfix(span->off_y, span->y_scale);
1162 392 : sx = gf_mulfix(span->font_scale, span->x_scale);
1163 392 : sy = gf_mulfix(span->font_scale, span->y_scale);
1164 392 : ascent = span->font->ascent*sy;
1165 392 : descent = span->font->descent*sy;
1166 :
1167 : width = 0;
1168 : flags = 0;
1169 392 : if (ctx) {
1170 392 : width = ctx->aspect.pen_props.width;
1171 392 : ctx->aspect.pen_props.width = 0;
1172 392 : flags = ctx->flags;
1173 392 : gf_mx2d_copy(mx, ctx->transform);
1174 : }
1175 :
1176 : /*compute sel rectangle*/
1177 392 : if (!rc->width) {
1178 216 : *rc = font_get_sel_rect(tr_state);
1179 : }
1180 :
1181 392 : color = compositor->text_sel_color;
1182 :
1183 3024 : for (i=0; i<span->nb_glyphs; i++) {
1184 : GF_Rect g_rc;
1185 : Bool end_of_line = 0;
1186 : Fixed advance;
1187 4324 : if (!span->glyphs[i]) continue;
1188 3024 : advance = sx * span->glyphs[i]->horiz_advance;
1189 3024 : if (span->dx) dx = span->dx[i];
1190 3024 : if (span->dy) dy = span->dy[i];
1191 3024 : if (dx + advance/2 < rc->x) {
1192 1300 : dx += advance;
1193 1300 : continue;
1194 : }
1195 :
1196 1724 : g_rc.height = ascent-descent;
1197 :
1198 1724 : if (dx + advance/2 > rc->x + rc->width) {
1199 : u32 j;
1200 : Bool has_several_lines = 0;
1201 392 : if (!span->dy) break;
1202 0 : if (!has_selection) continue;
1203 :
1204 0 : for (j=i+1; j<span->nb_glyphs; j++) {
1205 0 : if (span->dy[j] > span->dy[i]) {
1206 0 : if (span->flags & GF_TEXT_SPAN_FLIP)
1207 0 : g_rc.y = span->dy[j]-descent;
1208 : else
1209 0 : g_rc.y = span->dy[j]+ascent;
1210 0 : g_rc.x = span->bounds.x;
1211 0 : g_rc.width = span->bounds.width;
1212 0 : if (gf_rect_overlaps(g_rc, *rc)) {
1213 : has_several_lines =1;
1214 : break;
1215 : }
1216 : }
1217 : }
1218 0 : if (has_more_spans && dy<rc->y) has_several_lines =1;
1219 :
1220 0 : if (!has_several_lines) break;
1221 : end_of_line = 1;
1222 : /*move selection rect to include start of line - FIXME this depends on ltr/rtl*/
1223 0 : rc->width = rc->width+rc->x - span->bounds.x;
1224 0 : rc->x = span->bounds.x;
1225 : }
1226 :
1227 1332 : if (span->flags & GF_TEXT_SPAN_FLIP)
1228 0 : g_rc.y = dy-descent;
1229 : else
1230 1332 : g_rc.y = dy+ascent;
1231 1332 : g_rc.x = dx;
1232 1332 : g_rc.width = advance;
1233 :
1234 :
1235 1332 : if (!end_of_line && !gf_rect_overlaps(g_rc, *rc))
1236 0 : continue;
1237 : /*
1238 : if (dy < rc.y-rc.height) {
1239 : if (!span->dx) break;
1240 : continue;
1241 : }
1242 : */
1243 : has_selection = 1;
1244 1332 : if (ctx) {
1245 1332 : g_rc.width+=2;
1246 1332 : if (span->rot) {
1247 0 : gf_mx2d_init(ctx->transform);
1248 0 : gf_mx2d_add_rotation(&ctx->transform, g_rc.x , g_rc.y, span->rot[i]);
1249 0 : gf_mx2d_add_matrix(&ctx->transform, &mx);
1250 : }
1251 1332 : visual_2d_fill_rect(tr_state->visual, ctx, &g_rc, color, 0, tr_state);
1252 1332 : ctx->flags = flags;
1253 1332 : compositor->store_text_state = GF_SC_TSEL_ACTIVE;
1254 : } else {
1255 0 : if (!compositor->sel_buffer_alloc || compositor->sel_buffer_len == compositor->sel_buffer_alloc) {
1256 0 : if (!compositor->sel_buffer_alloc) compositor->sel_buffer_alloc ++;
1257 0 : compositor->sel_buffer_alloc = 2*compositor->sel_buffer_alloc;
1258 0 : compositor->sel_buffer = gf_realloc(compositor->sel_buffer, sizeof(u16)*compositor->sel_buffer_alloc);
1259 : }
1260 0 : compositor->sel_buffer[compositor->sel_buffer_len] = (u16) span->glyphs[i]->utf_name;
1261 0 : compositor->sel_buffer_len++;
1262 : }
1263 :
1264 1332 : dx += advance;
1265 : }
1266 392 : if (ctx) {
1267 392 : gf_mx2d_copy(ctx->transform, mx);
1268 392 : ctx->aspect.pen_props.width = width;
1269 : }
1270 392 : if (has_selection && has_more_spans && dy<rc->y) {
1271 140 : rc->width = rc->width+rc->x - span->bounds.x;
1272 140 : rc->x = span->bounds.x;
1273 : }
1274 : }
1275 :
1276 0 : void gf_font_spans_get_selection(GF_Node *node, GF_List *spans, GF_TraverseState *tr_state)
1277 : {
1278 : u32 i, count;
1279 : GF_Rect rc;
1280 0 : count = gf_list_count(spans);
1281 0 : for (i=0; i<count; i++) {
1282 0 : GF_TextSpan *span = (GF_TextSpan *)gf_list_get(spans, i);
1283 0 : gf_font_spans_select(span, tr_state, NULL, (i+1<count) ? 1 : 0, (i==0) ? 1 : 0, &rc);
1284 : }
1285 0 : }
1286 :
1287 :
1288 34503 : void gf_font_spans_draw_2d(GF_List *spans, GF_TraverseState *tr_state, u32 hl_color, Bool force_texture_text, GF_Rect *bounds)
1289 : {
1290 : Bool use_texture_text, is_rv;
1291 34503 : GF_Compositor *compositor = tr_state->visual->compositor;
1292 : u32 i, count;
1293 : GF_TextSpan *span;
1294 34503 : DrawableContext *ctx = tr_state->ctx;
1295 : GF_Rect rc;
1296 :
1297 : use_texture_text = 0;
1298 34503 : if (force_texture_text || (compositor->textxt==GF_TEXTURE_TEXT_ALWAYS) ) {
1299 20 : use_texture_text = !ctx->aspect.fill_texture && !ctx->aspect.pen_props.width;
1300 : }
1301 :
1302 : is_rv = 0;
1303 34503 : if (hl_color) {
1304 : /*reverse video: highlighting uses the text color, and text color is inverted (except alpha channel)
1305 : the ideal impl would be to use the background color for the text, but since the text may be
1306 : displayed over anything non uniform this would require clipping the highlight rect with the text
1307 : which is too onerous (and not supported anyway) */
1308 0 : if (hl_color==0x00FFFFFF) {
1309 : u32 a, r, g, b;
1310 0 : hl_color = tr_state->ctx->aspect.fill_color;
1311 :
1312 0 : a = GF_COL_A(tr_state->ctx->aspect.fill_color);
1313 0 : if (a) {
1314 0 : r = GF_COL_R(tr_state->ctx->aspect.fill_color);
1315 0 : g = GF_COL_G(tr_state->ctx->aspect.fill_color);
1316 : b = GF_COL_B(tr_state->ctx->aspect.fill_color);
1317 0 : tr_state->ctx->aspect.fill_color = GF_COL_ARGB(a, 255-r, 255-g, 255-b);
1318 : }
1319 : is_rv = 1;
1320 : }
1321 0 : if (GF_COL_A(hl_color) == 0) hl_color = 0;
1322 : }
1323 :
1324 : memset(&rc, 0, sizeof(GF_Rect));
1325 34503 : count = gf_list_count(spans);
1326 34503 : i=ctx->sub_path_index ? ctx->sub_path_index-1 : 0;
1327 37340 : for(; i<count; i++) {
1328 40605 : span = (GF_TextSpan *)gf_list_get(spans, i);
1329 40605 : if (compositor->text_selection)
1330 641 : gf_font_spans_select(span, tr_state, ctx, (i+1<count) ? 1 : 0, (i==0)? GF_TRUE : GF_FALSE, &rc);
1331 39964 : else if (hl_color)
1332 0 : visual_2d_fill_rect(tr_state->visual, ctx, &span->bounds, hl_color, 0, tr_state);
1333 :
1334 40605 : if (use_texture_text && span_setup_texture(compositor, span, 0, tr_state)) {
1335 40 : visual_2d_texture_path_text(tr_state->visual, ctx, span->ext->path, &span->bounds, span->ext->txh, tr_state);
1336 : } else {
1337 40565 : gf_font_span_draw_2d(tr_state, span, ctx, bounds);
1338 : }
1339 40605 : if (span->anchor || (span->flags & GF_TEXT_SPAN_UNDERLINE) ) gf_font_underline_span(tr_state, span, ctx, GF_FALSE);
1340 40605 : if (span->flags & GF_TEXT_SPAN_STRIKEOUT) gf_font_underline_span(tr_state, span, ctx, GF_TRUE);
1341 40605 : if (ctx->sub_path_index) break;
1342 : }
1343 34503 : if (is_rv) tr_state->ctx->aspect.fill_color = hl_color;
1344 34503 : }
1345 :
1346 7642 : void gf_font_spans_pick(GF_Node *node, GF_List *spans, GF_TraverseState *tr_state, GF_Rect *node_bounds, Bool use_dom_events, Drawable *drawable)
1347 : {
1348 : u32 i, count, j, glyph_idx;
1349 : Fixed dx, dy;
1350 : #ifndef GPAC_DISABLE_3D
1351 : GF_Matrix inv_mx;
1352 : #endif
1353 : GF_TextSpan *span;
1354 : DrawAspect2D asp;
1355 : GF_Matrix2D inv_2d;
1356 : Fixed x, y;
1357 7642 : GF_Compositor *compositor = tr_state->visual->compositor;
1358 :
1359 7642 : if (compositor->text_selection) {
1360 7574 : if (compositor->text_selection != tr_state->text_parent) return;
1361 88 : if (compositor->store_text_state==GF_SC_TSEL_FROZEN) return;
1362 : }
1363 :
1364 : /*TODO: pick the real glyph and not just the bounds of the text span*/
1365 7638 : count = gf_list_count(spans);
1366 :
1367 : #ifndef GPAC_DISABLE_3D
1368 7638 : if (tr_state->visual->type_3d) {
1369 : GF_Ray r;
1370 : SFVec3f local_pt;
1371 :
1372 221 : r = tr_state->ray;
1373 221 : gf_mx_copy(inv_mx, tr_state->model_matrix);
1374 221 : gf_mx_inverse(&inv_mx);
1375 221 : gf_mx_apply_ray(&inv_mx, &r);
1376 :
1377 221 : if (!compositor_get_2d_plane_intersection(&r, &local_pt)) return;
1378 :
1379 221 : x = local_pt.x;
1380 221 : y = local_pt.y;
1381 : } else
1382 : #endif
1383 : {
1384 7417 : gf_mx2d_copy(inv_2d, tr_state->transform);
1385 7417 : gf_mx2d_inverse(&inv_2d);
1386 7417 : x = tr_state->ray.orig.x;
1387 7417 : y = tr_state->ray.orig.y;
1388 7417 : gf_mx2d_apply_coords(&inv_2d, &x, &y);
1389 : }
1390 :
1391 7638 : if (use_dom_events) {
1392 : memset(&asp, 0, sizeof(DrawAspect2D));
1393 148 : drawable_get_aspect_2d_svg(node, &asp, tr_state);
1394 : }
1395 :
1396 : span = NULL;
1397 10924 : for (i=0; i<count; i++) {
1398 : Fixed loc_x, loc_y;
1399 11080 : span = (GF_TextSpan*)gf_list_get(spans, i);
1400 :
1401 11080 : if ((x>=span->bounds.x)
1402 7451 : && (y<=span->bounds.y)
1403 4135 : && (x<=span->bounds.x+span->bounds.width)
1404 1544 : && (y>=span->bounds.y-span->bounds.height)) {
1405 : } else {
1406 10855 : continue;
1407 : }
1408 :
1409 : glyph_idx = 0;
1410 225 : dx = span->off_x;
1411 225 : dy = span->off_y;
1412 1765 : for (j=0; j<span->nb_glyphs; j++) {
1413 : GF_Rect rc;
1414 1696 : if (!span->glyphs[j]) {
1415 0 : if (span->flags & GF_TEXT_SPAN_HORIZONTAL) {
1416 0 : dx += span->font_scale * span->font->max_advance_h;
1417 : } else {
1418 0 : dy -= span->font_scale * span->font->max_advance_v;
1419 : }
1420 0 : continue;
1421 : }
1422 1696 : else if (span->glyphs[j]->ID==GF_CARET_CHAR) {
1423 0 : continue;
1424 : }
1425 1696 : glyph_idx++;
1426 :
1427 1696 : loc_x = x - (span->dx ? span->dx[j] : dx);
1428 1696 : loc_y = y - (span->dy ? span->dy[j] : dy);
1429 1696 : loc_x = gf_divfix(loc_x, span->font_scale);
1430 1696 : loc_y = gf_divfix(loc_y, span->font_scale) + span->font->baseline;
1431 1696 : if (span->flags & GF_TEXT_SPAN_FLIP) loc_y = - loc_y;
1432 :
1433 1696 : gf_path_get_bounds(span->glyphs[j]->path, &rc);
1434 1696 : rc.height = INT2FIX(span->font->ascent + span->font->descent);
1435 :
1436 1696 : if (use_dom_events && !compositor->sel_buffer) {
1437 278 : if (svg_drawable_is_over(drawable, loc_x, loc_y, &asp, tr_state, &rc))
1438 : goto picked;
1439 : } else {
1440 1418 : if ( (loc_x >= rc.x) && (loc_y <= rc.y) && (loc_x <= rc.x + rc.width) && (loc_y >= rc.y - rc.height) ) {
1441 : goto picked;
1442 : }
1443 : // if (gf_path_point_over(span->glyphs[j]->path, loc_x, loc_y)) goto picked;
1444 : }
1445 :
1446 1540 : if (span->flags & GF_TEXT_SPAN_HORIZONTAL) {
1447 1540 : dx += span->font_scale * span->glyphs[j]->horiz_advance;
1448 : } else {
1449 0 : dy -= span->font_scale * span->glyphs[j]->vert_advance;
1450 : }
1451 : }
1452 : }
1453 : return;
1454 :
1455 : picked:
1456 156 : compositor->hit_local_point.x = x;
1457 156 : compositor->hit_local_point.y = y;
1458 156 : compositor->hit_local_point.z = 0;
1459 :
1460 : #ifndef GPAC_DISABLE_3D
1461 156 : if (tr_state->visual->type_3d) {
1462 0 : gf_mx_copy(compositor->hit_world_to_local, tr_state->model_matrix);
1463 0 : gf_mx_copy(compositor->hit_local_to_world, inv_mx);
1464 : } else
1465 : #endif
1466 : {
1467 156 : gf_mx_from_mx2d(&compositor->hit_world_to_local, &tr_state->transform);
1468 156 : gf_mx_from_mx2d(&compositor->hit_local_to_world, &inv_2d);
1469 : }
1470 :
1471 156 : compositor->hit_node = node;
1472 156 : if (span->anchor) compositor->hit_node = span->anchor;
1473 :
1474 156 : compositor->end_sel.x = compositor->hit_world_point.x;
1475 156 : compositor->end_sel.y = compositor->hit_world_point.y;
1476 156 : if (compositor->text_selection) {
1477 40 : gf_sc_next_frame_state(compositor, GF_SC_DRAW_FRAME);
1478 40 : if (tr_state->visual->offscreen) gf_node_dirty_set(tr_state->visual->offscreen, GF_SG_CHILD_DIRTY, 0);
1479 40 : span->flags |= GF_TEXT_SPAN_SELECTED;
1480 : } else {
1481 116 : compositor->start_sel = compositor->end_sel;
1482 : }
1483 156 : compositor->picked_span_idx = i;
1484 156 : compositor->picked_glyph_idx = glyph_idx;
1485 :
1486 156 : compositor->hit_text = tr_state->text_parent;
1487 156 : compositor->hit_use_dom_events = use_dom_events;
1488 156 : compositor->hit_normal.x = compositor->hit_normal.y = 0;
1489 156 : compositor->hit_normal.z = FIX_ONE;
1490 156 : compositor->hit_texcoords.x = gf_divfix(x, node_bounds->width) + FIX_ONE/2;
1491 156 : compositor->hit_texcoords.y = gf_divfix(y, node_bounds->height) + FIX_ONE/2;
1492 :
1493 : #ifndef GPAC_DISABLE_VRML
1494 156 : if (compositor_is_composite_texture(tr_state->appear)) {
1495 0 : compositor->hit_appear = tr_state->appear;
1496 : } else
1497 : #endif /*GPAC_DISABLE_VRML*/
1498 : {
1499 156 : compositor->hit_appear = NULL;
1500 : }
1501 :
1502 156 : gf_list_reset(tr_state->visual->compositor->sensors);
1503 156 : count = gf_list_count(tr_state->vrml_sensors);
1504 284 : for (i=0; i<count; i++) {
1505 128 : gf_list_add(tr_state->visual->compositor->sensors, gf_list_get(tr_state->vrml_sensors, i));
1506 : }
1507 : }
1508 :
1509 : #endif // GPAC_DISABLE_PLAYER
|