LCOV - code coverage report
Current view: top level - compositor - font_engine.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 637 781 81.6 %
Date: 2021-04-29 23:48:07 Functions: 25 26 96.2 %

          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

Generated by: LCOV version 1.13